diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 83dc35584a..cd0c4a4bdb 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -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 !(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 { 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) } + } 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 } @@ -1156,15 +1164,16 @@ fn (c mut Checker) stmt(node ast.Stmt) { ast.ForInStmt { c.in_for_count++ typ := c.expr(it.cond) + typ_idx := table.type_idx(typ) if it.is_range { - high_type := c.expr(it.high) - if typ in table.integer_type_idxs && high_type !in table.integer_type_idxs { + high_type_idx := table.type_idx(c.expr(it.high)) + 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()) - } 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()) - } 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()) - } 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.expr(it.high) diff --git a/vlib/v/checker/tests/inout/in_array_mismatch_type.out b/vlib/v/checker/tests/inout/in_array_mismatch_type.out deleted file mode 100644 index 248601018b..0000000000 --- a/vlib/v/checker/tests/inout/in_array_mismatch_type.out +++ /dev/null @@ -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| } \ No newline at end of file diff --git a/vlib/v/checker/tests/inout/in_array_mismatch_type.vv b/vlib/v/checker/tests/inout/in_array_mismatch_type.vv deleted file mode 100644 index 0d9e17e7ef..0000000000 --- a/vlib/v/checker/tests/inout/in_array_mismatch_type.vv +++ /dev/null @@ -1,5 +0,0 @@ -fn main() { - if 1 in ['1', '2'] { - println('ok') - } -} \ No newline at end of file diff --git a/vlib/v/checker/tests/inout/in_mismatch_type.out b/vlib/v/checker/tests/inout/in_mismatch_type.out new file mode 100644 index 0000000000..dc1c88a79d --- /dev/null +++ b/vlib/v/checker/tests/inout/in_mismatch_type.out @@ -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| } \ No newline at end of file diff --git a/vlib/v/checker/tests/inout/in_mismatch_type.vv b/vlib/v/checker/tests/inout/in_mismatch_type.vv new file mode 100644 index 0000000000..0754815d00 --- /dev/null +++ b/vlib/v/checker/tests/inout/in_mismatch_type.vv @@ -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') + } +} diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index cfe58821a1..b43e7441e1 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -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.write(' })') } - } else if (node.left_type == node.right_type) && node.left_type in [table.f32_type_idx, - table.f64_type_idx - ] && node.op in [.eq, .ne] { + } else if (node.left_type == node.right_type) && table.is_float(node.left_type) && node.op in [.eq, .ne] { // floats should be compared with epsilon if node.left_type == table.f64_type_idx { if node.op == .eq { diff --git a/vlib/v/table/atypes.v b/vlib/v/table/atypes.v index 5dad83db0c..8d80a4a325 100644 --- a/vlib/v/table/atypes.v +++ b/vlib/v/table/atypes.v @@ -130,6 +130,17 @@ pub fn new_type_ptr(idx, nr_muls int) Type { 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 { return type_idx(typ) in number_type_idxs } diff --git a/vlib/v/tests/in_expression_test.v b/vlib/v/tests/in_expression_test.v index 5c335ce6a6..9ed89d3ba4 100644 --- a/vlib/v/tests/in_expression_test.v +++ b/vlib/v/tests/in_expression_test.v @@ -104,6 +104,24 @@ fn test_in_expression_with_string() { 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() { mut a := false a = true && 2 in [1, 2]