authorgravatar for evan@lagerdata.comEvan Haas <evan@lagerdata.com> 2021-11-19 14:00:18-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-20 13:52:07-05:00
log3fefdc1a0b3177519aea8e8309983a41957555ab
tree7c63a6014a773c60c369c76c7fc5f72738be435a
parente8112f774450be081cc210cf03c114f64b78731e

translate-c: Allow negative denominator in remainder (%) operator

Fixes #10176

5 files changed, 38 insertions(+), 14 deletions(-)

lib/std/zig/c_translation.zig+11
...@@ -346,6 +346,17 @@ test "Flexible Array Type" {...@@ -346,6 +346,17 @@ test "Flexible Array Type" {
346 try testing.expectEqual(FlexibleArrayType(*const volatile Container, c_int), [*c]const volatile c_int);346 try testing.expectEqual(FlexibleArrayType(*const volatile Container, c_int), [*c]const volatile c_int);
347}347}
348348
349/// C `%` operator for signed integers
350/// C standard states: "If the quotient a/b is representable, the expression (a/b)*b + a%b shall equal a"
351/// The quotient is not representable if denominator is zero, or if numerator is the minimum integer for
352/// the type and denominator is -1. C has undefined behavior for those two cases; this function has safety
353/// checked undefined behavior
354pub fn signedRemainder(numerator: anytype, denominator: anytype) @TypeOf(numerator, denominator) {
355 std.debug.assert(@typeInfo(@TypeOf(numerator, denominator)).Int.signedness == .signed);
356 if (denominator > 0) return @rem(numerator, denominator);
357 return numerator - @divTrunc(numerator, denominator) * denominator;
358}
359
349pub const Macros = struct {360pub const Macros = struct {
350 pub fn U_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_uint, n, .decimal)) {361 pub fn U_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_uint, n, .decimal)) {
351 return promoteIntLiteral(c_uint, n, .decimal);362 return promoteIntLiteral(c_uint, n, .decimal);
src/translate_c.zig+4-4
...@@ -1620,10 +1620,10 @@ fn transBinaryOperator(...@@ -1620,10 +1620,10 @@ fn transBinaryOperator(
1620 },1620 },
1621 .Rem => {1621 .Rem => {
1622 if (cIsSignedInteger(qt)) {1622 if (cIsSignedInteger(qt)) {
1623 // signed integer division uses @rem1623 // signed integer remainder uses std.zig.c_translation.signedRemainder
1624 const lhs = try transExpr(c, scope, stmt.getLHS(), .used);1624 const lhs = try transExpr(c, scope, stmt.getLHS(), .used);
1625 const rhs = try transExpr(c, scope, stmt.getRHS(), .used);1625 const rhs = try transExpr(c, scope, stmt.getRHS(), .used);
1626 const rem = try Tag.rem.create(c.arena, .{ .lhs = lhs, .rhs = rhs });1626 const rem = try Tag.signed_remainder.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
1627 return maybeSuppressResult(c, scope, result_used, rem);1627 return maybeSuppressResult(c, scope, result_used, rem);
1628 }1628 }
1629 },1629 },
...@@ -3831,7 +3831,7 @@ fn transCreateCompoundAssign(...@@ -3831,7 +3831,7 @@ fn transCreateCompoundAssign(
3831 if (requires_int_cast) rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node);3831 if (requires_int_cast) rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node);
3832 const operands = .{ .lhs = lhs_node, .rhs = rhs_node };3832 const operands = .{ .lhs = lhs_node, .rhs = rhs_node };
3833 const builtin = if (is_mod)3833 const builtin = if (is_mod)
3834 try Tag.rem.create(c.arena, operands)3834 try Tag.signed_remainder.create(c.arena, operands)
3835 else3835 else
3836 try Tag.div_trunc.create(c.arena, operands);3836 try Tag.div_trunc.create(c.arena, operands);
38373837
...@@ -3871,7 +3871,7 @@ fn transCreateCompoundAssign(...@@ -3871,7 +3871,7 @@ fn transCreateCompoundAssign(
3871 if (requires_int_cast) rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node);3871 if (requires_int_cast) rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node);
3872 const operands = .{ .lhs = ref_node, .rhs = rhs_node };3872 const operands = .{ .lhs = ref_node, .rhs = rhs_node };
3873 const builtin = if (is_mod)3873 const builtin = if (is_mod)
3874 try Tag.rem.create(c.arena, operands)3874 try Tag.signed_remainder.create(c.arena, operands)
3875 else3875 else
3876 try Tag.div_trunc.create(c.arena, operands);3876 try Tag.div_trunc.create(c.arena, operands);
38773877
src/translate_c/ast.zig+8-7
...@@ -128,8 +128,8 @@ pub const Node = extern union {...@@ -128,8 +128,8 @@ pub const Node = extern union {
128 helpers_promoteIntLiteral,128 helpers_promoteIntLiteral,
129 /// @import("std").meta.alignment(value)129 /// @import("std").meta.alignment(value)
130 std_meta_alignment,130 std_meta_alignment,
131 /// @rem(lhs, rhs)131 /// @import("std").zig.c_translation.signedRemainder(lhs, rhs)
132 rem,132 signed_remainder,
133 /// @divTrunc(lhs, rhs)133 /// @divTrunc(lhs, rhs)
134 div_trunc,134 div_trunc,
135 /// @boolToInt(operand)135 /// @boolToInt(operand)
...@@ -310,7 +310,7 @@ pub const Node = extern union {...@@ -310,7 +310,7 @@ pub const Node = extern union {
310 .bit_xor,310 .bit_xor,
311 .bit_xor_assign,311 .bit_xor_assign,
312 .div_trunc,312 .div_trunc,
313 .rem,313 .signed_remainder,
314 .int_cast,314 .int_cast,
315 .as,315 .as,
316 .truncate,316 .truncate,
...@@ -1293,9 +1293,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {...@@ -1293,9 +1293,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
1293 const payload = node.castTag(.int_cast).?.data;1293 const payload = node.castTag(.int_cast).?.data;
1294 return renderBuiltinCall(c, "@intCast", &.{ payload.lhs, payload.rhs });1294 return renderBuiltinCall(c, "@intCast", &.{ payload.lhs, payload.rhs });
1295 },1295 },
1296 .rem => {1296 .signed_remainder => {
1297 const payload = node.castTag(.rem).?.data;1297 const payload = node.castTag(.signed_remainder).?.data;
1298 return renderBuiltinCall(c, "@rem", &.{ payload.lhs, payload.rhs });1298 const import_node = try renderStdImport(c, &.{ "zig", "c_translation", "signedRemainder" });
1299 return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
1299 },1300 },
1300 .div_trunc => {1301 .div_trunc => {
1301 const payload = node.castTag(.div_trunc).?.data;1302 const payload = node.castTag(.div_trunc).?.data;
...@@ -2207,7 +2208,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {...@@ -2207,7 +2208,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
2207 .noreturn_type,2208 .noreturn_type,
2208 .@"anytype",2209 .@"anytype",
2209 .div_trunc,2210 .div_trunc,
2210 .rem,2211 .signed_remainder,
2211 .int_cast,2212 .int_cast,
2212 .as,2213 .as,
2213 .truncate,2214 .truncate,
test/run_translated_c.zig+12
...@@ -1784,4 +1784,16 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {...@@ -1784,4 +1784,16 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
1784 \\ return 0;1784 \\ return 0;
1785 \\}1785 \\}
1786 , "");1786 , "");
1787
1788 cases.add("Remainder operator with negative integers. Issue #10176",
1789 \\#include <stdlib.h>
1790 \\int main(void) {
1791 \\ int denominator = -2;
1792 \\ int numerator = 5;
1793 \\ if (numerator % denominator != 1) abort();
1794 \\ numerator = -5; denominator = 2;
1795 \\ if (numerator % denominator != -1) abort();
1796 \\ return 0;
1797 \\}
1798 , "");
1787}1799}
test/translate_c.zig+3-3
...@@ -885,7 +885,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -885,7 +885,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
885 \\ c = a - b;885 \\ c = a - b;
886 \\ c = a * b;886 \\ c = a * b;
887 \\ c = @divTrunc(a, b);887 \\ c = @divTrunc(a, b);
888 \\ c = @rem(a, b);888 \\ c = @import("std").zig.c_translation.signedRemainder(a, b);
889 \\ return 0;889 \\ return 0;
890 \\}890 \\}
891 \\pub export fn u() c_uint {891 \\pub export fn u() c_uint {
...@@ -2932,9 +2932,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2932,9 +2932,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2932 \\ ref.* = @divTrunc(ref.*, @as(c_int, 1));2932 \\ ref.* = @divTrunc(ref.*, @as(c_int, 1));
2933 \\ break :blk ref.*;2933 \\ break :blk ref.*;
2934 \\ });2934 \\ });
2935 \\ a = @rem(a, blk: {2935 \\ a = @import("std").zig.c_translation.signedRemainder(a, blk: {
2936 \\ const ref = &a;2936 \\ const ref = &a;
2937 \\ ref.* = @rem(ref.*, @as(c_int, 1));2937 \\ ref.* = @import("std").zig.c_translation.signedRemainder(ref.*, @as(c_int, 1));
2938 \\ break :blk ref.*;2938 \\ break :blk ref.*;
2939 \\ });2939 \\ });
2940 \\ b /= blk: {2940 \\ b /= blk: {