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
|
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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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'
|
||||||
|
|
Loading…
Reference in New Issue