From 60b705b4c4080035b03a74964abf5eac03c427e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20D=C3=A4schle?= Date: Tue, 20 Jul 2021 14:06:06 +0200 Subject: [PATCH] checker, gen: implement basic struct embed direct field init support (#10871) --- vlib/v/ast/table.v | 2 +- vlib/v/checker/checker.v | 43 +++++++++++++++++--------------- vlib/v/gen/c/cgen.v | 10 ++++++++ vlib/v/tests/struct_embed_test.v | 9 ++++--- 4 files changed, 39 insertions(+), 25 deletions(-) diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index 71d1d4aec5..1234c78252 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -415,7 +415,7 @@ pub fn (t &Table) find_field(s &TypeSymbol, name string) ?StructField { return none } -// search for a given field, looking through embedded fields +// find_field_with_embeds searches for a given field, also looking through embedded fields pub fn (t &Table) find_field_with_embeds(sym &TypeSymbol, field_name string) ?StructField { if f := t.find_field(sym, field_name) { return f diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index b6b356c9a1..62604007fd 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1009,7 +1009,7 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { } mut inited_fields := []string{} for i, mut field in node.fields { - mut info_field := ast.StructField{} + mut field_info := ast.StructField{} mut embed_type := ast.Type(0) mut is_embed := false mut field_name := '' @@ -1019,18 +1019,15 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { // We should just stop here. break } - info_field = info.fields[i] - field_name = info_field.name + field_info = info.fields[i] + field_name = field_info.name node.fields[i].name = field_name } else { field_name = field.name - mut exists := false - for f in info.fields { - if f.name == field_name { - info_field = f - exists = true - break - } + mut exists := true + field_info = info.find_field(field_name) or { + exists = false + ast.StructField{} } if !exists { for embed in info.embeds { @@ -1041,6 +1038,12 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { is_embed = true break } + embed_struct_info := embed_sym.info as ast.Struct + if embed_field_info := embed_struct_info.find_field(field_name) { + exists = true + field_info = embed_field_info + break + } } } if !exists { @@ -1063,7 +1066,7 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { expr_type_sym := c.table.get_type_symbol(expr_type) if expr_type != ast.void_type && expr_type_sym.kind != .placeholder { c.check_expected(expr_type, embed_type) or { - c.error('cannot assign to field `$info_field.name`: $err.msg', + c.error('cannot assign to field `$field_info.name`: $err.msg', field.pos) } } @@ -1071,41 +1074,41 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { node.fields[i].expected_type = embed_type } else { inited_fields << field_name - field_type_sym := c.table.get_type_symbol(info_field.typ) - expected_type = info_field.typ + field_type_sym := c.table.get_type_symbol(field_info.typ) + expected_type = field_info.typ c.expected_type = expected_type expr_type = c.unwrap_generic(c.expr(field.expr)) - if !info_field.typ.has_flag(.optional) { + if !field_info.typ.has_flag(.optional) { expr_type = c.check_expr_opt_call(field.expr, expr_type) } expr_type_sym := c.table.get_type_symbol(expr_type) if field_type_sym.kind == .interface_ { - if c.type_implements(expr_type, info_field.typ, field.pos) { + if c.type_implements(expr_type, field_info.typ, field.pos) { if !expr_type.is_ptr() && !expr_type.is_pointer() && expr_type_sym.kind != .interface_ && !c.inside_unsafe { c.mark_as_referenced(mut &field.expr, true) } } } else if expr_type != ast.void_type && expr_type_sym.kind != .placeholder { - c.check_expected(expr_type, info_field.typ) or { - c.error('cannot assign to field `$info_field.name`: $err.msg', + c.check_expected(expr_type, field_info.typ) or { + c.error('cannot assign to field `$field_info.name`: $err.msg', field.pos) } } - if info_field.typ.has_flag(.shared_f) { + if field_info.typ.has_flag(.shared_f) { if !expr_type.has_flag(.shared_f) && expr_type.is_ptr() { c.error('`shared` field must be initialized with `shared` or value', field.pos) } } else { - if info_field.typ.is_ptr() && !expr_type.is_ptr() && !expr_type.is_pointer() + if field_info.typ.is_ptr() && !expr_type.is_ptr() && !expr_type.is_pointer() && !expr_type.is_number() { c.error('reference field must be initialized with reference', field.pos) } } node.fields[i].typ = expr_type - node.fields[i].expected_type = info_field.typ + node.fields[i].expected_type = field_info.typ } if expr_type.is_ptr() && expected_type.is_ptr() { if mut field.expr is ast.Ident { diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 45ada89f10..155f2b4057 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -5122,12 +5122,22 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) { verror('union must not have more than 1 initializer') } if !info.is_union { + mut used_embed_fields := []string{} + init_field_names := info.fields.map(it.name) + // fields that are initialized but belong to the embedding + init_fields_to_embed := struct_init.fields.filter(it.name !in init_field_names) for embed in info.embeds { embed_sym := g.table.get_type_symbol(embed) embed_name := embed_sym.embed_name() if embed_name !in inited_fields { + embed_info := embed_sym.info as ast.Struct + embed_field_names := embed_info.fields.map(it.name) + fields_to_embed := init_fields_to_embed.filter(it.name !in used_embed_fields + && it.name in embed_field_names) + used_embed_fields << fields_to_embed.map(it.name) default_init := ast.StructInit{ typ: embed + fields: fields_to_embed } g.write('.$embed_name = ') g.struct_init(default_init) diff --git a/vlib/v/tests/struct_embed_test.v b/vlib/v/tests/struct_embed_test.v index 6732ebb616..46fef8eb96 100644 --- a/vlib/v/tests/struct_embed_test.v +++ b/vlib/v/tests/struct_embed_test.v @@ -37,14 +37,15 @@ fn test_default_value_without_init() { assert b.y == 5 } -/* -TODO fn test_initialize() { - b := Bar{x: 1, y: 2} + b := Bar{ + x: 1 + y: 2 + } assert b.x == 1 assert b.y == 2 } -*/ + struct Bar3 { Foo y string = 'test'