From 21cd765eaaac4289429899e5e1f607497291208c Mon Sep 17 00:00:00 2001 From: yuyi Date: Mon, 21 Dec 2020 23:59:43 +0800 Subject: [PATCH] cgen: fix `in` multi_array, generate `.contains()` (fix #7427) (#7448) --- vlib/builtin/array_test.v | 21 ++++++++++++ vlib/v/checker/checker.v | 5 ++- vlib/v/gen/cgen.v | 71 +++++++++++++++++++++++++++++++++------ vlib/v/gen/cheaders.v | 15 --------- vlib/v/gen/fn.v | 4 +++ 5 files changed, 90 insertions(+), 26 deletions(-) diff --git a/vlib/builtin/array_test.v b/vlib/builtin/array_test.v index 514a8f7987..7f8bb20dbc 100644 --- a/vlib/builtin/array_test.v +++ b/vlib/builtin/array_test.v @@ -1174,3 +1174,24 @@ fn test_multi_array_insert() { b.insert(0, [[1, 2, 3]]) assert b == [[1, 2, 3]] } + +fn test_multi_array_in() { + a := [[1]] + println([1] in a) + assert [1] in a +} + +fn test_any_type_array_contains() { + a := [true, false] + assert a.contains(true) + assert true in a + assert a.contains(false) + assert false in a + b := [i64(2), 3, 4] + assert b.contains(i64(3)) + assert 5 !in b + c := [[1], [2]] + assert c.contains([1]) + assert [2] in c + assert [3] !in c +} diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 79d10f046a..34864061b4 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1155,7 +1155,8 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { // TODO: remove this for actual methods, use only for compiler magic // FIXME: Argument count != 1 will break these if left_type_sym.kind == .array && - method_name in ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', 'sort'] { + method_name in + ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', 'sort', 'contains'] { mut elem_typ := table.void_type is_filter_map := method_name in ['filter', 'map'] is_sort := method_name == 'sort' @@ -1204,6 +1205,8 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { // call_expr.return_type = call_expr.receiver_type } else if method_name == 'sort' { call_expr.return_type = table.void_type + } else if method_name == 'contains' { + call_expr.return_type = table.bool_type } return call_expr.return_type } else if left_type_sym.kind == .map && method_name == 'clone' { diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 0fec9754d2..82708505d0 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -2982,20 +2982,16 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) { return } } - if left_sym.kind == .function { - g.write('_IN(voidptr, ') - } else { - elem_type := right_sym.array_info().elem_type - styp := g.typ(g.table.mktyp(elem_type)) - g.write('_IN($styp, ') - } - g.expr(node.left) - g.write(', ') + fn_name := g.gen_array_contains_method(node.right_type) + g.write('(${fn_name}(') if node.right_type.is_ptr() { g.write('*') } g.expr(node.right) - g.write(')') + g.write(',') + g.expr(node.left) + g.write('))') + return } else if right_sym.kind == .map { g.write('_IN_MAP(') g.expr(node.left) @@ -5134,6 +5130,61 @@ fn (mut g Gen) gen_array_prepend(node ast.CallExpr) { } } +fn (mut g Gen) gen_array_contains_method(left_type table.Type) string { + mut left_sym := g.table.get_type_symbol(left_type) + mut left_type_str := g.typ(left_type).replace('*', '') + left_info := left_sym.info as table.Array + mut elem_type_str := g.typ(left_info.elem_type) + elem_sym := g.table.get_type_symbol(left_info.elem_type) + if elem_sym.kind == .function { + left_type_str = 'array_voidptr' + elem_type_str = 'voidptr' + } + fn_name := '${left_type_str}_contains' + if !left_sym.has_method('contains') { + g.type_definitions.writeln('static bool ${fn_name}($left_type_str a, $elem_type_str v); // auto') + g.auto_str_funcs.writeln('static bool ${fn_name}($left_type_str a, $elem_type_str v) {') + g.auto_str_funcs.writeln('\tfor (int i = 0; i < a.len; ++i) {') + if elem_sym.kind == .string { + g.auto_str_funcs.writeln('\t\tif (string_eq((*(string*)array_get(a, i)), v)) {') + } else if elem_sym.kind == .array { + ptr_typ := g.gen_array_equality_fn(left_info.elem_type) + g.auto_str_funcs.writeln('\t\tif (${ptr_typ}_arr_eq(*($elem_type_str*)array_get(a, i), v)) {') + } else if elem_sym.kind == .function { + g.auto_str_funcs.writeln('\t\tif ((*(voidptr*)array_get(a, i)) == v) {') + } else { + g.auto_str_funcs.writeln('\t\tif ((*($elem_type_str*)array_get(a, i)) == v) {') + } + g.auto_str_funcs.writeln('\t\t\treturn true;') + g.auto_str_funcs.writeln('\t\t}') + g.auto_str_funcs.writeln('\t}') + g.auto_str_funcs.writeln('\treturn false;') + g.auto_str_funcs.writeln('}') + left_sym.register_method(&table.Fn{ + name: 'contains' + params: [table.Param{ + typ: left_type + }, table.Param{ + typ: left_info.elem_type + }] + }) + } + return fn_name +} + +// `nums.contains(2)` +fn (mut g Gen) gen_array_contains(node ast.CallExpr) { + fn_name := g.gen_array_contains_method(node.left_type) + g.write('${fn_name}(') + if node.left_type.is_ptr() { + g.write('*') + } + g.expr(node.left) + g.write(',') + g.expr(node.args[0].expr) + g.write(')') +} + [inline] fn (g &Gen) nth_stmt_pos(n int) int { return g.stmt_path_pos[g.stmt_path_pos.len - (1 + n)] diff --git a/vlib/v/gen/cheaders.v b/vlib/v/gen/cheaders.v index cb01218ac5..0e1c796a83 100644 --- a/vlib/v/gen/cheaders.v +++ b/vlib/v/gen/cheaders.v @@ -272,23 +272,8 @@ static void* g_live_info = NULL; // take the address of an rvalue #define ADDR(type, expr) (&((type[]){expr}[0])) #define _PUSH_MANY(arr, val, tmp, tmp_typ) {tmp_typ tmp = (val); array_push_many(arr, tmp.data, tmp.len);} -#define _IN(typ, val, arr) array_##typ##_contains(arr, val) #define _IN_MAP(val, m) map_exists(m, val) -// these macros have corresponding implementations in builtin/int.v with different signedness -#define array_i8_contains(a, b) array_byte_contains(a, (byte)(b)) -#define array_i16_contains(a, b) array_u16_contains(a, (u16)(b)) -#define array_u32_contains(a, b) array_int_contains(a, (int)(b)) -#define array_i64_contains(a, b) array_u64_contains(a, (u64)(b)) -#define array_rune_contains(a, b) array_int_contains(a, (int)(b)) -#define array_f32_contains(a, b) array_int_contains(a, *(int*)&((f32[]){(b)})) -#define array_f64_contains(a, b) array_u64_contains(a, *(u64*)&((f64[]){(b)})) -#ifdef TARGET_IS_64BIT -#define array_voidptr_contains(a, b) array_u64_contains(a, (u64)(b)) -#else -#define array_voidptr_contains(a, b) array_int_contains(a, (int)(b)) -#endif - // unsigned/signed comparisons static inline bool _us32_gt(uint32_t a, int32_t b) { return a > INT32_MAX || (int32_t)a > b; } static inline bool _us32_ge(uint32_t a, int32_t b) { return a >= INT32_MAX || (int32_t)a >= b; } diff --git a/vlib/v/gen/fn.v b/vlib/v/gen/fn.v index cc05286339..d1955caf2b 100644 --- a/vlib/v/gen/fn.v +++ b/vlib/v/gen/fn.v @@ -370,6 +370,10 @@ fn (mut g Gen) method_call(node ast.CallExpr) { g.gen_array_prepend(node) return } + 'contains' { + g.gen_array_contains(node) + return + } else {} } }