diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index da7053d25..66dd8d581 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -1170,7 +1170,7 @@ fn transBinaryOperator( } }, .Div => { - if (!cIsUnsignedInteger(qt)) { + if (cIsSignedInteger(qt)) { // signed integer division uses @divTrunc const div_trunc_node = try transCreateNodeBuiltinFnCall(rp.c, "@divTrunc"); try div_trunc_node.params.push(try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value)); @@ -1182,7 +1182,7 @@ fn transBinaryOperator( } }, .Rem => { - if (!cIsUnsignedInteger(qt)) { + if (cIsSignedInteger(qt)) { // signed integer division uses @rem const rem_node = try transCreateNodeBuiltinFnCall(rp.c, "@rem"); try rem_node.params.push(try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value)); @@ -3048,13 +3048,37 @@ fn transCreateCompoundAssign( used: ResultUsed, ) TransError!*ast.Node { const is_shift = bin_op == .BitShiftLeft or bin_op == .BitShiftRight; + const is_div = bin_op == .Div; + const is_mod = bin_op == .Mod; const lhs = ZigClangCompoundAssignOperator_getLHS(stmt); const rhs = ZigClangCompoundAssignOperator_getRHS(stmt); const loc = ZigClangCompoundAssignOperator_getBeginLoc(stmt); + const is_signed = cIsSignedInteger(getExprQualType(rp.c, lhs)); if (used == .unused) { // common case // c: lhs += rhs // zig: lhs += rhs + + if ((is_mod or is_div) and is_signed) { + const op_token = try appendToken(rp.c, .Equal, "="); + const op_node = try rp.c.a().create(ast.Node.InfixOp); + const builtin = if (is_mod) "@rem" else "@divTrunc"; + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, builtin); + const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value); + try builtin_node.params.push(lhs_node); + _ = try appendToken(rp.c, .Comma, ","); + try builtin_node.params.push(try transExpr(rp, scope, rhs, .used, .r_value)); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + op_node.* = .{ + .op_token = op_token, + .lhs = lhs_node, + .op = .Assign, + .rhs = &builtin_node.base, + }; + _ = try appendToken(rp.c, .Semicolon, ";"); + return &op_node.base; + } + const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value); const eq_token = try appendToken(rp.c, assign_tok_id, assign_bytes); var rhs_node = if (is_shift) @@ -3097,31 +3121,53 @@ fn transCreateCompoundAssign( const lhs_node = try transCreateNodeIdentifier(rp.c, ref); const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node); _ = try appendToken(rp.c, .Semicolon, ";"); - const bin_token = try appendToken(rp.c, bin_tok_id, bin_bytes); - var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value); - if (is_shift) { - const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@intCast"); - const rhs_type = try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc); - try cast_node.params.push(rhs_type); + + if ((is_mod or is_div) and is_signed) { + const op_token = try appendToken(rp.c, .Equal, "="); + const op_node = try rp.c.a().create(ast.Node.InfixOp); + const builtin = if (is_mod) "@rem" else "@divTrunc"; + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, builtin); + try builtin_node.params.push(try transCreateNodePtrDeref(rp.c, lhs_node)); _ = try appendToken(rp.c, .Comma, ","); - try cast_node.params.push(rhs_node); - cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - rhs_node = &cast_node.base; + try builtin_node.params.push(try transExpr(rp, scope, rhs, .used, .r_value)); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + _ = try appendToken(rp.c, .Semicolon, ";"); + op_node.* = .{ + .op_token = op_token, + .lhs = ref_node, + .op = .Assign, + .rhs = &builtin_node.base, + }; + _ = try appendToken(rp.c, .Semicolon, ";"); + try block_scope.block_node.statements.push(&op_node.base); + } else { + const bin_token = try appendToken(rp.c, bin_tok_id, bin_bytes); + var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value); + + if (is_shift) { + const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@intCast"); + const rhs_type = try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc); + try cast_node.params.push(rhs_type); + _ = try appendToken(rp.c, .Comma, ","); + try cast_node.params.push(rhs_node); + cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + rhs_node = &cast_node.base; + } + + const rhs_bin = try transCreateNodeInfixOp(rp, scope, ref_node, bin_op, bin_token, rhs_node, .used, false); + _ = try appendToken(rp.c, .Semicolon, ";"); + + const eq_token = try appendToken(rp.c, .Equal, "="); + const assign = try transCreateNodeInfixOp(rp, scope, ref_node, .Assign, eq_token, rhs_bin, .used, false); + try block_scope.block_node.statements.push(assign); } - const rhs_bin = try transCreateNodeInfixOp(rp, scope, ref_node, bin_op, bin_token, rhs_node, .used, false); - - _ = try appendToken(rp.c, .Semicolon, ";"); - - const eq_token = try appendToken(rp.c, .Equal, "="); - const assign = try transCreateNodeInfixOp(rp, scope, ref_node, .Assign, eq_token, rhs_bin, .used, false); - try block_scope.block_node.statements.push(assign); const break_node = try transCreateNodeBreak(rp.c, block_scope.label); break_node.rhs = ref_node; try block_scope.block_node.statements.push(&break_node.base); block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); // semicolon must immediately follow rbrace because it is the last token in a block - _ = try appendToken(rp.c, .Semicolon, ";"); + _ = try appendToken(rp.c, .Semicolon, ";4"); const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression); grouped_expr.* = .{ .lparen = try appendToken(rp.c, .LParen, "("), diff --git a/test/translate_c.zig b/test/translate_c.zig index a09f6b42b..a3a22a199 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -2375,20 +2375,24 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("compound assignment operators", \\void foo(void) { \\ int a = 0; + \\ unsigned b = 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); \\ a >>= (a >>= 1); \\ a <<= (a <<= 1); + \\ a /= (a /= 1); + \\ a %= (a %= 1); + \\ b /= (b /= 1); + \\ b %= (b %= 1); \\} , &[_][]const u8{ \\pub export fn foo() void { \\ var a: c_int = 0; + \\ var b: c_uint = @bitCast(c_uint, @as(c_int, 0)); \\ a += (blk: { \\ const ref = &a; \\ ref.* = ref.* + @as(c_int, 1); @@ -2419,16 +2423,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ ref.* = ref.* ^ @as(c_int, 1); \\ break :blk ref.*; \\ }); - \\ a /= (blk: { - \\ const ref = &a; - \\ ref.* = ref.* / @as(c_int, 1); - \\ break :blk ref.*; - \\ }); - \\ a %= (blk: { - \\ const ref = &a; - \\ ref.* = ref.* % @as(c_int, 1); - \\ break :blk ref.*; - \\ }); \\ a >>= @intCast(@import("std").math.Log2Int(c_int), (blk: { \\ const ref = &a; \\ ref.* = ref.* >> @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); @@ -2439,6 +2433,26 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ ref.* = ref.* << @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); \\ break :blk ref.*; \\ })); + \\ a = @divTrunc(a, (blk: { + \\ const ref = &a; + \\ ref.* = @divTrunc(ref.*, @as(c_int, 1)); + \\ break :blk ref.*; + \\ })); + \\ a = @rem(a, (blk: { + \\ const ref = &a; + \\ ref.* = @rem(ref.*, @as(c_int, 1)); + \\ break :blk ref.*; + \\ })); + \\ b /= (blk: { + \\ const ref = &b; + \\ ref.* = ref.* / @bitCast(c_uint, @as(c_int, 1)); + \\ break :blk ref.*; + \\ }); + \\ b %= (blk: { + \\ const ref = &b; + \\ ref.* = ref.* % @bitCast(c_uint, @as(c_int, 1)); + \\ break :blk ref.*; + \\ }); \\} });