| 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,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 | } |
| 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 | pub const Macros = struct { | 360 | pub 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 @rem | 1623 | // 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 | else | 3835 | else |
| 3836 | try Tag.div_trunc.create(c.arena, operands); | 3836 | try Tag.div_trunc.create(c.arena, operands); |
| 3837 | 3837 | ||
| ... | @@ -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 | else | 3875 | else |
| 3876 | try Tag.div_trunc.create(c.arena, operands); | 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,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: { |