checker, gen: implement basic struct embed direct field init support (#10871)
parent
85658bc700
commit
60b705b4c4
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Reference in New Issue