From b6058bfd6e247e0a02b55dc06821043c7ccbc355 Mon Sep 17 00:00:00 2001 From: yuyi Date: Thu, 5 May 2022 16:24:20 +0800 Subject: [PATCH] parser, checker: fix generic method on nested struct (fix #14089) (#14310) --- vlib/v/checker/fn.v | 47 +++++++++++-------- vlib/v/parser/fn.v | 5 +- .../generics_method_on_nested_struct_test.v | 36 ++++++++++++++ 3 files changed, 66 insertions(+), 22 deletions(-) create mode 100644 vlib/v/tests/generics_method_on_nested_struct_test.v diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 5d4960fa9a..a68281a7f1 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -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, x.foo() -> x.foo() - 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, x.foo() -> x.foo() + 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 { diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 09b68a1bd5..59a364e5dc 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -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 } diff --git a/vlib/v/tests/generics_method_on_nested_struct_test.v b/vlib/v/tests/generics_method_on_nested_struct_test.v new file mode 100644 index 0000000000..04c7b85255 --- /dev/null +++ b/vlib/v/tests/generics_method_on_nested_struct_test.v @@ -0,0 +1,36 @@ +struct Outer { +mut: + inner Inner +} + +struct Inner { + val T +} + +fn (mut i Inner) next(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{ + inner: Inner{ + val: 1.1 + } + } + r1 := outer.inner.next(99.0) + println(r1) + assert r1 == 32.0 + + r2 := outer.inner.next(99.0) + println(r2) + assert r2 == 32.0 + + r3 := outer.inner.next(f32(99.0)) + println(r3) + assert r3 == 32.0 +}