checker: check `in` left type

pull/4587/head
Enzo Baldisserri 2020-04-25 06:14:17 +02:00 committed by GitHub
parent fa4739794f
commit a924defb94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 141 additions and 23 deletions

View File

@ -310,14 +310,22 @@ pub fn (c mut Checker) infix_expr(infix_expr mut ast.InfixExpr) table.Type {
} }
} }
if infix_expr.op in [.key_in, .not_in] { if infix_expr.op in [.key_in, .not_in] {
if !(right.kind in [.array, .map, .string]) {
c.error('`in` can only be used with an array/map/string', infix_expr.pos)
}
if right.kind == .array { if right.kind == .array {
right_sym := c.table.get_type_symbol(right.array_info().elem_type) right_sym := c.table.get_type_symbol(right.array_info().elem_type)
if left.kind != .alias && left.kind != right_sym.kind { if left.kind != right_sym.kind {
c.error('the data type on the left of `in` does not match the array item type', infix_expr.pos) c.error('the data type on the left of `in` does not match the array item type', infix_expr.pos)
} }
} else if right.kind == .map {
key_sym := c.table.get_type_symbol(right.map_info().key_type)
if left.kind != key_sym.kind {
c.error('the data type on the left of `in` does not match the map key type', infix_expr.pos)
}
} else if right.kind == .string {
if left.kind != .string {
c.error('the data type on the left of `in` must be a string', infix_expr.pos)
}
} else {
c.error('`in` can only be used with an array/map/string', infix_expr.pos)
} }
return table.bool_type return table.bool_type
} }
@ -1156,15 +1164,16 @@ fn (c mut Checker) stmt(node ast.Stmt) {
ast.ForInStmt { ast.ForInStmt {
c.in_for_count++ c.in_for_count++
typ := c.expr(it.cond) typ := c.expr(it.cond)
typ_idx := table.type_idx(typ)
if it.is_range { if it.is_range {
high_type := c.expr(it.high) high_type_idx := table.type_idx(c.expr(it.high))
if typ in table.integer_type_idxs && high_type !in table.integer_type_idxs { if typ_idx in table.integer_type_idxs && high_type_idx !in table.integer_type_idxs {
c.error('range types do not match', it.cond.position()) c.error('range types do not match', it.cond.position())
} else if typ in table.float_type_idxs || high_type in table.float_type_idxs { } else if typ_idx in table.float_type_idxs || high_type_idx in table.float_type_idxs {
c.error('range type can not be float', it.cond.position()) c.error('range type can not be float', it.cond.position())
} else if typ == table.bool_type_idx || high_type == table.bool_type_idx { } else if typ_idx == table.bool_type_idx || high_type_idx == table.bool_type_idx {
c.error('range type can not be bool', it.cond.position()) c.error('range type can not be bool', it.cond.position())
} else if typ == table.string_type_idx || high_type == table.string_type_idx { } else if typ_idx == table.string_type_idx || high_type_idx == table.string_type_idx {
c.error('range type can not be string', it.cond.position()) c.error('range type can not be string', it.cond.position())
} }
c.expr(it.high) c.expr(it.high)

View File

@ -1,6 +0,0 @@
vlib/v/checker/tests/inout/in_array_mismatch_type.v:2:7: error: the data type on the left of `in` does not match the array item type
1| fn main() {
2| if 1 in ['1', '2'] {
~~
3| println('ok')
4| }

View File

@ -1,5 +0,0 @@
fn main() {
if 1 in ['1', '2'] {
println('ok')
}
}

View File

@ -0,0 +1,56 @@
vlib/v/checker/tests/inout/in_mismatch_type.v:13:7: error: the data type on the left of `in` does not match the array item type
11| }
12| s := 'abcd'
13| if 1 in a_s {
~~
14| println('ok')
15| }
vlib/v/checker/tests/inout/in_mismatch_type.v:16:7: error: the data type on the left of `in` does not match the map key type
14| println('ok')
15| }
16| if 2 in m {
~~
17| println('yeah')
18| }
vlib/v/checker/tests/inout/in_mismatch_type.v:19:7: error: the data type on the left of `in` must be a string
17| println('yeah')
18| }
19| if 3 in s {
~~
20| println('dope')
21| }
vlib/v/checker/tests/inout/in_mismatch_type.v:22:9: error: the data type on the left of `in` must be a string
20| println('dope')
21| }
22| if `a` in s {
~~
23| println("oh no :'(")
24| }
vlib/v/checker/tests/inout/in_mismatch_type.v:25:7: error: `in` can only be used with an array/map/string
23| println("oh no :'(")
24| }
25| if 1 in 12 {
~~
26| println('right')
27| }
vlib/v/checker/tests/inout/in_mismatch_type.v:28:12: error: the data type on the left of `in` does not match the map key type
26| println('right')
27| }
28| if Int(2) in m {
~~
29| println('yeah')
30| }
vlib/v/checker/tests/inout/in_mismatch_type.v:31:15: error: the data type on the left of `in` does not match the array item type
29| println('yeah')
30| }
31| if Str2('3') in a_i {
~~
32| println('sure')
33| }
vlib/v/checker/tests/inout/in_mismatch_type.v:34:15: error: the data type on the left of `in` does not match the array item type
32| println('sure')
33| }
34| if Str3('2') in a_i {
~~
35| println('all right')
36| }

