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
}
// 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

View File

@ -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 {

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')
}
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)

View File

@ -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'