checker: force interface init (#10910)
							parent
							
								
									1eac351f25
								
							
						
					
					
						commit
						6fa8e4269e
					
				|  | @ -57,6 +57,7 @@ fn new_cancel_context(parent Context) &CancelContext { | ||||||
| 		context: parent | 		context: parent | ||||||
| 		mutex: sync.new_mutex() | 		mutex: sync.new_mutex() | ||||||
| 		done: chan int{cap: 2} | 		done: chan int{cap: 2} | ||||||
|  | 		err: none | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -540,6 +540,10 @@ pub fn (mut c Checker) interface_decl(mut decl ast.InterfaceDecl) { | ||||||
| 				c.check_valid_snake_case(field.name, 'field name', field.pos) | 				c.check_valid_snake_case(field.name, 'field name', field.pos) | ||||||
| 			} | 			} | ||||||
| 			c.ensure_type_exists(field.typ, field.pos) or { return } | 			c.ensure_type_exists(field.typ, field.pos) or { return } | ||||||
|  | 			if field.typ == decl.typ { | ||||||
|  | 				c.error('recursive interface fields are not allowed because they cannot be initialised', | ||||||
|  | 					field.type_pos) | ||||||
|  | 			} | ||||||
| 			for j in 0 .. i { | 			for j in 0 .. i { | ||||||
| 				if field.name == decl.fields[j].name { | 				if field.name == decl.fields[j].name { | ||||||
| 					c.error('field name `$field.name` duplicate', field.pos) | 					c.error('field name `$field.name` duplicate', field.pos) | ||||||
|  | @ -1154,6 +1158,12 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { | ||||||
| 					c.error('reference field `${type_sym.name}.$field.name` must be initialized', | 					c.error('reference field `${type_sym.name}.$field.name` must be initialized', | ||||||
| 						node.pos) | 						node.pos) | ||||||
| 				} | 				} | ||||||
|  | 				// Do not allow empty uninitialized interfaces
 | ||||||
|  | 				sym := c.table.get_type_symbol(field.typ) | ||||||
|  | 				if sym.kind == .interface_ { | ||||||
|  | 					c.error('interface field `${type_sym.name}.$field.name` must be initialized', | ||||||
|  | 						node.pos) | ||||||
|  | 				} | ||||||
| 				// Do not allow empty uninitialized sum types
 | 				// Do not allow empty uninitialized sum types
 | ||||||
| 				/* | 				/* | ||||||
| 				sym := c.table.get_type_symbol(field.typ) | 				sym := c.table.get_type_symbol(field.typ) | ||||||
|  |  | ||||||
|  | @ -0,0 +1,6 @@ | ||||||
|  | vlib/v/checker/tests/interface_init_err.vv:15:7: error: interface field `Server.handler` must be initialized | ||||||
|  |    13 |  | ||||||
|  |    14 | fn main() { | ||||||
|  |    15 |     _ := Server{} | ||||||
|  |       |          ~~~~~~~~ | ||||||
|  |    16 | } | ||||||
|  | @ -0,0 +1,16 @@ | ||||||
|  | interface Handler { | ||||||
|  | 	foo string | ||||||
|  | 	handle(int) int | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct Server { | ||||||
|  | 	handler Handler | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn (s Server) handle(x int) int { | ||||||
|  | 	return x | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn main() { | ||||||
|  | 	_ := Server{} | ||||||
|  | } | ||||||
|  | @ -0,0 +1,5 @@ | ||||||
|  | vlib/v/checker/tests/recursive_interface_err.vv:2:6: error: recursive interface fields are not allowed because they cannot be initialised | ||||||
|  |     1 | interface Foo { | ||||||
|  |     2 |     foo Foo | ||||||
|  |       |         ~~~ | ||||||
|  |     3 | } | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | interface Foo { | ||||||
|  | 	foo Foo | ||||||
|  | } | ||||||
|  | @ -1923,6 +1923,10 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ | ||||||
| 		g.write('.msg))') | 		g.write('.msg))') | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  | 	if got_sym.kind == .none_ && exp_sym.name == 'IError' { | ||||||
|  | 		g.expr(expr) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
| 	if exp_sym.info is ast.Interface && got_type_raw.idx() != expected_type.idx() | 	if exp_sym.info is ast.Interface && got_type_raw.idx() != expected_type.idx() | ||||||
| 		&& !expected_type.has_flag(.optional) { | 		&& !expected_type.has_flag(.optional) { | ||||||
| 		if expr is ast.StructInit && !got_type.is_ptr() { | 		if expr is ast.StructInit && !got_type.is_ptr() { | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ import v.ast | ||||||
| 
 | 
 | ||||||
| pub struct Amd64 { | pub struct Amd64 { | ||||||
| mut: | mut: | ||||||
| 	g &Gen | 	g Gen | ||||||
| 	// arm64 specific stuff for code generation
 | 	// arm64 specific stuff for code generation
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -24,7 +24,7 @@ enum Arm64Register { | ||||||
| 
 | 
 | ||||||
| pub struct Arm64 { | pub struct Arm64 { | ||||||
| mut: | mut: | ||||||
| 	g &Gen | 	g Gen | ||||||
| 	// arm64 specific stuff for code generation
 | 	// arm64 specific stuff for code generation
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -15,7 +15,8 @@ import term | ||||||
| pub const builtins = ['println', 'exit'] | pub const builtins = ['println', 'exit'] | ||||||
| 
 | 
 | ||||||
| interface CodeGen { | interface CodeGen { | ||||||
| 	g &Gen | mut: | ||||||
|  | 	g Gen | ||||||
| 	gen_exit(mut g Gen, expr ast.Expr) | 	gen_exit(mut g Gen, expr ast.Expr) | ||||||
| 	// XXX WHY gen_exit fn (expr ast.Expr)
 | 	// XXX WHY gen_exit fn (expr ast.Expr)
 | ||||||
| } | } | ||||||
|  | @ -54,13 +55,13 @@ enum Size { | ||||||
| 	_64 | 	_64 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn (g &Gen) get_backend() ?CodeGen { | fn get_backend(arch pref.Arch) ?CodeGen { | ||||||
| 	match g.pref.arch { | 	match arch { | ||||||
| 		.arm64 { | 		.arm64 { | ||||||
| 			return Arm64{g} | 			return Arm64{} | ||||||
| 		} | 		} | ||||||
| 		.amd64 { | 		.amd64 { | ||||||
| 			return Amd64{g} | 			return Amd64{} | ||||||
| 		} | 		} | ||||||
| 		else {} | 		else {} | ||||||
| 	} | 	} | ||||||
|  | @ -73,11 +74,13 @@ pub fn gen(files []&ast.File, table &ast.Table, out_name string, pref &pref.Pref | ||||||
| 		sect_header_name_pos: 0 | 		sect_header_name_pos: 0 | ||||||
| 		out_name: out_name | 		out_name: out_name | ||||||
| 		pref: pref | 		pref: pref | ||||||
|  | 		// TODO: workaround, needs to support recursive init
 | ||||||
|  | 		cgen: get_backend(pref.arch) or { | ||||||
|  | 			eprintln('No available backend for this configuration. Use `-a arm64` or `-a amd64`.') | ||||||
|  | 			exit(1) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	g.cgen = g.get_backend() or { | 	g.cgen.g = g | ||||||
| 		eprintln('No available backend for this configuration. Use `-a arm64` or `-a amd64`.') |  | ||||||
| 		exit(1) |  | ||||||
| 	} |  | ||||||
| 	g.generate_header() | 	g.generate_header() | ||||||
| 	for file in files { | 	for file in files { | ||||||
| 		if file.warnings.len > 0 { | 		if file.warnings.len > 0 { | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ fn test_macho() { | ||||||
| 		pref: &pref.Preferences{} | 		pref: &pref.Preferences{} | ||||||
| 		out_name: 'test.bin' | 		out_name: 'test.bin' | ||||||
| 		table: ast.new_table() | 		table: ast.new_table() | ||||||
|  | 		cgen: native.Amd64{} | ||||||
| 	} | 	} | ||||||
| 	g.generate_macho_header() | 	g.generate_macho_header() | ||||||
| 	g.generate_macho_footer() | 	g.generate_macho_footer() | ||||||
|  |  | ||||||
|  | @ -20,8 +20,7 @@ mut: | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn test_a_struct_implementing_an_interface_can_be_assigned_without_explicit_casts() { | fn test_a_struct_implementing_an_interface_can_be_assigned_without_explicit_casts() { | ||||||
| 	mut anyplanet := AnyPlanet{} | 	mut anyplanet := AnyPlanet{Moon{}} | ||||||
| 	anyplanet.planet = Moon{} |  | ||||||
| 	assert anyplanet.planet.name() == 'moon' | 	assert anyplanet.planet.name() == 'moon' | ||||||
| 	anyplanet.planet = Mars{} | 	anyplanet.planet = Mars{} | ||||||
| 	assert anyplanet.planet.name() == 'mars' | 	assert anyplanet.planet.name() == 'mars' | ||||||
|  |  | ||||||
|  | @ -2,20 +2,30 @@ struct Base { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface Foo { | interface Foo { | ||||||
| 	parent Foo | 	parent Foo2 | ||||||
|  | 	thing(mut b Base, value i64) string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | interface Foo2 { | ||||||
| 	thing(mut b Base, value i64) string | 	thing(mut b Base, value i64) string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct Bar { | struct Bar { | ||||||
| 	parent Foo | 	parent Foo2 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | struct Bar2 {} | ||||||
|  | 
 | ||||||
| fn (f Bar) thing(mut b Base, value i64) string { | fn (f Bar) thing(mut b Base, value i64) string { | ||||||
| 	return 'bar' | 	return 'bar' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | fn (f Bar2) thing(mut b Base, value i64) string { | ||||||
|  | 	return 'bar2' | ||||||
|  | } | ||||||
|  | 
 | ||||||
| struct SubBar { | struct SubBar { | ||||||
| 	parent Foo = Bar{} | 	parent Foo2 = Bar2{} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn (f SubBar) thing(mut b Base, value i64) string { | fn (f SubBar) thing(mut b Base, value i64) string { | ||||||
|  | @ -23,8 +33,8 @@ fn (f SubBar) thing(mut b Base, value i64) string { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn test_interface_nested_field() { | fn test_interface_nested_field() { | ||||||
| 	mut foo_group := []Foo{} | 	mut foo_group := []Foo2{} | ||||||
| 	foo_group << Bar{} | 	foo_group << Bar2{} | ||||||
| 	foo_group << SubBar{} | 	foo_group << SubBar{} | ||||||
| 
 | 
 | ||||||
| 	mut b := Base{} | 	mut b := Base{} | ||||||
|  | @ -34,7 +44,7 @@ fn test_interface_nested_field() { | ||||||
| 		ret << foo.thing(mut b, 22) | 		ret << foo.thing(mut b, 22) | ||||||
| 	} | 	} | ||||||
| 	assert ret.len == 2 | 	assert ret.len == 2 | ||||||
| 	assert ret[0] == 'bar' | 	assert ret[0] == 'bar2' | ||||||
| 	assert ret[1] == 'subbar' | 	assert ret[1] == 'subbar' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,10 @@ | ||||||
|  | struct Test { | ||||||
|  | 	err IError | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn test_init_with_none() { | ||||||
|  | 	t := Test{ | ||||||
|  | 		err: none | ||||||
|  | 	} | ||||||
|  | 	// compiles successfully
 | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue