From 6fa8e4269e19a7330339cc98cb000530e42829f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20D=C3=A4schle?= Date: Fri, 30 Jul 2021 02:18:20 +0200 Subject: [PATCH] checker: force interface init (#10910) --- vlib/context/cancel.v | 1 + vlib/v/checker/checker.v | 10 +++++++++ vlib/v/checker/tests/interface_init_err.out | 6 +++++ vlib/v/checker/tests/interface_init_err.vv | 16 ++++++++++++++ .../checker/tests/recursive_interface_err.out | 5 +++++ .../checker/tests/recursive_interface_err.vv | 3 +++ vlib/v/gen/c/cgen.v | 4 ++++ vlib/v/gen/native/amd64.v | 2 +- vlib/v/gen/native/arm64.v | 2 +- vlib/v/gen/native/gen.v | 21 ++++++++++-------- vlib/v/gen/native/macho_test.v | 1 + .../assign_to_interface_field_test.v | 3 +-- vlib/v/tests/interface_nested_field_test.v | 22 ++++++++++++++----- vlib/v/tests/struct_ierror_test.v | 10 +++++++++ 14 files changed, 87 insertions(+), 19 deletions(-) create mode 100644 vlib/v/checker/tests/interface_init_err.out create mode 100644 vlib/v/checker/tests/interface_init_err.vv create mode 100644 vlib/v/checker/tests/recursive_interface_err.out create mode 100644 vlib/v/checker/tests/recursive_interface_err.vv create mode 100644 vlib/v/tests/struct_ierror_test.v diff --git a/vlib/context/cancel.v b/vlib/context/cancel.v index 023b1a8e29..2972b17cc5 100644 --- a/vlib/context/cancel.v +++ b/vlib/context/cancel.v @@ -57,6 +57,7 @@ fn new_cancel_context(parent Context) &CancelContext { context: parent mutex: sync.new_mutex() done: chan int{cap: 2} + err: none } } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 7aaf4fd3ec..343b13a7b8 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -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) diff --git a/vlib/v/checker/tests/interface_init_err.out b/vlib/v/checker/tests/interface_init_err.out new file mode 100644 index 0000000000..238395c279 --- /dev/null +++ b/vlib/v/checker/tests/interface_init_err.out @@ -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 | } diff --git a/vlib/v/checker/tests/interface_init_err.vv b/vlib/v/checker/tests/interface_init_err.vv new file mode 100644 index 0000000000..e2def73128 --- /dev/null +++ b/vlib/v/checker/tests/interface_init_err.vv @@ -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{} +} diff --git a/vlib/v/checker/tests/recursive_interface_err.out b/vlib/v/checker/tests/recursive_interface_err.out new file mode 100644 index 0000000000..f3ec2a751a --- /dev/null +++ b/vlib/v/checker/tests/recursive_interface_err.out @@ -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 | } diff --git a/vlib/v/checker/tests/recursive_interface_err.vv b/vlib/v/checker/tests/recursive_interface_err.vv new file mode 100644 index 0000000000..74a3dd9a50 --- /dev/null +++ b/vlib/v/checker/tests/recursive_interface_err.vv @@ -0,0 +1,3 @@ +interface Foo { + foo Foo +} diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 8a8131eaa4..29cbd44296 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -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() { diff --git a/vlib/v/gen/native/amd64.v b/vlib/v/gen/native/amd64.v index a568e5d79d..a690d0511c 100644 --- a/vlib/v/gen/native/amd64.v +++ b/vlib/v/gen/native/amd64.v @@ -5,7 +5,7 @@ import v.ast pub struct Amd64 { mut: - g &Gen + g Gen // arm64 specific stuff for code generation } diff --git a/vlib/v/gen/native/arm64.v b/vlib/v/gen/native/arm64.v index c0fceece35..f4eb5aac12 100644 --- a/vlib/v/gen/native/arm64.v +++ b/vlib/v/gen/native/arm64.v @@ -24,7 +24,7 @@ enum Arm64Register { pub struct Arm64 { mut: - g &Gen + g Gen // arm64 specific stuff for code generation } diff --git a/vlib/v/gen/native/gen.v b/vlib/v/gen/native/gen.v index 9695e4966b..8df68a923d 100644 --- a/vlib/v/gen/native/gen.v +++ b/vlib/v/gen/native/gen.v @@ -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 { diff --git a/vlib/v/gen/native/macho_test.v b/vlib/v/gen/native/macho_test.v index 8035e4c597..9da074d71d 100644 --- a/vlib/v/gen/native/macho_test.v +++ b/vlib/v/gen/native/macho_test.v @@ -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() diff --git a/vlib/v/tests/interface_edge_cases/assign_to_interface_field_test.v b/vlib/v/tests/interface_edge_cases/assign_to_interface_field_test.v index cb69e550c8..9f279bafaa 100644 --- a/vlib/v/tests/interface_edge_cases/assign_to_interface_field_test.v +++ b/vlib/v/tests/interface_edge_cases/assign_to_interface_field_test.v @@ -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' diff --git a/vlib/v/tests/interface_nested_field_test.v b/vlib/v/tests/interface_nested_field_test.v index 606df66f52..7577bcb89d 100644 --- a/vlib/v/tests/interface_nested_field_test.v +++ b/vlib/v/tests/interface_nested_field_test.v @@ -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' } diff --git a/vlib/v/tests/struct_ierror_test.v b/vlib/v/tests/struct_ierror_test.v new file mode 100644 index 0000000000..af1303380d --- /dev/null +++ b/vlib/v/tests/struct_ierror_test.v @@ -0,0 +1,10 @@ +struct Test { + err IError +} + +fn test_init_with_none() { + t := Test{ + err: none + } + // compiles successfully +}