cgen: fix `in` multi_array, generate `.contains()` (fix #7427) (#7448)

pull/7450/head
yuyi 2020-12-21 23:59:43 +08:00 committed by GitHub
parent 0edec06eac
commit 21cd765eaa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 90 additions and 26 deletions

View File

@ -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
}

View File

@ -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' {

View File

@ -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)]

View File

@ -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; }

View File

@ -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 {}
}
}