From 5abd49d9bce3faea0f955d2a4aca48433f4c30b5 Mon Sep 17 00:00:00 2001 From: Lukas Neubert Date: Mon, 8 Feb 2021 16:33:05 +0100 Subject: [PATCH] cgen: cast default struct field value to correct SumType/interface (#8619) --- vlib/v/checker/checker.v | 8 ++++++-- vlib/v/gen/c/cgen.v | 8 +++++++- vlib/v/table/types.v | 1 + ...ct_field_default_value_interface_cast_test.v | 17 +++++++++++++++++ ...ruct_field_default_value_sumtype_cast_test.v | 17 +++++++++++++++++ 5 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 vlib/v/tests/struct_field_default_value_interface_cast_test.v create mode 100644 vlib/v/tests/struct_field_default_value_sumtype_cast_test.v diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index fd1500a7bb..873c2a5859 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -465,9 +465,13 @@ pub fn (mut c Checker) struct_decl(mut decl ast.StructDecl) { if field.has_default_expr { c.expected_type = field.typ field_expr_type := c.expr(field.default_expr) + struct_sym.info.fields[i].default_expr_typ = field_expr_type c.check_expected(field_expr_type, field.typ) or { - c.error('incompatible initializer for field `$field.name`: $err', - field.default_expr.position()) + if !(sym.kind == .interface_ + && c.type_implements(field_expr_type, field.typ, field.pos)) { + c.error('incompatible initializer for field `$field.name`: $err', + field.default_expr.position()) + } } // Check for unnecessary inits like ` = 0` and ` = ''` if field.typ.is_ptr() { diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 66631b304b..b766390ef8 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -5074,8 +5074,14 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) { fn (mut g Gen) zero_struct_field(field table.Field) { field_name := c_name(field.name) g.write('.$field_name = ') + sym := g.table.get_type_symbol(field.typ) + defex := ast.fe2ex(field.default_expr) if field.has_default_expr { - g.expr(ast.fe2ex(field.default_expr)) + if sym.kind in [.sum_type, .interface_] { + g.expr_with_cast(defex, field.default_expr_typ, field.typ) + return + } + g.expr(defex) } else { g.write(g.type_default(field.typ)) } diff --git a/vlib/v/table/types.v b/vlib/v/table/types.v index 0a3ec0b733..6d0a03a50a 100644 --- a/vlib/v/table/types.v +++ b/vlib/v/table/types.v @@ -694,6 +694,7 @@ pub mut: typ Type default_expr FExpr has_default_expr bool + default_expr_typ Type default_val string attrs []Attr is_pub bool diff --git a/vlib/v/tests/struct_field_default_value_interface_cast_test.v b/vlib/v/tests/struct_field_default_value_interface_cast_test.v new file mode 100644 index 0000000000..0613a2f229 --- /dev/null +++ b/vlib/v/tests/struct_field_default_value_interface_cast_test.v @@ -0,0 +1,17 @@ +struct Foo { + x int +} + +interface FooBar { + x int +} + +struct Abc { + foobar FooBar = Foo { x: 123 } +} + +fn test_struct_field_default_value_interface_cast() { + x := Abc{} + assert x.foobar is Foo + assert x.foobar.x == 123 +} diff --git a/vlib/v/tests/struct_field_default_value_sumtype_cast_test.v b/vlib/v/tests/struct_field_default_value_sumtype_cast_test.v new file mode 100644 index 0000000000..df907f0b04 --- /dev/null +++ b/vlib/v/tests/struct_field_default_value_sumtype_cast_test.v @@ -0,0 +1,17 @@ +struct Foo { + x int +} + +struct Bar {} + +type FooBar = Foo | Bar + +struct Abc { + foobar FooBar = Foo { x: 123 } +} + +fn test_struct_field_default_value_sumtype_cast() { + x := Abc{} + assert x.foobar is Foo + assert (x.foobar as Foo).x == 123 +}