mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 14:23:09 +00:00
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
This commit is contained in:
parent
c47b46fc8d
commit
8028e46f01
@ -3273,30 +3273,113 @@ fn transMemberExpr(c: *Context, scope: *Scope, stmt: *const clang.MemberExpr, re
|
|||||||
return maybeSuppressResult(c, scope, result_used, node);
|
return maybeSuppressResult(c, scope, result_used, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ptr[subscr] (`subscr` is a signed integer expression, `ptr` a pointer) becomes:
|
||||||
|
/// (blk: {
|
||||||
|
/// const tmp = subscr;
|
||||||
|
/// if (tmp >= 0) break :blk ptr + @intCast(usize, tmp) else break :blk ptr - ~@bitCast(usize, @intCast(isize, tmp) +% -1);
|
||||||
|
/// }).*
|
||||||
|
/// Todo: rip this out once `[*]T + isize` becomes valid.
|
||||||
|
fn transSignedArrayAccess(
|
||||||
|
c: *Context,
|
||||||
|
scope: *Scope,
|
||||||
|
container_expr: *const clang.Expr,
|
||||||
|
subscr_expr: *const clang.Expr,
|
||||||
|
result_used: ResultUsed,
|
||||||
|
) TransError!Node {
|
||||||
|
var block_scope = try Scope.Block.init(c, scope, true);
|
||||||
|
defer block_scope.deinit();
|
||||||
|
|
||||||
|
const tmp = try block_scope.makeMangledName(c, "tmp");
|
||||||
|
|
||||||
|
const subscr_node = try transExpr(c, &block_scope.base, subscr_expr, .used);
|
||||||
|
const subscr_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = subscr_node });
|
||||||
|
try block_scope.statements.append(subscr_decl);
|
||||||
|
|
||||||
|
const tmp_ref = try Tag.identifier.create(c.arena, tmp);
|
||||||
|
|
||||||
|
const container_node = try transExpr(c, &block_scope.base, container_expr, .used);
|
||||||
|
|
||||||
|
const cond_node = try Tag.greater_than_equal.create(c.arena, .{ .lhs = tmp_ref, .rhs = Tag.zero_literal.init() });
|
||||||
|
|
||||||
|
const then_value = try Tag.add.create(c.arena, .{
|
||||||
|
.lhs = container_node,
|
||||||
|
.rhs = try Tag.int_cast.create(c.arena, .{
|
||||||
|
.lhs = try Tag.identifier.create(c.arena, "usize"),
|
||||||
|
.rhs = tmp_ref,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const then_body = try Tag.break_val.create(c.arena, .{
|
||||||
|
.label = block_scope.label,
|
||||||
|
.val = then_value,
|
||||||
|
});
|
||||||
|
|
||||||
|
const minuend = container_node;
|
||||||
|
const signed_size = try Tag.int_cast.create(c.arena, .{
|
||||||
|
.lhs = try Tag.identifier.create(c.arena, "isize"),
|
||||||
|
.rhs = tmp_ref,
|
||||||
|
});
|
||||||
|
const to_cast = try Tag.add_wrap.create(c.arena, .{
|
||||||
|
.lhs = signed_size,
|
||||||
|
.rhs = try Tag.negate.create(c.arena, Tag.one_literal.init()),
|
||||||
|
});
|
||||||
|
const bitcast_node = try Tag.bit_cast.create(c.arena, .{
|
||||||
|
.lhs = try Tag.identifier.create(c.arena, "usize"),
|
||||||
|
.rhs = to_cast,
|
||||||
|
});
|
||||||
|
const subtrahend = try Tag.bit_not.create(c.arena, bitcast_node);
|
||||||
|
const difference = try Tag.sub.create(c.arena, .{
|
||||||
|
.lhs = minuend,
|
||||||
|
.rhs = subtrahend,
|
||||||
|
});
|
||||||
|
const else_body = try Tag.break_val.create(c.arena, .{
|
||||||
|
.label = block_scope.label,
|
||||||
|
.val = difference,
|
||||||
|
});
|
||||||
|
|
||||||
|
const if_node = try Tag.@"if".create(c.arena, .{
|
||||||
|
.cond = cond_node,
|
||||||
|
.then = then_body,
|
||||||
|
.@"else" = else_body,
|
||||||
|
});
|
||||||
|
|
||||||
|
try block_scope.statements.append(if_node);
|
||||||
|
const block_node = try block_scope.complete(c);
|
||||||
|
|
||||||
|
const derefed = try Tag.deref.create(c.arena, block_node);
|
||||||
|
|
||||||
|
return maybeSuppressResult(c, &block_scope.base, result_used, derefed);
|
||||||
|
}
|
||||||
|
|
||||||
fn transArrayAccess(c: *Context, scope: *Scope, stmt: *const clang.ArraySubscriptExpr, result_used: ResultUsed) TransError!Node {
|
fn transArrayAccess(c: *Context, scope: *Scope, stmt: *const clang.ArraySubscriptExpr, result_used: ResultUsed) TransError!Node {
|
||||||
var base_stmt = stmt.getBase();
|
const base_stmt = stmt.getBase();
|
||||||
|
const base_qt = getExprQualType(c, base_stmt);
|
||||||
|
const is_vector = cIsVector(base_qt);
|
||||||
|
|
||||||
|
const subscr_expr = stmt.getIdx();
|
||||||
|
const subscr_qt = getExprQualType(c, subscr_expr);
|
||||||
|
const is_longlong = cIsLongLongInteger(subscr_qt);
|
||||||
|
const is_signed = cIsSignedInteger(subscr_qt);
|
||||||
|
|
||||||
// Unwrap the base statement if it's an array decayed to a bare pointer type
|
// Unwrap the base statement if it's an array decayed to a bare pointer type
|
||||||
// so that we index the array itself
|
// so that we index the array itself
|
||||||
|
var unwrapped_base = base_stmt;
|
||||||
if (@ptrCast(*const clang.Stmt, base_stmt).getStmtClass() == .ImplicitCastExprClass) {
|
if (@ptrCast(*const clang.Stmt, base_stmt).getStmtClass() == .ImplicitCastExprClass) {
|
||||||
const implicit_cast = @ptrCast(*const clang.ImplicitCastExpr, base_stmt);
|
const implicit_cast = @ptrCast(*const clang.ImplicitCastExpr, base_stmt);
|
||||||
|
|
||||||
if (implicit_cast.getCastKind() == .ArrayToPointerDecay) {
|
if (implicit_cast.getCastKind() == .ArrayToPointerDecay) {
|
||||||
base_stmt = implicit_cast.getSubExpr();
|
unwrapped_base = implicit_cast.getSubExpr();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const container_node = try transExpr(c, scope, base_stmt, .used);
|
// Special case: actual pointer (not decayed array) and signed integer subscript
|
||||||
|
// See discussion at https://github.com/ziglang/zig/pull/8589
|
||||||
// cast if the index is long long or signed
|
if (is_signed and (base_stmt == unwrapped_base) and !is_vector) return transSignedArrayAccess(c, scope, base_stmt, subscr_expr, result_used);
|
||||||
const subscr_expr = stmt.getIdx();
|
|
||||||
const qt = getExprQualType(c, subscr_expr);
|
|
||||||
const is_longlong = cIsLongLongInteger(qt);
|
|
||||||
const is_signed = cIsSignedInteger(qt);
|
|
||||||
|
|
||||||
|
const container_node = try transExpr(c, scope, unwrapped_base, .used);
|
||||||
const rhs = if (is_longlong or is_signed) blk: {
|
const rhs = if (is_longlong or is_signed) blk: {
|
||||||
// check if long long first so that signed long long doesn't just become unsigned long long
|
// check if long long first so that signed long long doesn't just become unsigned long long
|
||||||
var typeid_node = if (is_longlong) try Tag.identifier.create(c.arena, "usize") else try transQualTypeIntWidthOf(c, qt, false);
|
const typeid_node = if (is_longlong) try Tag.identifier.create(c.arena, "usize") else try transQualTypeIntWidthOf(c, subscr_qt, false);
|
||||||
break :blk try Tag.int_cast.create(c.arena, .{ .lhs = typeid_node, .rhs = try transExpr(c, scope, subscr_expr, .used) });
|
break :blk try Tag.int_cast.create(c.arena, .{ .lhs = typeid_node, .rhs = try transExpr(c, scope, subscr_expr, .used) });
|
||||||
} else try transExpr(c, scope, subscr_expr, .used);
|
} else try transExpr(c, scope, subscr_expr, .used);
|
||||||
|
|
||||||
|
|||||||
@ -1709,4 +1709,44 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
|
|||||||
\\ return 0;
|
\\ return 0;
|
||||||
\\}
|
\\}
|
||||||
, "");
|
, "");
|
||||||
|
|
||||||
|
cases.add("signed array subscript. Issue #8556",
|
||||||
|
\\#include <stdint.h>
|
||||||
|
\\#include <stdlib.h>
|
||||||
|
\\#define TEST_NEGATIVE(type) { type x = -1; if (ptr[x] != 42) abort(); }
|
||||||
|
\\#define TEST_UNSIGNED(type) { type x = 2; if (arr[x] != 42) abort(); }
|
||||||
|
\\int main(void) {
|
||||||
|
\\ int arr[] = {40, 41, 42, 43};
|
||||||
|
\\ int *ptr = arr + 3;
|
||||||
|
\\ if (ptr[-1] != 42) abort();
|
||||||
|
\\ TEST_NEGATIVE(int);
|
||||||
|
\\ TEST_NEGATIVE(long);
|
||||||
|
\\ TEST_NEGATIVE(long long);
|
||||||
|
\\ TEST_NEGATIVE(int64_t);
|
||||||
|
\\ TEST_NEGATIVE(__int128);
|
||||||
|
\\ TEST_UNSIGNED(unsigned);
|
||||||
|
\\ TEST_UNSIGNED(unsigned long);
|
||||||
|
\\ TEST_UNSIGNED(unsigned long long);
|
||||||
|
\\ TEST_UNSIGNED(uint64_t);
|
||||||
|
\\ TEST_UNSIGNED(size_t);
|
||||||
|
\\ TEST_UNSIGNED(unsigned __int128);
|
||||||
|
\\ return 0;
|
||||||
|
\\}
|
||||||
|
, "");
|
||||||
|
|
||||||
|
cases.add("Ensure side-effects only evaluated once for signed array indices",
|
||||||
|
\\#include <stdlib.h>
|
||||||
|
\\int main(void) {
|
||||||
|
\\ int foo[] = {1, 2, 3, 4};
|
||||||
|
\\ int *p = foo;
|
||||||
|
\\ int idx = 1;
|
||||||
|
\\ if ((++p)[--idx] != 2) abort();
|
||||||
|
\\ if (p != foo + 1) abort();
|
||||||
|
\\ if (idx != 0) abort();
|
||||||
|
\\ if ((p++)[idx++] != 2) abort();
|
||||||
|
\\ if (p != foo + 2) abort();
|
||||||
|
\\ if (idx != 1) abort();
|
||||||
|
\\ return 0;
|
||||||
|
\\}
|
||||||
|
, "");
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user