authorgravatar for evan@lagerdata.comEvan Haas <evan@lagerdata.com> 2021-04-19 12:15:38-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-28 17:46:02-04:00
log8028e46f0150842df4ac6fc050dca8dc58ae5f18
treeef09f706a9012e5e2be5615dd01cffe94dcf74c9
parentc47b46fc8dcb24a9af8e28d169d93170fce0e061

translate-c: handle signed array subscripts

A rather complicated workaround for handling signed array subscripts. Once `[*]T + isize` is allowed, this can be removed. Fixes #8556

2 files changed, 133 insertions(+), 10 deletions(-)

src/translate_c.zig+93-10
......@@ -3273,30 +3273,113 @@ fn transMemberExpr(c: *Context, scope: *Scope, stmt: *const clang.MemberExpr, re
32733273 return maybeSuppressResult(c, scope, result_used, node);
32743274}
32753275
3276/// ptr[subscr] (`subscr` is a signed integer expression, `ptr` a pointer) becomes:
3277/// (blk: {
3278/// const tmp = subscr;
3279/// if (tmp >= 0) break :blk ptr + @intCast(usize, tmp) else break :blk ptr - ~@bitCast(usize, @intCast(isize, tmp) +% -1);
3280/// }).*
3281/// Todo: rip this out once `[*]T + isize` becomes valid.
3282fn transSignedArrayAccess(
3283 c: *Context,
3284 scope: *Scope,
3285 container_expr: *const clang.Expr,
3286 subscr_expr: *const clang.Expr,
3287 result_used: ResultUsed,
3288) TransError!Node {
3289 var block_scope = try Scope.Block.init(c, scope, true);
3290 defer block_scope.deinit();
3291
3292 const tmp = try block_scope.makeMangledName(c, "tmp");
3293
3294 const subscr_node = try transExpr(c, &block_scope.base, subscr_expr, .used);
3295 const subscr_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = subscr_node });
3296 try block_scope.statements.append(subscr_decl);
3297
3298 const tmp_ref = try Tag.identifier.create(c.arena, tmp);
3299
3300 const container_node = try transExpr(c, &block_scope.base, container_expr, .used);
3301
3302 const cond_node = try Tag.greater_than_equal.create(c.arena, .{ .lhs = tmp_ref, .rhs = Tag.zero_literal.init() });
3303
3304 const then_value = try Tag.add.create(c.arena, .{
3305 .lhs = container_node,
3306 .rhs = try Tag.int_cast.create(c.arena, .{
3307 .lhs = try Tag.identifier.create(c.arena, "usize"),
3308 .rhs = tmp_ref,
3309 }),
3310 });
3311
3312 const then_body = try Tag.break_val.create(c.arena, .{
3313 .label = block_scope.label,
3314 .val = then_value,
3315 });
3316
3317 const minuend = container_node;
3318 const signed_size = try Tag.int_cast.create(c.arena, .{
3319 .lhs = try Tag.identifier.create(c.arena, "isize"),
3320 .rhs = tmp_ref,
3321 });
3322 const to_cast = try Tag.add_wrap.create(c.arena, .{
3323 .lhs = signed_size,
3324 .rhs = try Tag.negate.create(c.arena, Tag.one_literal.init()),
3325 });
3326 const bitcast_node = try Tag.bit_cast.create(c.arena, .{
3327 .lhs = try Tag.identifier.create(c.arena, "usize"),
3328 .rhs = to_cast,
3329 });
3330 const subtrahend = try Tag.bit_not.create(c.arena, bitcast_node);
3331 const difference = try Tag.sub.create(c.arena, .{
3332 .lhs = minuend,
3333 .rhs = subtrahend,
3334 });
3335 const else_body = try Tag.break_val.create(c.arena, .{
3336 .label = block_scope.label,
3337 .val = difference,
3338 });
3339
3340 const if_node = try Tag.@"if".create(c.arena, .{
3341 .cond = cond_node,
3342 .then = then_body,
3343 .@"else" = else_body,
3344 });
3345
3346 try block_scope.statements.append(if_node);
3347 const block_node = try block_scope.complete(c);
3348
3349 const derefed = try Tag.deref.create(c.arena, block_node);
3350
3351 return maybeSuppressResult(c, &block_scope.base, result_used, derefed);
3352}
3353
32763354fn transArrayAccess(c: *Context, scope: *Scope, stmt: *const clang.ArraySubscriptExpr, result_used: ResultUsed) TransError!Node {
3277 var base_stmt = stmt.getBase();
3355 const base_stmt = stmt.getBase();
3356 const base_qt = getExprQualType(c, base_stmt);
3357 const is_vector = cIsVector(base_qt);
3358
3359 const subscr_expr = stmt.getIdx();
3360 const subscr_qt = getExprQualType(c, subscr_expr);
3361 const is_longlong = cIsLongLongInteger(subscr_qt);
3362 const is_signed = cIsSignedInteger(subscr_qt);
32783363
32793364 // Unwrap the base statement if it's an array decayed to a bare pointer type
32803365 // so that we index the array itself
3366 var unwrapped_base = base_stmt;
32813367 if (@ptrCast(*const clang.Stmt, base_stmt).getStmtClass() == .ImplicitCastExprClass) {
32823368 const implicit_cast = @ptrCast(*const clang.ImplicitCastExpr, base_stmt);
32833369
32843370 if (implicit_cast.getCastKind() == .ArrayToPointerDecay) {
3285 base_stmt = implicit_cast.getSubExpr();
3371 unwrapped_base = implicit_cast.getSubExpr();
32863372 }
32873373 }
32883374
3289 const container_node = try transExpr(c, scope, base_stmt, .used);
3290
3291 // cast if the index is long long or signed
3292 const subscr_expr = stmt.getIdx();
3293 const qt = getExprQualType(c, subscr_expr);
3294 const is_longlong = cIsLongLongInteger(qt);
3295 const is_signed = cIsSignedInteger(qt);
3375 // Special case: actual pointer (not decayed array) and signed integer subscript
3376 // See discussion at https://github.com/ziglang/zig/pull/8589
3377 if (is_signed and (base_stmt == unwrapped_base) and !is_vector) return transSignedArrayAccess(c, scope, base_stmt, subscr_expr, result_used);
32963378
3379 const container_node = try transExpr(c, scope, unwrapped_base, .used);
32973380 const rhs = if (is_longlong or is_signed) blk: {
32983381 // check if long long first so that signed long long doesn't just become unsigned long long
3299 var typeid_node = if (is_longlong) try Tag.identifier.create(c.arena, "usize") else try transQualTypeIntWidthOf(c, qt, false);
3382 const typeid_node = if (is_longlong) try Tag.identifier.create(c.arena, "usize") else try transQualTypeIntWidthOf(c, subscr_qt, false);
33003383 break :blk try Tag.int_cast.create(c.arena, .{ .lhs = typeid_node, .rhs = try transExpr(c, scope, subscr_expr, .used) });
33013384 } else try transExpr(c, scope, subscr_expr, .used);
33023385
test/run_translated_c.zig+40
......@@ -1709,4 +1709,44 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
17091709 \\ return 0;
17101710 \\}
17111711 , "");
1712
1713 cases.add("signed array subscript. Issue #8556",
1714 \\#include <stdint.h>
1715 \\#include <stdlib.h>
1716 \\#define TEST_NEGATIVE(type) { type x = -1; if (ptr[x] != 42) abort(); }
1717 \\#define TEST_UNSIGNED(type) { type x = 2; if (arr[x] != 42) abort(); }
1718 \\int main(void) {
1719 \\ int arr[] = {40, 41, 42, 43};
1720 \\ int *ptr = arr + 3;
1721 \\ if (ptr[-1] != 42) abort();
1722 \\ TEST_NEGATIVE(int);
1723 \\ TEST_NEGATIVE(long);
1724 \\ TEST_NEGATIVE(long long);
1725 \\ TEST_NEGATIVE(int64_t);
1726 \\ TEST_NEGATIVE(__int128);
1727 \\ TEST_UNSIGNED(unsigned);
1728 \\ TEST_UNSIGNED(unsigned long);
1729 \\ TEST_UNSIGNED(unsigned long long);
1730 \\ TEST_UNSIGNED(uint64_t);
1731 \\ TEST_UNSIGNED(size_t);
1732 \\ TEST_UNSIGNED(unsigned __int128);
1733 \\ return 0;
1734 \\}
1735 , "");
1736
1737 cases.add("Ensure side-effects only evaluated once for signed array indices",
1738 \\#include <stdlib.h>
1739 \\int main(void) {
1740 \\ int foo[] = {1, 2, 3, 4};
1741 \\ int *p = foo;
1742 \\ int idx = 1;
1743 \\ if ((++p)[--idx] != 2) abort();
1744 \\ if (p != foo + 1) abort();
1745 \\ if (idx != 0) abort();
1746 \\ if ((p++)[idx++] != 2) abort();
1747 \\ if (p != foo + 2) abort();
1748 \\ if (idx != 1) abort();
1749 \\ return 0;
1750 \\}
1751 , "");
17121752}