checker, gen: implement basic struct embed direct field init support (#10871)

pull/10873/head^2
Daniel Däschle 2021-07-20 14:06:06 +02:00 committed by GitHub
parent 85658bc700
commit 60b705b4c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 39 additions and 25 deletions

View File

@ -415,7 +415,7 @@ pub fn (t &Table) find_field(s &TypeSymbol, name string) ?StructField {
return none 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 { pub fn (t &Table) find_field_with_embeds(sym &TypeSymbol, field_name string) ?StructField {
if f := t.find_field(sym, field_name) { if f := t.find_field(sym, field_name) {
return f return f

View File

@ -1009,7 +1009,7 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
} }
mut inited_fields := []string{} mut inited_fields := []string{}
for i, mut field in node.fields { for i, mut field in node.fields {
mut info_field := ast.StructField{} mut field_info := ast.StructField{}
mut embed_type := ast.Type(0) mut embed_type := ast.Type(0)
mut is_embed := false mut is_embed := false
mut field_name := '' 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. // We should just stop here.
break break
} }
info_field = info.fields[i] field_info = info.fields[i]
field_name = info_field.name field_name = field_info.name
node.fields[i].name = field_name node.fields[i].name = field_name
} else { } else {
field_name = field.name field_name = field.name
mut exists := false mut exists := true
for f in info.fields { field_info = info.find_field(field_name) or {
if f.name == field_name { exists = false
info_field = f ast.StructField{}
exists = true
break
}
} }
if !exists { if !exists {
for embed in info.embeds { 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 is_embed = true
break 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 { 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) expr_type_sym := c.table.get_type_symbol(expr_type)
if expr_type != ast.void_type && expr_type_sym.kind != .placeholder { if expr_type != ast.void_type && expr_type_sym.kind != .placeholder {
c.check_expected(expr_type, embed_type) or { 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) 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 node.fields[i].expected_type = embed_type
} else { } else {
inited_fields << field_name inited_fields << field_name
field_type_sym := c.table.get_type_symbol(info_field.typ) field_type_sym := c.table.get_type_symbol(field_info.typ)
expected_type = info_field.typ expected_type = field_info.typ
c.expected_type = expected_type c.expected_type = expected_type
expr_type = c.unwrap_generic(c.expr(field.expr)) 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 = c.check_expr_opt_call(field.expr, expr_type)
} }
expr_type_sym := c.table.get_type_symbol(expr_type) expr_type_sym := c.table.get_type_symbol(expr_type)
if field_type_sym.kind == .interface_ { 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() if !expr_type.is_ptr() && !expr_type.is_pointer()
&& expr_type_sym.kind != .interface_ && !c.inside_unsafe { && expr_type_sym.kind != .interface_ && !c.inside_unsafe {
c.mark_as_referenced(mut &field.expr, true) c.mark_as_referenced(mut &field.expr, true)
} }
} }
} else if expr_type != ast.void_type && expr_type_sym.kind != .placeholder { } else if expr_type != ast.void_type && expr_type_sym.kind != .placeholder {
c.check_expected(expr_type, info_field.typ) or { c.check_expected(expr_type, field_info.typ) 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) 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() { if !expr_type.has_flag(.shared_f) && expr_type.is_ptr() {
c.error('`shared` field must be initialized with `shared` or value', c.error('`shared` field must be initialized with `shared` or value',
field.pos) field.pos)
} }
} else { } 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() { && !expr_type.is_number() {
c.error('reference field must be initialized with reference', c.error('reference field must be initialized with reference',
field.pos) field.pos)
} }
} }
node.fields[i].typ = expr_type 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 expr_type.is_ptr() && expected_type.is_ptr() {
if mut field.expr is ast.Ident { if mut field.expr is ast.Ident {

View File

@ -5122,12 +5122,22 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
verror('union must not have more than 1 initializer') verror('union must not have more than 1 initializer')
} }
if !info.is_union { 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 { for embed in info.embeds {
embed_sym := g.table.get_type_symbol(embed) embed_sym := g.table.get_type_symbol(embed)
embed_name := embed_sym.embed_name() embed_name := embed_sym.embed_name()
if embed_name !in inited_fields { 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{ default_init := ast.StructInit{
typ: embed typ: embed
fields: fields_to_embed
} }
g.write('.$embed_name = ') g.write('.$embed_name = ')
g.struct_init(default_init) g.struct_init(default_init)

View File

@ -37,14 +37,15 @@ fn test_default_value_without_init() {
assert b.y == 5 assert b.y == 5
} }
/*
TODO
fn test_initialize() { fn test_initialize() {
b := Bar{x: 1, y: 2} b := Bar{
x: 1
y: 2
}
assert b.x == 1 assert b.x == 1
assert b.y == 2 assert b.y == 2
} }
*/
struct Bar3 { struct Bar3 {
Foo Foo
y string = 'test' y string = 'test'