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