From 09eaae5f7ad54401684be67dba12409a28e6a758 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Tue, 28 May 2024 17:27:43 +0300 Subject: [PATCH] checker: fix checking of default field initialisations, that are part of unions of structs tagged with `@[noinit]` (#21587) --- vlib/v/ast/ast.v | 2 + vlib/v/checker/struct.v | 27 +++++++++--- .../modules/structs_with_noinit/shapes.v | 22 ++++++++++ ...union_of_structs_marked_with_noinit_test.v | 44 +++++++++++++++++++ 4 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 vlib/v/tests/modules/structs_with_noinit/shapes.v create mode 100644 vlib/v/tests/struct_field_default_union_of_structs_marked_with_noinit_test.v diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 145ae076de..3e38beff30 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -341,6 +341,8 @@ pub: is_deprecated bool pub mut: is_recursive bool + is_part_of_union bool + container_typ Type default_expr Expr default_expr_typ Type name string diff --git a/vlib/v/checker/struct.v b/vlib/v/checker/struct.v index 8fd1273321..2de80b5516 100644 --- a/vlib/v/checker/struct.v +++ b/vlib/v/checker/struct.v @@ -14,6 +14,13 @@ fn (mut c Checker) struct_decl(mut node ast.StructDecl) { mut struct_sym, struct_typ_idx := c.table.find_sym_and_type_idx(node.name) mut has_generic_types := false if mut struct_sym.info is ast.Struct { + for mut symfield in struct_sym.info.fields { + symfield.container_typ = struct_typ_idx + if struct_sym.info.is_union { + symfield.is_part_of_union = true + } + } + if node.language == .v && !c.is_builtin_mod && !struct_sym.info.is_anon { c.check_valid_pascal_case(node.name, 'struct name', node.pos) } @@ -844,12 +851,22 @@ or use an explicit `unsafe{ a[..] }`, if you do not want a copy of the slice.', node.pos) } if !node.has_update_expr && !field.has_default_expr && !field.typ.is_ptr() - && !field.typ.has_flag(.option) && c.table.final_sym(field.typ).kind == .struct_ { - mut zero_struct_init := ast.StructInit{ - pos: node.pos - typ: field.typ + && !field.typ.has_flag(.option) { + field_final_sym := c.table.final_sym(field.typ) + if field_final_sym.kind == .struct_ { + mut zero_struct_init := ast.StructInit{ + pos: node.pos + typ: field.typ + } + if field.is_part_of_union { + if field.name in inited_fields { + // fields that are part of an union, should only be checked, when they are explicitly initialised + c.struct_init(mut zero_struct_init, true, mut inited_fields) + } + } else { + c.struct_init(mut zero_struct_init, true, mut inited_fields) + } } - c.struct_init(mut zero_struct_init, true, mut inited_fields) } } for embed in info.embeds { diff --git a/vlib/v/tests/modules/structs_with_noinit/shapes.v b/vlib/v/tests/modules/structs_with_noinit/shapes.v new file mode 100644 index 0000000000..1b137d6d0b --- /dev/null +++ b/vlib/v/tests/modules/structs_with_noinit/shapes.v @@ -0,0 +1,22 @@ +module structs_with_noinit + +@[noinit] +pub struct Image {} + +@[noinit] +pub struct Rect {} + +@[noinit] +pub struct Circle {} + +pub fn make_circle() Circle { + return Circle{} +} + +pub fn make_image() Image { + return Image{} +} + +pub fn make_rect() Rect { + return Rect{} +} diff --git a/vlib/v/tests/struct_field_default_union_of_structs_marked_with_noinit_test.v b/vlib/v/tests/struct_field_default_union_of_structs_marked_with_noinit_test.v new file mode 100644 index 0000000000..dc1b44d341 --- /dev/null +++ b/vlib/v/tests/struct_field_default_union_of_structs_marked_with_noinit_test.v @@ -0,0 +1,44 @@ +module main + +import structs_with_noinit + +enum RenderableKind { + circle + image + rect +} + +union RenderableValue { + circle structs_with_noinit.Circle + image structs_with_noinit.Image + rect structs_with_noinit.Rect +} + +struct Renderable { + RenderableValue + kind RenderableKind +} + +fn draw(r Renderable) int { + match r.kind { + .circle { + println('()') + return 999 + } + .rect { + println('[]') + } + .image { + println('o_O') + } + } + return 1 +} + +fn test_initialisation_of_a_struct_containing_embedded_union_field() { + r := Renderable{ + kind: .circle + circle: structs_with_noinit.make_circle() + } + assert draw(r) == 999 +}