@@ -3273,30 +3273,113 @@ fn transMemberExpr(c: *Context, scope: *Scope, stmt: *const clang.MemberExpr, re
3273
3273
return maybeSuppressResult (c , scope , result_used , node );
3274
3274
}
3275
3275
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.
3282
+ fn 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
+
3276
3354
fn 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 );
3278
3363
3279
3364
// Unwrap the base statement if it's an array decayed to a bare pointer type
3280
3365
// so that we index the array itself
3366
+ var unwrapped_base = base_stmt ;
3281
3367
if (@ptrCast (* const clang .Stmt , base_stmt ).getStmtClass () == .ImplicitCastExprClass ) {
3282
3368
const implicit_cast = @ptrCast (* const clang .ImplicitCastExpr , base_stmt );
3283
3369
3284
3370
if (implicit_cast .getCastKind () == .ArrayToPointerDecay ) {
3285
- base_stmt = implicit_cast .getSubExpr ();
3371
+ unwrapped_base = implicit_cast .getSubExpr ();
3286
3372
}
3287
3373
}
3288
3374
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 );
3296
3378
3379
+ const container_node = try transExpr (c , scope , unwrapped_base , .used );
3297
3380
const rhs = if (is_longlong or is_signed ) blk : {
3298
3381
// 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 );
3300
3383
break :blk try Tag .int_cast .create (c .arena , .{ .lhs = typeid_node , .rhs = try transExpr (c , scope , subscr_expr , .used ) });
3301
3384
} else try transExpr (c , scope , subscr_expr , .used );
3302
3385
0 commit comments