| author | |
| committer | |
| log | 3fefdc1a0b3177519aea8e8309983a41957555ab |
| tree | 7c63a6014a773c60c369c76c7fc5f72738be435a |
| parent | e8112f774450be081cc210cf03c114f64b78731e |
Fixes #101765 files changed, 38 insertions(+), 14 deletions(-)
lib/std/zig/c_translation.zig+11| ... | ... | @@ -346,6 +346,17 @@ test "Flexible Array Type" { |
| 346 | 346 | try testing.expectEqual(FlexibleArrayType(*const volatile Container, c_int), [*c]const volatile c_int); |
| 347 | 347 | } |
| 348 | 348 | |
| 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 | |
| 354 | pub 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 | ||
| 349 | 360 | pub const Macros = struct { |
| 350 | 361 | pub fn U_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_uint, n, .decimal)) { |
| 351 | 362 | return promoteIntLiteral(c_uint, n, .decimal); |
src/translate_c.zig+4-4| ... | ... | @@ -1620,10 +1620,10 @@ fn transBinaryOperator( |
| 1620 | 1620 | }, |
| 1621 | 1621 | .Rem => { |
| 1622 | 1622 | if (cIsSignedInteger(qt)) { |
| 1623 | // signed integer division uses @rem | |
| 1623 | // signed integer remainder uses std.zig.c_translation.signedRemainder | |
| 1624 | 1624 | const lhs = try transExpr(c, scope, stmt.getLHS(), .used); |
| 1625 | 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 | 1627 | return maybeSuppressResult(c, scope, result_used, rem); |
| 1628 | 1628 | } |
| 1629 | 1629 | }, |
| ... | ... | @@ -3831,7 +3831,7 @@ fn transCreateCompoundAssign( |
| 3831 | 3831 | if (requires_int_cast) rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node); |
| 3832 | 3832 | const operands = .{ .lhs = lhs_node, .rhs = rhs_node }; |
| 3833 | 3833 | const builtin = if (is_mod) |
| 3834 | try Tag.rem.create(c.arena, operands) | |
| 3834 | try Tag.signed_remainder.create(c.arena, operands) | |
| 3835 | 3835 | else |
| 3836 | 3836 | try Tag.div_trunc.create(c.arena, operands); |
| 3837 | 3837 | |
| ... | ... | @@ -3871,7 +3871,7 @@ fn transCreateCompoundAssign( |
| 3871 | 3871 | if (requires_int_cast) rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node); |
| 3872 | 3872 | const operands = .{ .lhs = ref_node, .rhs = rhs_node }; |
| 3873 | 3873 | const builtin = if (is_mod) |
| 3874 | try Tag.rem.create(c.arena, operands) | |
| 3874 | try Tag.signed_remainder.create(c.arena, operands) | |
| 3875 | 3875 | else |
| 3876 | 3876 | try Tag.div_trunc.create(c.arena, operands); |
| 3877 | 3877 |
src/translate_c/ast.zig+8-7| ... | ... | @@ -128,8 +128,8 @@ pub const Node = extern union { |
| 128 | 128 | helpers_promoteIntLiteral, |
| 129 | 129 | /// @import("std").meta.alignment(value) |
| 130 | 130 | std_meta_alignment, |
| 131 | /// @rem(lhs, rhs) | |
| 132 | rem, | |
| 131 | /// @import("std").zig.c_translation.signedRemainder(lhs, rhs) | |
| 132 | signed_remainder, | |
| 133 | 133 | /// @divTrunc(lhs, rhs) |
| 134 | 134 | div_trunc, |
| 135 | 135 | /// @boolToInt(operand) |
| ... | ... | @@ -310,7 +310,7 @@ pub const Node = extern union { |
| 310 | 310 | .bit_xor, |
| 311 | 311 | .bit_xor_assign, |
| 312 | 312 | .div_trunc, |
| 313 | .rem, | |
| 313 | .signed_remainder, | |
| 314 | 314 | .int_cast, |
| 315 | 315 | .as, |
| 316 | 316 | .truncate, |
| ... | ... | @@ -1293,9 +1293,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { |
| 1293 | 1293 | const payload = node.castTag(.int_cast).?.data; |
| 1294 | 1294 | return renderBuiltinCall(c, "@intCast", &.{ payload.lhs, payload.rhs }); |
| 1295 | 1295 | }, |
| 1296 | .rem => { | |
| 1297 | const payload = node.castTag(.rem).?.data; | |
| 1298 | return renderBuiltinCall(c, "@rem", &.{ payload.lhs, payload.rhs }); | |
| 1296 | .signed_remainder => { | |
| 1297 | const payload = node.castTag(.signed_remainder).?.data; | |
| 1298 | const import_node = try renderStdImport(c, &.{ "zig", "c_translation", "signedRemainder" }); | |
| 1299 | return renderCall(c, import_node, &.{ payload.lhs, payload.rhs }); | |
| 1299 | 1300 | }, |
| 1300 | 1301 | .div_trunc => { |
| 1301 | 1302 | const payload = node.castTag(.div_trunc).?.data; |
| ... | ... | @@ -2207,7 +2208,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { |
| 2207 | 2208 | .noreturn_type, |
| 2208 | 2209 | .@"anytype", |
| 2209 | 2210 | .div_trunc, |
| 2210 | .rem, | |
| 2211 | .signed_remainder, | |
| 2211 | 2212 | .int_cast, |
| 2212 | 2213 | .as, |
| 2213 | 2214 | .truncate, |
test/run_translated_c.zig+12| ... | ... | @@ -1784,4 +1784,16 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { |
| 1784 | 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 | 885 | \\ c = a - b; |
| 886 | 886 | \\ c = a * b; |
| 887 | 887 | \\ c = @divTrunc(a, b); |
| 888 | \\ c = @rem(a, b); | |
| 888 | \\ c = @import("std").zig.c_translation.signedRemainder(a, b); | |
| 889 | 889 | \\ return 0; |
| 890 | 890 | \\} |
| 891 | 891 | \\pub export fn u() c_uint { |
| ... | ... | @@ -2932,9 +2932,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2932 | 2932 | \\ ref.* = @divTrunc(ref.*, @as(c_int, 1)); |
| 2933 | 2933 | \\ break :blk ref.*; |
| 2934 | 2934 | \\ }); |
| 2935 | \\ a = @rem(a, blk: { | |
| 2935 | \\ a = @import("std").zig.c_translation.signedRemainder(a, blk: { | |
| 2936 | 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 | 2938 | \\ break :blk ref.*; |
| 2939 | 2939 | \\ }); |
| 2940 | 2940 | \\ b /= blk: { |