Skip to content

Commit b0c35f5

Browse files
committed
translate-c: handle negative array indices
Take advantage of wrapping pointer arithmetic to enable negative array subscripts. Fixes #8556
1 parent c8753ac commit b0c35f5

File tree

3 files changed

+34
-7
lines changed

3 files changed

+34
-7
lines changed

src/translate_c.zig

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3052,10 +3052,13 @@ fn transArrayAccess(c: *Context, scope: *Scope, stmt: *const clang.ArraySubscrip
30523052
const is_longlong = cIsLongLongInteger(qt);
30533053
const is_signed = cIsSignedInteger(qt);
30543054

3055-
const rhs = if (is_longlong or is_signed) blk: {
3056-
// check if long long first so that signed long long doesn't just become unsigned long long
3057-
var typeid_node = if (is_longlong) try Tag.identifier.create(c.arena, "usize") else try transQualTypeIntWidthOf(c, qt, false);
3058-
break :blk try Tag.int_cast.create(c.arena, .{ .lhs = typeid_node, .rhs = try transExpr(c, scope, subscr_expr, .used) });
3055+
const rhs = if (is_longlong and !is_signed) blk: {
3056+
var typeid_node = try Tag.identifier.create(c.arena, "usize");
3057+
const subscr_node = try transExpr(c, scope, subscr_expr, .used);
3058+
break :blk try Tag.int_cast.create(c.arena, .{ .lhs = typeid_node, .rhs = subscr_node });
3059+
} else if (is_signed) blk: {
3060+
const subscr_node = try transExprCoercing(c, scope, subscr_expr, .used);
3061+
break :blk try usizeCastForWrappingPtrArithmetic(c.arena, subscr_node);
30593062
} else try transExpr(c, scope, subscr_expr, .used);
30603063

30613064
const node = try Tag.array_access.create(c.arena, .{

test/run_translated_c.zig

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,4 +1476,28 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
14761476
\\ return 0;
14771477
\\}
14781478
, "");
1479+
1480+
cases.add("Negative array subscript. Issue #8556",
1481+
\\#include <stdint.h>
1482+
\\#include <stdlib.h>
1483+
\\#define TEST_NEGATIVE(type) { type x = -1; if (ptr[x] != 42) abort(); }
1484+
\\#define TEST_UNSIGNED(type) { type x = 2; if (arr[x] != 42) abort(); }
1485+
\\int main(void) {
1486+
\\ int arr[] = {40, 41, 42, 43};
1487+
\\ int *ptr = arr + 3;
1488+
\\ if (ptr[-1] != 42) abort();
1489+
\\ TEST_NEGATIVE(int);
1490+
\\ TEST_NEGATIVE(long);
1491+
\\ TEST_NEGATIVE(long long);
1492+
\\ TEST_NEGATIVE(int64_t);
1493+
\\ TEST_NEGATIVE(__int128);
1494+
\\ TEST_UNSIGNED(unsigned);
1495+
\\ TEST_UNSIGNED(unsigned long);
1496+
\\ TEST_UNSIGNED(unsigned long long);
1497+
\\ TEST_UNSIGNED(uint64_t);
1498+
\\ TEST_UNSIGNED(size_t);
1499+
\\ TEST_UNSIGNED(unsigned __int128);
1500+
\\ return 0;
1501+
\\}
1502+
, "");
14791503
}

test/translate_c.zig

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2501,7 +2501,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
25012501
\\pub export var array: [100]c_int = [1]c_int{0} ** 100;
25022502
\\pub export fn foo(arg_index: c_int) c_int {
25032503
\\ var index = arg_index;
2504-
\\ return array[@intCast(c_uint, index)];
2504+
\\ return array[@bitCast(usize, @intCast(isize, index))];
25052505
\\}
25062506
,
25072507
\\pub const ACCESS = array[@as(c_int, 2)];
@@ -2516,7 +2516,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
25162516
\\pub export fn foo() void {
25172517
\\ var a: [10]c_int = undefined;
25182518
\\ var i: c_int = 0;
2519-
\\ a[@intCast(c_uint, i)] = 0;
2519+
\\ a[@bitCast(usize, @intCast(isize, i))] = 0;
25202520
\\}
25212521
});
25222522

@@ -2529,7 +2529,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
25292529
\\pub export fn foo() void {
25302530
\\ var a: [10]c_longlong = undefined;
25312531
\\ var i: c_longlong = 0;
2532-
\\ a[@intCast(usize, i)] = 0;
2532+
\\ a[@bitCast(usize, @intCast(isize, i))] = 0;
25332533
\\}
25342534
});
25352535

0 commit comments

Comments
 (0)