diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v new file mode 100644 index 0000000000..11b2aac458 --- /dev/null +++ b/vlib/v/gen/c/assign.v @@ -0,0 +1,740 @@ +// 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.util +import v.token + +fn (mut g Gen) gen_assign_stmt(node ast.AssignStmt) { + if node.is_static { + g.write('static ') + } + if node.is_volatile { + g.write('volatile ') + } + mut return_type := ast.void_type + is_decl := node.op == .decl_assign + g.assign_op = node.op + op := if is_decl { token.Kind.assign } else { node.op } + right_expr := node.right[0] + match right_expr { + ast.CallExpr { return_type = right_expr.return_type } + ast.LockExpr { return_type = right_expr.typ } + ast.MatchExpr { return_type = right_expr.return_type } + ast.IfExpr { return_type = right_expr.typ } + else {} + } + // Free the old value assigned to this string var (only if it's `str = [new value]` + // or `x.str = [new value]` ) + mut af := g.is_autofree && !g.is_builtin_mod && node.op == .assign && node.left_types.len == 1 + && (node.left[0] is ast.Ident || node.left[0] is ast.SelectorExpr) + // node.left_types[0] in [ast.string_type, ast.array_type] && + mut sref_name := '' + mut type_to_free := '' + if af { + first_left_type := node.left_types[0] + first_left_sym := g.table.sym(node.left_types[0]) + if first_left_type == ast.string_type || first_left_sym.kind == .array { + type_to_free = if first_left_type == ast.string_type { 'string' } else { 'array' } + mut ok := true + left0 := node.left[0] + if left0 is ast.Ident { + if left0.name == '_' { + ok = false + } + } + if ok { + sref_name = '_sref$node.pos.pos' + g.write('$type_to_free $sref_name = (') // TODO we are copying the entire string here, optimize + // we can't just do `.str` since we need the extra data from the string struct + // doing `&string` is also not an option since the stack memory with the data will be overwritten + g.expr(left0) // node.left[0]) + g.writeln('); // free $type_to_free on re-assignment2') + defer { + if af { + g.writeln('${type_to_free}_free(&$sref_name);') + } + } + } else { + af = false + } + } else { + af = false + } + } + g.gen_assign_vars_autofree(node) + // json_test failed w/o this check + if return_type != ast.void_type && return_type != 0 { + sym := g.table.sym(return_type) + if sym.kind == .multi_return { + g.gen_multi_return_assign(node, return_type) + return + } + } + // TODO: non idents on left (exprs) + if node.has_cross_var { + g.gen_cross_var_assign(node) + } + // `a := 1` | `a,b := 1,2` + if node.right.len < node.left.len { + g.checker_bug('node.right.len < node.left.len', node.pos) + } + if node.right_types.len < node.left.len { + g.checker_bug('node.right_types.len < node.left.len', node.pos) + } + if node.left_types.len < node.left.len { + g.checker_bug('node.left_types.len < node.left.len', node.pos) + } + + for i, left in node.left { + mut is_auto_heap := false + mut var_type := node.left_types[i] + mut val_type := node.right_types[i] + val := node.right[i] + mut is_call := false + mut blank_assign := false + mut ident := ast.Ident{ + scope: 0 + } + left_sym := g.table.sym(g.unwrap_generic(var_type)) + if mut left is ast.Ident { + ident = left + // id_info := ident.var_info() + // var_type = id_info.typ + blank_assign = left.kind == .blank_ident + // TODO: temporary, remove this + left_info := left.info + if left_info is ast.IdentVar { + share := left_info.share + if share == .shared_t { + var_type = var_type.set_flag(.shared_f) + } + if share == .atomic_t { + var_type = var_type.set_flag(.atomic_f) + } + } + if mut left.obj is ast.Var { + if val is ast.ComptimeSelector { + if val.field_expr is ast.SelectorExpr { + if val.field_expr.expr is ast.Ident { + key_str := '${val.field_expr.expr.name}.typ' + var_type = g.comptime_var_type_map[key_str] or { var_type } + left.obj.typ = var_type + } + } + } else if val is ast.ComptimeCall { + key_str := '${val.method_name}.return_type' + var_type = g.comptime_var_type_map[key_str] or { var_type } + left.obj.typ = var_type + } + is_auto_heap = left.obj.is_auto_heap + } + } + styp := g.typ(var_type) + mut is_fixed_array_init := false + mut has_val := false + match val { + ast.ArrayInit { + is_fixed_array_init = val.is_fixed + has_val = val.has_val + } + ast.CallExpr { + is_call = true + return_type = val.return_type + } + // TODO: no buffer fiddling + ast.AnonFn { + if blank_assign { + g.write('{') + } + // if it's a decl assign (`:=`) or a blank assignment `_ =`/`_ :=` then generate `void (*ident) (args) =` + if (is_decl || blank_assign) && left is ast.Ident { + ret_styp := g.typ(val.decl.return_type) + g.write('$ret_styp (*$ident.name) (') + def_pos := g.definitions.len + g.fn_args(val.decl.params, voidptr(0)) + g.definitions.go_back(g.definitions.len - def_pos) + g.write(') = ') + } else { + g.is_assign_lhs = true + g.assign_op = node.op + g.expr(left) + g.is_assign_lhs = false + g.is_arraymap_set = false + if left is ast.IndexExpr { + sym := g.table.sym(left.left_type) + if sym.kind in [.map, .array] { + g.expr(val) + g.writeln('});') + continue + } + } + g.write(' = ') + } + g.expr(val) + g.writeln(';') + if blank_assign { + g.write('}') + } + continue + } + else {} + } + unwrapped_val_type := g.unwrap_generic(val_type) + right_sym := g.table.sym(unwrapped_val_type) + unaliased_right_sym := g.table.final_sym(unwrapped_val_type) + is_fixed_array_var := unaliased_right_sym.kind == .array_fixed && val !is ast.ArrayInit + && (val in [ast.Ident, ast.IndexExpr, ast.CallExpr, ast.SelectorExpr] + || (val is ast.CastExpr && (val as ast.CastExpr).expr !is ast.ArrayInit)) + g.is_assign_lhs = true + g.assign_op = node.op + if val_type.has_flag(.optional) { + g.right_is_opt = true + } + if blank_assign { + if val is ast.IndexExpr { + g.assign_op = .decl_assign + } + if is_call { + old_is_void_expr_stmt := g.is_void_expr_stmt + g.is_void_expr_stmt = true + g.expr(val) + g.is_void_expr_stmt = old_is_void_expr_stmt + } else { + g.write('{$styp _ = ') + g.expr(val) + g.writeln(';}') + } + g.is_assign_lhs = false + } else if node.op == .assign + && (is_fixed_array_init || (right_sym.kind == .array_fixed && val is ast.Ident)) { + mut v_var := '' + arr_typ := styp.trim('*') + if is_fixed_array_init { + right := val as ast.ArrayInit + v_var = g.new_tmp_var() + g.write('$arr_typ $v_var = ') + g.expr(right) + g.writeln(';') + } else { + right := val as ast.Ident + v_var = right.name + } + pos := g.out.len + g.expr(left) + + if g.is_arraymap_set && g.arraymap_set_pos > 0 { + g.out.go_back_to(g.arraymap_set_pos) + g.write(', &$v_var)') + g.is_arraymap_set = false + g.arraymap_set_pos = 0 + } else { + g.out.go_back_to(pos) + is_var_mut := !is_decl && left.is_auto_deref_var() + addr := if is_var_mut { '' } else { '&' } + g.writeln('') + g.write('memcpy($addr') + g.expr(left) + g.writeln(', &$v_var, sizeof($arr_typ));') + } + g.is_assign_lhs = false + } else { + is_inside_ternary := g.inside_ternary != 0 + cur_line := if is_inside_ternary && is_decl { + g.register_ternary_name(ident.name) + g.empty_line = false + g.go_before_ternary() + } else { + '' + } + mut str_add := false + mut op_overloaded := false + mut op_expected_left := ast.Type(0) + mut op_expected_right := ast.Type(0) + if var_type == ast.string_type_idx && node.op == .plus_assign { + if left is ast.IndexExpr { + // a[0] += str => `array_set(&a, 0, &(string[]) {string__plus(...))})` + g.expr(left) + g.write('string__plus(') + } else { + // str += str2 => `str = string__plus(str, str2)` + g.expr(left) + g.write(' = /*f*/string__plus(') + } + g.is_assign_lhs = false + str_add = true + } + // Assignment Operator Overloading + if ((left_sym.kind == .struct_ && right_sym.kind == .struct_) + || (left_sym.kind == .alias && right_sym.kind == .alias)) + && node.op in [.plus_assign, .minus_assign, .div_assign, .mult_assign, .mod_assign] { + extracted_op := match node.op { + .plus_assign { '+' } + .minus_assign { '-' } + .div_assign { '/' } + .mod_assign { '%' } + .mult_assign { '*' } + else { 'unknown op' } + } + g.expr(left) + if left_sym.kind == .struct_ && (left_sym.info as ast.Struct).generic_types.len > 0 { + concrete_types := (left_sym.info as ast.Struct).concrete_types + mut method_name := left_sym.cname + '_' + util.replace_op(extracted_op) + method_name = g.generic_fn_name(concrete_types, method_name, true) + g.write(' = ${method_name}(') + g.expr(left) + g.write(', ') + g.expr(val) + g.writeln(');') + return + } else { + g.write(' = ${styp}_${util.replace_op(extracted_op)}(') + method := g.table.find_method(left_sym, extracted_op) or { + // the checker will most likely have found this, already... + g.error('assignemnt operator `$extracted_op=` used but no `$extracted_op` method defined', + node.pos) + ast.Fn{} + } + op_expected_left = method.params[0].typ + op_expected_right = method.params[1].typ + op_overloaded = true + } + } + if right_sym.kind == .function && is_decl { + if is_inside_ternary && is_decl { + g.out.write_string(util.tabs(g.indent - g.inside_ternary)) + } + func := right_sym.info as ast.FnType + ret_styp := g.typ(func.func.return_type) + g.write('$ret_styp (*${g.get_ternary_name(ident.name)}) (') + def_pos := g.definitions.len + g.fn_args(func.func.params, voidptr(0)) + g.definitions.go_back(g.definitions.len - def_pos) + g.write(')') + } else { + if is_decl { + if is_inside_ternary { + g.out.write_string(util.tabs(g.indent - g.inside_ternary)) + } + mut is_used_var_styp := false + if ident.name !in g.defer_vars { + val_sym := g.table.sym(val_type) + if val_sym.info is ast.Struct { + if val_sym.info.generic_types.len > 0 { + if val is ast.StructInit { + var_styp := g.typ(val.typ) + g.write('$var_styp ') + is_used_var_styp = true + } else if val is ast.PrefixExpr { + if val.op == .amp && val.right is ast.StructInit { + var_styp := g.typ(val.right.typ.ref()) + g.write('$var_styp ') + is_used_var_styp = true + } + } + } + } + if !is_used_var_styp { + g.write('$styp ') + } + if is_auto_heap { + g.write('*') + } + } + } + if left in [ast.Ident, ast.SelectorExpr] { + g.prevent_sum_type_unwrapping_once = true + } + if !is_fixed_array_var || is_decl { + if op_overloaded { + g.op_arg(left, op_expected_left, var_type) + } else { + if !is_decl && left.is_auto_deref_var() { + g.write('*') + } + g.expr(left) + } + } + } + if is_inside_ternary && is_decl { + g.write(';\n$cur_line') + g.out.write_string(util.tabs(g.indent)) + g.expr(left) + } + g.is_assign_lhs = false + if is_fixed_array_var { + if is_decl { + g.writeln(';') + } + } else if !g.is_arraymap_set && !str_add && !op_overloaded { + g.write(' $op ') + } else if str_add || op_overloaded { + g.write(', ') + } + mut cloned := false + if g.is_autofree && right_sym.kind in [.array, .string] { + if g.gen_clone_assignment(val, unwrapped_val_type, false) { + cloned = true + } + } + unwrap_optional := !var_type.has_flag(.optional) && val_type.has_flag(.optional) + if unwrap_optional { + // Unwrap the optional now that the testing code has been prepended. + // `pos := s.index(... + // `int pos = *(int)_t10.data;` + // if g.is_autofree { + /* + if is_optional { + g.write('*($styp*)') + g.write(tmp_opt + '.data/*FFz*/') + g.right_is_opt = false + if g.inside_ternary == 0 && !node.is_simple { + g.writeln(';') + } + return + } + */ + } + g.is_shared = var_type.has_flag(.shared_f) + if !cloned { + if is_fixed_array_var { + typ_str := g.typ(val_type).trim('*') + ref_str := if val_type.is_ptr() { '' } else { '&' } + g.write('memcpy(($typ_str*)') + g.expr(left) + g.write(', (byte*)$ref_str') + g.expr(val) + g.write(', sizeof($typ_str))') + } else if is_decl { + if is_fixed_array_init && !has_val { + if val is ast.ArrayInit { + g.array_init(val) + } else { + g.write('{0}') + } + } else { + if is_auto_heap { + g.write('HEAP($styp, (') + } + if val.is_auto_deref_var() { + g.write('*') + } + g.expr(val) + if is_auto_heap { + g.write('))') + } + } + } else { + if node.has_cross_var { + g.gen_cross_tmp_variable(node.left, val) + } else { + if op_overloaded { + g.op_arg(val, op_expected_right, val_type) + } else { + g.expr_with_cast(val, val_type, var_type) + } + } + } + } + if str_add || op_overloaded { + g.write(')') + } + if g.is_arraymap_set { + g.write(' })') + g.is_arraymap_set = false + } + g.is_shared = false + } + g.right_is_opt = false + if g.inside_ternary == 0 && (node.left.len > 1 || !node.is_simple) { + g.writeln(';') + } + } +} + +fn (mut g Gen) gen_multi_return_assign(node &ast.AssignStmt, return_type ast.Type) { + // multi return + // TODO Handle in if_expr + is_opt := return_type.has_flag(.optional) + mr_var_name := 'mr_$node.pos.pos' + mr_styp := g.typ(return_type) + g.write('$mr_styp $mr_var_name = ') + g.expr(node.right[0]) + g.writeln(';') + for i, lx in node.left { + mut is_auto_heap := false + mut ident := ast.Ident{ + scope: 0 + } + if lx is ast.Ident { + ident = lx + if lx.kind == .blank_ident { + continue + } + if lx.obj is ast.Var { + is_auto_heap = lx.obj.is_auto_heap + } + } + styp := if ident.name in g.defer_vars { '' } else { g.typ(node.left_types[i]) } + if node.op == .decl_assign { + g.write('$styp ') + if is_auto_heap { + g.write('*') + } + } + if lx.is_auto_deref_var() { + g.write('*') + } + g.expr(lx) + noscan := if is_auto_heap { g.check_noscan(return_type) } else { '' } + if g.is_arraymap_set { + if is_opt { + mr_base_styp := g.base_type(return_type) + if is_auto_heap { + g.writeln('HEAP${noscan}($mr_base_styp, *($mr_base_styp*)${mr_var_name}.data).arg$i) });') + } else { + g.writeln('(*($mr_base_styp*)${mr_var_name}.data).arg$i });') + } + } else { + if is_auto_heap { + g.writeln('HEAP${noscan}($styp, ${mr_var_name}.arg$i) });') + } else { + g.writeln('${mr_var_name}.arg$i });') + } + } + } else { + if is_opt { + mr_base_styp := g.base_type(return_type) + if is_auto_heap { + g.writeln(' = HEAP${noscan}($mr_base_styp, *($mr_base_styp*)${mr_var_name}.data).arg$i);') + } else { + g.writeln(' = (*($mr_base_styp*)${mr_var_name}.data).arg$i;') + } + } else { + if is_auto_heap { + g.writeln(' = HEAP${noscan}($styp, ${mr_var_name}.arg$i);') + } else { + g.writeln(' = ${mr_var_name}.arg$i;') + } + } + } + } + if g.is_arraymap_set { + g.is_arraymap_set = false + } +} + +fn (mut g Gen) gen_assign_vars_autofree(node &ast.AssignStmt) { + // Autofree tmp arg vars + // first_right := node.right[0] + // af := g.autofree && first_right is ast.CallExpr && !g.is_builtin_mod + // if af { + // g.autofree_call_pregen(first_right as ast.CallExpr) + // } + // + // + // Handle optionals. We need to declare a temp variable for them, that's why they are handled + // here, not in call_expr(). + // `pos := s.index('x') or { return }` + // ==========> + // Option_int _t190 = string_index(s, _STR("x")); // _STR() no more used!! + // if (_t190.state != 2) { + // Error err = _t190.err; + // return; + // } + // int pos = *(int*)_t190.data; + // mut tmp_opt := '' + /* + is_optional := false && g.is_autofree && (node.op in [.decl_assign, .assign]) + && node.left_types.len == 1 && node.right[0] is ast.CallExpr + if is_optional { + // g.write('/* optional assignment */') + call_expr := node.right[0] as ast.CallExpr + if call_expr.or_block.kind != .absent { + styp := g.typ(call_expr.return_type.set_flag(.optional)) + tmp_opt = g.new_tmp_var() + g.write('/*AF opt*/$styp $tmp_opt = ') + g.expr(node.right[0]) + g.or_block(tmp_opt, call_expr.or_block, call_expr.return_type) + g.writeln('/*=============ret*/') + // if af && is_optional { + // g.autofree_call_postgen() + // } + // return + } + } + */ +} + +fn (mut g Gen) gen_cross_var_assign(node &ast.AssignStmt) { + for i, left in node.left { + match left { + ast.Ident { + left_typ := node.left_types[i] + left_sym := g.table.sym(left_typ) + if left_sym.kind == .function { + g.write_fn_ptr_decl(left_sym.info as ast.FnType, '_var_$left.pos.pos') + g.writeln(' = $left.name;') + } else { + styp := g.typ(left_typ) + g.writeln('$styp _var_$left.pos.pos = $left.name;') + } + } + ast.IndexExpr { + sym := g.table.sym(left.left_type) + if sym.kind == .array { + info := sym.info as ast.Array + elem_typ := g.table.sym(info.elem_type) + if elem_typ.kind == .function { + left_typ := node.left_types[i] + left_sym := g.table.sym(left_typ) + g.write_fn_ptr_decl(left_sym.info as ast.FnType, '_var_$left.pos.pos') + g.write(' = *(voidptr*)array_get(') + } else { + styp := g.typ(info.elem_type) + g.write('$styp _var_$left.pos.pos = *($styp*)array_get(') + } + if left.left_type.is_ptr() { + g.write('*') + } + needs_clone := info.elem_type == ast.string_type && g.is_autofree + if needs_clone { + g.write('/*1*/string_clone(') + } + g.expr(left.left) + if needs_clone { + g.write(')') + } + g.write(', ') + g.expr(left.index) + g.writeln(');') + } else if sym.kind == .map { + info := sym.info as ast.Map + skeytyp := g.typ(info.key_type) + styp := g.typ(info.value_type) + zero := g.type_default(info.value_type) + val_typ := g.table.sym(info.value_type) + if val_typ.kind == .function { + left_type := node.left_types[i] + left_sym := g.table.sym(left_type) + g.write_fn_ptr_decl(left_sym.info as ast.FnType, '_var_$left.pos.pos') + g.write(' = *(voidptr*)map_get(') + } else { + g.write('$styp _var_$left.pos.pos = *($styp*)map_get(') + } + if !left.left_type.is_ptr() { + g.write('ADDR(map, ') + g.expr(left.left) + g.write(')') + } else { + g.expr(left.left) + } + g.write(', &($skeytyp[]){') + g.expr(left.index) + g.write('}') + if val_typ.kind == .function { + g.writeln(', &(voidptr[]){ $zero });') + } else { + g.writeln(', &($styp[]){ $zero });') + } + } + } + ast.SelectorExpr { + styp := g.typ(left.typ) + g.write('$styp _var_$left.pos.pos = ') + g.expr(left.expr) + mut sel := '.' + if left.expr_type.is_ptr() { + if left.expr_type.has_flag(.shared_f) { + sel = '->val.' + } else { + sel = '->' + } + } + g.writeln('$sel$left.field_name;') + } + else {} + } + } +} + +fn (mut g Gen) gen_cross_tmp_variable(left []ast.Expr, val ast.Expr) { + val_ := val + match val { + ast.Ident { + mut has_var := false + for lx in left { + if lx is ast.Ident { + if val.name == lx.name { + g.write('_var_') + g.write(lx.pos.pos.str()) + has_var = true + break + } + } + } + if !has_var { + g.expr(val_) + } + } + ast.IndexExpr { + mut has_var := false + for lx in left { + if val_.str() == lx.str() { + g.write('_var_') + g.write(lx.position().pos.str()) + has_var = true + break + } + } + if !has_var { + g.expr(val_) + } + } + ast.InfixExpr { + sym := g.table.sym(val.left_type) + if _ := g.table.find_method(sym, val.op.str()) { + left_styp := g.typ(val.left_type.set_nr_muls(0)) + g.write(left_styp) + g.write('_') + g.write(util.replace_op(val.op.str())) + g.write('(') + g.gen_cross_tmp_variable(left, val.left) + g.write(', ') + g.gen_cross_tmp_variable(left, val.right) + g.write(')') + } else { + g.gen_cross_tmp_variable(left, val.left) + g.write(val.op.str()) + g.gen_cross_tmp_variable(left, val.right) + } + } + ast.PrefixExpr { + g.write(val.op.str()) + g.gen_cross_tmp_variable(left, val.right) + } + ast.PostfixExpr { + g.gen_cross_tmp_variable(left, val.expr) + g.write(val.op.str()) + } + ast.SelectorExpr { + mut has_var := false + for lx in left { + if val_.str() == lx.str() { + g.write('_var_') + g.write(lx.position().pos.str()) + has_var = true + break + } + } + if !has_var { + g.expr(val_) + } + } + else { + g.expr(val_) + } + } +} diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 7f9b1834a5..4f3992aef0 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -2695,732 +2695,6 @@ fn (mut g Gen) write_fn_ptr_decl(func &ast.FnType, ptr_name string) { g.write(')') } -// TODO this function is scary. Simplify/split up. -fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { - if assign_stmt.is_static { - g.write('static ') - } - if assign_stmt.is_volatile { - g.write('volatile ') - } - mut return_type := ast.void_type - is_decl := assign_stmt.op == .decl_assign - g.assign_op = assign_stmt.op - op := if is_decl { token.Kind.assign } else { assign_stmt.op } - right_expr := assign_stmt.right[0] - match right_expr { - ast.CallExpr { return_type = right_expr.return_type } - ast.LockExpr { return_type = right_expr.typ } - ast.MatchExpr { return_type = right_expr.return_type } - ast.IfExpr { return_type = right_expr.typ } - else {} - } - // Free the old value assigned to this string var (only if it's `str = [new value]` - // or `x.str = [new value]` ) - mut af := g.is_autofree && !g.is_builtin_mod && assign_stmt.op == .assign - && assign_stmt.left_types.len == 1 - && (assign_stmt.left[0] is ast.Ident || assign_stmt.left[0] is ast.SelectorExpr) - // assign_stmt.left_types[0] in [ast.string_type, ast.array_type] && - mut sref_name := '' - mut type_to_free := '' - if af { - first_left_type := assign_stmt.left_types[0] - first_left_sym := g.table.sym(assign_stmt.left_types[0]) - if first_left_type == ast.string_type || first_left_sym.kind == .array { - type_to_free = if first_left_type == ast.string_type { 'string' } else { 'array' } - mut ok := true - left0 := assign_stmt.left[0] - if left0 is ast.Ident { - if left0.name == '_' { - ok = false - } - } - if ok { - sref_name = '_sref$assign_stmt.pos.pos' - g.write('$type_to_free $sref_name = (') // TODO we are copying the entire string here, optimize - // we can't just do `.str` since we need the extra data from the string struct - // doing `&string` is also not an option since the stack memory with the data will be overwritten - g.expr(left0) // assign_stmt.left[0]) - g.writeln('); // free $type_to_free on re-assignment2') - defer { - if af { - g.writeln('${type_to_free}_free(&$sref_name);') - } - } - } else { - af = false - } - } else { - af = false - } - } - // Autofree tmp arg vars - // first_right := assign_stmt.right[0] - // af := g.autofree && first_right is ast.CallExpr && !g.is_builtin_mod - // if af { - // g.autofree_call_pregen(first_right as ast.CallExpr) - // } - // - // - // Handle optionals. We need to declare a temp variable for them, that's why they are handled - // here, not in call_expr(). - // `pos := s.index('x') or { return }` - // ==========> - // Option_int _t190 = string_index(s, _STR("x")); // _STR() no more used!! - // if (_t190.state != 2) { - // Error err = _t190.err; - // return; - // } - // int pos = *(int*)_t190.data; - // mut tmp_opt := '' - /* - is_optional := false && g.is_autofree && (assign_stmt.op in [.decl_assign, .assign]) - && assign_stmt.left_types.len == 1 && assign_stmt.right[0] is ast.CallExpr - if is_optional { - // g.write('/* optional assignment */') - call_expr := assign_stmt.right[0] as ast.CallExpr - if call_expr.or_block.kind != .absent { - styp := g.typ(call_expr.return_type.set_flag(.optional)) - tmp_opt = g.new_tmp_var() - g.write('/*AF opt*/$styp $tmp_opt = ') - g.expr(assign_stmt.right[0]) - g.or_block(tmp_opt, call_expr.or_block, call_expr.return_type) - g.writeln('/*=============ret*/') - // if af && is_optional { - // g.autofree_call_postgen() - // } - // return - } - } - */ - // json_test failed w/o this check - if return_type != ast.void_type && return_type != 0 { - sym := g.table.sym(return_type) - if sym.kind == .multi_return { - // multi return - // TODO Handle in if_expr - is_opt := return_type.has_flag(.optional) - mr_var_name := 'mr_$assign_stmt.pos.pos' - mr_styp := g.typ(return_type) - g.write('$mr_styp $mr_var_name = ') - g.expr(assign_stmt.right[0]) - g.writeln(';') - for i, lx in assign_stmt.left { - mut is_auto_heap := false - mut ident := ast.Ident{ - scope: 0 - } - if lx is ast.Ident { - ident = lx - if lx.kind == .blank_ident { - continue - } - if lx.obj is ast.Var { - is_auto_heap = lx.obj.is_auto_heap - } - } - styp := if ident.name in g.defer_vars { - '' - } else { - g.typ(assign_stmt.left_types[i]) - } - if assign_stmt.op == .decl_assign { - g.write('$styp ') - if is_auto_heap { - g.write('*') - } - } - if lx.is_auto_deref_var() { - g.write('*') - } - g.expr(lx) - noscan := if is_auto_heap { g.check_noscan(return_type) } else { '' } - if g.is_arraymap_set { - if is_opt { - mr_base_styp := g.base_type(return_type) - if is_auto_heap { - g.writeln('HEAP${noscan}($mr_base_styp, *($mr_base_styp*)${mr_var_name}.data).arg$i) });') - } else { - g.writeln('(*($mr_base_styp*)${mr_var_name}.data).arg$i });') - } - } else { - if is_auto_heap { - g.writeln('HEAP${noscan}($styp, ${mr_var_name}.arg$i) });') - } else { - g.writeln('${mr_var_name}.arg$i });') - } - } - } else { - if is_opt { - mr_base_styp := g.base_type(return_type) - if is_auto_heap { - g.writeln(' = HEAP${noscan}($mr_base_styp, *($mr_base_styp*)${mr_var_name}.data).arg$i);') - } else { - g.writeln(' = (*($mr_base_styp*)${mr_var_name}.data).arg$i;') - } - } else { - if is_auto_heap { - g.writeln(' = HEAP${noscan}($styp, ${mr_var_name}.arg$i);') - } else { - g.writeln(' = ${mr_var_name}.arg$i;') - } - } - } - } - if g.is_arraymap_set { - g.is_arraymap_set = false - } - return - } - } - // TODO: non idents on left (exprs) - if assign_stmt.has_cross_var { - for i, left in assign_stmt.left { - match left { - ast.Ident { - left_typ := assign_stmt.left_types[i] - left_sym := g.table.sym(left_typ) - if left_sym.kind == .function { - g.write_fn_ptr_decl(left_sym.info as ast.FnType, '_var_$left.pos.pos') - g.writeln(' = $left.name;') - } else { - styp := g.typ(left_typ) - g.writeln('$styp _var_$left.pos.pos = $left.name;') - } - } - ast.IndexExpr { - sym := g.table.sym(left.left_type) - if sym.kind == .array { - info := sym.info as ast.Array - elem_typ := g.table.sym(info.elem_type) - if elem_typ.kind == .function { - left_typ := assign_stmt.left_types[i] - left_sym := g.table.sym(left_typ) - g.write_fn_ptr_decl(left_sym.info as ast.FnType, '_var_$left.pos.pos') - g.write(' = *(voidptr*)array_get(') - } else { - styp := g.typ(info.elem_type) - g.write('$styp _var_$left.pos.pos = *($styp*)array_get(') - } - if left.left_type.is_ptr() { - g.write('*') - } - needs_clone := info.elem_type == ast.string_type && g.is_autofree - if needs_clone { - g.write('/*1*/string_clone(') - } - g.expr(left.left) - if needs_clone { - g.write(')') - } - g.write(', ') - g.expr(left.index) - g.writeln(');') - } else if sym.kind == .map { - info := sym.info as ast.Map - skeytyp := g.typ(info.key_type) - styp := g.typ(info.value_type) - zero := g.type_default(info.value_type) - val_typ := g.table.sym(info.value_type) - if val_typ.kind == .function { - left_type := assign_stmt.left_types[i] - left_sym := g.table.sym(left_type) - g.write_fn_ptr_decl(left_sym.info as ast.FnType, '_var_$left.pos.pos') - g.write(' = *(voidptr*)map_get(') - } else { - g.write('$styp _var_$left.pos.pos = *($styp*)map_get(') - } - if !left.left_type.is_ptr() { - g.write('ADDR(map, ') - g.expr(left.left) - g.write(')') - } else { - g.expr(left.left) - } - g.write(', &($skeytyp[]){') - g.expr(left.index) - g.write('}') - if val_typ.kind == .function { - g.writeln(', &(voidptr[]){ $zero });') - } else { - g.writeln(', &($styp[]){ $zero });') - } - } - } - ast.SelectorExpr { - styp := g.typ(left.typ) - g.write('$styp _var_$left.pos.pos = ') - g.expr(left.expr) - mut sel := '.' - if left.expr_type.is_ptr() { - if left.expr_type.has_flag(.shared_f) { - sel = '->val.' - } else { - sel = '->' - } - } - g.writeln('$sel$left.field_name;') - } - else {} - } - } - } - // `a := 1` | `a,b := 1,2` - if assign_stmt.right.len < assign_stmt.left.len { - g.checker_bug('assign_stmt.right.len < assign_stmt.left.len', assign_stmt.pos) - } - if assign_stmt.right_types.len < assign_stmt.left.len { - g.checker_bug('assign_stmt.right_types.len < assign_stmt.left.len', assign_stmt.pos) - } - if assign_stmt.left_types.len < assign_stmt.left.len { - g.checker_bug('assign_stmt.left_types.len < assign_stmt.left.len', assign_stmt.pos) - } - - for i, left in assign_stmt.left { - mut is_auto_heap := false - mut var_type := assign_stmt.left_types[i] - mut val_type := assign_stmt.right_types[i] - val := assign_stmt.right[i] - mut is_call := false - mut blank_assign := false - mut ident := ast.Ident{ - scope: 0 - } - left_sym := g.table.sym(g.unwrap_generic(var_type)) - if mut left is ast.Ident { - ident = left - // id_info := ident.var_info() - // var_type = id_info.typ - blank_assign = left.kind == .blank_ident - // TODO: temporary, remove this - left_info := left.info - if left_info is ast.IdentVar { - share := left_info.share - if share == .shared_t { - var_type = var_type.set_flag(.shared_f) - } - if share == .atomic_t { - var_type = var_type.set_flag(.atomic_f) - } - } - if mut left.obj is ast.Var { - if val is ast.ComptimeSelector { - if val.field_expr is ast.SelectorExpr { - if val.field_expr.expr is ast.Ident { - key_str := '${val.field_expr.expr.name}.typ' - var_type = g.comptime_var_type_map[key_str] or { var_type } - left.obj.typ = var_type - } - } - } else if val is ast.ComptimeCall { - key_str := '${val.method_name}.return_type' - var_type = g.comptime_var_type_map[key_str] or { var_type } - left.obj.typ = var_type - } - is_auto_heap = left.obj.is_auto_heap - } - } - styp := g.typ(var_type) - mut is_fixed_array_init := false - mut has_val := false - match val { - ast.ArrayInit { - is_fixed_array_init = val.is_fixed - has_val = val.has_val - } - ast.CallExpr { - is_call = true - return_type = val.return_type - } - // TODO: no buffer fiddling - ast.AnonFn { - if blank_assign { - g.write('{') - } - // if it's a decl assign (`:=`) or a blank assignment `_ =`/`_ :=` then generate `void (*ident) (args) =` - if (is_decl || blank_assign) && left is ast.Ident { - ret_styp := g.typ(val.decl.return_type) - g.write('$ret_styp (*$ident.name) (') - def_pos := g.definitions.len - g.fn_args(val.decl.params, voidptr(0)) - g.definitions.go_back(g.definitions.len - def_pos) - g.write(') = ') - } else { - g.is_assign_lhs = true - g.assign_op = assign_stmt.op - g.expr(left) - g.is_assign_lhs = false - g.is_arraymap_set = false - if left is ast.IndexExpr { - sym := g.table.sym(left.left_type) - if sym.kind in [.map, .array] { - g.expr(val) - g.writeln('});') - continue - } - } - g.write(' = ') - } - g.expr(val) - g.writeln(';') - if blank_assign { - g.write('}') - } - continue - } - else {} - } - unwrapped_val_type := g.unwrap_generic(val_type) - right_sym := g.table.sym(unwrapped_val_type) - unaliased_right_sym := g.table.final_sym(unwrapped_val_type) - is_fixed_array_var := unaliased_right_sym.kind == .array_fixed && val !is ast.ArrayInit - && (val in [ast.Ident, ast.IndexExpr, ast.CallExpr, ast.SelectorExpr] - || (val is ast.CastExpr && (val as ast.CastExpr).expr !is ast.ArrayInit)) - g.is_assign_lhs = true - g.assign_op = assign_stmt.op - if val_type.has_flag(.optional) { - g.right_is_opt = true - } - if blank_assign { - if val is ast.IndexExpr { - g.assign_op = .decl_assign - } - if is_call { - old_is_void_expr_stmt := g.is_void_expr_stmt - g.is_void_expr_stmt = true - g.expr(val) - g.is_void_expr_stmt = old_is_void_expr_stmt - } else { - g.write('{$styp _ = ') - g.expr(val) - g.writeln(';}') - } - g.is_assign_lhs = false - } else if assign_stmt.op == .assign - && (is_fixed_array_init || (right_sym.kind == .array_fixed && val is ast.Ident)) { - mut v_var := '' - arr_typ := styp.trim('*') - if is_fixed_array_init { - right := val as ast.ArrayInit - v_var = g.new_tmp_var() - g.write('$arr_typ $v_var = ') - g.expr(right) - g.writeln(';') - } else { - right := val as ast.Ident - v_var = right.name - } - pos := g.out.len - g.expr(left) - - if g.is_arraymap_set && g.arraymap_set_pos > 0 { - g.out.go_back_to(g.arraymap_set_pos) - g.write(', &$v_var)') - g.is_arraymap_set = false - g.arraymap_set_pos = 0 - } else { - g.out.go_back_to(pos) - is_var_mut := !is_decl && left.is_auto_deref_var() - addr := if is_var_mut { '' } else { '&' } - g.writeln('') - g.write('memcpy($addr') - g.expr(left) - g.writeln(', &$v_var, sizeof($arr_typ));') - } - g.is_assign_lhs = false - } else { - is_inside_ternary := g.inside_ternary != 0 - cur_line := if is_inside_ternary && is_decl { - g.register_ternary_name(ident.name) - g.empty_line = false - g.go_before_ternary() - } else { - '' - } - mut str_add := false - mut op_overloaded := false - mut op_expected_left := ast.Type(0) - mut op_expected_right := ast.Type(0) - if var_type == ast.string_type_idx && assign_stmt.op == .plus_assign { - if left is ast.IndexExpr { - // a[0] += str => `array_set(&a, 0, &(string[]) {string__plus(...))})` - g.expr(left) - g.write('string__plus(') - } else { - // str += str2 => `str = string__plus(str, str2)` - g.expr(left) - g.write(' = /*f*/string__plus(') - } - g.is_assign_lhs = false - str_add = true - } - // Assignment Operator Overloading - if ((left_sym.kind == .struct_ && right_sym.kind == .struct_) - || (left_sym.kind == .alias && right_sym.kind == .alias)) - && assign_stmt.op in [.plus_assign, .minus_assign, .div_assign, .mult_assign, .mod_assign] { - extracted_op := match assign_stmt.op { - .plus_assign { '+' } - .minus_assign { '-' } - .div_assign { '/' } - .mod_assign { '%' } - .mult_assign { '*' } - else { 'unknown op' } - } - g.expr(left) - if left_sym.kind == .struct_ && (left_sym.info as ast.Struct).generic_types.len > 0 { - concrete_types := (left_sym.info as ast.Struct).concrete_types - mut method_name := left_sym.cname + '_' + util.replace_op(extracted_op) - method_name = g.generic_fn_name(concrete_types, method_name, true) - g.write(' = ${method_name}(') - g.expr(left) - g.write(', ') - g.expr(val) - g.writeln(');') - return - } else { - g.write(' = ${styp}_${util.replace_op(extracted_op)}(') - method := g.table.find_method(left_sym, extracted_op) or { - // the checker will most likely have found this, already... - g.error('assignemnt operator `$extracted_op=` used but no `$extracted_op` method defined', - assign_stmt.pos) - ast.Fn{} - } - op_expected_left = method.params[0].typ - op_expected_right = method.params[1].typ - op_overloaded = true - } - } - if right_sym.kind == .function && is_decl { - if is_inside_ternary && is_decl { - g.out.write_string(util.tabs(g.indent - g.inside_ternary)) - } - func := right_sym.info as ast.FnType - ret_styp := g.typ(func.func.return_type) - g.write('$ret_styp (*${g.get_ternary_name(ident.name)}) (') - def_pos := g.definitions.len - g.fn_args(func.func.params, voidptr(0)) - g.definitions.go_back(g.definitions.len - def_pos) - g.write(')') - } else { - if is_decl { - if is_inside_ternary { - g.out.write_string(util.tabs(g.indent - g.inside_ternary)) - } - mut is_used_var_styp := false - if ident.name !in g.defer_vars { - val_sym := g.table.sym(val_type) - if val_sym.info is ast.Struct { - if val_sym.info.generic_types.len > 0 { - if val is ast.StructInit { - var_styp := g.typ(val.typ) - g.write('$var_styp ') - is_used_var_styp = true - } else if val is ast.PrefixExpr { - if val.op == .amp && val.right is ast.StructInit { - var_styp := g.typ(val.right.typ.ref()) - g.write('$var_styp ') - is_used_var_styp = true - } - } - } - } - if !is_used_var_styp { - g.write('$styp ') - } - if is_auto_heap { - g.write('*') - } - } - } - if left in [ast.Ident, ast.SelectorExpr] { - g.prevent_sum_type_unwrapping_once = true - } - if !is_fixed_array_var || is_decl { - if op_overloaded { - g.op_arg(left, op_expected_left, var_type) - } else { - if !is_decl && left.is_auto_deref_var() { - g.write('*') - } - g.expr(left) - } - } - } - if is_inside_ternary && is_decl { - g.write(';\n$cur_line') - g.out.write_string(util.tabs(g.indent)) - g.expr(left) - } - g.is_assign_lhs = false - if is_fixed_array_var { - if is_decl { - g.writeln(';') - } - } else if !g.is_arraymap_set && !str_add && !op_overloaded { - g.write(' $op ') - } else if str_add || op_overloaded { - g.write(', ') - } - mut cloned := false - if g.is_autofree && right_sym.kind in [.array, .string] { - if g.gen_clone_assignment(val, unwrapped_val_type, false) { - cloned = true - } - } - unwrap_optional := !var_type.has_flag(.optional) && val_type.has_flag(.optional) - if unwrap_optional { - // Unwrap the optional now that the testing code has been prepended. - // `pos := s.index(... - // `int pos = *(int)_t10.data;` - // if g.is_autofree { - /* - if is_optional { - g.write('*($styp*)') - g.write(tmp_opt + '.data/*FFz*/') - g.right_is_opt = false - if g.inside_ternary == 0 && !assign_stmt.is_simple { - g.writeln(';') - } - return - } - */ - } - g.is_shared = var_type.has_flag(.shared_f) - if !cloned { - if is_fixed_array_var { - typ_str := g.typ(val_type).trim('*') - ref_str := if val_type.is_ptr() { '' } else { '&' } - g.write('memcpy(($typ_str*)') - g.expr(left) - g.write(', (byte*)$ref_str') - g.expr(val) - g.write(', sizeof($typ_str))') - } else if is_decl { - if is_fixed_array_init && !has_val { - if val is ast.ArrayInit { - g.array_init(val) - } else { - g.write('{0}') - } - } else { - if is_auto_heap { - g.write('HEAP($styp, (') - } - if val.is_auto_deref_var() { - g.write('*') - } - g.expr(val) - if is_auto_heap { - g.write('))') - } - } - } else { - if assign_stmt.has_cross_var { - g.gen_cross_tmp_variable(assign_stmt.left, val) - } else { - if op_overloaded { - g.op_arg(val, op_expected_right, val_type) - } else { - g.expr_with_cast(val, val_type, var_type) - } - } - } - } - if str_add || op_overloaded { - g.write(')') - } - if g.is_arraymap_set { - g.write(' })') - g.is_arraymap_set = false - } - g.is_shared = false - } - g.right_is_opt = false - if g.inside_ternary == 0 && (assign_stmt.left.len > 1 || !assign_stmt.is_simple) { - g.writeln(';') - } - } -} - -fn (mut g Gen) gen_cross_tmp_variable(left []ast.Expr, val ast.Expr) { - val_ := val - match val { - ast.Ident { - mut has_var := false - for lx in left { - if lx is ast.Ident { - if val.name == lx.name { - g.write('_var_') - g.write(lx.pos.pos.str()) - has_var = true - break - } - } - } - if !has_var { - g.expr(val_) - } - } - ast.IndexExpr { - mut has_var := false - for lx in left { - if val_.str() == lx.str() { - g.write('_var_') - g.write(lx.position().pos.str()) - has_var = true - break - } - } - if !has_var { - g.expr(val_) - } - } - ast.InfixExpr { - sym := g.table.sym(val.left_type) - if _ := g.table.find_method(sym, val.op.str()) { - left_styp := g.typ(val.left_type.set_nr_muls(0)) - g.write(left_styp) - g.write('_') - g.write(util.replace_op(val.op.str())) - g.write('(') - g.gen_cross_tmp_variable(left, val.left) - g.write(', ') - g.gen_cross_tmp_variable(left, val.right) - g.write(')') - } else { - g.gen_cross_tmp_variable(left, val.left) - g.write(val.op.str()) - g.gen_cross_tmp_variable(left, val.right) - } - } - ast.PrefixExpr { - g.write(val.op.str()) - g.gen_cross_tmp_variable(left, val.right) - } - ast.PostfixExpr { - g.gen_cross_tmp_variable(left, val.expr) - g.write(val.op.str()) - } - ast.SelectorExpr { - mut has_var := false - for lx in left { - if val_.str() == lx.str() { - g.write('_var_') - g.write(lx.position().pos.str()) - has_var = true - break - } - } - if !has_var { - g.expr(val_) - } - } - else { - g.expr(val_) - } - } -} - fn (mut g Gen) register_ternary_name(name string) { level_key := g.inside_ternary.str() if level_key !in g.ternary_level_names {