Commit 3f98756f85

travisstaloch <twostepted@gmail.com>
2020-01-10 06:08:24
Fix translation of signed array indices (#4113)
* cast only if the index is long long or signed * cast long long to usize rather than c_uint closes #4075
1 parent ae32498
Changed files (3)
src-self-hosted/translate_c.zig
@@ -2577,8 +2577,27 @@ fn transArrayAccess(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangArrayS
 
     const container_node = try transExpr(rp, scope, base_stmt, .used, .r_value);
     const node = try transCreateNodeArrayAccess(rp.c, container_node);
-    node.op.ArrayAccess = try transExpr(rp, scope, ZigClangArraySubscriptExpr_getIdx(stmt), .used, .r_value);
-    node.rtoken = try appendToken(rp.c, .RBrace, "]");
+
+    // cast if the index is long long or signed
+    const subscr_expr = ZigClangArraySubscriptExpr_getIdx(stmt);
+    const qt = getExprQualType(rp.c, subscr_expr);
+    const is_longlong = cIsLongLongInteger(qt);
+    const is_signed = cIsSignedInteger(qt);
+
+    if (is_longlong or is_signed) {
+        const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@intCast");
+        // check if long long first so that signed long long doesn't just become unsigned long long
+        var typeid_node = if (is_longlong) try transCreateNodeIdentifier(rp.c, "usize") else try transQualTypeIntWidthOf(rp.c, qt, false);
+        try cast_node.params.push(typeid_node);
+        _ = try appendToken(rp.c, .Comma, ",");
+        try cast_node.params.push(try transExpr(rp, scope, subscr_expr, .used, .r_value));
+        cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+        node.rtoken = try appendToken(rp.c, .RBrace, "]");
+        node.op.ArrayAccess = &cast_node.base;
+    } else {
+        node.op.ArrayAccess = try transExpr(rp, scope, subscr_expr, .used, .r_value);
+        node.rtoken = try appendToken(rp.c, .RBrace, "]");
+    }
     return maybeSuppressResult(rp, scope, result_used, &node.base);
 }
 
@@ -3528,6 +3547,15 @@ fn cIsFloating(qt: ZigClangQualType) bool {
     };
 }
 
+fn cIsLongLongInteger(qt: ZigClangQualType) bool {
+    const c_type = qualTypeCanon(qt);
+    if (ZigClangType_getTypeClass(c_type) != .Builtin) return false;
+    const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type);
+    return switch (ZigClangBuiltinType_getKind(builtin_ty)) {
+        .LongLong, .ULongLong, .Int128, .UInt128 => true,
+        else => false,
+    };
+}
 fn transCreateNodeAssign(
     rp: RestorePoint,
     scope: *Scope,
test/run_translated_c.zig
@@ -149,4 +149,24 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
         \\  return 0;
         \\}
     , "");
+
+    cases.add("cast signed array index to unsigned",
+        \\#include <stdlib.h>
+        \\int main(int argc, char **argv) {
+        \\  int a[10], i = 0;
+        \\  a[i] = 0;
+        \\  if (a[i] != 0) abort();
+        \\  return 0;
+        \\}
+    , "");
+
+    cases.add("cast long long array index to unsigned",
+        \\#include <stdlib.h>
+        \\int main(int argc, char **argv) {
+        \\  long long a[10], i = 0;
+        \\  a[i] = 0;
+        \\  if (a[i] != 0) abort();
+        \\  return 0;
+        \\}
+    , "");
 }
test/translate_c.zig
@@ -1843,12 +1843,51 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\pub export var array: [100]c_int = .{0} ** 100;
         \\pub export fn foo(arg_index: c_int) c_int {
         \\    var index = arg_index;
-        \\    return array[index];
+        \\    return array[@intCast(c_uint, index)];
         \\}
     ,
         \\pub const ACCESS = array[2];
     });
 
+    cases.add("cast signed array index to unsigned",
+        \\void foo() {
+        \\  int a[10], i = 0;
+        \\  a[i] = 0;
+        \\}
+    , &[_][]const u8{
+        \\pub export fn foo() void {
+        \\    var a: [10]c_int = undefined;
+        \\    var i: c_int = 0;
+        \\    a[@intCast(c_uint, i)] = 0;
+        \\}
+    });
+
+    cases.add("long long array index cast to usize",
+        \\void foo() {
+        \\  long long a[10], i = 0;
+        \\  a[i] = 0;
+        \\}
+    , &[_][]const u8{
+        \\pub export fn foo() void {
+        \\    var a: [10]c_longlong = undefined;
+        \\    var i: c_longlong = @bitCast(c_longlong, @as(c_longlong, @as(c_int, 0)));
+        \\    a[@intCast(usize, i)] = @bitCast(c_longlong, @as(c_longlong, @as(c_int, 0)));
+        \\}
+    });
+
+    cases.add("unsigned array index skips cast",
+        \\void foo() {
+        \\  unsigned int a[10], i = 0;
+        \\  a[i] = 0;
+        \\}
+    , &[_][]const u8{
+        \\pub export fn foo() void {
+        \\    var a: [10]c_uint = undefined;
+        \\    var i: c_uint = @bitCast(c_uint, @as(c_int, 0));
+        \\    a[i] = @bitCast(c_uint, @as(c_int, 0));
+        \\}
+    });
+
     cases.add("macro call",
         \\#define CALL(arg) bar(arg)
     , &[_][]const u8{