From f31a3b4250a834f3deed83e4816f99c7ba599455 Mon Sep 17 00:00:00 2001 From: yuyi Date: Sun, 13 Nov 2022 16:58:53 +0800 Subject: [PATCH] checker: check sumtype argument mismatch in generic methods (fix #16340 part2) (#16403) --- vlib/v/checker/check_types.v | 5 +- vlib/v/checker/fn.v | 39 ++++++++----- .../generics_method_sumtype_arg_mismatch.out | 7 +++ .../generics_method_sumtype_arg_mismatch.vv | 58 +++++++++++++++++++ 4 files changed, 92 insertions(+), 17 deletions(-) create mode 100644 vlib/v/checker/tests/generics_method_sumtype_arg_mismatch.out create mode 100644 vlib/v/checker/tests/generics_method_sumtype_arg_mismatch.vv diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v index 368ded77af..cbfedd8b7a 100644 --- a/vlib/v/checker/check_types.v +++ b/vlib/v/checker/check_types.v @@ -267,7 +267,10 @@ pub fn (mut c Checker) check_expected_call_arg(got ast.Type, expected_ ast.Type, if got_typ_sym.symbol_name_except_generic() == expected_typ_sym.symbol_name_except_generic() { // Check if we are making a comparison between two different types of // the same type like `Type and &Type<>` - if (got.is_ptr() != expected.is_ptr()) || !c.check_same_module(got, expected) { + if (got.is_ptr() != expected.is_ptr()) + || !c.check_same_module(got, expected) + || (!got.is_ptr() && !expected.is_ptr() + && got_typ_sym.name != expected_typ_sym.name) { got_typ_str, expected_typ_str := c.get_string_names_of(got, expected) return error('cannot use `$got_typ_str` as `$expected_typ_str`') } diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index cc23897e59..09bb7a8bc9 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -1428,18 +1428,21 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { rec_sym := c.table.final_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 - } 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 + match rec_sym.info { + ast.Struct, ast.SumType, ast.Interface { + 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 + } 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 + } } + else {} } mut concrete_types := []ast.Type{} for concrete_type in node.concrete_types { @@ -1644,12 +1647,16 @@ 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, - method_concrete_types) - { - got_arg_typ = got_utyp + if c.table.cur_fn != unsafe { nil } && c.table.cur_concrete_types.len > 0 { + got_arg_typ = c.unwrap_generic(got_arg_typ) } else { - continue + if got_utyp := c.table.resolve_generic_to_concrete(got_arg_typ, + method.generic_names, method_concrete_types) + { + got_arg_typ = got_utyp + } else { + continue + } } } } diff --git a/vlib/v/checker/tests/generics_method_sumtype_arg_mismatch.out b/vlib/v/checker/tests/generics_method_sumtype_arg_mismatch.out new file mode 100644 index 0000000000..4651a6a798 --- /dev/null +++ b/vlib/v/checker/tests/generics_method_sumtype_arg_mismatch.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/generics_method_sumtype_arg_mismatch.vv:56:13: error: cannot use `Maybe` as `Maybe` in argument 1 to `Maybe.or` + 54 | b := some('abc') + 55 | + 56 | c := a.@or(b) + | ^ + 57 | println(c) + 58 | } diff --git a/vlib/v/checker/tests/generics_method_sumtype_arg_mismatch.vv b/vlib/v/checker/tests/generics_method_sumtype_arg_mismatch.vv new file mode 100644 index 0000000000..0d8c7987f2 --- /dev/null +++ b/vlib/v/checker/tests/generics_method_sumtype_arg_mismatch.vv @@ -0,0 +1,58 @@ +module main + +struct None {} + +pub type Maybe = None | T + +pub fn (m Maybe) str() string { + return if m is T { + x := m as T + 'Some($x)' + } else { + 'Noth' + } +} + +pub fn some(v T) Maybe { + return Maybe(v) +} + +pub fn noth() Maybe { + return Maybe(None{}) +} + +pub fn (m Maybe) is_some() bool { + return match m { + None { false } + T { true } + } +} + +pub fn (m Maybe) is_noth() bool { + return match m { + None { true } + T { false } + } +} + +pub fn (m Maybe) @or(m2 Maybe) Maybe { + return match m { + None { + match m2 { + None { None{} } + T { m2 } + } + } + T { + m + } + } +} + +fn main() { + a := some(123) + b := some('abc') + + c := a.@or(b) + println(c) +}