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:
@ -3,6 +3,7 @@
|
|||||||
- `go foo()` has been replaced with `spawn foo()` (launches an OS thread, `go` will be used for
|
- `go foo()` has been replaced with `spawn foo()` (launches an OS thread, `go` will be used for
|
||||||
upcoming coroutines instead).
|
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 intact.
|
- 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
|
## V 0.3.2
|
||||||
*31 Oct 2022*
|
*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
|
because the range is *inclusive* of the last element, rather than exclusive
|
||||||
(as `..` ranges are). Using `..` in a match branch will throw an error.
|
(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.
|
Note: `match` as an expression is not usable in `for` loop and `if` statements.
|
||||||
|
|
||||||
### In operator
|
### In operator
|
||||||
|
@ -1325,6 +1325,7 @@ pub:
|
|||||||
pub mut:
|
pub mut:
|
||||||
low Expr
|
low Expr
|
||||||
high 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]
|
[minify]
|
||||||
|
@ -12,8 +12,19 @@ fn (mut c Checker) check_types(got ast.Type, expected ast.Type) bool {
|
|||||||
}
|
}
|
||||||
got_is_ptr := got.is_ptr()
|
got_is_ptr := got.is_ptr()
|
||||||
exp_is_ptr := expected.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 c.pref.translated {
|
||||||
if expected.is_int() && got.is_int() {
|
if exp_is_int && got_is_int {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if expected == ast.byteptr_type {
|
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 {
|
if expected == ast.voidptr_type || expected == ast.nil_type {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (expected == ast.bool_type && (got.is_any_kind_of_pointer() || got.is_int()))
|
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) {
|
|| ((expected.is_any_kind_of_pointer() || exp_is_int) && got == ast.bool_type) {
|
||||||
return true
|
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
|
// allow rune -> any int and vice versa
|
||||||
if (expected == ast.rune_type && got.is_int())
|
if (expected == ast.rune_type && got_is_int) || (got == ast.rune_type && exp_is_int) {
|
||||||
|| (got == ast.rune_type && expected.is_int()) {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
got_sym := c.table.sym(got)
|
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 {
|
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 left_type.is_any_kind_of_pointer() {
|
||||||
if right_type.is_int() || c.pref.translated {
|
if right_type.is_int() || c.pref.translated {
|
||||||
return left_type
|
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
|
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() {
|
if right_type.is_number() && left_type.is_number() {
|
||||||
return c.promote_num(left_type, right_type)
|
return c.promote_num(left_type, right_type)
|
||||||
} else if left_type.has_flag(.optional) != right_type.has_flag(.optional) {
|
} 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)
|
return c.expr(node.expr)
|
||||||
}
|
}
|
||||||
ast.RangeExpr {
|
ast.RangeExpr {
|
||||||
// never happens
|
// branch range expression of `match x { a...b {} }`, or: `a#[x..y]`:
|
||||||
return ast.void_type
|
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 {
|
ast.SelectExpr {
|
||||||
return c.select_expr(mut node)
|
return c.select_expr(mut node)
|
||||||
|
@ -139,50 +139,81 @@ 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) {
|
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
|
// branch_exprs is a histogram of how many times
|
||||||
// an expr was used in the match
|
// an expr was used in the match
|
||||||
mut branch_exprs := map[string]int{}
|
mut branch_exprs := map[string]int{}
|
||||||
for branch_i, _ in node.branches {
|
for branch_i, _ in node.branches {
|
||||||
mut branch := node.branches[branch_i]
|
mut branch := node.branches[branch_i]
|
||||||
mut expr_types := []ast.TypeNode{}
|
mut expr_types := []ast.TypeNode{}
|
||||||
for k, expr in branch.exprs {
|
for k, mut expr in branch.exprs {
|
||||||
mut key := ''
|
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 low := i64(0)
|
||||||
mut high := i64(0)
|
mut high := i64(0)
|
||||||
c.expected_type = node.expected_type
|
mut both_low_and_high_are_known := false
|
||||||
low_expr := expr.low
|
if low_value := c.get_comptime_number_value(mut expr.low) {
|
||||||
high_expr := expr.high
|
low = low_value
|
||||||
final_cond_sym := c.table.final_sym(node.cond_type)
|
if high_value := c.get_comptime_number_value(mut expr.high) {
|
||||||
if low_expr is ast.IntegerLiteral {
|
high = high_value
|
||||||
if high_expr is ast.IntegerLiteral
|
both_low_and_high_are_known = true
|
||||||
&& (final_cond_sym.is_int() || final_cond_sym.info is ast.Enum) {
|
if low_value > high_value {
|
||||||
low = low_expr.val.i64()
|
low_value_higher_than_high_value = true
|
||||||
high = high_expr.val.i64()
|
|
||||||
if low > high {
|
|
||||||
c.error('start value is higher than end value', branch.pos)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.error('mismatched range types - ${expr.low} is an integer, but ${expr.high} is not',
|
if expr.high !is ast.EnumVal {
|
||||||
low_expr.pos)
|
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 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)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
typ := c.table.type_to_str(c.expr(node.cond))
|
if expr.low !is ast.EnumVal {
|
||||||
c.error('mismatched range types - trying to match `${node.cond}`, which has type `${typ}`, against a range of `rune`',
|
c.error('match branch range expressions need the start value to be known at compile time (only enums, const or literals are supported)',
|
||||||
low_expr.pos)
|
expr.low.pos())
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
typ := c.table.type_to_str(c.expr(expr.low))
|
|
||||||
c.error('cannot use type `${typ}` in match range', branch.pos)
|
|
||||||
}
|
}
|
||||||
|
if low_value_higher_than_high_value {
|
||||||
|
c.error('the start value `${low}` should be lower than the end value `${high}`',
|
||||||
|
branch.pos)
|
||||||
|
}
|
||||||
|
if both_low_and_high_are_known {
|
||||||
high_low_cutoff := 1000
|
high_low_cutoff := 1000
|
||||||
if high - low > high_low_cutoff {
|
if high - low > high_low_cutoff {
|
||||||
c.warn('more than ${high_low_cutoff} possibilities (${low} ... ${high}) in match range',
|
c.warn('more than ${high_low_cutoff} possibilities (${low} ... ${high}) in match range',
|
||||||
@ -196,9 +227,10 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym
|
|||||||
}
|
}
|
||||||
branch_exprs[key] = val + 1
|
branch_exprs[key] = val + 1
|
||||||
}
|
}
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
match expr {
|
match mut expr {
|
||||||
ast.TypeNode {
|
ast.TypeNode {
|
||||||
key = c.table.type_to_str(expr.typ)
|
key = c.table.type_to_str(expr.typ)
|
||||||
expr_types << expr
|
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
|
key = expr.val
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
key = expr.str()
|
key = (*expr).str()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val := if key in branch_exprs { branch_exprs[key] } else { 0 }
|
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
|
3 | value := 10
|
||||||
4 | match value {
|
4 | match value {
|
||||||
5 | 20...10 {}
|
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'
|
2 | x := '1'
|
||||||
3 | match x {
|
3 | match x {
|
||||||
4 | `0`...`9` {
|
4 | `0`...`9` {
|
||||||
| ~~~
|
| ~~~~~~~~~
|
||||||
5 | println('0-9')
|
5 | println('0-9')
|
||||||
6 | }
|
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
|
14 | x := 1
|
||||||
15 | match x {
|
15 | match x {
|
||||||
16 | 0...`9` {}
|
16 | 0...`9` {}
|
||||||
| ^
|
| ~~~~~~~
|
||||||
17 | else {}
|
17 | else {}
|
||||||
18 | }
|
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) {
|
fn (mut g Gen) index_expr(node ast.IndexExpr) {
|
||||||
if node.index is ast.RangeExpr {
|
if node.index is ast.RangeExpr {
|
||||||
g.range_expr(node, node.index)
|
g.index_range_expr(node, node.index)
|
||||||
} else {
|
} else {
|
||||||
sym := g.table.final_sym(g.unwrap_generic(node.left_type))
|
sym := g.table.final_sym(g.unwrap_generic(node.left_type))
|
||||||
if sym.kind == .array {
|
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)
|
sym := g.table.final_sym(node.left_type)
|
||||||
mut tmp_opt := ''
|
mut tmp_opt := ''
|
||||||
mut cur_line := ''
|
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(' || ')
|
g.write(' || ')
|
||||||
}
|
}
|
||||||
if expr is ast.RangeExpr {
|
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('(')
|
g.write('(')
|
||||||
if !skip_low {
|
if g.should_check_low_bound_in_range_expr(expr, node_cond_type_unsigned) {
|
||||||
g.write('${cond_var} >= ')
|
g.write('${cond_var} >= ')
|
||||||
g.expr(expr.low)
|
g.expr(expr.low)
|
||||||
g.write(' && ')
|
g.write(' && ')
|
||||||
@ -324,15 +317,8 @@ fn (mut g Gen) match_expr_switch(node ast.MatchExpr, is_expr bool, cond_var stri
|
|||||||
g.write(' || ')
|
g.write(' || ')
|
||||||
}
|
}
|
||||||
if expr is ast.RangeExpr {
|
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('(')
|
g.write('(')
|
||||||
if !skip_low {
|
if g.should_check_low_bound_in_range_expr(expr, node_cond_type_unsigned) {
|
||||||
g.write('${cond_var} >= ')
|
g.write('${cond_var} >= ')
|
||||||
g.expr(expr.low)
|
g.expr(expr.low)
|
||||||
g.write(' && ')
|
g.write(' && ')
|
||||||
@ -359,6 +345,28 @@ fn (mut g Gen) match_expr_switch(node ast.MatchExpr, is_expr bool, cond_var stri
|
|||||||
g.writeln('}')
|
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) {
|
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]
|
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)
|
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 {
|
else {
|
||||||
if expr is ast.RangeExpr {
|
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('(')
|
g.write('(')
|
||||||
if !skip_low {
|
if g.should_check_low_bound_in_range_expr(expr, node_cond_type_unsigned) {
|
||||||
g.write('${cond_var} >= ')
|
g.write('${cond_var} >= ')
|
||||||
g.expr(expr.low)
|
g.expr(expr.low)
|
||||||
g.write(' && ')
|
g.write(' && ')
|
||||||
|
@ -267,6 +267,7 @@ fn (mut p Parser) match_expr() ast.MatchExpr {
|
|||||||
// Expression match
|
// Expression match
|
||||||
for {
|
for {
|
||||||
p.inside_match_case = true
|
p.inside_match_case = true
|
||||||
|
mut range_pos := p.tok.pos()
|
||||||
expr := p.expr(0)
|
expr := p.expr(0)
|
||||||
ecmnts << p.eat_comments()
|
ecmnts << p.eat_comments()
|
||||||
p.inside_match_case = false
|
p.inside_match_case = false
|
||||||
@ -276,13 +277,15 @@ fn (mut p Parser) match_expr() ast.MatchExpr {
|
|||||||
return ast.MatchExpr{}
|
return ast.MatchExpr{}
|
||||||
} else if p.tok.kind == .ellipsis {
|
} else if p.tok.kind == .ellipsis {
|
||||||
p.next()
|
p.next()
|
||||||
|
p.inside_match_case = true
|
||||||
expr2 := p.expr(0)
|
expr2 := p.expr(0)
|
||||||
|
p.inside_match_case = false
|
||||||
exprs << ast.RangeExpr{
|
exprs << ast.RangeExpr{
|
||||||
low: expr
|
low: expr
|
||||||
high: expr2
|
high: expr2
|
||||||
has_low: true
|
has_low: true
|
||||||
has_high: true
|
has_high: true
|
||||||
pos: p.tok.pos()
|
pos: range_pos.extend(p.prev_tok.pos())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
exprs << expr
|
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]
|
||||||
|
}
|
Reference in New Issue
Block a user