parser, checker: fix generic method on nested struct (fix #14089) (#14310)

yuyi 2022-05-05 16:24:20 +08:00 committed by Jef Roosens
parent a17d744e8b
commit 798873fe44
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
3 changed files with 66 additions and 22 deletions

View File

@ -1069,13 +1069,6 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
left_type := c.expr(node.left)
c.expected_type = left_type
mut is_generic := left_type.has_flag(.generic)
// x is Bar<T>, x.foo() -> x.foo<T>()
if is_generic && node.concrete_types.len == 0 {
rec_sym := c.table.sym(left_type)
if rec_sym.info is ast.Struct {
node.concrete_types = rec_sym.info.generic_types
}
}
node.left_type = left_type
// Set default values for .return_type & .receiver_type too,
// 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 {
// c.error('`void` type has no methods', node.left.pos())
return ast.void_type
}
mut concrete_types := []ast.Type{}
for concrete_type in node.concrete_types {
if concrete_type.has_flag(.generic) {
concrete_types << c.unwrap_generic(concrete_type)
} else {
concrete_types << concrete_type
}
}
if concrete_types.len > 0 {
if c.table.register_fn_concrete_types(node.fkey(), concrete_types) {
c.need_recheck_generic_fns = true
}
}
// TODO: remove this for actual methods, use only for compiler magic
// FIXME: Argument count != 1 will break these
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 {
}
}
if has_method {
// x is Bar<T>, x.foo() -> x.foo<T>()
rec_sym := c.table.sym(node.left_type)
rec_is_generic := left_type.has_flag(.generic)
if rec_sym.info is ast.Struct {
if rec_is_generic && node.concrete_types.len == 0 {
node.concrete_types = rec_sym.info.generic_types
} else if !rec_is_generic && rec_sym.info.concrete_types.len > 0
&& node.concrete_types.len > 0
&& rec_sym.info.concrete_types.len + node.concrete_types.len == method.generic_names.len {
t_concrete_types := node.concrete_types.clone()
node.concrete_types = rec_sym.info.concrete_types
node.concrete_types << t_concrete_types
}
}
mut concrete_types := []ast.Type{}
for concrete_type in node.concrete_types {
if concrete_type.has_flag(.generic) {
concrete_types << c.unwrap_generic(concrete_type)
} else {
concrete_types << concrete_type
}
}
if concrete_types.len > 0 {
if c.table.register_fn_concrete_types(node.fkey(), concrete_types) {
c.need_recheck_generic_fns = true
}
}
node.is_noreturn = method.is_noreturn
node.is_ctor_new = method.is_ctor_new
if !method.is_pub && !c.pref.is_test && method.mod != c.mod {

View File

@ -352,8 +352,9 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
if is_method && rec.typ.has_flag(.generic) {
sym := p.table.sym(rec.typ)
if sym.info is ast.Struct {
rec_generic_names := sym.info.generic_types.map(p.table.sym(it).name)
for gname in rec_generic_names {
fn_generic_names := generic_names.clone()
generic_names = sym.info.generic_types.map(p.table.sym(it).name)
for gname in fn_generic_names {
if gname !in generic_names {
generic_names << gname
}

View File

@ -0,0 +1,36 @@
struct Outer<T> {
mut:
inner Inner<T>
}
struct Inner<T> {
val T
}
fn (mut i Inner<T>) next<S>(input S) f64 {
$if S is f32 {
return 32
} $else {
panic('"$S.name" is not supported')
return 0
}
}
fn test_generics_method_on_nested_struct() {
mut outer := Outer<f64>{
inner: Inner<f64>{
val: 1.1
}
}
r1 := outer.inner.next<f32>(99.0)
println(r1)
assert r1 == 32.0
r2 := outer.inner.next<f64, f32>(99.0)
println(r2)
assert r2 == 32.0
r3 := outer.inner.next(f32(99.0))
println(r3)
assert r3 == 32.0
}