From 05f0f3e180c004bd74bbe449787358f0aaa6f009 Mon Sep 17 00:00:00 2001 From: Enzo Date: Fri, 18 Jun 2021 13:27:52 +0200 Subject: [PATCH] v.gen.c: refactor infix expression (#10485) --- vlib/v/ast/table.v | 3 +- vlib/v/gen/c/cgen.v | 471 --------------------------------- vlib/v/gen/c/fn.v | 11 - vlib/v/gen/c/infix_expr.v | 540 ++++++++++++++++++++++++++++++++++++++ vlib/v/gen/c/utils.v | 49 ++++ 5 files changed, 590 insertions(+), 484 deletions(-) create mode 100644 vlib/v/gen/c/infix_expr.v create mode 100644 vlib/v/gen/c/utils.v diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index 0337c4603d..8a75cb7d78 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -309,8 +309,7 @@ pub fn (t &Table) type_find_method(s &TypeSymbol, name string) ?Fn { return method } if ts.kind == .aggregate { - method := t.register_aggregate_method(mut ts, name) ? - return method + return t.register_aggregate_method(mut ts, name) } if ts.parent_idx == 0 { break diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 7e9f044735..86755255bc 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -3531,421 +3531,6 @@ fn (mut g Gen) enum_expr(node ast.Expr) { } } -fn (mut g Gen) infix_gen_equality(node ast.InfixExpr, left_type ast.Type, left_sym ast.TypeSymbol, right_sym ast.TypeSymbol) { - if left_sym.kind != right_sym.kind { - return - } - match left_sym.kind { - .array { - ptr_typ := g.gen_array_equality_fn(left_type.clear_flag(.shared_f)) - if node.op == .ne { - g.write('!') - } - g.write('${ptr_typ}_arr_eq(') - if node.left_type.is_ptr() && !node.left_type.has_flag(.shared_f) { - g.write('*') - } - g.expr(node.left) - if node.left_type.has_flag(.shared_f) { - if node.left_type.is_ptr() { - g.write('->val') - } else { - g.write('.val') - } - } - g.write(', ') - if node.right_type.is_ptr() && !node.right_type.has_flag(.shared_f) { - g.write('*') - } - g.expr(node.right) - if node.right_type.has_flag(.shared_f) { - if node.right_type.is_ptr() { - g.write('->val') - } else { - g.write('.val') - } - } - g.write(')') - } - .array_fixed { - ptr_typ := g.gen_fixed_array_equality_fn(left_type) - if node.op == .ne { - g.write('!') - } - g.write('${ptr_typ}_arr_eq(') - if node.left_type.is_ptr() { - g.write('*') - } - if node.left is ast.ArrayInit { - s := g.typ(left_type) - g.write('($s)') - } - g.expr(node.left) - g.write(', ') - if node.right is ast.ArrayInit { - s := g.typ(left_type) - g.write('($s)') - } - g.expr(node.right) - g.write(')') - } - .alias { - ptr_typ := g.gen_alias_equality_fn(left_type) - if node.op == .eq { - g.write('${ptr_typ}_alias_eq(') - } else if node.op == .ne { - g.write('!${ptr_typ}_alias_eq(') - } - if node.left_type.is_ptr() { - g.write('*') - } - g.expr(node.left) - g.write(', ') - if node.right_type.is_ptr() { - g.write('*') - } - g.expr(node.right) - g.write(')') - } - .map { - ptr_typ := g.gen_map_equality_fn(left_type) - if node.op == .eq { - g.write('${ptr_typ}_map_eq(') - } else if node.op == .ne { - g.write('!${ptr_typ}_map_eq(') - } - if node.left_type.is_ptr() { - g.write('*') - } - g.expr(node.left) - g.write(', ') - if node.right_type.is_ptr() { - g.write('*') - } - g.expr(node.right) - g.write(')') - } - .string, .struct_ { - // Auto generate both `==` and `!=` - ptr_typ := g.gen_struct_equality_fn(left_type) - if node.op == .eq { - g.write('${ptr_typ}_struct_eq(') - } else if node.op == .ne { - g.write('!${ptr_typ}_struct_eq(') - } - if node.left_type.is_ptr() { - g.write('*') - } - g.expr(node.left) - g.write(', ') - if node.right_type.is_ptr() { - g.write('*') - } - g.expr(node.right) - g.write(')') - } - .sum_type { - ptr_typ := g.gen_sumtype_equality_fn(left_type) - if node.op == .eq { - g.write('${ptr_typ}_sumtype_eq(') - } else if node.op == .ne { - g.write('!${ptr_typ}_sumtype_eq(') - } - if node.left_type.is_ptr() { - g.write('*') - } - g.expr(node.left) - g.write(', ') - if node.right_type.is_ptr() { - g.write('*') - } - g.expr(node.right) - g.write(')') - } - else {} - } -} - -fn (mut g Gen) infix_in_or_not_in(node ast.InfixExpr, left_sym ast.TypeSymbol, right_sym ast.TypeSymbol) { - if node.op == .not_in { - g.write('!') - } - if right_sym.kind == .array { - if mut node.right is ast.ArrayInit { - if node.right.exprs.len > 0 { - // `a in [1,2,3]` optimization => `a == 1 || a == 2 || a == 3` - // avoids an allocation - // g.write('/*in opt*/') - g.write('(') - g.in_optimization(node.left, node.right) - g.write(')') - return - } - } - 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.expr(node.left) - g.write('))') - return - } else if right_sym.kind == .map { - g.write('_IN_MAP(') - if !node.left_type.is_ptr() { - styp := g.typ(node.left_type) - g.write('ADDR($styp, ') - g.expr(node.left) - g.write(')') - } else { - g.expr(node.left) - } - g.write(', ') - if !node.right_type.is_ptr() { - g.write('ADDR(map, ') - g.expr(node.right) - g.write(')') - } else { - g.expr(node.right) - } - g.write(')') - } else if right_sym.kind == .string { - g.write('string_contains(') - g.expr(node.right) - g.write(', ') - g.expr(node.left) - g.write(')') - } -} - -fn (mut g Gen) infix_expr(node ast.InfixExpr) { - // TODO lot of clean required here - if node.auto_locked != '' { - g.writeln('sync__RwMutex_lock(&$node.auto_locked->mtx);') - } - if node.op in [.key_is, .not_is] { - g.is_expr(node) - return - } - left_type := g.unwrap_generic(node.left_type) - // println('>>$node') - left_sym := g.table.get_type_symbol(left_type) - left_final_sym := g.table.get_final_type_symbol(left_type) - // TODO cleanup: linked to left/right_final_sym, unaliasing done twice - unaliased_left := if left_sym.kind == .alias { - (left_sym.info as ast.Alias).parent_type - } else { - left_type - } - op_is_eq_or_ne := node.op in [.eq, .ne] - right_sym := g.table.get_type_symbol(node.right_type) - right_final_sym := g.table.get_final_type_symbol(node.right_type) - if node.op in [.key_in, .not_in] { - // TODO cleanup: handle the same as is / !is - g.infix_in_or_not_in(node, left_final_sym, right_final_sym) - return - } - unaliased_right := if right_sym.info is ast.Alias { - right_sym.info.parent_type - } else { - node.right_type - } - if unaliased_left == ast.string_type_idx && op_is_eq_or_ne && node.right is ast.StringLiteral - && (node.right as ast.StringLiteral).val == '' { - // `str == ''` -> `str.len == 0` optimization - g.write('(') - g.expr(node.left) - g.write(')') - arrow := if left_type.is_ptr() { '->' } else { '.' } - g.write('${arrow}len $node.op 0') - } else if op_is_eq_or_ne && left_sym.kind == right_sym.kind - && left_sym.kind in [.array, .array_fixed, .alias, .map, .struct_, .sum_type] { - g.infix_gen_equality(node, left_type, left_sym, right_sym) - } else if op_is_eq_or_ne && left_sym.kind == .alias && unaliased_right == ast.string_type { - // TODO cleanup: almost copy of above - g.infix_gen_equality(node, unaliased_left, left_final_sym, right_sym) - } else if node.op == .left_shift && left_final_sym.kind == .array { - // arr << val - tmp := g.new_tmp_var() - info := left_final_sym.info as ast.Array - noscan := g.check_noscan(info.elem_type) - if right_final_sym.kind == .array && info.elem_type != g.unwrap_generic(node.right_type) { - // push an array => PUSH_MANY, but not if pushing an array to 2d array (`[][]int << []int`) - g.write('_PUSH_MANY${noscan}(') - mut expected_push_many_atype := left_type - if !expected_push_many_atype.is_ptr() { - // fn f(mut a []int) { a << [1,2,3] } -> type of `a` is `array_int*` -> no need for & - g.write('&') - } else { - expected_push_many_atype = expected_push_many_atype.deref() - } - g.expr(node.left) - g.write(', (') - g.expr_with_cast(node.right, node.right_type, left_type) - styp := g.typ(expected_push_many_atype) - g.write('), $tmp, $styp)') - } else { - // push a single element - elem_type_str := g.typ(info.elem_type) - elem_sym := g.table.get_type_symbol(info.elem_type) - g.write('array_push${noscan}((array*)') - if !left_type.is_ptr() { - g.write('&') - } - g.expr(node.left) - if elem_sym.kind == .function { - g.write(', _MOV((voidptr[]){ ') - } else { - g.write(', _MOV(($elem_type_str[]){ ') - } - // if g.autofree - needs_clone := info.elem_type == ast.string_type && !g.is_builtin_mod - if needs_clone { - g.write('string_clone(') - } - g.expr_with_cast(node.right, node.right_type, info.elem_type) - if needs_clone { - g.write(')') - } - g.write(' }))') - } - } else if node.op == .arrow { - // chan <- val - gen_or := node.or_block.kind != .absent - styp := left_sym.cname - mut left_inf := left_sym.info as ast.Chan - elem_type := left_inf.elem_type - tmp_opt := if gen_or { g.new_tmp_var() } else { '' } - if gen_or { - elem_styp := g.typ(elem_type) - g.register_chan_push_optional_call(elem_styp, styp) - g.write('Option_void $tmp_opt = __Option_${styp}_pushval(') - } else { - g.write('__${styp}_pushval(') - } - g.expr(node.left) - g.write(', ') - g.expr(node.right) - g.write(')') - if gen_or { - g.or_block(tmp_opt, node.or_block, ast.void_type) - } - } else if unaliased_left.idx() in [ast.u32_type_idx, ast.u64_type_idx] - && unaliased_right.is_signed() && node.op in [.eq, .ne, .gt, .lt, .ge, .le] { - bitsize := if unaliased_left.idx() == ast.u32_type_idx - && unaliased_right.idx() != ast.i64_type_idx { - 32 - } else { - 64 - } - g.write('_us${bitsize}_${c.cmp_str[int(node.op) - int(token.Kind.eq)]}(') - g.expr(node.left) - g.write(',') - g.expr(node.right) - g.write(')') - } else if unaliased_right.idx() in [ast.u32_type_idx, ast.u64_type_idx] - && unaliased_left.is_signed() && node.op in [.eq, .ne, .gt, .lt, .ge, .le] { - bitsize := if unaliased_right.idx() == ast.u32_type_idx - && unaliased_left.idx() != ast.i64_type_idx { - 32 - } else { - 64 - } - g.write('_us${bitsize}_${c.cmp_rev[int(node.op) - int(token.Kind.eq)]}(') - g.expr(node.right) - g.write(',') - g.expr(node.left) - g.write(')') - } else { - // this likely covers more than V struct, but no idea what... - is_v_struct := ((left_sym.name[0].is_capital() || left_sym.name.contains('.')) - && left_sym.kind !in [.enum_, .function, .interface_, .sum_type] - && left_sym.language != .c) || left_sym.kind == .string - || unaliased_left == ast.ustring_type - is_alias := left_sym.kind == .alias - is_c_alias := is_alias && (left_sym.info as ast.Alias).language == .c - // Check if aliased type is a struct - is_struct_alias := is_alias - && g.typ((left_sym.info as ast.Alias).parent_type).split('__').last()[0].is_capital() - // Do not generate operator overloading with these `right_sym.kind`. - not_exception := right_sym.kind !in [.voidptr, .int_literal, .int] - if node.op in [.plus, .minus, .mul, .div, .mod, .lt, .eq] - && ((is_v_struct && !is_alias && not_exception) || is_c_alias - || is_struct_alias) { - // Overloaded operators - the_left_type := if !is_struct_alias - || g.table.get_type_symbol((left_sym.info as ast.Alias).parent_type).kind in [.array, .array_fixed, .map] { - left_type - } else { - (left_sym.info as ast.Alias).parent_type - } - left_nr_muls := '*'.repeat(the_left_type.nr_muls()) - g.write(g.typ(the_left_type.set_nr_muls(0))) - g.write('_') - g.write(util.replace_op(node.op.str())) - g.write('($left_nr_muls') - g.expr(node.left) - right_nr_muls := '*'.repeat(node.right_type.nr_muls()) - g.write(', $right_nr_muls') - g.expr(node.right) - g.write(')') - } else if node.op in [.ne, .gt, .ge, .le] && ((is_v_struct && !is_alias && not_exception) - || is_c_alias || is_struct_alias) { - the_left_type := if !is_struct_alias { - left_type - } else { - (left_sym.info as ast.Alias).parent_type - } - nr_muls := '*'.repeat(the_left_type.nr_muls()) - typ := g.typ(the_left_type.set_nr_muls(0)) - if node.op == .gt { - g.write('$typ') - } else { - g.write('!$typ') - } - g.write('_') - if node.op == .ne { - g.write('_eq') - } else if node.op in [.ge, .le, .gt] { - g.write('_lt') - } - if node.op in [.le, .gt] { - g.write('($nr_muls') - g.expr(node.right) - g.write(', $nr_muls') - g.expr(node.left) - g.write(')') - } else { - g.write('($nr_muls') - g.expr(node.left) - g.write(', $nr_muls') - g.expr(node.right) - g.write(')') - } - } else { - need_par := node.op in [.amp, .pipe, .xor] // `x & y == 0` => `(x & y) == 0` in C - if need_par { - g.write('(') - } - if node.left_type.is_ptr() && node.left.is_auto_deref_var() { - g.write('*') - } - g.expr(node.left) - g.write(' $node.op.str() ') - g.expr_with_cast(node.right, node.right_type, node.left_type) - if need_par { - g.write(')') - } - } - } - if node.auto_locked != '' { - g.writeln(';') - g.write('sync__RwMutex_unlock(&$node.auto_locked->mtx)') - } -} - fn (mut g Gen) lock_expr(node ast.LockExpr) { g.cur_lock = unsafe { node } // is ok because it is discarded at end of fn defer { @@ -6021,34 +5606,6 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type ast.Ty g.writeln('}') } -// `a in [1,2,3]` => `a == 1 || a == 2 || a == 3` -fn (mut g Gen) in_optimization(left ast.Expr, right ast.ArrayInit) { - is_str := right.elem_type == ast.string_type - elem_sym := g.table.get_type_symbol(right.elem_type) - is_array := elem_sym.kind == .array - for i, array_expr in right.exprs { - if is_str { - g.write('string__eq(') - } else if is_array { - ptr_typ := g.gen_array_equality_fn(right.elem_type) - g.write('${ptr_typ}_arr_eq(') - } - g.expr(left) - if is_str || is_array { - g.write(', ') - } else { - g.write(' == ') - } - g.expr(array_expr) - if is_str || is_array { - g.write(')') - } - if i < right.exprs.len - 1 { - g.write(' || ') - } - } -} - [inline] fn c_name(name_ string) string { name := util.no_dots(name_) @@ -6451,34 +6008,6 @@ fn (g Gen) as_cast_name_table() string { return name_ast.str() } -fn (mut g Gen) is_expr(node ast.InfixExpr) { - eq := if node.op == .key_is { '==' } else { '!=' } - g.write('(') - g.expr(node.left) - g.write(')') - if node.left_type.is_ptr() { - g.write('->') - } else { - g.write('.') - } - sym := g.table.get_type_symbol(node.left_type) - if sym.kind == .interface_ { - g.write('_typ $eq ') - // `_Animal_Dog_index` - sub_type := match mut node.right { - ast.TypeNode { node.right.typ } - ast.None { g.table.type_idxs['None__'] } - else { ast.Type(0) } - } - sub_sym := g.table.get_type_symbol(sub_type) - g.write('_${c_name(sym.name)}_${c_name(sub_sym.name)}_index') - return - } else if sym.kind == .sum_type { - g.write('_typ $eq ') - } - g.expr(node.right) -} - // Generates interface table and interface indexes fn (mut g Gen) interface_table() string { mut sb := strings.new_builder(100) diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 12010ff673..71eba8c335 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -502,17 +502,6 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { } } -pub fn (mut g Gen) unwrap_generic(typ ast.Type) ast.Type { - if typ.has_flag(.generic) { - if t_typ := g.table.resolve_generic_to_concrete(typ, g.table.cur_fn.generic_names, - g.table.cur_concrete_types, true) - { - return t_typ - } - } - return typ -} - fn (mut g Gen) method_call(node ast.CallExpr) { // TODO: there are still due to unchecked exprs (opt/some fn arg) if node.left_type == 0 { diff --git a/vlib/v/gen/c/infix_expr.v b/vlib/v/gen/c/infix_expr.v new file mode 100644 index 0000000000..6324218b36 --- /dev/null +++ b/vlib/v/gen/c/infix_expr.v @@ -0,0 +1,540 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module c + +import v.ast +import v.token +import v.util + +fn (mut g Gen) infix_expr(node ast.InfixExpr) { + if node.auto_locked != '' { + g.writeln('sync__RwMutex_lock(&$node.auto_locked->mtx);') + } + match node.op { + .arrow { + g.infix_expr_arrow_op(node) + } + .eq, .ne { + g.infix_expr_eq_op(node) + } + .gt, .ge, .lt, .le { + g.infix_expr_cmp_op(node) + } + .key_in, .not_in { + g.infix_expr_in_op(node) + } + .key_is, .not_is { + g.infix_expr_is_op(node) + } + .plus, .minus, .mul, .div, .mod { + g.infix_expr_arithmetic_op(node) + } + .left_shift { + g.infix_expr_left_shift_op(node) + } + else { + // `x & y == 0` => `(x & y) == 0` in C + need_par := node.op in [.amp, .pipe, .xor] + if need_par { + g.write('(') + } + g.gen_plain_infix_expr(node) + if need_par { + g.write(')') + } + } + } + if node.auto_locked != '' { + g.writeln(';') + g.write('sync__RwMutex_unlock(&$node.auto_locked->mtx)') + } +} + +// infix_expr_arrow_op generates C code for pushing into channels (chan <- val) +fn (mut g Gen) infix_expr_arrow_op(node ast.InfixExpr) { + left := g.unwrap(node.left_type) + styp := left.sym.cname + elem_type := (left.sym.info as ast.Chan).elem_type + gen_or := node.or_block.kind != .absent + tmp_opt := if gen_or { g.new_tmp_var() } else { '' } + if gen_or { + elem_styp := g.typ(elem_type) + g.register_chan_push_optional_call(elem_styp, styp) + g.write('Option_void $tmp_opt = __Option_${styp}_pushval(') + } else { + g.write('__${styp}_pushval(') + } + g.expr(node.left) + g.write(', ') + g.expr(node.right) + g.write(')') + if gen_or { + g.or_block(tmp_opt, node.or_block, ast.void_type) + } +} + +// infix_expr_eq_op generates code for `==` and `!=` +fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) { + left := g.unwrap(node.left_type) + right := g.unwrap(node.right_type) + has_operator_overloading := g.table.type_has_method(left.sym, '==') + if (left.typ.is_ptr() && right.typ.is_int()) || (right.typ.is_ptr() && left.typ.is_int()) { + g.gen_plain_infix_expr(node) + } else if (left.typ.idx() == ast.string_type_idx || (!has_operator_overloading + && left.unaliased.idx() == ast.string_type_idx)) && node.right is ast.StringLiteral + && (node.right as ast.StringLiteral).val == '' { + // `str == ''` -> `str.len == 0` optimization + g.write('(') + g.expr(node.left) + g.write(')') + arrow := if left.typ.is_ptr() { '->' } else { '.' } + g.write('${arrow}len $node.op 0') + } else if has_operator_overloading { + if node.op == .ne { + g.write('!') + } + g.write(g.typ(left.unaliased.set_nr_muls(0))) + g.write('__eq(') + g.write('*'.repeat(left.typ.nr_muls())) + g.expr(node.left) + g.write(', ') + g.write('*'.repeat(right.typ.nr_muls())) + g.expr(node.right) + g.write(')') + } else if left.typ.idx() == right.typ.idx() + && left.sym.kind in [.array, .array_fixed, .alias, .map, .struct_, .sum_type] { + match left.sym.kind { + .alias { + ptr_typ := g.gen_alias_equality_fn(left.typ) + if node.op == .ne { + g.write('!') + } + g.write('${ptr_typ}_alias_eq(') + if left.typ.is_ptr() { + g.write('*') + } + g.expr(node.left) + g.write(', ') + if right.typ.is_ptr() { + g.write('*') + } + g.expr(node.right) + g.write(')') + } + .array { + ptr_typ := g.gen_array_equality_fn(left.unaliased.clear_flag(.shared_f)) + if node.op == .ne { + g.write('!') + } + g.write('${ptr_typ}_arr_eq(') + if left.typ.is_ptr() && !left.typ.has_flag(.shared_f) { + g.write('*') + } + g.expr(node.left) + if left.typ.has_flag(.shared_f) { + if left.typ.is_ptr() { + g.write('->val') + } else { + g.write('.val') + } + } + g.write(', ') + if right.typ.is_ptr() && !right.typ.has_flag(.shared_f) { + g.write('*') + } + g.expr(node.right) + if right.typ.has_flag(.shared_f) { + if right.typ.is_ptr() { + g.write('->val') + } else { + g.write('.val') + } + } + g.write(')') + } + .array_fixed { + ptr_typ := g.gen_fixed_array_equality_fn(left.unaliased) + if node.op == .ne { + g.write('!') + } + g.write('${ptr_typ}_arr_eq(') + if left.typ.is_ptr() { + g.write('*') + } + if node.left is ast.ArrayInit { + s := g.typ(left.unaliased) + g.write('($s)') + } + g.expr(node.left) + g.write(', ') + if node.right is ast.ArrayInit { + s := g.typ(right.unaliased) + g.write('($s)') + } + g.expr(node.right) + g.write(')') + } + .map { + ptr_typ := g.gen_map_equality_fn(left.unaliased) + if node.op == .ne { + g.write('!') + } + g.write('${ptr_typ}_map_eq(') + if left.typ.is_ptr() { + g.write('*') + } + g.expr(node.left) + g.write(', ') + if right.typ.is_ptr() { + g.write('*') + } + g.expr(node.right) + g.write(')') + } + .struct_ { + ptr_typ := g.gen_struct_equality_fn(left.unaliased) + if node.op == .ne { + g.write('!') + } + g.write('${ptr_typ}_struct_eq(') + if left.typ.is_ptr() { + g.write('*') + } + g.expr(node.left) + g.write(', ') + if right.typ.is_ptr() { + g.write('*') + } + g.expr(node.right) + g.write(')') + } + .sum_type { + ptr_typ := g.gen_sumtype_equality_fn(left.unaliased) + if node.op == .ne { + g.write('!') + } + g.write('${ptr_typ}_sumtype_eq(') + if left.typ.is_ptr() { + g.write('*') + } + g.expr(node.left) + g.write(', ') + if right.typ.is_ptr() { + g.write('*') + } + g.expr(node.right) + g.write(')') + } + else {} + } + } else if left.unaliased.idx() in [ast.u32_type_idx, ast.u64_type_idx] + && right.unaliased.is_signed() { + g.gen_safe_integer_infix_expr( + op: node.op + unsigned_type: left.unaliased + unsigned_expr: node.left + signed_type: right.unaliased + signed_expr: node.right + ) + } else if right.unaliased.idx() in [ast.u32_type_idx, ast.u64_type_idx] + && left.unaliased.is_signed() { + g.gen_safe_integer_infix_expr( + op: node.op + reverse: true + unsigned_type: right.unaliased + unsigned_expr: node.right + signed_type: left.unaliased + signed_expr: node.left + ) + } else { + g.gen_plain_infix_expr(node) + } +} + +// infix_expr_cmp_op generates code for `<`, `<=`, `>`, `>=` +// It handles operator overloading when necessary +fn (mut g Gen) infix_expr_cmp_op(node ast.InfixExpr) { + left := g.unwrap(node.left_type) + right := g.unwrap(node.right_type) + has_operator_overloading := g.table.type_has_method(left.sym, '<') + if left.sym.kind == right.sym.kind && has_operator_overloading { + if node.op in [.le, .ge] { + g.write('!') + } + g.write(g.typ(left.typ.set_nr_muls(0))) + g.write('__lt') + if node.op in [.lt, .ge] { + g.write('(') + g.write('*'.repeat(left.typ.nr_muls())) + g.expr(node.left) + g.write(', ') + g.write('*'.repeat(right.typ.nr_muls())) + g.expr(node.right) + g.write(')') + } else { + g.write('(') + g.write('*'.repeat(right.typ.nr_muls())) + g.expr(node.right) + g.write(', ') + g.write('*'.repeat(left.typ.nr_muls())) + g.expr(node.left) + g.write(')') + } + } else if left.unaliased.idx() in [ast.u32_type_idx, ast.u64_type_idx] + && right.unaliased.is_signed() { + g.gen_safe_integer_infix_expr( + op: node.op + unsigned_type: left.unaliased + unsigned_expr: node.left + signed_type: right.unaliased + signed_expr: node.right + ) + } else if right.unaliased.idx() in [ast.u32_type_idx, ast.u64_type_idx] + && left.unaliased.is_signed() { + g.gen_safe_integer_infix_expr( + op: node.op + reverse: true + unsigned_type: right.unaliased + unsigned_expr: node.right + signed_type: left.unaliased + signed_expr: node.left + ) + } else { + g.gen_plain_infix_expr(node) + } +} + +// infix_expr_in_op generates code for `in` and `!in` +fn (mut g Gen) infix_expr_in_op(node ast.InfixExpr) { + left := g.unwrap(node.left_type) + right := g.unwrap(node.right_type) + if node.op == .not_in { + g.write('!') + } + if right.unaliased_sym.kind == .array { + if mut node.right is ast.ArrayInit { + if node.right.exprs.len > 0 { + // `a in [1,2,3]` optimization => `a == 1 || a == 2 || a == 3` + // avoids an allocation + g.write('(') + g.infix_expr_in_optimization(node.left, node.right) + g.write(')') + return + } + } + fn_name := g.gen_array_contains_method(node.right_type) + g.write('(${fn_name}(') + if right.typ.is_ptr() { + g.write('*') + } + g.expr(node.right) + g.write(', ') + g.expr(node.left) + g.write('))') + return + } else if right.unaliased_sym.kind == .map { + g.write('_IN_MAP(') + if !left.typ.is_ptr() { + styp := g.typ(node.left_type) + g.write('ADDR($styp, ') + g.expr(node.left) + g.write(')') + } else { + g.expr(node.left) + } + g.write(', ') + if !right.typ.is_ptr() { + g.write('ADDR(map, ') + g.expr(node.right) + g.write(')') + } else { + g.expr(node.right) + } + g.write(')') + } else if right.unaliased_sym.kind == .string { + g.write('string_contains(') + g.expr(node.right) + g.write(', ') + g.expr(node.left) + g.write(')') + } +} + +// infix_expr_in_optimization optimizes ` in ` expressions, +// and transform them in a serie of equality comparison +// i.e. `a in [1,2,3]` => `a == 1 || a == 2 || a == 3` +fn (mut g Gen) infix_expr_in_optimization(left ast.Expr, right ast.ArrayInit) { + is_str := right.elem_type.idx() == ast.string_type_idx + elem_sym := g.table.get_type_symbol(right.elem_type) + is_array := elem_sym.kind == .array + for i, array_expr in right.exprs { + if is_str { + g.write('string__eq(') + } else if is_array { + ptr_typ := g.gen_array_equality_fn(right.elem_type) + g.write('${ptr_typ}_arr_eq(') + } + g.expr(left) + if is_str || is_array { + g.write(', ') + } else { + g.write(' == ') + } + g.expr(array_expr) + if is_str || is_array { + g.write(')') + } + if i < right.exprs.len - 1 { + g.write(' || ') + } + } +} + +// infix_expr_is_op generates code for `is` and `!is` +fn (mut g Gen) infix_expr_is_op(node ast.InfixExpr) { + cmp_op := if node.op == .key_is { '==' } else { '!=' } + g.write('(') + g.expr(node.left) + g.write(')') + if node.left_type.is_ptr() { + g.write('->') + } else { + g.write('.') + } + sym := g.table.get_type_symbol(node.left_type) + if sym.kind == .interface_ { + g.write('_typ $cmp_op ') + // `_Animal_Dog_index` + sub_type := match mut node.right { + ast.TypeNode { node.right.typ } + ast.None { g.table.type_idxs['None__'] } + else { ast.Type(0) } + } + sub_sym := g.table.get_type_symbol(sub_type) + g.write('_${c_name(sym.name)}_${c_name(sub_sym.name)}_index') + return + } else if sym.kind == .sum_type { + g.write('_typ $cmp_op ') + } + g.expr(node.right) +} + +// infix_expr_arithmetic_op generates code for `+`, `-`, `*`, `/`, and `%` +// It handles operator overloading when necessary +fn (mut g Gen) infix_expr_arithmetic_op(node ast.InfixExpr) { + left := g.unwrap(node.left_type) + right := g.unwrap(node.right_type) + has_operator_overloading := g.table.type_has_method(left.sym, node.op.str()) + if left.sym.kind == right.sym.kind && has_operator_overloading { + g.write(g.typ(left.typ.set_nr_muls(0))) + g.write('_') + g.write(util.replace_op(node.op.str())) + g.write('(') + g.write('*'.repeat(left.typ.nr_muls())) + g.expr(node.left) + g.write(', ') + g.write('*'.repeat(right.typ.nr_muls())) + g.expr(node.right) + g.write(')') + } else { + g.gen_plain_infix_expr(node) + } +} + +// infix_expr_left_shift_op generates code for the `<<` operator +// This can either be a value pushed into an array or a bit shift +fn (mut g Gen) infix_expr_left_shift_op(node ast.InfixExpr) { + left := g.unwrap(node.left_type) + right := g.unwrap(node.right_type) + if left.unaliased_sym.kind == .array { + // arr << val + tmp_var := g.new_tmp_var() + array_info := left.unaliased_sym.info as ast.Array + noscan := g.check_noscan(array_info.elem_type) + //&& array_info.elem_type != g.unwrap_generic(node.right_type) + if right.unaliased_sym.kind == .array && array_info.elem_type != right.typ { + // push an array => PUSH_MANY, but not if pushing an array to 2d array (`[][]int << []int`) + g.write('_PUSH_MANY${noscan}(') + mut expected_push_many_atype := left.typ + if !expected_push_many_atype.is_ptr() { + // fn f(mut a []int) { a << [1,2,3] } -> type of `a` is `array_int*` -> no need for & + g.write('&') + } else { + expected_push_many_atype = expected_push_many_atype.deref() + } + g.expr(node.left) + g.write(', (') + g.expr_with_cast(node.right, node.right_type, left.unaliased) + styp := g.typ(expected_push_many_atype) + g.write('), $tmp_var, $styp)') + } else { + // push a single element + elem_type_str := g.typ(array_info.elem_type) + elem_sym := g.table.get_type_symbol(array_info.elem_type) + g.write('array_push${noscan}((array*)') + if !left.typ.is_ptr() { + g.write('&') + } + g.expr(node.left) + if elem_sym.kind == .function { + g.write(', _MOV((voidptr[]){ ') + } else { + g.write(', _MOV(($elem_type_str[]){ ') + } + // if g.autofree + needs_clone := array_info.elem_type.idx() == ast.string_type_idx && !g.is_builtin_mod + if needs_clone { + g.write('string_clone(') + } + g.expr_with_cast(node.right, node.right_type, array_info.elem_type) + if needs_clone { + g.write(')') + } + g.write(' }))') + } + } else { + g.gen_plain_infix_expr(node) + } +} + +// gen_plain_infix_expr generates basic code for infix expressions, +// without any overloading of any kind +// i.e. v`a + 1` => c`a + 1` +// It handles auto dereferencing of variables, as well as automatic casting +// (see Gen.expr_with_cast for more details) +fn (mut g Gen) gen_plain_infix_expr(node ast.InfixExpr) { + if node.left_type.is_ptr() && node.left.is_auto_deref_var() { + g.write('*') + } + g.expr(node.left) + g.write(' $node.op.str() ') + g.expr_with_cast(node.right, node.right_type, node.left_type) +} + +struct GenSafeIntegerCfg { + op token.Kind + reverse bool + unsigned_type ast.Type + unsigned_expr ast.Expr + signed_type ast.Type + signed_expr ast.Expr +} + +// gen_safe_integer_infix_expr generates code for comparison of +// unsigned and signed integers +fn (mut g Gen) gen_safe_integer_infix_expr(cfg GenSafeIntegerCfg) { + bitsize := if cfg.unsigned_type.idx() == ast.u32_type_idx + && cfg.signed_type.idx() != ast.i64_type_idx { + 32 + } else { + 64 + } + op_idx := int(cfg.op) - int(token.Kind.eq) + op_str := if cfg.reverse { cmp_rev[op_idx] } else { cmp_str[op_idx] } + g.write('_us${bitsize}_${op_str}(') + g.expr(cfg.unsigned_expr) + g.write(',') + g.expr(cfg.signed_expr) + g.write(')') +} diff --git a/vlib/v/gen/c/utils.v b/vlib/v/gen/c/utils.v new file mode 100644 index 0000000000..2e02a3534b --- /dev/null +++ b/vlib/v/gen/c/utils.v @@ -0,0 +1,49 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module c + +import v.ast + +fn (mut g Gen) unwrap_generic(typ ast.Type) ast.Type { + if typ.has_flag(.generic) { + if t_typ := g.table.resolve_generic_to_concrete(typ, g.table.cur_fn.generic_names, + g.table.cur_concrete_types, true) + { + return t_typ + } + } + return typ +} + +struct Type { + // typ is the original type + typ ast.Type [required] + sym &ast.TypeSymbol [required] + // unaliased is `typ` once aliased have been resolved + // it may not contain informations such as flags and nr_muls + unaliased ast.Type [required] + unaliased_sym &ast.TypeSymbol [required] +} + +// unwrap returns the following variants of a type: +// * generics unwrapped +// * alias unwrapped +fn (mut g Gen) unwrap(typ ast.Type) Type { + no_generic := g.unwrap_generic(typ) + no_generic_sym := g.table.get_type_symbol(no_generic) + if no_generic_sym.kind != .alias { + return Type{ + typ: no_generic + sym: no_generic_sym + unaliased: no_generic + unaliased_sym: no_generic_sym + } + } + return Type{ + typ: no_generic + sym: no_generic_sym + unaliased: no_generic_sym.parent_idx + unaliased_sym: g.table.get_type_symbol(no_generic_sym.parent_idx) + } +}