Skip to content

Commit

Permalink
checker: fix missing check for concrete type on match branch expr (fix
Browse files Browse the repository at this point in the history
  • Loading branch information
felipensp authored Jan 19, 2025
1 parent fda0af4 commit b51dfcf
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 19 deletions.
27 changes: 15 additions & 12 deletions vlib/v/checker/match.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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)
}
Expand All @@ -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)
}
}
Expand Down Expand Up @@ -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() {
Expand Down
7 changes: 7 additions & 0 deletions vlib/v/checker/tests/match_generic_case_err.out
Original file line number Diff line number Diff line change
@@ -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 {
22 changes: 22 additions & 0 deletions vlib/v/checker/tests/match_generic_case_err.vv
Original file line number Diff line number Diff line change
@@ -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
}
}
}
10 changes: 5 additions & 5 deletions vlib/v/checker/tests/match_return_mismatch_type_err.out
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
2 changes: 1 addition & 1 deletion vlib/v/checker/tests/match_return_sumtype_mismatch_err.out
Original file line number Diff line number Diff line change
@@ -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` }
Expand Down
2 changes: 1 addition & 1 deletion vlib/v/checker/tests/return_match_expr_type_mismatch.out
Original file line number Diff line number Diff line change
@@ -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 }
Expand Down
24 changes: 24 additions & 0 deletions vlib/v/tests/generics/generic_match_expr_test.v
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit b51dfcf

Please sign in to comment.