Skip to content

Commit 0cb4557

Browse files
authored
ast, checker, cgen: fix nested struct embed method call (#12714)
1 parent d59aa14 commit 0cb4557

File tree

7 files changed

+74
-13
lines changed

7 files changed

+74
-13
lines changed

cmd/tools/vast/vast.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1456,7 +1456,7 @@ fn (t Tree) call_expr(node ast.CallExpr) &Node {
14561456
obj.add('concrete_types', t.array_node_type(node.concrete_types))
14571457
obj.add('or_block', t.or_expr(node.or_block))
14581458
obj.add('concrete_list_pos', t.position(node.concrete_list_pos))
1459-
obj.add('from_embed_type', t.type_node(node.from_embed_type))
1459+
obj.add('from_embed_types', t.array_node_type(node.from_embed_types))
14601460
obj.add('comments', t.array_node_comment(node.comments))
14611461
obj.add('pos', t.position(node.pos))
14621462
obj.add('name_pos', t.position(node.name_pos))

vlib/v/ast/ast.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ pub mut:
529529
concrete_list_pos token.Position
530530
free_receiver bool // true if the receiver expression needs to be freed
531531
scope &Scope
532-
from_embed_type Type // holds the type of the embed that the method is called from
532+
from_embed_types []Type // holds the type of the embed that the method is called from
533533
comments []Comment
534534
}
535535

vlib/v/ast/table.v

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,43 @@ pub fn (t &Table) type_find_method_from_embeds(sym &TypeSymbol, method_name stri
409409
return none
410410
}
411411

412+
pub fn (t &Table) type_find_method_from_embeds_recursive(sym &TypeSymbol, method_name string) ?(Fn, []Type) {
413+
if sym.info is Struct {
414+
mut found_methods := []Fn{}
415+
mut embed_of_found_methods := []Type{}
416+
for embed in sym.info.embeds {
417+
embed_sym := t.get_type_symbol(embed)
418+
if m := t.type_find_method(embed_sym, method_name) {
419+
found_methods << m
420+
embed_of_found_methods << embed
421+
} else {
422+
method, types := t.type_find_method_from_embeds_recursive(embed_sym, method_name) or {
423+
continue
424+
}
425+
found_methods << method
426+
embed_of_found_methods << embed
427+
embed_of_found_methods << types
428+
}
429+
}
430+
if found_methods.len == 1 {
431+
return found_methods[0], embed_of_found_methods
432+
} else if found_methods.len > 1 {
433+
return error('ambiguous method `$method_name`')
434+
}
435+
} else if sym.info is Aggregate {
436+
for typ in sym.info.types {
437+
agg_sym := t.get_type_symbol(typ)
438+
method, embed_types := t.type_find_method_from_embeds_recursive(agg_sym, method_name) or {
439+
continue
440+
}
441+
if embed_types.len != 0 {
442+
return method, embed_types
443+
}
444+
}
445+
}
446+
return none
447+
}
448+
412449
fn (t &Table) register_aggregate_field(mut sym TypeSymbol, name string) ?StructField {
413450
if sym.kind != .aggregate {
414451
t.panic('Unexpected type symbol: $sym.kind')

vlib/v/checker/checker.v

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2083,17 +2083,18 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
20832083
}
20842084
if !has_method {
20852085
has_method = true
2086-
mut embed_type := ast.Type(0)
2087-
method, embed_type = c.table.type_find_method_from_embeds(left_sym, method_name) or {
2086+
mut embed_types := []ast.Type{}
2087+
method, embed_types = c.table.type_find_method_from_embeds_recursive(left_sym,
2088+
method_name) or {
20882089
if err.msg != '' {
20892090
c.error(err.msg, node.pos)
20902091
}
20912092
has_method = false
2092-
ast.Fn{}, ast.Type(0)
2093+
ast.Fn{}, []ast.Type{}
20932094
}
2094-
if embed_type != 0 {
2095+
if embed_types.len != 0 {
20952096
is_method_from_embed = true
2096-
node.from_embed_type = embed_type
2097+
node.from_embed_types = embed_types
20972098
}
20982099
}
20992100
if left_sym.kind == .aggregate {
@@ -2262,7 +2263,7 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
22622263
}
22632264
}
22642265
if is_method_from_embed {
2265-
node.receiver_type = node.from_embed_type.derive(method.params[0].typ)
2266+
node.receiver_type = node.from_embed_types.last().derive(method.params[0].typ)
22662267
} else if is_generic {
22672268
// We need the receiver to be T in cgen.
22682269
// TODO: cant we just set all these to the concrete type in checker? then no need in gen

vlib/v/gen/c/fn.v

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -946,7 +946,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
946946
}
947947
if node.receiver_type.is_ptr()
948948
&& (!node.left_type.is_ptr() || node.left_type.has_flag(.variadic)
949-
|| node.from_embed_type != 0
949+
|| node.from_embed_types.len != 0
950950
|| (node.left_type.has_flag(.shared_f) && node.name != 'str')) {
951951
// The receiver is a reference, but the caller provided a value
952952
// Add `&` automatically.
@@ -960,11 +960,11 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
960960
}
961961
}
962962
} else if !node.receiver_type.is_ptr() && node.left_type.is_ptr() && node.name != 'str'
963-
&& node.from_embed_type == 0 {
963+
&& node.from_embed_types.len == 0 {
964964
if !node.left_type.has_flag(.shared_f) {
965965
g.write('/*rec*/*')
966966
}
967-
} else if !is_range_slice && node.from_embed_type == 0 && node.name != 'str' {
967+
} else if !is_range_slice && node.from_embed_types.len == 0 && node.name != 'str' {
968968
diff := node.left_type.nr_muls() - node.receiver_type.nr_muls()
969969
if diff < 0 {
970970
// TODO
@@ -996,8 +996,9 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
996996
} else {
997997
g.expr(node.left)
998998
}
999-
if node.from_embed_type != 0 {
1000-
embed_name := typ_sym.embed_name()
999+
for embed in node.from_embed_types {
1000+
embed_sym := g.table.get_type_symbol(embed)
1001+
embed_name := embed_sym.embed_name()
10011002
if node.left_type.is_ptr() {
10021003
g.write('->')
10031004
} else {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
struct Foo1 {
2+
x int
3+
}
4+
5+
struct Foo2 {
6+
Foo1
7+
}
8+
9+
struct Foo3 {
10+
Foo2
11+
}
12+
13+
fn (f Foo1) bar() string {
14+
println('Foo1.bar()')
15+
return 'Foo1.bar()'
16+
}
17+
18+
fn test_nested_struct_embed_method_call() {
19+
f3 := Foo3{}
20+
ret := f3.bar()
21+
assert ret == 'Foo1.bar()'
22+
}

0 commit comments

Comments
 (0)