all: nested sum types (#6913)
							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
 | ||||||
|  | @ -375,11 +375,11 @@ pub mut: | ||||||
| // struct fields change type in scopes
 | // struct fields change type in scopes
 | ||||||
| pub struct ScopeStructField { | pub struct ScopeStructField { | ||||||
| pub: | pub: | ||||||
| 	struct_type   table.Type // type of struct
 | 	struct_type    table.Type // type of struct
 | ||||||
| 	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 | ||||||
|  | 								} | ||||||
|  | 								if field := scope.find_struct_field(infix.left.expr_type, | ||||||
|  | 									infix.left.field_name) { | ||||||
|  | 									sum_type_casts << field.sum_type_casts | ||||||
| 								} | 								} | ||||||
| 								is_mut = field.is_mut |  | ||||||
| 								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 | ||||||
| 									}) | 									}) | ||||||
| 								} | 								} | ||||||
|  |  | ||||||
|  | @ -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 | } | ||||||
|  | @ -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
 | ||||||
| 							g.write('(*') | 							for i, typ in field.sum_type_casts { | ||||||
| 							cast_sym := g.table.get_type_symbol(field.sum_type_cast) | 								g.write('(*') | ||||||
| 							if cast_sym.info is table.Aggregate as sym_info { | 								cast_sym := g.table.get_type_symbol(typ) | ||||||
| 								sum_type_deref_field = '_${sym_info.types[g.aggregate_type_idx]}' | 								if i != 0 { | ||||||
| 							} else { | 									sum_type_deref_field += ').' | ||||||
| 								sum_type_deref_field = '_$field.sum_type_cast' | 								} | ||||||
|  | 								if cast_sym.info is table.Aggregate as sym_info { | ||||||
|  | 									sum_type_deref_field += '_${sym_info.types[g.aggregate_type_idx]}' | ||||||
|  | 								} else { | ||||||
|  | 									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]})') | 					} | ||||||
| 					} else { | 					for i, typ in v.sum_type_casts { | ||||||
| 						g.write('(*${name}._$v.sum_type_cast)') | 						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 { | ||||||
|  | 							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,15 @@ fn test_converting_down() { | ||||||
| 	assert res[1].name == 'three' | 	assert res[1].name == 'three' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn test_nested_sumtype() { | struct NodeWrapper { | ||||||
| 	mut a := Node{} | 	node Node | ||||||
| 	mut b := Node{} | } | ||||||
| 	a = StructDecl{pos: 1} | 
 | ||||||
| 	b = IfExpr{pos: 1} | fn test_nested_sumtype_selector() { | ||||||
| 	c := Node(Expr(IfExpr{pos:1})) | 	c := NodeWrapper{Node(Expr(IfExpr{pos: 1}))} | ||||||
| 	if c is Expr { | 	if c.node is Expr { | ||||||
| 		if c is IfExpr { | 		if c.node is IfExpr { | ||||||
| 			assert true | 			assert c.node.pos == 1 | ||||||
| 		} | 		} | ||||||
| 		else { | 		else { | ||||||
| 			assert false | 			assert false | ||||||
|  | @ -125,6 +125,59 @@ fn test_nested_sumtype() { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 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() { | ||||||
|  | 	c := Node(Expr(IfExpr{pos:1})) | ||||||
|  | 	if c is Expr { | ||||||
|  | 		if c is IfExpr { | ||||||
|  | 			assert c.pos == 1 | ||||||
|  | 		} | ||||||
|  | 		else { | ||||||
|  | 			assert false | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	else { | ||||||
|  | 		assert false | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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…
	
		Reference in New Issue