mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
all: add support for const
ident in match
range (#16526)
This commit is contained in:
parent
dee75fe970
commit
a9b41d2980
@ -2,7 +2,8 @@
|
||||
*Not yet released*
|
||||
- `go foo()` has been replaced with `spawn foo()` (launches an OS thread, `go` will be used for
|
||||
upcoming coroutines instead).
|
||||
- vfmt now supports `// vfmt off` and `// vfmt on` for turning off the formatting locally for *short* snippets of code. Useful for keeping your carefully arranged matrices in tact.
|
||||
- vfmt now supports `// vfmt off` and `// vfmt on` for turning off the formatting locally for *short* snippets of code. Useful for keeping your carefully arranged matrices intact.
|
||||
- support match branch range expressions with consts: `match x { const1...const2 {} }`
|
||||
|
||||
## V 0.3.2
|
||||
*31 Oct 2022*
|
||||
|
20
doc/docs.md
20
doc/docs.md
@ -1676,6 +1676,26 @@ Note that the ranges use `...` (three dots) rather than `..` (two dots). This is
|
||||
because the range is *inclusive* of the last element, rather than exclusive
|
||||
(as `..` ranges are). Using `..` in a match branch will throw an error.
|
||||
|
||||
```v
|
||||
const start = 1
|
||||
|
||||
const end = 10
|
||||
|
||||
c := 2
|
||||
num := match c {
|
||||
start...end {
|
||||
1000
|
||||
}
|
||||
else {
|
||||
0
|
||||
}
|
||||
}
|
||||
println(num)
|
||||
// 1000
|
||||
```
|
||||
|
||||
Constants can also be used in the range branch expressions.
|
||||
|
||||
Note: `match` as an expression is not usable in `for` loop and `if` statements.
|
||||
|
||||
### In operator
|
||||
|
@ -1325,6 +1325,7 @@ pub:
|
||||
pub mut:
|
||||
low Expr
|
||||
high Expr
|
||||
typ Type // filled in by checker; the type of `0...1` is `int` for example, while `a`...`z` is `rune` etc
|
||||
}
|
||||
|
||||
[minify]
|
||||
|
@ -12,8 +12,19 @@ fn (mut c Checker) check_types(got ast.Type, expected ast.Type) bool {
|
||||
}
|
||||
got_is_ptr := got.is_ptr()
|
||||
exp_is_ptr := expected.is_ptr()
|
||||
got_is_int := got.is_int()
|
||||
exp_is_int := expected.is_int()
|
||||
|
||||
exp_is_pure_int := expected.is_pure_int()
|
||||
got_is_pure_int := got.is_pure_int()
|
||||
// allow int literals where any kind of real integers are expected:
|
||||
if (exp_is_pure_int && got == ast.int_literal_type)
|
||||
|| (got_is_pure_int && expected == ast.int_literal_type) {
|
||||
return true
|
||||
}
|
||||
|
||||
if c.pref.translated {
|
||||
if expected.is_int() && got.is_int() {
|
||||
if exp_is_int && got_is_int {
|
||||
return true
|
||||
}
|
||||
if expected == ast.byteptr_type {
|
||||
@ -22,8 +33,8 @@ fn (mut c Checker) check_types(got ast.Type, expected ast.Type) bool {
|
||||
if expected == ast.voidptr_type || expected == ast.nil_type {
|
||||
return true
|
||||
}
|
||||
if (expected == ast.bool_type && (got.is_any_kind_of_pointer() || got.is_int()))
|
||||
|| ((expected.is_any_kind_of_pointer() || expected.is_int()) && got == ast.bool_type) {
|
||||
if (expected == ast.bool_type && (got.is_any_kind_of_pointer() || got_is_int))
|
||||
|| ((expected.is_any_kind_of_pointer() || exp_is_int) && got == ast.bool_type) {
|
||||
return true
|
||||
}
|
||||
|
||||
@ -38,8 +49,7 @@ fn (mut c Checker) check_types(got ast.Type, expected ast.Type) bool {
|
||||
}
|
||||
|
||||
// allow rune -> any int and vice versa
|
||||
if (expected == ast.rune_type && got.is_int())
|
||||
|| (got == ast.rune_type && expected.is_int()) {
|
||||
if (expected == ast.rune_type && got_is_int) || (got == ast.rune_type && exp_is_int) {
|
||||
return true
|
||||
}
|
||||
got_sym := c.table.sym(got)
|
||||
@ -546,6 +556,9 @@ fn (mut c Checker) promote_keeping_aliases(left_type ast.Type, right_type ast.Ty
|
||||
}
|
||||
|
||||
fn (mut c Checker) promote(left_type ast.Type, right_type ast.Type) ast.Type {
|
||||
if left_type == right_type {
|
||||
return left_type // strings, self defined operators
|
||||
}
|
||||
if left_type.is_any_kind_of_pointer() {
|
||||
if right_type.is_int() || c.pref.translated {
|
||||
return left_type
|
||||
@ -559,9 +572,6 @@ fn (mut c Checker) promote(left_type ast.Type, right_type ast.Type) ast.Type {
|
||||
return ast.void_type
|
||||
}
|
||||
}
|
||||
if left_type == right_type {
|
||||
return left_type // strings, self defined operators
|
||||
}
|
||||
if right_type.is_number() && left_type.is_number() {
|
||||
return c.promote_num(left_type, right_type)
|
||||
} else if left_type.has_flag(.optional) != right_type.has_flag(.optional) {
|
||||
|
@ -2452,8 +2452,20 @@ pub fn (mut c Checker) expr(node_ ast.Expr) ast.Type {
|
||||
return c.expr(node.expr)
|
||||
}
|
||||
ast.RangeExpr {
|
||||
// never happens
|
||||
return ast.void_type
|
||||
// branch range expression of `match x { a...b {} }`, or: `a#[x..y]`:
|
||||
ltyp := c.expr(node.low)
|
||||
htyp := c.expr(node.high)
|
||||
if !c.check_types(ltyp, htyp) {
|
||||
lstype := c.table.type_to_str(ltyp)
|
||||
hstype := c.table.type_to_str(htyp)
|
||||
c.add_error_detail('')
|
||||
c.add_error_detail(' low part type: ${lstype}')
|
||||
c.add_error_detail('high part type: ${hstype}')
|
||||
c.error('the low and high parts of a range expression, should have matching types',
|
||||
node.pos)
|
||||
}
|
||||
node.typ = c.promote(ltyp, htyp)
|
||||
return ltyp
|
||||
}
|
||||
ast.SelectExpr {
|
||||
return c.select_expr(mut node)
|
||||
|
@ -139,66 +139,98 @@ fn (mut c Checker) check_match_branch_last_stmt(last_stmt ast.ExprStmt, ret_type
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut c Checker) get_comptime_number_value(mut expr ast.Expr) ?i64 {
|
||||
if mut expr is ast.CharLiteral {
|
||||
return expr.val[0]
|
||||
}
|
||||
if mut expr is ast.IntegerLiteral {
|
||||
return expr.val.i64()
|
||||
}
|
||||
if mut expr is ast.Ident {
|
||||
if mut obj := c.table.global_scope.find_const(expr.name) {
|
||||
if obj.typ == 0 {
|
||||
obj.typ = c.expr(obj.expr)
|
||||
}
|
||||
return c.get_comptime_number_value(mut obj.expr)
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSymbol) {
|
||||
c.expected_type = node.expected_type
|
||||
cond_sym := c.table.sym(node.cond_type)
|
||||
// branch_exprs is a histogram of how many times
|
||||
// an expr was used in the match
|
||||
mut branch_exprs := map[string]int{}
|
||||
for branch_i, _ in node.branches {
|
||||
mut branch := node.branches[branch_i]
|
||||
mut expr_types := []ast.TypeNode{}
|
||||
for k, expr in branch.exprs {
|
||||
for k, mut expr in branch.exprs {
|
||||
mut key := ''
|
||||
if expr is ast.RangeExpr {
|
||||
// TODO: investigate why enums are different here:
|
||||
if expr !is ast.EnumVal {
|
||||
// ensure that the sub expressions of the branch are actually checked, before anything else:
|
||||
_ := c.expr(expr)
|
||||
}
|
||||
if mut expr is ast.RangeExpr {
|
||||
// Allow for `match enum_value { 4..5 { } }`, even though usually int and enum values,
|
||||
// are considered incompatible outside unsafe{}, and are not allowed to be compared directly
|
||||
if cond_sym.kind != .enum_ && !c.check_types(expr.typ, node.cond_type) {
|
||||
mcstype := c.table.type_to_str(node.cond_type)
|
||||
brstype := c.table.type_to_str(expr.typ)
|
||||
c.add_error_detail('')
|
||||
c.add_error_detail('match condition type: ${mcstype}')
|
||||
c.add_error_detail(' range type: ${brstype}')
|
||||
c.error('the range type and the match condition type should match',
|
||||
expr.pos)
|
||||
}
|
||||
mut low_value_higher_than_high_value := false
|
||||
mut low := i64(0)
|
||||
mut high := i64(0)
|
||||
c.expected_type = node.expected_type
|
||||
low_expr := expr.low
|
||||
high_expr := expr.high
|
||||
final_cond_sym := c.table.final_sym(node.cond_type)
|
||||
if low_expr is ast.IntegerLiteral {
|
||||
if high_expr is ast.IntegerLiteral
|
||||
&& (final_cond_sym.is_int() || final_cond_sym.info is ast.Enum) {
|
||||
low = low_expr.val.i64()
|
||||
high = high_expr.val.i64()
|
||||
if low > high {
|
||||
c.error('start value is higher than end value', branch.pos)
|
||||
mut both_low_and_high_are_known := false
|
||||
if low_value := c.get_comptime_number_value(mut expr.low) {
|
||||
low = low_value
|
||||
if high_value := c.get_comptime_number_value(mut expr.high) {
|
||||
high = high_value
|
||||
both_low_and_high_are_known = true
|
||||
if low_value > high_value {
|
||||
low_value_higher_than_high_value = true
|
||||
}
|
||||
} else {
|
||||
c.error('mismatched range types - ${expr.low} is an integer, but ${expr.high} is not',
|
||||
low_expr.pos)
|
||||
}
|
||||
} else if low_expr is ast.CharLiteral {
|
||||
if high_expr is ast.CharLiteral && final_cond_sym.kind in [.u8, .char, .rune] {
|
||||
low = low_expr.val[0]
|
||||
high = high_expr.val[0]
|
||||
if low > high {
|
||||
c.error('start value is higher than end value', branch.pos)
|
||||
if expr.high !is ast.EnumVal {
|
||||
c.error('match branch range expressions need the end value to be known at compile time (only enums, const or literals are supported)',
|
||||
expr.high.pos())
|
||||
}
|
||||
} else {
|
||||
typ := c.table.type_to_str(c.expr(node.cond))
|
||||
c.error('mismatched range types - trying to match `${node.cond}`, which has type `${typ}`, against a range of `rune`',
|
||||
low_expr.pos)
|
||||
}
|
||||
} else {
|
||||
typ := c.table.type_to_str(c.expr(expr.low))
|
||||
c.error('cannot use type `${typ}` in match range', branch.pos)
|
||||
if expr.low !is ast.EnumVal {
|
||||
c.error('match branch range expressions need the start value to be known at compile time (only enums, const or literals are supported)',
|
||||
expr.low.pos())
|
||||
}
|
||||
}
|
||||
high_low_cutoff := 1000
|
||||
if high - low > high_low_cutoff {
|
||||
c.warn('more than ${high_low_cutoff} possibilities (${low} ... ${high}) in match range',
|
||||
if low_value_higher_than_high_value {
|
||||
c.error('the start value `${low}` should be lower than the end value `${high}`',
|
||||
branch.pos)
|
||||
}
|
||||
for i in low .. high + 1 {
|
||||
key = i.str()
|
||||
val := if key in branch_exprs { branch_exprs[key] } else { 0 }
|
||||
if val == 1 {
|
||||
c.error('match case `${key}` is handled more than once', branch.pos)
|
||||
if both_low_and_high_are_known {
|
||||
high_low_cutoff := 1000
|
||||
if high - low > high_low_cutoff {
|
||||
c.warn('more than ${high_low_cutoff} possibilities (${low} ... ${high}) in match range',
|
||||
branch.pos)
|
||||
}
|
||||
for i in low .. high + 1 {
|
||||
key = i.str()
|
||||
val := if key in branch_exprs { branch_exprs[key] } else { 0 }
|
||||
if val == 1 {
|
||||
c.error('match case `${key}` is handled more than once', branch.pos)
|
||||
}
|
||||
branch_exprs[key] = val + 1
|
||||
}
|
||||
branch_exprs[key] = val + 1
|
||||
}
|
||||
continue
|
||||
}
|
||||
match expr {
|
||||
match mut expr {
|
||||
ast.TypeNode {
|
||||
key = c.table.type_to_str(expr.typ)
|
||||
expr_types << expr
|
||||
@ -207,7 +239,7 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym
|
||||
key = expr.val
|
||||
}
|
||||
else {
|
||||
key = expr.str()
|
||||
key = (*expr).str()
|
||||
}
|
||||
}
|
||||
val := if key in branch_exprs { branch_exprs[key] } else { 0 }
|
||||
|
20
vlib/v/checker/tests/const_expr_match_range_invalid_err.out
Normal file
20
vlib/v/checker/tests/const_expr_match_range_invalid_err.out
Normal file
@ -0,0 +1,20 @@
|
||||
vlib/v/checker/tests/const_expr_match_range_invalid_err.vv:6:2: error: the range type and the match condition type should match
|
||||
4 |
|
||||
5 | match 5 {
|
||||
6 | start...`1` {
|
||||
| ~~~~~~~~~~~
|
||||
7 | println(start)
|
||||
8 | }
|
||||
Details:
|
||||
match condition type: int
|
||||
range type: rune
|
||||
vlib/v/checker/tests/const_expr_match_range_invalid_err.vv:9:2: error: the low and high parts of a range expression, should have matching types
|
||||
7 | println(start)
|
||||
8 | }
|
||||
9 | 'str'...end {
|
||||
| ~~~~~~~~~~~
|
||||
10 | println(end)
|
||||
11 | }
|
||||
Details:
|
||||
low part type: string
|
||||
high part type: int literal
|
13
vlib/v/checker/tests/const_expr_match_range_invalid_err.vv
Normal file
13
vlib/v/checker/tests/const_expr_match_range_invalid_err.vv
Normal file
@ -0,0 +1,13 @@
|
||||
const start = 1
|
||||
|
||||
const end = 20
|
||||
|
||||
match 5 {
|
||||
start...`1` {
|
||||
println(start)
|
||||
}
|
||||
'str'...end {
|
||||
println(end)
|
||||
}
|
||||
else {}
|
||||
}
|
20
vlib/v/checker/tests/const_match_invalid_type_range_err.out
Normal file
20
vlib/v/checker/tests/const_match_invalid_type_range_err.out
Normal file
@ -0,0 +1,20 @@
|
||||
vlib/v/checker/tests/const_match_invalid_type_range_err.vv:6:2: error: the range type and the match condition type should match
|
||||
4 |
|
||||
5 | match 5 {
|
||||
6 | start...end {
|
||||
| ~~~~~~~~~~~
|
||||
7 | println(start)
|
||||
8 | }
|
||||
Details:
|
||||
match condition type: int
|
||||
range type: rune
|
||||
vlib/v/checker/tests/const_match_invalid_type_range_err.vv:13:2: error: the range type and the match condition type should match
|
||||
11 |
|
||||
12 | b := match 5 {
|
||||
13 | start...end {
|
||||
| ~~~~~~~~~~~
|
||||
14 | 3
|
||||
15 | }
|
||||
Details:
|
||||
match condition type: int
|
||||
range type: rune
|
20
vlib/v/checker/tests/const_match_invalid_type_range_err.vv
Normal file
20
vlib/v/checker/tests/const_match_invalid_type_range_err.vv
Normal file
@ -0,0 +1,20 @@
|
||||
const start = `a`
|
||||
|
||||
const end = 2
|
||||
|
||||
match 5 {
|
||||
start...end {
|
||||
println(start)
|
||||
}
|
||||
else {}
|
||||
}
|
||||
|
||||
b := match 5 {
|
||||
start...end {
|
||||
3
|
||||
}
|
||||
else {
|
||||
2
|
||||
}
|
||||
}
|
||||
println(b)
|
40
vlib/v/checker/tests/const_match_mismatch_end_range_err.out
Normal file
40
vlib/v/checker/tests/const_match_mismatch_end_range_err.out
Normal file
@ -0,0 +1,40 @@
|
||||
vlib/v/checker/tests/const_match_mismatch_end_range_err.vv:6:2: error: the range type and the match condition type should match
|
||||
4 |
|
||||
5 | match 5 {
|
||||
6 | 0...end {
|
||||
| ~~~~~~~
|
||||
7 | println(start)
|
||||
8 | }
|
||||
Details:
|
||||
match condition type: int
|
||||
range type: rune
|
||||
vlib/v/checker/tests/const_match_mismatch_end_range_err.vv:9:2: error: the range type and the match condition type should match
|
||||
7 | println(start)
|
||||
8 | }
|
||||
9 | `a`...start {
|
||||
| ~~~~~~~~~~~
|
||||
10 | println(end)
|
||||
11 | }
|
||||
Details:
|
||||
match condition type: int
|
||||
range type: rune
|
||||
vlib/v/checker/tests/const_match_mismatch_end_range_err.vv:16:2: error: the range type and the match condition type should match
|
||||
14 |
|
||||
15 | a := match 5 {
|
||||
16 | 0...end {
|
||||
| ~~~~~~~
|
||||
17 | 1
|
||||
18 | }
|
||||
Details:
|
||||
match condition type: int
|
||||
range type: rune
|
||||
vlib/v/checker/tests/const_match_mismatch_end_range_err.vv:19:2: error: the range type and the match condition type should match
|
||||
17 | 1
|
||||
18 | }
|
||||
19 | `a`...start {
|
||||
| ~~~~~~~~~~~
|
||||
20 | `a`
|
||||
21 | }
|
||||
Details:
|
||||
match condition type: int
|
||||
range type: rune
|
26
vlib/v/checker/tests/const_match_mismatch_end_range_err.vv
Normal file
26
vlib/v/checker/tests/const_match_mismatch_end_range_err.vv
Normal file
@ -0,0 +1,26 @@
|
||||
const start = 12
|
||||
|
||||
const end = `a`
|
||||
|
||||
match 5 {
|
||||
0...end {
|
||||
println(start)
|
||||
}
|
||||
`a`...start {
|
||||
println(end)
|
||||
}
|
||||
else {}
|
||||
}
|
||||
|
||||
a := match 5 {
|
||||
0...end {
|
||||
1
|
||||
}
|
||||
`a`...start {
|
||||
`a`
|
||||
}
|
||||
else {
|
||||
2
|
||||
}
|
||||
}
|
||||
println(a)
|
@ -0,0 +1,14 @@
|
||||
vlib/v/checker/tests/const_match_range_duplicate_case_err.vv:9:2: error: match case `2` is handled more than once
|
||||
7 | println(1)
|
||||
8 | }
|
||||
9 | start...end {
|
||||
| ~~~~~~~~~~~
|
||||
10 | println(3)
|
||||
11 | }
|
||||
vlib/v/checker/tests/const_match_range_duplicate_case_err.vv:21:2: error: match case `2` is handled more than once
|
||||
19 | 1
|
||||
20 | }
|
||||
21 | start...end {
|
||||
| ~~~~~~~~~~~
|
||||
22 | 3
|
||||
23 | }
|
28
vlib/v/checker/tests/const_match_range_duplicate_case_err.vv
Normal file
28
vlib/v/checker/tests/const_match_range_duplicate_case_err.vv
Normal file
@ -0,0 +1,28 @@
|
||||
const start = 2
|
||||
|
||||
const end = 12
|
||||
|
||||
match 5 {
|
||||
start...end {
|
||||
println(1)
|
||||
}
|
||||
start...end {
|
||||
println(3)
|
||||
}
|
||||
else {
|
||||
println(2)
|
||||
}
|
||||
}
|
||||
|
||||
a := match 5 {
|
||||
start...end {
|
||||
1
|
||||
}
|
||||
start...end {
|
||||
3
|
||||
}
|
||||
else {
|
||||
2
|
||||
}
|
||||
}
|
||||
println(a)
|
14
vlib/v/checker/tests/const_match_range_invalid_err.out
Normal file
14
vlib/v/checker/tests/const_match_range_invalid_err.out
Normal file
@ -0,0 +1,14 @@
|
||||
vlib/v/checker/tests/const_match_range_invalid_err.vv:6:2: error: the start value `10` should be lower than the end value `1`
|
||||
4 |
|
||||
5 | match 5 {
|
||||
6 | end...start {
|
||||
| ~~~~~~~~~~~
|
||||
7 | println(start)
|
||||
8 | }
|
||||
vlib/v/checker/tests/const_match_range_invalid_err.vv:13:2: error: the start value `10` should be lower than the end value `1`
|
||||
11 |
|
||||
12 | a := match 5 {
|
||||
13 | end...start {
|
||||
| ~~~~~~~~~~~
|
||||
14 | 4
|
||||
15 | }
|
20
vlib/v/checker/tests/const_match_range_invalid_err.vv
Normal file
20
vlib/v/checker/tests/const_match_range_invalid_err.vv
Normal file
@ -0,0 +1,20 @@
|
||||
const end = 10
|
||||
|
||||
const start = 1
|
||||
|
||||
match 5 {
|
||||
end...start {
|
||||
println(start)
|
||||
}
|
||||
else {}
|
||||
}
|
||||
|
||||
a := match 5 {
|
||||
end...start {
|
||||
4
|
||||
}
|
||||
else {
|
||||
3
|
||||
}
|
||||
}
|
||||
println(a)
|
20
vlib/v/checker/tests/const_match_type_mismatch_range_err.out
Normal file
20
vlib/v/checker/tests/const_match_type_mismatch_range_err.out
Normal file
@ -0,0 +1,20 @@
|
||||
vlib/v/checker/tests/const_match_type_mismatch_range_err.vv:6:2: error: the low and high parts of a range expression, should have matching types
|
||||
4 |
|
||||
5 | match 5 {
|
||||
6 | start...end {
|
||||
| ~~~~~~~~~~~
|
||||
7 | println(start)
|
||||
8 | }
|
||||
Details:
|
||||
low part type: string
|
||||
high part type: int literal
|
||||
vlib/v/checker/tests/const_match_type_mismatch_range_err.vv:13:2: error: the low and high parts of a range expression, should have matching types
|
||||
11 |
|
||||
12 | z := match 5 {
|
||||
13 | start...end {
|
||||
| ~~~~~~~~~~~
|
||||
14 | 2
|
||||
15 | }
|
||||
Details:
|
||||
low part type: string
|
||||
high part type: int literal
|
20
vlib/v/checker/tests/const_match_type_mismatch_range_err.vv
Normal file
20
vlib/v/checker/tests/const_match_type_mismatch_range_err.vv
Normal file
@ -0,0 +1,20 @@
|
||||
const end = 10
|
||||
|
||||
const start = 'str'
|
||||
|
||||
match 5 {
|
||||
start...end {
|
||||
println(start)
|
||||
}
|
||||
else {}
|
||||
}
|
||||
|
||||
z := match 5 {
|
||||
start...end {
|
||||
2
|
||||
}
|
||||
else {
|
||||
5
|
||||
}
|
||||
}
|
||||
println(z)
|
28
vlib/v/checker/tests/invalid_const_expr_match_range_err.out
Normal file
28
vlib/v/checker/tests/invalid_const_expr_match_range_err.out
Normal file
@ -0,0 +1,28 @@
|
||||
vlib/v/checker/tests/invalid_const_expr_match_range_err.vv:5:2: error: match branch range expressions need the start value to be known at compile time (only enums, const or literals are supported)
|
||||
3 |
|
||||
4 | match 5 {
|
||||
5 | start...10 {
|
||||
| ~~~~~
|
||||
6 | println(start)
|
||||
7 | }
|
||||
vlib/v/checker/tests/invalid_const_expr_match_range_err.vv:8:7: error: match branch range expressions need the end value to be known at compile time (only enums, const or literals are supported)
|
||||
6 | println(start)
|
||||
7 | }
|
||||
8 | 10...end {
|
||||
| ~~~
|
||||
9 | println(end)
|
||||
10 | }
|
||||
vlib/v/checker/tests/invalid_const_expr_match_range_err.vv:15:2: error: match branch range expressions need the start value to be known at compile time (only enums, const or literals are supported)
|
||||
13 |
|
||||
14 | a := match 5 {
|
||||
15 | start...10 {
|
||||
| ~~~~~
|
||||
16 | 2
|
||||
17 | }
|
||||
vlib/v/checker/tests/invalid_const_expr_match_range_err.vv:18:7: error: match branch range expressions need the end value to be known at compile time (only enums, const or literals are supported)
|
||||
16 | 2
|
||||
17 | }
|
||||
18 | 10...end {
|
||||
| ~~~
|
||||
19 | 3
|
||||
20 | }
|
25
vlib/v/checker/tests/invalid_const_expr_match_range_err.vv
Normal file
25
vlib/v/checker/tests/invalid_const_expr_match_range_err.vv
Normal file
@ -0,0 +1,25 @@
|
||||
start := 1
|
||||
end := 20
|
||||
|
||||
match 5 {
|
||||
start...10 {
|
||||
println(start)
|
||||
}
|
||||
10...end {
|
||||
println(end)
|
||||
}
|
||||
else {}
|
||||
}
|
||||
|
||||
a := match 5 {
|
||||
start...10 {
|
||||
2
|
||||
}
|
||||
10...end {
|
||||
3
|
||||
}
|
||||
else {
|
||||
1
|
||||
}
|
||||
}
|
||||
println(a)
|
@ -1,4 +1,4 @@
|
||||
vlib/v/checker/tests/match_expr_range_low_higher_than_high.vv:5:3: error: start value is higher than end value
|
||||
vlib/v/checker/tests/match_expr_range_low_higher_than_high.vv:5:3: error: the start value `20` should be lower than the end value `10`
|
||||
3 | value := 10
|
||||
4 | match value {
|
||||
5 | 20...10 {}
|
||||
|
@ -1,14 +1,20 @@
|
||||
vlib/v/checker/tests/match_range_mismatch_type_err.vv:4:3: error: mismatched range types - trying to match `x`, which has type `string`, against a range of `rune`
|
||||
vlib/v/checker/tests/match_range_mismatch_type_err.vv:4:3: error: the range type and the match condition type should match
|
||||
2 | x := '1'
|
||||
3 | match x {
|
||||
4 | `0`...`9` {
|
||||
| ~~~
|
||||
| ~~~~~~~~~
|
||||
5 | println('0-9')
|
||||
6 | }
|
||||
vlib/v/checker/tests/match_range_mismatch_type_err.vv:16:3: error: mismatched range types - 0 is an integer, but `9` is not
|
||||
Details:
|
||||
match condition type: string
|
||||
range type: rune
|
||||
vlib/v/checker/tests/match_range_mismatch_type_err.vv:16:3: error: the range type and the match condition type should match
|
||||
14 | x := 1
|
||||
15 | match x {
|
||||
16 | 0...`9` {}
|
||||
| ^
|
||||
| ~~~~~~~
|
||||
17 | else {}
|
||||
18 | }
|
||||
Details:
|
||||
match condition type: int
|
||||
range type: rune
|
||||
|
14
vlib/v/checker/tests/non_const_match_range_err.out
Normal file
14
vlib/v/checker/tests/non_const_match_range_err.out
Normal file
@ -0,0 +1,14 @@
|
||||
vlib/v/checker/tests/non_const_match_range_err.vv:6:2: error: match branch range expressions need the start value to be known at compile time (only enums, const or literals are supported)
|
||||
4 |
|
||||
5 | match 5 {
|
||||
6 | start...end {
|
||||
| ~~~~~
|
||||
7 | println(start)
|
||||
8 | }
|
||||
vlib/v/checker/tests/non_const_match_range_err.vv:13:2: error: match branch range expressions need the start value to be known at compile time (only enums, const or literals are supported)
|
||||
11 |
|
||||
12 | a := match 5 {
|
||||
13 | start...end {
|
||||
| ~~~~~
|
||||
14 | 0
|
||||
15 | }
|
20
vlib/v/checker/tests/non_const_match_range_err.vv
Normal file
20
vlib/v/checker/tests/non_const_match_range_err.vv
Normal file
@ -0,0 +1,20 @@
|
||||
const end = 10
|
||||
|
||||
start := 1
|
||||
|
||||
match 5 {
|
||||
start...end {
|
||||
println(start)
|
||||
}
|
||||
else {}
|
||||
}
|
||||
|
||||
a := match 5 {
|
||||
start...end {
|
||||
0
|
||||
}
|
||||
else {
|
||||
1
|
||||
}
|
||||
}
|
||||
println(a)
|
@ -8,7 +8,7 @@ import v.util
|
||||
|
||||
fn (mut g Gen) index_expr(node ast.IndexExpr) {
|
||||
if node.index is ast.RangeExpr {
|
||||
g.range_expr(node, node.index)
|
||||
g.index_range_expr(node, node.index)
|
||||
} else {
|
||||
sym := g.table.final_sym(g.unwrap_generic(node.left_type))
|
||||
if sym.kind == .array {
|
||||
@ -57,7 +57,7 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) {
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) {
|
||||
fn (mut g Gen) index_range_expr(node ast.IndexExpr, range ast.RangeExpr) {
|
||||
sym := g.table.final_sym(node.left_type)
|
||||
mut tmp_opt := ''
|
||||
mut cur_line := ''
|
||||
|
@ -252,15 +252,8 @@ fn (mut g Gen) match_expr_switch(node ast.MatchExpr, is_expr bool, cond_var stri
|
||||
g.write(' || ')
|
||||
}
|
||||
if expr is ast.RangeExpr {
|
||||
// if type is unsigned and low is 0, check is unneeded
|
||||
mut skip_low := false
|
||||
if expr.low is ast.IntegerLiteral {
|
||||
if node_cond_type_unsigned && expr.low.val == '0' {
|
||||
skip_low = true
|
||||
}
|
||||
}
|
||||
g.write('(')
|
||||
if !skip_low {
|
||||
if g.should_check_low_bound_in_range_expr(expr, node_cond_type_unsigned) {
|
||||
g.write('${cond_var} >= ')
|
||||
g.expr(expr.low)
|
||||
g.write(' && ')
|
||||
@ -324,15 +317,8 @@ fn (mut g Gen) match_expr_switch(node ast.MatchExpr, is_expr bool, cond_var stri
|
||||
g.write(' || ')
|
||||
}
|
||||
if expr is ast.RangeExpr {
|
||||
// if type is unsigned and low is 0, check is unneeded
|
||||
mut skip_low := false
|
||||
if expr.low is ast.IntegerLiteral {
|
||||
if node_cond_type_unsigned && expr.low.val == '0' {
|
||||
skip_low = true
|
||||
}
|
||||
}
|
||||
g.write('(')
|
||||
if !skip_low {
|
||||
if g.should_check_low_bound_in_range_expr(expr, node_cond_type_unsigned) {
|
||||
g.write('${cond_var} >= ')
|
||||
g.expr(expr.low)
|
||||
g.write(' && ')
|
||||
@ -359,6 +345,28 @@ fn (mut g Gen) match_expr_switch(node ast.MatchExpr, is_expr bool, cond_var stri
|
||||
g.writeln('}')
|
||||
}
|
||||
|
||||
fn (mut g Gen) should_check_low_bound_in_range_expr(expr ast.RangeExpr, node_cond_type_unsigned bool) bool {
|
||||
// if the type is unsigned, and the low bound of the range expression is 0,
|
||||
// checking it at runtime is not needed:
|
||||
mut should_check_low_bound := true
|
||||
if node_cond_type_unsigned {
|
||||
if expr.low is ast.IntegerLiteral {
|
||||
if expr.low.val == '0' {
|
||||
should_check_low_bound = false
|
||||
}
|
||||
} else if expr.low is ast.Ident {
|
||||
if mut obj := g.table.global_scope.find_const(expr.low.name) {
|
||||
if mut obj.expr is ast.IntegerLiteral {
|
||||
if obj.expr.val == '0' {
|
||||
should_check_low_bound = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return should_check_low_bound
|
||||
}
|
||||
|
||||
fn (mut g Gen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var string, tmp_var string) {
|
||||
node_cond_type_unsigned := node.cond_type in [ast.u16_type, ast.u32_type, ast.u64_type]
|
||||
type_sym := g.table.sym(node.cond_type)
|
||||
@ -431,15 +439,8 @@ fn (mut g Gen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var str
|
||||
}
|
||||
else {
|
||||
if expr is ast.RangeExpr {
|
||||
// if type is unsigned and low is 0, check is unneeded
|
||||
mut skip_low := false
|
||||
if expr.low is ast.IntegerLiteral {
|
||||
if node_cond_type_unsigned && expr.low.val == '0' {
|
||||
skip_low = true
|
||||
}
|
||||
}
|
||||
g.write('(')
|
||||
if !skip_low {
|
||||
if g.should_check_low_bound_in_range_expr(expr, node_cond_type_unsigned) {
|
||||
g.write('${cond_var} >= ')
|
||||
g.expr(expr.low)
|
||||
g.write(' && ')
|
||||
|
@ -267,6 +267,7 @@ fn (mut p Parser) match_expr() ast.MatchExpr {
|
||||
// Expression match
|
||||
for {
|
||||
p.inside_match_case = true
|
||||
mut range_pos := p.tok.pos()
|
||||
expr := p.expr(0)
|
||||
ecmnts << p.eat_comments()
|
||||
p.inside_match_case = false
|
||||
@ -276,13 +277,15 @@ fn (mut p Parser) match_expr() ast.MatchExpr {
|
||||
return ast.MatchExpr{}
|
||||
} else if p.tok.kind == .ellipsis {
|
||||
p.next()
|
||||
p.inside_match_case = true
|
||||
expr2 := p.expr(0)
|
||||
p.inside_match_case = false
|
||||
exprs << ast.RangeExpr{
|
||||
low: expr
|
||||
high: expr2
|
||||
has_low: true
|
||||
has_high: true
|
||||
pos: p.tok.pos()
|
||||
pos: range_pos.extend(p.prev_tok.pos())
|
||||
}
|
||||
} else {
|
||||
exprs << expr
|
||||
|
65
vlib/v/tests/match_const_range_test.v
Normal file
65
vlib/v/tests/match_const_range_test.v
Normal file
@ -0,0 +1,65 @@
|
||||
const (
|
||||
start = 1
|
||||
start_2 = 4
|
||||
end = 3
|
||||
end_2 = 8
|
||||
//
|
||||
start_rune = `a`
|
||||
start_2_rune = `d`
|
||||
end_rune = `c`
|
||||
end_2_rune = `i`
|
||||
)
|
||||
|
||||
fn test_match_int_const_ranges() {
|
||||
mut results := []int{}
|
||||
for x in 0 .. 10 {
|
||||
match x {
|
||||
start...end { results << 1 }
|
||||
start_2...5 { results << 2 }
|
||||
6...end_2 { results << 3 }
|
||||
else { results << 4 }
|
||||
}
|
||||
}
|
||||
assert results == [4, 1, 1, 1, 2, 2, 3, 3, 3, 4]
|
||||
}
|
||||
|
||||
fn test_match_rune_const_ranges() {
|
||||
mut results := []int{}
|
||||
for x in `a` .. `l` {
|
||||
match x {
|
||||
start_rune...end_rune { results << 1 }
|
||||
start_2_rune...`e` { results << 2 }
|
||||
`f`...end_2_rune { results << 3 }
|
||||
else { results << 4 }
|
||||
}
|
||||
}
|
||||
assert results == [1, 1, 1, 2, 2, 3, 3, 3, 3, 4, 4]
|
||||
}
|
||||
|
||||
fn test_match_expr_int_const_ranges() {
|
||||
mut results := []int{}
|
||||
for x in 0 .. 10 {
|
||||
result := match x {
|
||||
start...end { 1 }
|
||||
start_2...5 { 2 }
|
||||
6...end_2 { 3 }
|
||||
else { 4 }
|
||||
}
|
||||
results << result
|
||||
}
|
||||
assert results == [4, 1, 1, 1, 2, 2, 3, 3, 3, 4]
|
||||
}
|
||||
|
||||
fn test_match_expr_rune_const_ranges() {
|
||||
mut results := []int{}
|
||||
for x in `a` .. `l` {
|
||||
result := match x {
|
||||
start_rune...end_rune { 1 }
|
||||
start_2_rune...`e` { 2 }
|
||||
`f`...end_2_rune { 3 }
|
||||
else { 4 }
|
||||
}
|
||||
results << result
|
||||
}
|
||||
assert results == [1, 1, 1, 2, 2, 3, 3, 3, 3, 4, 4]
|
||||
}
|
Loading…
Reference in New Issue
Block a user