checker: force interface init (#10910)
							parent
							
								
									1eac351f25
								
							
						
					
					
						commit
						6fa8e4269e
					
				|  | @ -57,6 +57,7 @@ fn new_cancel_context(parent Context) &CancelContext { | |||
| 		context: parent | ||||
| 		mutex: sync.new_mutex() | ||||
| 		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.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 { | ||||
| 				if field.name == decl.fields[j].name { | ||||
| 					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', | ||||
| 						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
 | ||||
| 				/* | ||||
| 				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))') | ||||
| 		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() | ||||
| 		&& !expected_type.has_flag(.optional) { | ||||
| 		if expr is ast.StructInit && !got_type.is_ptr() { | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ import v.ast | |||
| 
 | ||||
| pub struct Amd64 { | ||||
| mut: | ||||
| 	g &Gen | ||||
| 	g Gen | ||||
| 	// arm64 specific stuff for code generation
 | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -24,7 +24,7 @@ enum Arm64Register { | |||
| 
 | ||||
| pub struct Arm64 { | ||||
| mut: | ||||
| 	g &Gen | ||||
| 	g Gen | ||||
| 	// arm64 specific stuff for code generation
 | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,7 +15,8 @@ import term | |||
| pub const builtins = ['println', 'exit'] | ||||
| 
 | ||||
| interface CodeGen { | ||||
| 	g &Gen | ||||
| mut: | ||||
| 	g Gen | ||||
| 	gen_exit(mut g Gen, expr ast.Expr) | ||||
| 	// XXX WHY gen_exit fn (expr ast.Expr)
 | ||||
| } | ||||
|  | @ -54,13 +55,13 @@ enum Size { | |||
| 	_64 | ||||
| } | ||||
| 
 | ||||
| fn (g &Gen) get_backend() ?CodeGen { | ||||
| 	match g.pref.arch { | ||||
| fn get_backend(arch pref.Arch) ?CodeGen { | ||||
| 	match arch { | ||||
| 		.arm64 { | ||||
| 			return Arm64{g} | ||||
| 			return Arm64{} | ||||
| 		} | ||||
| 		.amd64 { | ||||
| 			return Amd64{g} | ||||
| 			return Amd64{} | ||||
| 		} | ||||
| 		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 | ||||
| 		out_name: out_name | ||||
| 		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 { | ||||
| 		eprintln('No available backend for this configuration. Use `-a arm64` or `-a amd64`.') | ||||
| 		exit(1) | ||||
| 	} | ||||
| 	g.cgen.g = g | ||||
| 	g.generate_header() | ||||
| 	for file in files { | ||||
| 		if file.warnings.len > 0 { | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ fn test_macho() { | |||
| 		pref: &pref.Preferences{} | ||||
| 		out_name: 'test.bin' | ||||
| 		table: ast.new_table() | ||||
| 		cgen: native.Amd64{} | ||||
| 	} | ||||
| 	g.generate_macho_header() | ||||
| 	g.generate_macho_footer() | ||||
|  |  | |||
|  | @ -20,8 +20,7 @@ mut: | |||
| } | ||||
| 
 | ||||
| fn test_a_struct_implementing_an_interface_can_be_assigned_without_explicit_casts() { | ||||
| 	mut anyplanet := AnyPlanet{} | ||||
| 	anyplanet.planet = Moon{} | ||||
| 	mut anyplanet := AnyPlanet{Moon{}} | ||||
| 	assert anyplanet.planet.name() == 'moon' | ||||
| 	anyplanet.planet = Mars{} | ||||
| 	assert anyplanet.planet.name() == 'mars' | ||||
|  |  | |||
|  | @ -2,20 +2,30 @@ struct Base { | |||
| } | ||||
| 
 | ||||
| interface Foo { | ||||
| 	parent Foo | ||||
| 	parent Foo2 | ||||
| 	thing(mut b Base, value i64) string | ||||
| } | ||||
| 
 | ||||
| interface Foo2 { | ||||
| 	thing(mut b Base, value i64) string | ||||
| } | ||||
| 
 | ||||
| struct Bar { | ||||
| 	parent Foo | ||||
| 	parent Foo2 | ||||
| } | ||||
| 
 | ||||
| struct Bar2 {} | ||||
| 
 | ||||
| fn (f Bar) thing(mut b Base, value i64) string { | ||||
| 	return 'bar' | ||||
| } | ||||
| 
 | ||||
| fn (f Bar2) thing(mut b Base, value i64) string { | ||||
| 	return 'bar2' | ||||
| } | ||||
| 
 | ||||
| struct SubBar { | ||||
| 	parent Foo = Bar{} | ||||
| 	parent Foo2 = Bar2{} | ||||
| } | ||||
| 
 | ||||
| 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() { | ||||
| 	mut foo_group := []Foo{} | ||||
| 	foo_group << Bar{} | ||||
| 	mut foo_group := []Foo2{} | ||||
| 	foo_group << Bar2{} | ||||
| 	foo_group << SubBar{} | ||||
| 
 | ||||
| 	mut b := Base{} | ||||
|  | @ -34,7 +44,7 @@ fn test_interface_nested_field() { | |||
| 		ret << foo.thing(mut b, 22) | ||||
| 	} | ||||
| 	assert ret.len == 2 | ||||
| 	assert ret[0] == 'bar' | ||||
| 	assert ret[0] == 'bar2' | ||||
| 	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