From f194d3ca2e2619c5e29880c55e10a5a6a89071c8 Mon Sep 17 00:00:00 2001 From: yuyi Date: Sun, 21 Aug 2022 19:12:31 +0800 Subject: [PATCH] checker: fix generic method with nested generic method (#15480) --- vlib/v/checker/fn.v | 24 ++++++++----------- ...s_method_with_nested_generic_method_test.v | 23 ++++++++++++++++++ 2 files changed, 33 insertions(+), 14 deletions(-) create mode 100644 vlib/v/tests/generics_method_with_nested_generic_method_test.v diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index e900b295aa..7c31ee2a15 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -307,7 +307,8 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) { } if node.return_type != ast.void_type_idx - && node.return_type.clear_flag(.optional) != ast.void_type_idx { + && node.return_type.clear_flag(.optional) != ast.void_type_idx + && node.return_type.clear_flag(.result) != ast.void_type_idx { c.error('test functions should either return nothing at all, or be marked to return `?`', node.pos) } @@ -1318,6 +1319,7 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { } node.is_noreturn = method.is_noreturn node.is_ctor_new = method.is_ctor_new + node.return_type = method.return_type if !method.is_pub && !c.pref.is_test && method.mod != c.mod { // If a private method is called outside of the module // its receiver type is defined in, show an error. @@ -1550,21 +1552,16 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { c.table.register_fn_concrete_types(method.fkey(), concrete_types) } - // resolve return generics struct to concrete type - if method.generic_names.len > 0 && method.return_type.has_flag(.generic) - && !isnil(c.table.cur_fn) && c.table.cur_fn.generic_names.len == 0 { - node.return_type = c.table.unwrap_generic_type(method.return_type, method.generic_names, - concrete_types) - } else { - node.return_type = method.return_type - } - if node.concrete_types.len > 0 && method.return_type != 0 && !isnil(c.table.cur_fn) - && c.table.cur_fn.generic_names.len == 0 { + if node.concrete_types.len > 0 && node.concrete_types.all(!it.has_flag(.generic)) + && method.return_type.has_flag(.generic) && method.generic_names.len > 0 + && method.generic_names.len == node.concrete_types.len { if typ := c.table.resolve_generic_to_concrete(method.return_type, method.generic_names, concrete_types) { node.return_type = typ - return typ + } else { + node.return_type = c.table.unwrap_generic_type(method.return_type, method.generic_names, + concrete_types) } } if node.concrete_types.len > 0 && method.generic_names.len == 0 { @@ -1578,9 +1575,8 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { } } } - return node.return_type } - return method.return_type + return node.return_type } // TODO: str methods if method_name == 'str' { diff --git a/vlib/v/tests/generics_method_with_nested_generic_method_test.v b/vlib/v/tests/generics_method_with_nested_generic_method_test.v new file mode 100644 index 0000000000..63212d689b --- /dev/null +++ b/vlib/v/tests/generics_method_with_nested_generic_method_test.v @@ -0,0 +1,23 @@ +struct Dummy {} + +fn (dummy &Dummy) res() !T { + $if T is int { + return 1 + } $else $if T is f32 { + return dummy.res()! + 1 + } $else { + return error('exhausted') + } +} + +fn test_generics_method_with_nested_generic_method() ! { + d := Dummy{} + + println(d.res()!) + ret1 := d.res()! + assert ret1 == 1 + + println(d.res()!) + ret2 := d.res()! + assert ret2 == 2.0 +}