diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index aa0a479de3..2580c495a3 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -538,6 +538,7 @@ pub mut: left_types []table.Type right_types []table.Type is_static bool // for translated code only + has_cross_var bool } pub struct AsCast { diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index b7ae93543b..b24a3702c2 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -384,10 +384,8 @@ typedef struct { .alias { parent := &g.table.types[typ.parent_idx] styp := typ.name.replace('.', '__') - is_c_parent := parent.name.len > 2 && parent.name[0] == `C` && parent.name[1] == - `.` - parent_styp := if is_c_parent { 'struct ' + parent.name[2..].replace('.', '__') } else { parent.name.replace('.', - '__') } + is_c_parent := parent.name.len > 2 && parent.name[0] == `C` && parent.name[1] == `.` + parent_styp := if is_c_parent { 'struct ' + parent.name[2..].replace('.', '__') } else { parent.name.replace('.', '__') } g.definitions.writeln('typedef $parent_styp $styp;') } .array { @@ -844,9 +842,7 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type, expected_type table.Type) got_is_ptr := got_type.is_ptr() expected_is_ptr := expected_type.is_ptr() neither_void := table.voidptr_type !in [got_type, expected_type] - if got_is_ptr && !expected_is_ptr && neither_void && expected_sym.kind !in [.interface_, - .placeholder - ] { + if got_is_ptr && !expected_is_ptr && neither_void && expected_sym.kind !in [.interface_, .placeholder] { got_deref_type := got_type.deref() deref_sym := g.table.get_type_symbol(got_deref_type) deref_will_match := expected_type in [got_type, got_deref_type, deref_sym.parent_idx] @@ -905,8 +901,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { if return_type != table.void_type && return_type != 0 { sym := g.table.get_type_symbol(return_type) // the left vs. right is ugly and should be removed - if sym.kind == .multi_return || assign_stmt.left.len > assign_stmt.right.len || assign_stmt.left.len > - 1 { + if sym.kind == .multi_return || assign_stmt.left.len > assign_stmt.right.len || assign_stmt.left.len > 1 { // multi return // TODO Handle in if_expr is_optional := return_type.flag_is(.optional) @@ -942,6 +937,12 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { return } } + if assign_stmt.has_cross_var { + for ident in assign_stmt.left { + type_str := g.typ(ident.var_info().typ) + g.writeln('$type_str _var_$ident.pos.pos = $ident.name;') + } + } // `a := 1` | `a,b := 1,2` for i, ident in assign_stmt.left { val := assign_stmt.right[i] @@ -1042,7 +1043,11 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { if is_decl { g.expr(val) } else { - g.expr_with_cast(val, assign_stmt.left_types[i], ident_var_info.typ) + if assign_stmt.has_cross_var { + g.gen_cross_tmp_variable(assign_stmt.left, val) + } else { + g.expr_with_cast(val, assign_stmt.left_types[i], ident_var_info.typ) + } } } } @@ -1053,6 +1058,32 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { } } +fn (mut g Gen) gen_cross_tmp_variable(idents []ast.Ident, val ast.Expr) { + match val { + ast.Ident { + mut has_var := false + for ident in idents { + if it.name == ident.name { + g.write('_var_${ident.pos.pos}') + has_var = true + break + } + } + if !has_var { + g.expr(val) + } + } + ast.InfixExpr { + g.gen_cross_tmp_variable(idents, it.left) + g.write(it.op.str()) + g.gen_cross_tmp_variable(idents, it.right) + } + else { + g.expr(val) + } + } +} + fn (mut g Gen) gen_default_init_value(val ast.Expr) (bool, bool) { mut is_fixed_array_init := false mut has_val := false @@ -1797,8 +1828,7 @@ fn (mut g Gen) match_expr(node ast.MatchExpr) { g.writeln('// match 0') return } - is_expr := (node.is_expr && node.return_type != table.void_type) || g.inside_ternary > - 0 + is_expr := (node.is_expr && node.return_type != table.void_type) || g.inside_ternary > 0 if is_expr { g.inside_ternary++ // g.write('/* EM ret type=${g.typ(node.return_type)} expected_type=${g.typ(node.expected_type)} */') diff --git a/vlib/v/parser/assign.v b/vlib/v/parser/assign.v index 646edcf605..2ee4527cf1 100644 --- a/vlib/v/parser/assign.v +++ b/vlib/v/parser/assign.v @@ -31,6 +31,35 @@ fn (mut p Parser) check_undefined_variables(idents []ast.Ident, expr ast.Expr) { } } +fn (mut p Parser) check_cross_variables(idents []ast.Ident, expr ast.Expr) bool { + match expr { + ast.Ident { + for ident in idents { + if ident.name == it.name { + return true + } + } + } + ast.InfixExpr { + if p.check_cross_variables(idents, it.left) { + return true + } + if p.check_cross_variables(idents, it.right) { + return true + } + } + ast.StringInterLiteral { + for expr_ in it.exprs { + if p.check_cross_variables(idents, expr_) { + return true + } + } + } + else {} + } + return false +} + fn (mut p Parser) partial_assign_stmt(known_lhs []ast.Ident) ast.Stmt { mut idents := known_lhs mut op := p.tok.kind @@ -46,11 +75,19 @@ fn (mut p Parser) partial_assign_stmt(known_lhs []ast.Ident) ast.Stmt { pos := p.tok.position() exprs := p.parse_assign_rhs() is_decl := op == .decl_assign + mut has_cross_var := false if is_decl { // a, b := a + 1, b for expr in exprs { p.check_undefined_variables(idents, expr) } + } else if idents.len > 1 { + // a, b = b, a + for expr in exprs { + if p.check_cross_variables(idents, expr) { + has_cross_var = true + } + } } for i, ident in idents { known_var := p.scope.known_var(ident.name) @@ -83,6 +120,7 @@ fn (mut p Parser) partial_assign_stmt(known_lhs []ast.Ident) ast.Stmt { op: op pos: pos is_static: false // individual idents may be static + has_cross_var: has_cross_var } } diff --git a/vlib/v/tests/multiple_assign_test.v b/vlib/v/tests/multiple_assign_test.v index bf05c72590..447ac52192 100644 --- a/vlib/v/tests/multiple_assign_test.v +++ b/vlib/v/tests/multiple_assign_test.v @@ -4,3 +4,33 @@ fn test_multiple_assign() { assert b == 2 assert c == 3 } + +fn test_multiple_assign_swap_simple() { + mut a := 11 + mut b := 22 + a, b = b, a + assert a == 22 + assert b == 11 +} + +fn test_multiple_assign_swap_complex() { + mut a := 11 + mut b := 22 + mut c := 33 + mut d := 44 + a, b, c, d = b, a, d, c + assert a == 22 + assert b == 11 + assert c == 44 + assert d == 33 +} + +fn test_multiple_assign_infix_expr() { + mut a := 11 + mut b := 22 + mut c := 33 + a, b, c = b + 1, a * 2, c - a + assert a == 23 + assert b == 22 + assert c == 22 +}