Skip to content

Commit

Permalink
checker: improve error messages for number casts used as branch value…
Browse files Browse the repository at this point in the history
…s in match expressions (#20111)
  • Loading branch information
Delta456 committed Dec 14, 2023
1 parent 0d2ed22 commit 68a6397
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 0 deletions.
83 changes: 83 additions & 0 deletions vlib/v/checker/match.v
Expand Up @@ -36,6 +36,8 @@ fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type {
c.match_exprs(mut node, cond_type_sym)
c.expected_type = cond_type
mut first_iteration := true
mut infer_cast_type := ast.void_type
mut need_explicit_cast := false
mut ret_type := ast.void_type
mut nbranches_with_return := 0
mut nbranches_without_return := 0
Expand Down Expand Up @@ -87,6 +89,11 @@ fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type {
}
}
}
infer_cast_type = stmt.typ
if mut stmt.expr is ast.CastExpr {
need_explicit_cast = true
infer_cast_type = stmt.expr.typ
}
} else {
if node.is_expr && ret_type.idx() != expr_type.idx() {
if (node.expected_type.has_flag(.option)
Expand Down Expand Up @@ -120,6 +127,82 @@ fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type {
stmt.pos)
}
}
if !node.is_sum_type {
if mut stmt.expr is ast.CastExpr {
expr_typ_sym := c.table.sym(stmt.expr.typ)
if need_explicit_cast {
if infer_cast_type != stmt.expr.typ
&& expr_typ_sym.kind !in [.interface_, .sum_type] {
c.error('the type of the last expression in the first match branch was an explicit `${c.table.type_to_str(infer_cast_type)}`, not `${c.table.type_to_str(stmt.expr.typ)}`',
stmt.pos)
}
} else {
if infer_cast_type != stmt.expr.typ
&& expr_typ_sym.kind !in [.interface_, .sum_type]
&& c.promote_num(stmt.expr.typ, ast.int_type) != ast.int_type {
c.error('the type of the last expression of the first match branch was `${c.table.type_to_str(infer_cast_type)}`, which is not compatible with `${c.table.type_to_str(stmt.expr.typ)}`',
stmt.pos)
}
}
} else {
if mut stmt.expr is ast.IntegerLiteral {
cast_type_sym := c.table.sym(infer_cast_type)
num := stmt.expr.val.i64()
mut needs_explicit_cast := false

match cast_type_sym.kind {
.u8 {
if !(num >= min_u8 && num <= max_u8) {
needs_explicit_cast = true
}
}
.u16 {
if !(num >= min_u16 && num <= max_u16) {
needs_explicit_cast = true
}
}
.u32 {
if !(num >= min_u32 && num <= max_u32) {
needs_explicit_cast = true
}
}
.u64 {
if !(num >= min_u64 && num <= max_u64) {
needs_explicit_cast = true
}
}
.i8 {
if !(num >= min_i32 && num <= max_i32) {
needs_explicit_cast = true
}
}
.i16 {
if !(num >= min_i16 && num <= max_i16) {
needs_explicit_cast = true
}
}
.i32, .int {
if !(num >= min_i32 && num <= max_i32) {
needs_explicit_cast = true
}
}
.i64 {
if !(num >= min_i64 && num <= max_i64) {
needs_explicit_cast = true
}
}
.int_literal {
needs_explicit_cast = false
}
else {}
}
if needs_explicit_cast {
c.error('${num} does not fit the range of `${c.table.type_to_str(infer_cast_type)}`',
stmt.pos)
}
}
}
}
}
} else if stmt !in [ast.Return, ast.BranchStmt] {
if node.is_expr && ret_type != ast.void_type {
Expand Down
7 changes: 7 additions & 0 deletions vlib/v/checker/tests/match_incorrect_cast_branch_err.out
@@ -0,0 +1,7 @@
vlib/v/checker/tests/match_incorrect_cast_branch_err.vv:5:9: error: the type of the last expression in the first match branch was an explicit `u8`, not `u16`
3 | nb := match name {
4 | 'a' { u8(10) }
5 | 'b' { u16(500) }
| ~~~~~~~~
6 | else { u8(0) }
7 | }
10 changes: 10 additions & 0 deletions vlib/v/checker/tests/match_incorrect_cast_branch_err.vv
@@ -0,0 +1,10 @@
fn main () {
name := 'b'
nb := match name {
'a' { u8(10) }
'b' { u16(500) }
else { u8(0) }
}

println(nb)
}
7 changes: 7 additions & 0 deletions vlib/v/checker/tests/match_last_branch_only_casted.out
@@ -0,0 +1,7 @@
vlib/v/checker/tests/match_last_branch_only_casted.vv:6:10: error: the type of the last expression of the first match branch was `int literal`, which is not compatible with `u64`
4 | 'a' { 10 }
5 | 'b' { 500 }
6 | else { u64(345454) }
| ~~~~~~~~~~~
7 | }
8 |
10 changes: 10 additions & 0 deletions vlib/v/checker/tests/match_last_branch_only_casted.vv
@@ -0,0 +1,10 @@
fn main () {
name := 'b'
nb := match name {
'a' { 10 }
'b' { 500 }
else { u64(345454) }
}

println(nb)
}
7 changes: 7 additions & 0 deletions vlib/v/checker/tests/match_no_branch_cast_rest_casted_err.out
@@ -0,0 +1,7 @@
vlib/v/checker/tests/match_no_branch_cast_rest_casted_err.vv:5:9: error: 345 does not fit the range of `u8`
3 | nb := match name {
4 | 'a' { u8(10) }
5 | 'b' { 345 }
| ~~~
6 | else { 0 }
7 | }
10 changes: 10 additions & 0 deletions vlib/v/checker/tests/match_no_branch_cast_rest_casted_err.vv
@@ -0,0 +1,10 @@
fn main () {
name := 'b'
nb := match name {
'a' { u8(10) }
'b' { 345 }
else { 0 }
}

println(nb)
}

0 comments on commit 68a6397

Please sign in to comment.