ast, checker, cgen: fix nested struct embed method call (#12714)
parent
d59aa14b26
commit
0cb4557a8d
|
@ -1456,7 +1456,7 @@ fn (t Tree) call_expr(node ast.CallExpr) &Node {
|
|||
obj.add('concrete_types', t.array_node_type(node.concrete_types))
|
||||
obj.add('or_block', t.or_expr(node.or_block))
|
||||
obj.add('concrete_list_pos', t.position(node.concrete_list_pos))
|
||||
obj.add('from_embed_type', t.type_node(node.from_embed_type))
|
||||
obj.add('from_embed_types', t.array_node_type(node.from_embed_types))
|
||||
obj.add('comments', t.array_node_comment(node.comments))
|
||||
obj.add('pos', t.position(node.pos))
|
||||
obj.add('name_pos', t.position(node.name_pos))
|
||||
|
|
|
@ -529,7 +529,7 @@ pub mut:
|
|||
concrete_list_pos token.Position
|
||||
free_receiver bool // true if the receiver expression needs to be freed
|
||||
scope &Scope
|
||||
from_embed_type Type // holds the type of the embed that the method is called from
|
||||
from_embed_types []Type // holds the type of the embed that the method is called from
|
||||
comments []Comment
|
||||
}
|
||||
|
||||
|
|
|
@ -409,6 +409,43 @@ pub fn (t &Table) type_find_method_from_embeds(sym &TypeSymbol, method_name stri
|
|||
return none
|
||||
}
|
||||
|
||||
pub fn (t &Table) type_find_method_from_embeds_recursive(sym &TypeSymbol, method_name string) ?(Fn, []Type) {
|
||||
if sym.info is Struct {
|
||||
mut found_methods := []Fn{}
|
||||
mut embed_of_found_methods := []Type{}
|
||||
for embed in sym.info.embeds {
|
||||
embed_sym := t.get_type_symbol(embed)
|
||||
if m := t.type_find_method(embed_sym, method_name) {
|
||||
found_methods << m
|
||||
embed_of_found_methods << embed
|
||||
} else {
|
||||
method, types := t.type_find_method_from_embeds_recursive(embed_sym, method_name) or {
|
||||
continue
|
||||
}
|
||||
found_methods << method
|
||||
embed_of_found_methods << embed
|
||||
embed_of_found_methods << types
|
||||
}
|
||||
}
|
||||
if found_methods.len == 1 {
|
||||
return found_methods[0], embed_of_found_methods
|
||||
} else if found_methods.len > 1 {
|
||||
return error('ambiguous method `$method_name`')
|
||||
}
|
||||
} else if sym.info is Aggregate {
|
||||
for typ in sym.info.types {
|
||||
agg_sym := t.get_type_symbol(typ)
|
||||
method, embed_types := t.type_find_method_from_embeds_recursive(agg_sym, method_name) or {
|
||||
continue
|
||||
}
|
||||
if embed_types.len != 0 {
|
||||
return method, embed_types
|
||||
}
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
fn (t &Table) register_aggregate_field(mut sym TypeSymbol, name string) ?StructField {
|
||||
if sym.kind != .aggregate {
|
||||
t.panic('Unexpected type symbol: $sym.kind')
|
||||
|
|
|
@ -2083,17 +2083,18 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
|||
}
|
||||
if !has_method {
|
||||
has_method = true
|
||||
mut embed_type := ast.Type(0)
|
||||
method, embed_type = c.table.type_find_method_from_embeds(left_sym, method_name) or {
|
||||
mut embed_types := []ast.Type{}
|
||||
method, embed_types = c.table.type_find_method_from_embeds_recursive(left_sym,
|
||||
method_name) or {
|
||||
if err.msg != '' {
|
||||
c.error(err.msg, node.pos)
|
||||
}
|
||||
has_method = false
|
||||
ast.Fn{}, ast.Type(0)
|
||||
ast.Fn{}, []ast.Type{}
|
||||
}
|
||||
if embed_type != 0 {
|
||||
if embed_types.len != 0 {
|
||||
is_method_from_embed = true
|
||||
node.from_embed_type = embed_type
|
||||
node.from_embed_types = embed_types
|
||||
}
|
||||
}
|
||||
if left_sym.kind == .aggregate {
|
||||
|
@ -2262,7 +2263,7 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
|||
}
|
||||
}
|
||||
if is_method_from_embed {
|
||||
node.receiver_type = node.from_embed_type.derive(method.params[0].typ)
|
||||
node.receiver_type = node.from_embed_types.last().derive(method.params[0].typ)
|
||||
} else if is_generic {
|
||||
// We need the receiver to be T in cgen.
|
||||
// TODO: cant we just set all these to the concrete type in checker? then no need in gen
|
||||
|
|
|
@ -946,7 +946,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
|||
}
|
||||
if node.receiver_type.is_ptr()
|
||||
&& (!node.left_type.is_ptr() || node.left_type.has_flag(.variadic)
|
||||
|| node.from_embed_type != 0
|
||||
|| node.from_embed_types.len != 0
|
||||
|| (node.left_type.has_flag(.shared_f) && node.name != 'str')) {
|
||||
// The receiver is a reference, but the caller provided a value
|
||||
// Add `&` automatically.
|
||||
|
@ -960,11 +960,11 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
|||
}
|
||||
}
|
||||
} else if !node.receiver_type.is_ptr() && node.left_type.is_ptr() && node.name != 'str'
|
||||
&& node.from_embed_type == 0 {
|
||||
&& node.from_embed_types.len == 0 {
|
||||
if !node.left_type.has_flag(.shared_f) {
|
||||
g.write('/*rec*/*')
|
||||
}
|
||||
} else if !is_range_slice && node.from_embed_type == 0 && node.name != 'str' {
|
||||
} else if !is_range_slice && node.from_embed_types.len == 0 && node.name != 'str' {
|
||||
diff := node.left_type.nr_muls() - node.receiver_type.nr_muls()
|
||||
if diff < 0 {
|
||||
// TODO
|
||||
|
@ -996,8 +996,9 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
|||
} else {
|
||||
g.expr(node.left)
|
||||
}
|
||||
if node.from_embed_type != 0 {
|
||||
embed_name := typ_sym.embed_name()
|
||||
for embed in node.from_embed_types {
|
||||
embed_sym := g.table.get_type_symbol(embed)
|
||||
embed_name := embed_sym.embed_name()
|
||||
if node.left_type.is_ptr() {
|
||||
g.write('->')
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
struct Foo1 {
|
||||
x int
|
||||
}
|
||||
|
||||
struct Foo2 {
|
||||
Foo1
|
||||
}
|
||||
|
||||
struct Foo3 {
|
||||
Foo2
|
||||
}
|
||||
|
||||
fn (f Foo1) bar() string {
|
||||
println('Foo1.bar()')
|
||||
return 'Foo1.bar()'
|
||||
}
|
||||
|
||||
fn test_nested_struct_embed_method_call() {
|
||||
f3 := Foo3{}
|
||||
ret := f3.bar()
|
||||
assert ret == 'Foo1.bar()'
|
||||
}
|
Loading…
Reference in New Issue