diff --git a/src/parsec.cpp b/src/parsec.cpp index b9328a8ba..988bf351e 100644 --- a/src/parsec.cpp +++ b/src/parsec.cpp @@ -942,19 +942,52 @@ static AstNode *trans_create_bin_op(Context *c, AstNode *block, Expr *lhs, BinOp return node; } -static AstNode *trans_create_assign(Context *c, AstNode *block, Expr *lhs, Expr *rhs) { - AstNode *node = trans_create_node(c, NodeTypeBinOpExpr); - node->data.bin_op_expr.bin_op = BinOpTypeAssign; +static AstNode *trans_create_assign(Context *c, bool result_used, AstNode *block, Expr *lhs, Expr *rhs) { + if (!result_used) { + // common case + AstNode *node = trans_create_node(c, NodeTypeBinOpExpr); + node->data.bin_op_expr.bin_op = BinOpTypeAssign; - node->data.bin_op_expr.op1 = trans_expr(c, true, block, lhs, TransLValue); - if (node->data.bin_op_expr.op1 == nullptr) - return nullptr; + node->data.bin_op_expr.op1 = trans_expr(c, true, block, lhs, TransLValue); + if (node->data.bin_op_expr.op1 == nullptr) + return nullptr; - node->data.bin_op_expr.op2 = trans_expr(c, true, block, rhs, TransRValue); - if (node->data.bin_op_expr.op2 == nullptr) - return nullptr; + node->data.bin_op_expr.op2 = trans_expr(c, true, block, rhs, TransRValue); + if (node->data.bin_op_expr.op2 == nullptr) + return nullptr; - return node; + return node; + } else { + // worst case + // c: lhs = rhs + // zig: { + // zig: const _tmp = rhs; + // zig: lhs = _tmp; + // zig: _tmp + // zig: } + + AstNode *child_block = trans_create_node(c, NodeTypeBlock); + + // const _tmp = rhs; + AstNode *rhs_node = trans_expr(c, true, child_block, rhs, TransRValue); + if (rhs_node == nullptr) return nullptr; + // TODO: avoid name collisions with generated variable names + Buf* tmp_var_name = buf_create_from_str("_tmp"); + AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, rhs_node); + child_block->data.block.statements.append(tmp_var_decl); + + // lhs = _tmp; + AstNode *lhs_node = trans_expr(c, true, child_block, lhs, TransLValue); + if (lhs_node == nullptr) return nullptr; + child_block->data.block.statements.append( + trans_create_node_bin_op(c, lhs_node, BinOpTypeAssign, + trans_create_node_symbol(c, tmp_var_name))); + + // _tmp + child_block->data.block.statements.append(trans_create_node_symbol(c, tmp_var_name)); + + return child_block; + } } static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *block, BinaryOperator *stmt) { @@ -1037,11 +1070,7 @@ static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *blo // TODO: int vs bool return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeBoolOr, stmt->getRHS()); case BO_Assign: - if (result_used) { - emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_Assign with result_used"); - return nullptr; - } - return trans_create_assign(c, block, stmt->getLHS(), stmt->getRHS()); + return trans_create_assign(c, result_used, block, stmt->getLHS(), stmt->getRHS()); case BO_MulAssign: emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_MulAssign"); return nullptr; diff --git a/test/parsec.zig b/test/parsec.zig index 814abbd52..54fd1c4c5 100644 --- a/test/parsec.zig +++ b/test/parsec.zig @@ -466,6 +466,23 @@ pub fn addCases(cases: &tests.ParseCContext) { \\} ); + cases.add("chaining assign", + \\void max(int a) { + \\ int b, c; + \\ c = b = a; + \\} + , + \\export fn max(a: c_int) { + \\ var b: c_int; + \\ var c: c_int; + \\ c = { + \\ const _tmp = a; + \\ b = _tmp; + \\ _tmp; + \\ }; + \\} + ); + cases.add("shift right assign with a fixed size type", \\#include \\int log2(uint32_t a) {