diff --git a/vlib/v/checker/match.v b/vlib/v/checker/match.v index b9d125494ca7ea..976002db11e5b6 100644 --- a/vlib/v/checker/match.v +++ b/vlib/v/checker/match.v @@ -71,16 +71,18 @@ fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type { } else { node.expected_type } - expr_type := if stmt.expr is ast.CallExpr { + expr_type := c.unwrap_generic(if stmt.expr is ast.CallExpr { stmt.typ } else { c.expr(mut stmt.expr) - } + }) + unwrapped_expected_type := c.unwrap_generic(node.expected_type) stmt.typ = expr_type if first_iteration { - if node.expected_type.has_option_or_result() - || c.table.type_kind(node.expected_type) in [.sum_type, .multi_return] { - c.check_match_branch_last_stmt(stmt, node.expected_type, expr_type) + if unwrapped_expected_type.has_option_or_result() + || c.table.type_kind(unwrapped_expected_type) in [.sum_type, .multi_return] { + c.check_match_branch_last_stmt(stmt, unwrapped_expected_type, + expr_type) ret_type = node.expected_type } else { ret_type = expr_type @@ -105,10 +107,9 @@ fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type { } } else { if ret_type.idx() != expr_type.idx() { - if node.expected_type.has_option_or_result() + if unwrapped_expected_type.has_option_or_result() && c.table.sym(stmt.typ).kind == .struct - && (c.table.sym(ret_type).kind != .sum_type - || !c.check_types(expr_type, ret_type)) + && !c.check_types(expr_type, c.unwrap_generic(ret_type)) && c.type_implements(stmt.typ, ast.error_type, node.pos) { stmt.expr = ast.CastExpr{ expr: stmt.expr @@ -119,7 +120,8 @@ fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type { } stmt.typ = ast.error_type } else { - c.check_match_branch_last_stmt(stmt, ret_type, expr_type) + c.check_match_branch_last_stmt(stmt, c.unwrap_generic(ret_type), + expr_type) if ret_type.is_number() && expr_type.is_number() && !c.inside_return { ret_type = c.promote_num(ret_type, expr_type) } @@ -130,13 +132,13 @@ fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type { stmt_sym := c.table.sym(stmt.typ) if ret_sym.kind !in [.sum_type, .interface] && stmt_sym.kind in [.sum_type, .interface] { - c.error('return type mismatch, it should be `${ret_sym.name}`', + c.error('return type mismatch, it should be `${ret_sym.name}`, but it is instead `${c.table.type_to_str(expr_type)}`', stmt.pos) } if ret_type.nr_muls() != stmt.typ.nr_muls() && stmt.typ.idx() !in [ast.voidptr_type_idx, ast.nil_type_idx] { type_name := '&'.repeat(ret_type.nr_muls()) + ret_sym.name - c.error('return type mismatch, it should be `${type_name}`', + c.error('return type mismatch, it should be `${type_name}`, but it is instead `${c.table.type_to_str(expr_type)}`', stmt.pos) } } @@ -279,7 +281,8 @@ fn (mut c Checker) check_match_branch_last_stmt(last_stmt ast.ExprStmt, ret_type return } } - c.error('return type mismatch, it should be `${ret_sym.name}`', last_stmt.pos) + c.error('return type mismatch, it should be `${ret_sym.name}`, but it is instead `${c.table.type_to_str(expr_type)}`', + last_stmt.pos) } } else if expr_type == ast.void_type && ret_type.idx() == ast.void_type_idx && ret_type.has_option_or_result() { diff --git a/vlib/v/checker/tests/match_generic_case_err.out b/vlib/v/checker/tests/match_generic_case_err.out new file mode 100644 index 00000000000000..3e01ab88c2e417 --- /dev/null +++ b/vlib/v/checker/tests/match_generic_case_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/match_generic_case_err.vv:16:4: error: return type mismatch, it should be `int`, but it is instead `string` + 14 | } + 15 | 'int' { + 16 | v + | ^ + 17 | } + 18 | else { diff --git a/vlib/v/checker/tests/match_generic_case_err.vv b/vlib/v/checker/tests/match_generic_case_err.vv new file mode 100644 index 00000000000000..eb6e08c048028a --- /dev/null +++ b/vlib/v/checker/tests/match_generic_case_err.vv @@ -0,0 +1,22 @@ +module main + +import strconv + +fn main() { + println(to_int('1')) + println(to_int(1)) +} + +fn to_int[T](v T) i64 { + return match typeof(v).name { + 'string' { + strconv.atoi(v) or { 0 } + } + 'int' { + v + } + else { + 0 + } + } +} diff --git a/vlib/v/checker/tests/match_return_mismatch_type_err.out b/vlib/v/checker/tests/match_return_mismatch_type_err.out index 9c4bc3cfa41109..ca6cf78897f191 100644 --- a/vlib/v/checker/tests/match_return_mismatch_type_err.out +++ b/vlib/v/checker/tests/match_return_mismatch_type_err.out @@ -5,35 +5,35 @@ vlib/v/checker/tests/match_return_mismatch_type_err.vv:27:6: warning: cannot ass | ^ 28 | string { &any } 29 | else { &variable } -vlib/v/checker/tests/match_return_mismatch_type_err.vv:4:10: error: return type mismatch, it should be `string` +vlib/v/checker/tests/match_return_mismatch_type_err.vv:4:10: error: return type mismatch, it should be `string`, but it is instead `int literal` 2 | a := match 1 { 3 | 1 { 'aa' } 4 | else { 22 } | ~~ 5 | } 6 | println(a) -vlib/v/checker/tests/match_return_mismatch_type_err.vv:18:10: error: return type mismatch, it should be `&string` +vlib/v/checker/tests/match_return_mismatch_type_err.vv:18:10: error: return type mismatch, it should be `&string`, but it is instead `string` 16 | _ = match any { 17 | string { &any } 18 | else { variable } | ~~~~~~~~ 19 | } 20 | -vlib/v/checker/tests/match_return_mismatch_type_err.vv:23:10: error: return type mismatch, it should be `string` +vlib/v/checker/tests/match_return_mismatch_type_err.vv:23:10: error: return type mismatch, it should be `string`, but it is instead `&string` 21 | _ = match any { 22 | string { any } 23 | else { &variable } | ^ 24 | } 25 | -vlib/v/checker/tests/match_return_mismatch_type_err.vv:43:10: error: return type mismatch, it should be `&string` +vlib/v/checker/tests/match_return_mismatch_type_err.vv:43:10: error: return type mismatch, it should be `&string`, but it is instead `string` 41 | _ = match any { 42 | string { &any } 43 | else { variable } | ~~~~~~~~ 44 | } 45 | -vlib/v/checker/tests/match_return_mismatch_type_err.vv:48:10: error: return type mismatch, it should be `string` +vlib/v/checker/tests/match_return_mismatch_type_err.vv:48:10: error: return type mismatch, it should be `string`, but it is instead `&string` 46 | _ = match any { 47 | string { any } 48 | else { &variable } diff --git a/vlib/v/checker/tests/match_return_sumtype_mismatch_err.out b/vlib/v/checker/tests/match_return_sumtype_mismatch_err.out index 25086c86293a1c..4c2e6eaf4ec69a 100644 --- a/vlib/v/checker/tests/match_return_sumtype_mismatch_err.out +++ b/vlib/v/checker/tests/match_return_sumtype_mismatch_err.out @@ -1,4 +1,4 @@ -vlib/v/checker/tests/match_return_sumtype_mismatch_err.vv:15:11: error: return type mismatch, it should be `Myt` +vlib/v/checker/tests/match_return_sumtype_mismatch_err.vv:15:11: error: return type mismatch, it should be `Myt`, but it is instead `rune` 13 | return match b { 14 | true { St('TRUE') } 15 | false { `F` } diff --git a/vlib/v/checker/tests/return_match_expr_type_mismatch.out b/vlib/v/checker/tests/return_match_expr_type_mismatch.out index aa57fb5d257ab1..fbd7de12d3779f 100644 --- a/vlib/v/checker/tests/return_match_expr_type_mismatch.out +++ b/vlib/v/checker/tests/return_match_expr_type_mismatch.out @@ -1,4 +1,4 @@ -vlib/v/checker/tests/return_match_expr_type_mismatch.vv:18:16: error: return type mismatch, it should be `SomeTuple` +vlib/v/checker/tests/return_match_expr_type_mismatch.vv:18:16: error: return type mismatch, it should be `SomeTuple`, but it is instead `Poss1` 16 | fn get_file(item PossOwner) ?SomeTuple { 17 | return match item.pos { 18 | Poss1 { item.pos } diff --git a/vlib/v/tests/generics/generic_match_expr_test.v b/vlib/v/tests/generics/generic_match_expr_test.v new file mode 100644 index 00000000000000..a6188b2c225a61 --- /dev/null +++ b/vlib/v/tests/generics/generic_match_expr_test.v @@ -0,0 +1,24 @@ +struct Foo[T] { + a T +} + +fn r[T]() Foo[T] { + return Foo[T]{} +} + +fn t[T](v T) !Foo[T] { + return match typeof(v).name { + 'string' { + r[T]() + } + else { + r[T]() + } + } +} + +fn test_main() { + t(1)! + t('')! + assert true +}