Skip to content

Commit b6058bf

Browse files
authored
parser, checker: fix generic method on nested struct (fix #14089) (#14310)
1 parent 8afdb1c commit b6058bf

File tree

3 files changed

+66
-22
lines changed

3 files changed

+66
-22
lines changed

vlib/v/checker/fn.v

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,13 +1069,6 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
10691069
left_type := c.expr(node.left)
10701070
c.expected_type = left_type
10711071
mut is_generic := left_type.has_flag(.generic)
1072-
// x is Bar<T>, x.foo() -> x.foo<T>()
1073-
if is_generic && node.concrete_types.len == 0 {
1074-
rec_sym := c.table.sym(left_type)
1075-
if rec_sym.info is ast.Struct {
1076-
node.concrete_types = rec_sym.info.generic_types
1077-
}
1078-
}
10791072
node.left_type = left_type
10801073
// Set default values for .return_type & .receiver_type too,
10811074
// or there will be hard tRo diagnose 0 type panics in cgen.
@@ -1113,19 +1106,6 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
11131106
// c.error('`void` type has no methods', node.left.pos())
11141107
return ast.void_type
11151108
}
1116-
mut concrete_types := []ast.Type{}
1117-
for concrete_type in node.concrete_types {
1118-
if concrete_type.has_flag(.generic) {
1119-
concrete_types << c.unwrap_generic(concrete_type)
1120-
} else {
1121-
concrete_types << concrete_type
1122-
}
1123-
}
1124-
if concrete_types.len > 0 {
1125-
if c.table.register_fn_concrete_types(node.fkey(), concrete_types) {
1126-
c.need_recheck_generic_fns = true
1127-
}
1128-
}
11291109
// TODO: remove this for actual methods, use only for compiler magic
11301110
// FIXME: Argument count != 1 will break these
11311111
if left_sym.kind == .array && method_name in array_builtin_methods {
@@ -1239,6 +1219,33 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
12391219
}
12401220
}
12411221
if has_method {
1222+
// x is Bar<T>, x.foo() -> x.foo<T>()
1223+
rec_sym := c.table.sym(node.left_type)
1224+
rec_is_generic := left_type.has_flag(.generic)
1225+
if rec_sym.info is ast.Struct {
1226+
if rec_is_generic && node.concrete_types.len == 0 {
1227+
node.concrete_types = rec_sym.info.generic_types
1228+
} else if !rec_is_generic && rec_sym.info.concrete_types.len > 0
1229+
&& node.concrete_types.len > 0
1230+
&& rec_sym.info.concrete_types.len + node.concrete_types.len == method.generic_names.len {
1231+
t_concrete_types := node.concrete_types.clone()
1232+
node.concrete_types = rec_sym.info.concrete_types
1233+
node.concrete_types << t_concrete_types
1234+
}
1235+
}
1236+
mut concrete_types := []ast.Type{}
1237+
for concrete_type in node.concrete_types {
1238+
if concrete_type.has_flag(.generic) {
1239+
concrete_types << c.unwrap_generic(concrete_type)
1240+
} else {
1241+
concrete_types << concrete_type
1242+
}
1243+
}
1244+
if concrete_types.len > 0 {
1245+
if c.table.register_fn_concrete_types(node.fkey(), concrete_types) {
1246+
c.need_recheck_generic_fns = true
1247+
}
1248+
}
12421249
node.is_noreturn = method.is_noreturn
12431250
node.is_ctor_new = method.is_ctor_new
12441251
if !method.is_pub && !c.pref.is_test && method.mod != c.mod {

vlib/v/parser/fn.v

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,8 +352,9 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
352352
if is_method && rec.typ.has_flag(.generic) {
353353
sym := p.table.sym(rec.typ)
354354
if sym.info is ast.Struct {
355-
rec_generic_names := sym.info.generic_types.map(p.table.sym(it).name)
356-
for gname in rec_generic_names {
355+
fn_generic_names := generic_names.clone()
356+
generic_names = sym.info.generic_types.map(p.table.sym(it).name)
357+
for gname in fn_generic_names {
357358
if gname !in generic_names {
358359
generic_names << gname
359360
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
struct Outer<T> {
2+
mut:
3+
inner Inner<T>
4+
}
5+
6+
struct Inner<T> {
7+
val T
8+
}
9+
10+
fn (mut i Inner<T>) next<S>(input S) f64 {
11+
$if S is f32 {
12+
return 32
13+
} $else {
14+
panic('"$S.name" is not supported')
15+
return 0
16+
}
17+
}
18+
19+
fn test_generics_method_on_nested_struct() {
20+
mut outer := Outer<f64>{
21+
inner: Inner<f64>{
22+
val: 1.1
23+
}
24+
}
25+
r1 := outer.inner.next<f32>(99.0)
26+
println(r1)
27+
assert r1 == 32.0
28+
29+
r2 := outer.inner.next<f64, f32>(99.0)
30+
println(r2)
31+
assert r2 == 32.0
32+
33+
r3 := outer.inner.next(f32(99.0))
34+
println(r3)
35+
assert r3 == 32.0
36+
}

0 commit comments

Comments
 (0)