diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index ca53c221b2..170ee3c74c 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -1519,6 +1519,9 @@ pub fn (t Table) does_type_implement_interface(typ Type, inter_typ Type) bool { // Even map[string]map[string]T can be resolved. // This is used for resolving the generic return type of CallExpr white `unwrap_generic` is used to resolve generic usage in FnDecl. pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_names []string, concrete_types []Type) ?Type { + if generic_names.len != concrete_types.len { + return none + } mut sym := t.sym(generic_type) if sym.name in generic_names { index := generic_names.index(sym.name) diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 25783c61b2..28d287a9c8 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -1222,7 +1222,9 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { // x is Bar, x.foo() -> x.foo() rec_sym := c.table.sym(node.left_type) rec_is_generic := left_type.has_flag(.generic) + mut rec_concrete_types := []ast.Type{} if rec_sym.info is ast.Struct { + rec_concrete_types = rec_sym.info.concrete_types.clone() if rec_is_generic && node.concrete_types.len == 0 && method.generic_names.len == rec_sym.info.generic_types.len { node.concrete_types = rec_sym.info.generic_types @@ -1304,11 +1306,7 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { no_type_promotion = true } } - // if method_name == 'clone' { - // println('CLONE nr args=$method.args.len') - // } - // node.args << method.args[0].typ - // node.exp_arg_types << method.args[0].typ + for i, mut arg in node.args { if i > 0 || exp_arg_typ == ast.Type(0) { exp_arg_typ = if method.is_variadic && i >= method.params.len - 1 { @@ -1341,8 +1339,13 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { final_arg_sym = c.table.sym(final_arg_typ) } if exp_arg_typ.has_flag(.generic) { + method_concrete_types := if method.generic_names.len == rec_concrete_types.len { + rec_concrete_types + } else { + concrete_types + } if exp_utyp := c.table.resolve_generic_to_concrete(exp_arg_typ, method.generic_names, - concrete_types) + method_concrete_types) { exp_arg_typ = exp_utyp } else { @@ -1351,7 +1354,7 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { if got_arg_typ.has_flag(.generic) { if got_utyp := c.table.resolve_generic_to_concrete(got_arg_typ, method.generic_names, - concrete_types) + method_concrete_types) { got_arg_typ = got_utyp } else { diff --git a/vlib/v/checker/tests/generic_parameter_on_method.out b/vlib/v/checker/tests/generic_parameter_on_method.out deleted file mode 100644 index 4d82cccbdf..0000000000 --- a/vlib/v/checker/tests/generic_parameter_on_method.out +++ /dev/null @@ -1,7 +0,0 @@ -vlib/v/checker/tests/generic_parameter_on_method.vv:15:15: error: cannot use `&Type` as `Type<>` in argument 1 to `ContainerType.contains` - 13 | fn main() { - 14 | con := ContainerType{typ: &Type{0}} - 15 | con.contains(con.typ) - | ~~~~~~~ - 16 | println(con) - 17 | } diff --git a/vlib/v/checker/tests/generic_parameter_on_method.vv b/vlib/v/checker/tests/generic_parameter_on_method.vv deleted file mode 100644 index 719dbe892e..0000000000 --- a/vlib/v/checker/tests/generic_parameter_on_method.vv +++ /dev/null @@ -1,17 +0,0 @@ -struct Type { - value T -} - -struct ContainerType { - typ &Type -} - -fn (instance &ContainerType) contains(typ Type) { - println(typ) -} - -fn main() { - con := ContainerType{typ: &Type{0}} - con.contains(con.typ) - println(con) -} diff --git a/vlib/v/checker/tests/generics_fn_arguments_count_err.out b/vlib/v/checker/tests/generics_fn_arguments_count_err.out index dc9feba117..62cb366698 100644 --- a/vlib/v/checker/tests/generics_fn_arguments_count_err.out +++ b/vlib/v/checker/tests/generics_fn_arguments_count_err.out @@ -26,16 +26,16 @@ vlib/v/checker/tests/generics_fn_arguments_count_err.vv:22:22: error: expected 2 | ~~~~~~~~~~~~~~~~~~ 23 | println(ret4) 24 | } -vlib/v/checker/tests/generics_fn_arguments_count_err.vv:2:15: error: no known default format for type `B` +vlib/v/checker/tests/generics_fn_arguments_count_err.vv:2:11: error: no known default format for type `A` 1 | fn get_name(a A, b B) string { 2 | return '$a, $b' - | ^ + | ^ 3 | } 4 | -vlib/v/checker/tests/generics_fn_arguments_count_err.vv:8:15: error: no known default format for type `B` +vlib/v/checker/tests/generics_fn_arguments_count_err.vv:8:11: error: no known default format for type `A` 6 | 7 | fn (f Foo) get_name(a A, b B) string { 8 | return '$a, $b' - | ^ + | ^ 9 | } 10 | diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 603aa77855..1ecf33ad06 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -2238,7 +2238,8 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ deref_sym := g.table.sym(got_deref_type) deref_will_match := expected_type in [got_type, got_deref_type, deref_sym.parent_idx] got_is_opt := got_type.has_flag(.optional) - if deref_will_match || got_is_opt || expr.is_auto_deref_var() { + if deref_will_match || got_is_opt || expr.is_auto_deref_var() + || expected_type.has_flag(.generic) { g.write('*') } } diff --git a/vlib/v/tests/generic_fn_call_with_reference_argument_test.v b/vlib/v/tests/generic_fn_call_with_reference_argument_test.v new file mode 100644 index 0000000000..e4d9944cdb --- /dev/null +++ b/vlib/v/tests/generic_fn_call_with_reference_argument_test.v @@ -0,0 +1,25 @@ +struct Type { + value T +} + +struct ContainerType { + typ &Type +} + +fn (instance &ContainerType) contains(typ Type) bool { + println(typ) + if instance.typ == typ { + return true + } else { + return false + } +} + +fn test_generic_fn_call_with_reference_argument() { + con := ContainerType{ + typ: &Type{0} + } + ret := con.contains(con.typ) + println(con) + assert ret +} diff --git a/vlib/v/tests/generics_method_with_generic_anon_fn_argument_test.v b/vlib/v/tests/generics_method_with_generic_anon_fn_argument_test.v new file mode 100644 index 0000000000..91d19907ca --- /dev/null +++ b/vlib/v/tests/generics_method_with_generic_anon_fn_argument_test.v @@ -0,0 +1,23 @@ +struct Foo { +} + +fn (f Foo) do(name string, d fn (T), v T) T { + println('running ' + name) + d(v) + println('ran ' + name) + return v +} + +fn test_generics_method_with_generic_anon_fn_argument() { + f1 := Foo{} + r1 := f1.do('foo', fn (s string) { + println('s value is ' + s) + }, 'bar') + assert r1 == 'bar' + + f2 := Foo{} + r2 := f2.do('bar', fn (s int) { + println('s value is $s') + }, 22) + assert r2 == 22 +}