mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
all: nested sum types (#6913)
This commit is contained in:
parent
c7ca1e7e13
commit
96539e43b5
@ -365,7 +365,7 @@ pub:
|
|||||||
is_arg bool // fn args should not be autofreed
|
is_arg bool // fn args should not be autofreed
|
||||||
pub mut:
|
pub mut:
|
||||||
typ table.Type
|
typ table.Type
|
||||||
sum_type_cast table.Type
|
sum_type_casts []table.Type // nested sum types require nested smart casting, for that a list of types is needed
|
||||||
pos token.Position
|
pos token.Position
|
||||||
is_used bool
|
is_used bool
|
||||||
is_changed bool // to detect mutable vars that are never changed
|
is_changed bool // to detect mutable vars that are never changed
|
||||||
@ -379,7 +379,7 @@ pub:
|
|||||||
name string
|
name string
|
||||||
pos token.Position
|
pos token.Position
|
||||||
typ table.Type
|
typ table.Type
|
||||||
sum_type_cast table.Type
|
sum_type_casts []table.Type // nested sum types require nested smart casting, for that a list of types is needed
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GlobalField {
|
pub struct GlobalField {
|
||||||
|
@ -1801,7 +1801,7 @@ pub fn (mut c Checker) selector_expr(mut selector_expr ast.SelectorExpr) table.T
|
|||||||
if !prevent_sum_type_unwrapping_once {
|
if !prevent_sum_type_unwrapping_once {
|
||||||
scope := c.file.scope.innermost(selector_expr.pos.pos)
|
scope := c.file.scope.innermost(selector_expr.pos.pos)
|
||||||
if scope_field := scope.find_struct_field(utyp, field_name) {
|
if scope_field := scope.find_struct_field(utyp, field_name) {
|
||||||
return scope_field.sum_type_cast
|
return scope_field.sum_type_casts.last()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3234,9 +3234,9 @@ pub fn (mut c Checker) ident(mut ident ast.Ident) table.Type {
|
|||||||
c.error('undefined variable `$ident.name` (used before declaration)',
|
c.error('undefined variable `$ident.name` (used before declaration)',
|
||||||
ident.pos)
|
ident.pos)
|
||||||
}
|
}
|
||||||
is_sum_type_cast := obj.sum_type_cast != 0 && !c.prevent_sum_type_unwrapping_once
|
is_sum_type_cast := obj.sum_type_casts.len != 0 && !c.prevent_sum_type_unwrapping_once
|
||||||
c.prevent_sum_type_unwrapping_once = false
|
c.prevent_sum_type_unwrapping_once = false
|
||||||
mut typ := if is_sum_type_cast { obj.sum_type_cast } else { obj.typ }
|
mut typ := if is_sum_type_cast { obj.sum_type_casts.last() } else { obj.typ }
|
||||||
if typ == 0 {
|
if typ == 0 {
|
||||||
if mut obj.expr is ast.Ident {
|
if mut obj.expr is ast.Ident {
|
||||||
if obj.expr.kind == .unresolved {
|
if obj.expr.kind == .unresolved {
|
||||||
@ -3561,37 +3561,47 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, type_sym table.TypeSymbol
|
|||||||
mut scope := c.file.scope.innermost(branch.pos.pos)
|
mut scope := c.file.scope.innermost(branch.pos.pos)
|
||||||
match mut node.cond {
|
match mut node.cond {
|
||||||
ast.SelectorExpr {
|
ast.SelectorExpr {
|
||||||
|
mut is_mut := false
|
||||||
|
mut sum_type_casts := []table.Type{}
|
||||||
expr_sym := c.table.get_type_symbol(node.cond.expr_type)
|
expr_sym := c.table.get_type_symbol(node.cond.expr_type)
|
||||||
field := c.table.struct_find_field(expr_sym, node.cond.field_name) or {
|
if field := c.table.struct_find_field(expr_sym, node.cond.field_name) {
|
||||||
table.Field{}
|
is_mut = field.is_mut
|
||||||
|
}
|
||||||
|
if field := scope.find_struct_field(node.cond.expr_type, node.cond.field_name) {
|
||||||
|
sum_type_casts << field.sum_type_casts
|
||||||
}
|
}
|
||||||
is_mut := field.is_mut
|
|
||||||
is_root_mut := scope.is_selector_root_mutable(c.table, node.cond)
|
is_root_mut := scope.is_selector_root_mutable(c.table, node.cond)
|
||||||
// smartcast either if the value is immutable or if the mut argument is explicitly given
|
// smartcast either if the value is immutable or if the mut argument is explicitly given
|
||||||
if (!is_root_mut && !is_mut) || node.is_mut {
|
if (!is_root_mut && !is_mut) || node.is_mut {
|
||||||
|
sum_type_casts << expr_type
|
||||||
scope.register_struct_field(ast.ScopeStructField{
|
scope.register_struct_field(ast.ScopeStructField{
|
||||||
struct_type: node.cond.expr_type
|
struct_type: node.cond.expr_type
|
||||||
name: node.cond.field_name
|
name: node.cond.field_name
|
||||||
typ: node.cond_type
|
typ: node.cond_type
|
||||||
sum_type_cast: expr_type
|
sum_type_casts: sum_type_casts
|
||||||
pos: node.cond.pos
|
pos: node.cond.pos
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast.Ident {
|
ast.Ident {
|
||||||
mut is_mut := false
|
mut is_mut := false
|
||||||
|
mut sum_type_casts := []table.Type{}
|
||||||
|
mut is_already_casted := false
|
||||||
if v := scope.find_var(node.cond.name) {
|
if v := scope.find_var(node.cond.name) {
|
||||||
is_mut = v.is_mut
|
is_mut = v.is_mut
|
||||||
|
sum_type_casts << v.sum_type_casts
|
||||||
|
is_already_casted = v.pos.pos == node.cond.pos.pos
|
||||||
}
|
}
|
||||||
// smartcast either if the value is immutable or if the mut argument is explicitly given
|
// smartcast either if the value is immutable or if the mut argument is explicitly given
|
||||||
if !is_mut || node.is_mut {
|
if (!is_mut || node.is_mut) && !is_already_casted {
|
||||||
|
sum_type_casts << expr_type
|
||||||
scope.register(node.var_name, ast.Var{
|
scope.register(node.var_name, ast.Var{
|
||||||
name: node.var_name
|
name: node.var_name
|
||||||
typ: node.cond_type
|
typ: node.cond_type
|
||||||
pos: node.cond.pos
|
pos: node.cond.pos
|
||||||
is_used: true
|
is_used: true
|
||||||
is_mut: node.is_mut
|
is_mut: node.is_mut
|
||||||
sum_type_cast: expr_type
|
sum_type_casts: sum_type_casts
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3823,38 +3833,46 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
|
|||||||
mut is_mut := false
|
mut is_mut := false
|
||||||
mut scope := c.file.scope.innermost(branch.body_pos.pos)
|
mut scope := c.file.scope.innermost(branch.body_pos.pos)
|
||||||
if mut infix.left is ast.Ident {
|
if mut infix.left is ast.Ident {
|
||||||
|
mut sum_type_casts := []table.Type{}
|
||||||
if v := scope.find_var(infix.left.name) {
|
if v := scope.find_var(infix.left.name) {
|
||||||
is_mut = v.is_mut
|
is_mut = v.is_mut
|
||||||
|
sum_type_casts << v.sum_type_casts
|
||||||
}
|
}
|
||||||
// smartcast either if the value is immutable or if the mut argument is explicitly given
|
// smartcast either if the value is immutable or if the mut argument is explicitly given
|
||||||
if (!is_mut || branch.is_mut_name) &&
|
if (!is_mut || branch.is_mut_name) &&
|
||||||
left_sym.kind == .union_sum_type {
|
left_sym.kind == .union_sum_type {
|
||||||
|
sum_type_casts << right_expr.typ
|
||||||
scope.register(branch.left_as_name, ast.Var{
|
scope.register(branch.left_as_name, ast.Var{
|
||||||
name: branch.left_as_name
|
name: branch.left_as_name
|
||||||
typ: infix.left_type
|
typ: infix.left_type
|
||||||
sum_type_cast: right_expr.typ
|
sum_type_casts: sum_type_casts
|
||||||
pos: infix.left.pos
|
pos: infix.left.pos
|
||||||
is_used: true
|
is_used: true
|
||||||
is_mut: is_mut
|
is_mut: is_mut
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else if mut infix.left is ast.SelectorExpr {
|
} else if mut infix.left is ast.SelectorExpr {
|
||||||
|
mut sum_type_casts := []table.Type{}
|
||||||
expr_sym := c.table.get_type_symbol(infix.left.expr_type)
|
expr_sym := c.table.get_type_symbol(infix.left.expr_type)
|
||||||
field := c.table.struct_find_field(expr_sym, infix.left.field_name) or {
|
if field := c.table.struct_find_field(expr_sym, infix.left.field_name) {
|
||||||
table.Field{}
|
|
||||||
}
|
|
||||||
is_mut = field.is_mut
|
is_mut = field.is_mut
|
||||||
|
}
|
||||||
|
if field := scope.find_struct_field(infix.left.expr_type,
|
||||||
|
infix.left.field_name) {
|
||||||
|
sum_type_casts << field.sum_type_casts
|
||||||
|
}
|
||||||
is_root_mut := scope.is_selector_root_mutable(c.table,
|
is_root_mut := scope.is_selector_root_mutable(c.table,
|
||||||
infix.left)
|
infix.left)
|
||||||
// smartcast either if the value is immutable or if the mut argument is explicitly given
|
// smartcast either if the value is immutable or if the mut argument is explicitly given
|
||||||
if ((!is_root_mut && !is_mut) ||
|
if ((!is_root_mut && !is_mut) ||
|
||||||
branch.is_mut_name) &&
|
branch.is_mut_name) &&
|
||||||
left_sym.kind == .union_sum_type {
|
left_sym.kind == .union_sum_type {
|
||||||
|
sum_type_casts << right_expr.typ
|
||||||
scope.register_struct_field(ast.ScopeStructField{
|
scope.register_struct_field(ast.ScopeStructField{
|
||||||
struct_type: infix.left.expr_type
|
struct_type: infix.left.expr_type
|
||||||
name: infix.left.field_name
|
name: infix.left.field_name
|
||||||
typ: infix.left_type
|
typ: infix.left_type
|
||||||
sum_type_cast: right_expr.typ
|
sum_type_casts: sum_type_casts
|
||||||
pos: infix.left.pos
|
pos: infix.left.pos
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
6
vlib/v/checker/tests/sum_type_assign_non_variant_err.out
Normal file
6
vlib/v/checker/tests/sum_type_assign_non_variant_err.out
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
vlib/v/checker/tests/sum_type_assign_non_variant_err.vv:11:6: error: cannot assign to `w`: expected `Stmt`, not `IfExpr`
|
||||||
|
9 | fn main() {
|
||||||
|
10 | mut w := Stmt{}
|
||||||
|
11 | w = IfExpr{}
|
||||||
|
| ~~~~~~~~
|
||||||
|
12 | }
|
12
vlib/v/checker/tests/sum_type_assign_non_variant_err.vv
Normal file
12
vlib/v/checker/tests/sum_type_assign_non_variant_err.vv
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
__type Expr = IfExpr | CallExpr | MatchExpr
|
||||||
|
struct MatchExpr {}
|
||||||
|
struct IfExpr {}
|
||||||
|
struct CallExpr {}
|
||||||
|
|
||||||
|
__type Stmt = Expr | AnotherThing
|
||||||
|
struct AnotherThing {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
mut w := Stmt{}
|
||||||
|
w = IfExpr{}
|
||||||
|
}
|
@ -1301,7 +1301,7 @@ fn (mut g Gen) union_expr_with_cast(expr ast.Expr, got_type table.Type, expected
|
|||||||
scope := g.file.scope.innermost(expr.position().pos)
|
scope := g.file.scope.innermost(expr.position().pos)
|
||||||
if expr is ast.Ident {
|
if expr is ast.Ident {
|
||||||
if v := scope.find_var(expr.name) {
|
if v := scope.find_var(expr.name) {
|
||||||
if v.sum_type_cast != 0 {
|
if v.sum_type_casts.len > 0 {
|
||||||
is_already_sum_type = true
|
is_already_sum_type = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2567,12 +2567,17 @@ fn (mut g Gen) expr(node ast.Expr) {
|
|||||||
scope := g.file.scope.innermost(node.pos.pos)
|
scope := g.file.scope.innermost(node.pos.pos)
|
||||||
if field := scope.find_struct_field(node.expr_type, node.field_name) {
|
if field := scope.find_struct_field(node.expr_type, node.field_name) {
|
||||||
// union sum type deref
|
// union sum type deref
|
||||||
|
for i, typ in field.sum_type_casts {
|
||||||
g.write('(*')
|
g.write('(*')
|
||||||
cast_sym := g.table.get_type_symbol(field.sum_type_cast)
|
cast_sym := g.table.get_type_symbol(typ)
|
||||||
|
if i != 0 {
|
||||||
|
sum_type_deref_field += ').'
|
||||||
|
}
|
||||||
if cast_sym.info is table.Aggregate as sym_info {
|
if cast_sym.info is table.Aggregate as sym_info {
|
||||||
sum_type_deref_field = '_${sym_info.types[g.aggregate_type_idx]}'
|
sum_type_deref_field += '_${sym_info.types[g.aggregate_type_idx]}'
|
||||||
} else {
|
} else {
|
||||||
sum_type_deref_field = '_$field.sum_type_cast'
|
sum_type_deref_field += '_$typ'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3403,13 +3408,22 @@ fn (mut g Gen) ident(node ast.Ident) {
|
|||||||
}
|
}
|
||||||
scope := g.file.scope.innermost(node.pos.pos)
|
scope := g.file.scope.innermost(node.pos.pos)
|
||||||
if v := scope.find_var(node.name) {
|
if v := scope.find_var(node.name) {
|
||||||
if v.sum_type_cast != 0 {
|
if v.sum_type_casts.len > 0 {
|
||||||
if !prevent_sum_type_unwrapping_once {
|
if !prevent_sum_type_unwrapping_once {
|
||||||
sym := g.table.get_type_symbol(v.sum_type_cast)
|
for _ in v.sum_type_casts {
|
||||||
if sym.info is table.Aggregate as sym_info {
|
g.write('(*')
|
||||||
g.write('(*${name}._${sym_info.types[g.aggregate_type_idx]})')
|
}
|
||||||
|
for i, typ in v.sum_type_casts {
|
||||||
|
cast_sym := g.table.get_type_symbol(typ)
|
||||||
|
if i == 0 {
|
||||||
|
g.write(name)
|
||||||
|
}
|
||||||
|
if cast_sym.info is table.Aggregate as sym_info {
|
||||||
|
g.write('._${sym_info.types[g.aggregate_type_idx]}')
|
||||||
} else {
|
} else {
|
||||||
g.write('(*${name}._$v.sum_type_cast)')
|
g.write('._$typ')
|
||||||
|
}
|
||||||
|
g.write(')')
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -4645,7 +4659,7 @@ fn (mut g Gen) write_types(types []table.TypeSymbol) {
|
|||||||
}
|
}
|
||||||
g.type_definitions.writeln('typedef struct {')
|
g.type_definitions.writeln('typedef struct {')
|
||||||
g.type_definitions.writeln(' union {')
|
g.type_definitions.writeln(' union {')
|
||||||
for variant in g.table.get_union_sum_type_variants(it) {
|
for variant in it.variants {
|
||||||
g.type_definitions.writeln(' ${g.typ(variant.to_ptr())} _$variant.idx();')
|
g.type_definitions.writeln(' ${g.typ(variant.to_ptr())} _$variant.idx();')
|
||||||
}
|
}
|
||||||
g.type_definitions.writeln(' };')
|
g.type_definitions.writeln(' };')
|
||||||
|
@ -853,18 +853,6 @@ pub:
|
|||||||
variants []Type
|
variants []Type
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (table &Table) get_union_sum_type_variants(sum_type UnionSumType) []Type {
|
|
||||||
mut variants := []Type{}
|
|
||||||
for variant in sum_type.variants {
|
|
||||||
sym := table.get_type_symbol(variant)
|
|
||||||
if sym.info is UnionSumType as sym_info {
|
|
||||||
variants << table.get_union_sum_type_variants(sym_info)
|
|
||||||
}
|
|
||||||
variants << variant
|
|
||||||
}
|
|
||||||
return variants
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (table &Table) type_to_str(t Type) string {
|
pub fn (table &Table) type_to_str(t Type) string {
|
||||||
sym := table.get_type_symbol(t)
|
sym := table.get_type_symbol(t)
|
||||||
mut res := sym.source_name
|
mut res := sym.source_name
|
||||||
|
@ -738,9 +738,6 @@ pub fn (table &Table) sumtype_has_variant(parent Type, variant Type) bool {
|
|||||||
if v.idx() == variant.idx() {
|
if v.idx() == variant.idx() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if table.sumtype_has_variant(v, variant) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -106,15 +106,49 @@ fn test_converting_down() {
|
|||||||
assert res[1].name == 'three'
|
assert res[1].name == 'three'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct NodeWrapper {
|
||||||
|
node Node
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_nested_sumtype_selector() {
|
||||||
|
c := NodeWrapper{Node(Expr(IfExpr{pos: 1}))}
|
||||||
|
if c.node is Expr {
|
||||||
|
if c.node is IfExpr {
|
||||||
|
assert c.node.pos == 1
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_nested_sumtype_match_selector() {
|
||||||
|
c := NodeWrapper{Node(Expr(IfExpr{pos: 1}))}
|
||||||
|
match union c.node {
|
||||||
|
Expr {
|
||||||
|
match union c.node {
|
||||||
|
IfExpr {
|
||||||
|
assert c.node.pos == 1
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn test_nested_sumtype() {
|
fn test_nested_sumtype() {
|
||||||
mut a := Node{}
|
|
||||||
mut b := Node{}
|
|
||||||
a = StructDecl{pos: 1}
|
|
||||||
b = IfExpr{pos: 1}
|
|
||||||
c := Node(Expr(IfExpr{pos:1}))
|
c := Node(Expr(IfExpr{pos:1}))
|
||||||
if c is Expr {
|
if c is Expr {
|
||||||
if c is IfExpr {
|
if c is IfExpr {
|
||||||
assert true
|
assert c.pos == 1
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assert false
|
assert false
|
||||||
@ -125,6 +159,25 @@ fn test_nested_sumtype() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_nested_sumtype_match() {
|
||||||
|
c := Node(Expr(IfExpr{pos: 1}))
|
||||||
|
match union c {
|
||||||
|
Expr {
|
||||||
|
match union c {
|
||||||
|
IfExpr {
|
||||||
|
assert c.pos == 1
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
__type Abc = int | string
|
__type Abc = int | string
|
||||||
|
|
||||||
fn test_string_cast_to_sumtype() {
|
fn test_string_cast_to_sumtype() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user