View File

@ -0,0 +1,37 @@
type Int = int
type Str2 = string
type Str3 = Str2
fn main() {
a_i := [1, 2, 3]
a_s := ['1', '2', '3']
m := {
'test': 1
}
s := 'abcd'
if 1 in a_s {
println('ok')
}
if 2 in m {
println('yeah')
}
if 3 in s {
println('dope')
}
if `a` in s {
println("oh no :'(")
}
if 1 in 12 {
println('right')
}
if Int(2) in m {
println('yeah')
}
if Str2('3') in a_i {
println('sure')
}
if Str3('2') in a_i {
println('all right')
}
}

View File

@ -1397,9 +1397,7 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
g.expr_with_cast(node.right, node.right_type, info.elem_type) g.expr_with_cast(node.right, node.right_type, info.elem_type)
g.write(' })') g.write(' })')
} }
} else if (node.left_type == node.right_type) && node.left_type in [table.f32_type_idx, } else if (node.left_type == node.right_type) && table.is_float(node.left_type) && node.op in [.eq, .ne] {
table.f64_type_idx
] && node.op in [.eq, .ne] {
// floats should be compared with epsilon // floats should be compared with epsilon
if node.left_type == table.f64_type_idx { if node.left_type == table.f64_type_idx {
if node.op == .eq { if node.op == .eq {

View File

@ -130,6 +130,17 @@ pub fn new_type_ptr(idx, nr_muls int) Type {
return (nr_muls << 16) | u16(idx) return (nr_muls << 16) | u16(idx)
} }
[inline]
pub fn is_float(typ Type) bool {
return type_idx(typ) in float_type_idxs
}
[inline]
pub fn is_int(typ Type) bool {
return type_idx(typ) in integer_type_idxs
}
[inline]
pub fn is_number(typ Type) bool { pub fn is_number(typ Type) bool {
return type_idx(typ) in number_type_idxs return type_idx(typ) in number_type_idxs
} }

View File

@ -104,6 +104,24 @@ fn test_in_expression_with_string() {
assert a == false assert a == false
} }
fn test_in_expression_in_map() {
m := {
'one': 1
'two': 2
'three': 3
}
assert 'one' in m
assert 'four' !in m
}
fn test_in_expression_in_string() {
s := 'abcd'
assert 'a' in s
assert 'ab' in s
assert 'abcd' in s
assert 'dbca' !in s
}
fn test_optimized_in_expression() { fn test_optimized_in_expression() {
mut a := false mut a := false
a = true && 2 in [1, 2] a = true && 2 in [1, 2]