diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 8961ffb680db..f9b70a3507b4 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -363,7 +363,7 @@ fn prepopulateGlobalNameTable(ast_unit: *ZigClangASTUnit, c: *Context) !void { } } -extern fn declVisitorNamesOnlyC(context: ?*c_void, decl: *const ZigClangDecl) bool { +fn declVisitorNamesOnlyC(context: ?*c_void, decl: *const ZigClangDecl) callconv(.C) bool { const c = @ptrCast(*Context, @alignCast(@alignOf(Context), context)); declVisitorNamesOnly(c, decl) catch |err| { c.err = err; @@ -372,7 +372,7 @@ extern fn declVisitorNamesOnlyC(context: ?*c_void, decl: *const ZigClangDecl) bo return true; } -extern fn declVisitorC(context: ?*c_void, decl: *const ZigClangDecl) bool { +fn declVisitorC(context: ?*c_void, decl: *const ZigClangDecl) callconv(.C) bool { const c = @ptrCast(*Context, @alignCast(@alignOf(Context), context)); declVisitor(c, decl) catch |err| { c.err = err; @@ -2499,8 +2499,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); } @@ -3399,6 +3418,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, diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index a9b9a8d6c239..30d96fb899a2 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -96,4 +96,24 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\ return 0; \\} , ""); + + cases.add("cast signed array index to unsigned", + \\#include + \\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 + \\int main(int argc, char **argv) { + \\ long long a[10], i = 0; + \\ a[i] = 0; + \\ if (a[i] != 0) abort(); + \\ return 0; + \\} + , ""); } diff --git a/test/translate_c.zig b/test/translate_c.zig index 6557c98963c9..b0e9bb9a6749 100644 --- a/test/translate_c.zig +++ b/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{