@@ -3038,30 +3038,113 @@ fn transMemberExpr(c: *Context, scope: *Scope, stmt: *const clang.MemberExpr, re
3038
3038
return maybeSuppressResult (c , scope , result_used , node );
3039
3039
}
3040
3040
3041
+ /// ptr[subscr] (`subscr` is a signed integer expression, `ptr` a pointer) becomes:
3042
+ /// (blk: {
3043
+ /// const tmp = subscr;
3044
+ /// if (tmp >= 0) break :blk ptr + @intCast(usize, tmp) else break :blk ptr - ~@bitCast(usize, @intCast(isize, tmp) +% -1);
3045
+ /// }).*
3046
+ /// Todo: rip this out once `[*]T + isize` becomes valid.
3047
+ fn transSignedArrayAccess (
3048
+ c : * Context ,
3049
+ scope : * Scope ,
3050
+ container_expr : * const clang.Expr ,
3051
+ subscr_expr : * const clang.Expr ,
3052
+ result_used : ResultUsed ,
3053
+ ) TransError ! Node {
3054
+ var block_scope = try Scope .Block .init (c , scope , true );
3055
+ defer block_scope .deinit ();
3056
+
3057
+ const tmp = try block_scope .makeMangledName (c , "tmp" );
3058
+
3059
+ const subscr_node = try transExpr (c , & block_scope .base , subscr_expr , .used );
3060
+ const subscr_decl = try Tag .var_simple .create (c .arena , .{ .name = tmp , .init = subscr_node });
3061
+ try block_scope .statements .append (subscr_decl );
3062
+
3063
+ const tmp_ref = try Tag .identifier .create (c .arena , tmp );
3064
+
3065
+ const container_node = try transExpr (c , & block_scope .base , container_expr , .used );
3066
+
3067
+ const cond_node = try Tag .greater_than_equal .create (c .arena , .{ .lhs = tmp_ref , .rhs = Tag .zero_literal .init () });
3068
+
3069
+ const then_value = try Tag .add .create (c .arena , .{
3070
+ .lhs = container_node ,
3071
+ .rhs = try Tag .int_cast .create (c .arena , .{
3072
+ .lhs = try Tag .identifier .create (c .arena , "usize" ),
3073
+ .rhs = tmp_ref ,
3074
+ }),
3075
+ });
3076
+
3077
+ const then_body = try Tag .break_val .create (c .arena , .{
3078
+ .label = block_scope .label ,
3079
+ .val = then_value ,
3080
+ });
3081
+
3082
+ const minuend = container_node ;
3083
+ const signed_size = try Tag .int_cast .create (c .arena , .{
3084
+ .lhs = try Tag .identifier .create (c .arena , "isize" ),
3085
+ .rhs = tmp_ref ,
3086
+ });
3087
+ const to_cast = try Tag .add_wrap .create (c .arena , .{
3088
+ .lhs = signed_size ,
3089
+ .rhs = try Tag .negate .create (c .arena , Tag .one_literal .init ()),
3090
+ });
3091
+ const bitcast_node = try Tag .bit_cast .create (c .arena , .{
3092
+ .lhs = try Tag .identifier .create (c .arena , "usize" ),
3093
+ .rhs = to_cast ,
3094
+ });
3095
+ const subtrahend = try Tag .bit_not .create (c .arena , bitcast_node );
3096
+ const difference = try Tag .sub .create (c .arena , .{
3097
+ .lhs = minuend ,
3098
+ .rhs = subtrahend ,
3099
+ });
3100
+ const else_body = try Tag .break_val .create (c .arena , .{
3101
+ .label = block_scope .label ,
3102
+ .val = difference ,
3103
+ });
3104
+
3105
+ const if_node = try Tag .@"if" .create (c .arena , .{
3106
+ .cond = cond_node ,
3107
+ .then = then_body ,
3108
+ .@"else" = else_body ,
3109
+ });
3110
+
3111
+ try block_scope .statements .append (if_node );
3112
+ const block_node = try block_scope .complete (c );
3113
+
3114
+ const derefed = try Tag .deref .create (c .arena , block_node );
3115
+
3116
+ return maybeSuppressResult (c , & block_scope .base , result_used , derefed );
3117
+ }
3118
+
3041
3119
fn transArrayAccess (c : * Context , scope : * Scope , stmt : * const clang.ArraySubscriptExpr , result_used : ResultUsed ) TransError ! Node {
3042
- var base_stmt = stmt .getBase ();
3120
+ const base_stmt = stmt .getBase ();
3121
+ const base_qt = getExprQualType (c , base_stmt );
3122
+ const is_vector = cIsVector (base_qt );
3123
+
3124
+ const subscr_expr = stmt .getIdx ();
3125
+ const subscr_qt = getExprQualType (c , subscr_expr );
3126
+ const is_longlong = cIsLongLongInteger (subscr_qt );
3127
+ const is_signed = cIsSignedInteger (subscr_qt );
3043
3128
3044
3129
// Unwrap the base statement if it's an array decayed to a bare pointer type
3045
3130
// so that we index the array itself
3131
+ var unwrapped_base = base_stmt ;
3046
3132
if (@ptrCast (* const clang .Stmt , base_stmt ).getStmtClass () == .ImplicitCastExprClass ) {
3047
3133
const implicit_cast = @ptrCast (* const clang .ImplicitCastExpr , base_stmt );
3048
3134
3049
3135
if (implicit_cast .getCastKind () == .ArrayToPointerDecay ) {
3050
- base_stmt = implicit_cast .getSubExpr ();
3136
+ unwrapped_base = implicit_cast .getSubExpr ();
3051
3137
}
3052
3138
}
3053
3139
3054
- const container_node = try transExpr (c , scope , base_stmt , .used );
3055
-
3056
- // cast if the index is long long or signed
3057
- const subscr_expr = stmt .getIdx ();
3058
- const qt = getExprQualType (c , subscr_expr );
3059
- const is_longlong = cIsLongLongInteger (qt );
3060
- const is_signed = cIsSignedInteger (qt );
3140
+ // Special case: actual pointer (not decayed array) and signed integer subscript
3141
+ // See discussion at https://github.com/ziglang/zig/pull/8589
3142
+ if (is_signed and (base_stmt == unwrapped_base ) and ! is_vector ) return transSignedArrayAccess (c , scope , base_stmt , subscr_expr , result_used );
3061
3143
3144
+ const container_node = try transExpr (c , scope , unwrapped_base , .used );
3062
3145
const rhs = if (is_longlong or is_signed ) blk : {
3063
3146
// check if long long first so that signed long long doesn't just become unsigned long long
3064
- var typeid_node = if (is_longlong ) try Tag .identifier .create (c .arena , "usize" ) else try transQualTypeIntWidthOf (c , qt , false );
3147
+ const typeid_node = if (is_longlong ) try Tag .identifier .create (c .arena , "usize" ) else try transQualTypeIntWidthOf (c , subscr_qt , false );
3065
3148
break :blk try Tag .int_cast .create (c .arena , .{ .lhs = typeid_node , .rhs = try transExpr (c , scope , subscr_expr , .used ) });
3066
3149
} else try transExpr (c , scope , subscr_expr , .used );
3067
3150
0 commit comments