v.gen.c: refactor infix expression (#10485)

pull/10506/head
Enzo 2021-06-18 13:27:52 +02:00 committed by GitHub
parent b7fea87d07
commit 05f0f3e180
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 590 additions and 484 deletions

View File

@ -309,8 +309,7 @@ pub fn (t &Table) type_find_method(s &TypeSymbol, name string) ?Fn {
return method return method
} }
if ts.kind == .aggregate { if ts.kind == .aggregate {
method := t.register_aggregate_method(mut ts, name) ? return t.register_aggregate_method(mut ts, name)
return method
} }
if ts.parent_idx == 0 { if ts.parent_idx == 0 {
break break

View File

@ -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) { fn (mut g Gen) lock_expr(node ast.LockExpr) {
g.cur_lock = unsafe { node } // is ok because it is discarded at end of fn g.cur_lock = unsafe { node } // is ok because it is discarded at end of fn
defer { defer {
@ -6021,34 +5606,6 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type ast.Ty
g.writeln('}') 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] [inline]
fn c_name(name_ string) string { fn c_name(name_ string) string {
name := util.no_dots(name_) name := util.no_dots(name_)
@ -6451,34 +6008,6 @@ fn (g Gen) as_cast_name_table() string {
return name_ast.str() 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 // Generates interface table and interface indexes
fn (mut g Gen) interface_table() string { fn (mut g Gen) interface_table() string {
mut sb := strings.new_builder(100) mut sb := strings.new_builder(100)

View File

@ -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) { fn (mut g Gen) method_call(node ast.CallExpr) {
// TODO: there are still due to unchecked exprs (opt/some fn arg) // TODO: there are still due to unchecked exprs (opt/some fn arg)
if node.left_type == 0 { if node.left_type == 0 {

View File

@ -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 `<var> in <array>` 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(')')
}

View File

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