diff --git a/src/parsec.cpp b/src/parsec.cpp index bd4ca1237d..1cc4a8c184 100644 --- a/src/parsec.cpp +++ b/src/parsec.cpp @@ -1499,38 +1499,72 @@ static AstNode *trans_decl_ref_expr(Context *c, DeclRefExpr *stmt, TransLRValue return trans_create_node_symbol(c, symbol_name); } +static AstNode *trans_create_post_crement(Context *c, bool result_used, AstNode *block, UnaryOperator *stmt, BinOpType assign_op) { + Expr *op_expr = stmt->getSubExpr(); + + if (!result_used) { + // common case + // c: expr++ + // zig: expr += 1 + return trans_create_node_bin_op(c, + trans_expr(c, true, block, op_expr, TransLValue), + assign_op, + trans_create_node_unsigned(c, 1)); + } else { + // worst case + // c: expr++ + // zig: { + // zig: const _ref = &expr; + // zig: const _tmp = *_ref; + // zig: *_ref += 1; + // zig: _tmp + // zig: } + AstNode *child_block = trans_create_node(c, NodeTypeBlock); + + // const _ref = &expr; + AstNode *expr = trans_expr(c, true, child_block, op_expr, TransLValue); + if (expr == nullptr) return nullptr; + AstNode *addr_of_expr = trans_create_node_addr_of(c, false, false, expr); + // TODO: avoid name collisions with generated variable names + Buf* ref_var_name = buf_create_from_str("_ref"); + AstNode *ref_var_decl = trans_create_node_var_decl_local(c, true, ref_var_name, nullptr, addr_of_expr); + child_block->data.block.statements.append(ref_var_decl); + + // const _tmp = *_ref; + 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, + trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_symbol(c, ref_var_name))); + child_block->data.block.statements.append(tmp_var_decl); + + // *_ref += 1; + AstNode *assign_statement = trans_create_node_bin_op(c, + trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_symbol(c, ref_var_name)), + assign_op, + trans_create_node_unsigned(c, 1)); + child_block->data.block.statements.append(assign_statement); + + // _tmp + child_block->data.block.statements.append(trans_create_node_symbol(c, tmp_var_name)); + child_block->data.block.last_statement_is_result_expression = true; + + return child_block; + } +} + static AstNode *trans_unary_operator(Context *c, bool result_used, AstNode *block, UnaryOperator *stmt) { switch (stmt->getOpcode()) { - case UO_PostInc: { - Expr *op_expr = stmt->getSubExpr(); - BinOpType bin_op = qual_type_has_wrapping_overflow(c, op_expr->getType()) - ? BinOpTypeAssignPlusWrap - : BinOpTypeAssignPlus; - - if (!result_used) { - // common case - // c: expr++ - // zig: expr += 1 - return trans_create_node_bin_op(c, - trans_expr(c, true, block, op_expr, TransLValue), - bin_op, - trans_create_node_unsigned(c, 1)); - } else { - // worst case - // c: expr++ - // zig: { - // zig: const _ref = &expr; - // zig: const _tmp = *_ref; - // zig: *_ref += 1; - // zig: _tmp - // zig: } - emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_PostInc with result_used"); - return nullptr; - } - } + case UO_PostInc: + if (qual_type_has_wrapping_overflow(c, stmt->getType())) + return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignPlusWrap); + else + return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignPlus); case UO_PostDec: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_PostDec"); - return nullptr; + if (qual_type_has_wrapping_overflow(c, stmt->getType())) + return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignMinusWrap); + else + return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignMinus); case UO_PreInc: emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_PreInc"); return nullptr; diff --git a/test/parsec.zig b/test/parsec.zig index b25de84f88..a6f23bd2a9 100644 --- a/test/parsec.zig +++ b/test/parsec.zig @@ -687,6 +687,65 @@ pub fn addCases(cases: &tests.ParseCContext) { \\ }); \\} ); + + cases.addC("compound assignment operators unsigned", + \\void foo(void) { + \\ unsigned a = 0; + \\ a += (a += 1); + \\ a -= (a -= 1); + \\ a *= (a *= 1); + \\ a &= (a &= 1); + \\ a |= (a |= 1); + \\ a ^= (a ^= 1); + \\ a >>= (a >>= 1); + \\ a <<= (a <<= 1); + \\} + , + \\export fn foo() { + \\ var a: c_uint = c_uint(0); + \\ a +%= { + \\ const _ref = &a; + \\ (*_ref) = ((*_ref) +% c_uint(1)); + \\ *_ref + \\ }; + \\ a -%= { + \\ const _ref = &a; + \\ (*_ref) = ((*_ref) -% c_uint(1)); + \\ *_ref + \\ }; + \\ a *%= { + \\ const _ref = &a; + \\ (*_ref) = ((*_ref) *% c_uint(1)); + \\ *_ref + \\ }; + \\ a &= { + \\ const _ref = &a; + \\ (*_ref) = ((*_ref) & c_uint(1)); + \\ *_ref + \\ }; + \\ a |= { + \\ const _ref = &a; + \\ (*_ref) = ((*_ref) | c_uint(1)); + \\ *_ref + \\ }; + \\ a ^= { + \\ const _ref = &a; + \\ (*_ref) = ((*_ref) ^ c_uint(1)); + \\ *_ref + \\ }; + \\ a >>= @import("std").math.Log2Int(c_uint)({ + \\ const _ref = &a; + \\ (*_ref) = c_uint(c_uint(*_ref) >> @import("std").math.Log2Int(c_uint)(1)); + \\ *_ref + \\ }); + \\ a <<= @import("std").math.Log2Int(c_uint)({ + \\ const _ref = &a; + \\ (*_ref) = c_uint(c_uint(*_ref) << @import("std").math.Log2Int(c_uint)(1)); + \\ *_ref + \\ }); + \\} + ); + cases.addC("duplicate typedef", \\typedef long foo; \\typedef int bar; @@ -697,6 +756,54 @@ pub fn addCases(cases: &tests.ParseCContext) { \\pub const bar = c_int; \\pub const baz = c_int; ); + + cases.addC("post increment/decrement", + \\void foo(void) { + \\ int i = 0; + \\ unsigned u = 0; + \\ i++; + \\ i--; + \\ u++; + \\ u--; + \\ i = i++; + \\ i = i--; + \\ u = u++; + \\ u = u--; + \\} + , + \\export fn foo() { + \\ var i: c_int = 0; + \\ var u: c_uint = c_uint(0); + \\ i += 1; + \\ i -= 1; + \\ u +%= 1; + \\ u -%= 1; + \\ i = { + \\ const _ref = &i; + \\ const _tmp = *_ref; + \\ (*_ref) += 1; + \\ _tmp + \\ }; + \\ i = { + \\ const _ref = &i; + \\ const _tmp = *_ref; + \\ (*_ref) -= 1; + \\ _tmp + \\ }; + \\ u = { + \\ const _ref = &u; + \\ const _tmp = *_ref; + \\ (*_ref) +%= 1; + \\ _tmp + \\ }; + \\ u = { + \\ const _ref = &u; + \\ const _tmp = *_ref; + \\ (*_ref) -%= 1; + \\ _tmp + \\ }; + \\} + ); }