Skip to content

Commit 2228b4b

Browse files
authored
clang: Handle deleting pointers to incomplete array types (#150359)
CodeGenFunction::EmitCXXDeleteExpr contains logic to go from a pointer to an array to a pointer to the first element of the array using a getelementptr LLVM IR instruction. This was done for pointers that were not variable length arrays, as pointers to variable length arrays never existed in LLVM IR, but rather than checking for arrays that were not variable length arrays, it checked for arrays that had a constant bound. This caused incomplete arrays to be inadvertently omitted. This getelementptr was necessary back when LLVM IR used typed pointers, but they have been gone for a while, a gep with a constant zero offset does nothing now, so we can simplify the code by removing that.
1 parent d750c6d commit 2228b4b

File tree

3 files changed

+29
-31
lines changed

3 files changed

+29
-31
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ Bug Fixes to C++ Support
134134
^^^^^^^^^^^^^^^^^^^^^^^^
135135
- Diagnose binding a reference to ``*nullptr`` during constant evaluation. (#GH48665)
136136
- Suppress ``-Wdeprecated-declarations`` in implicitly generated functions. (#GH147293)
137+
- Fix a crash when deleting a pointer to an incomplete array (#GH150359).
137138

138139
Bug Fixes to AST Handling
139140
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/lib/CodeGen/CGExprCXX.cpp

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2146,30 +2146,9 @@ void CodeGenFunction::EmitCXXDeleteExpr(const CXXDeleteExpr *E) {
21462146
return;
21472147
}
21482148

2149-
// We might be deleting a pointer to array. If so, GEP down to the
2150-
// first non-array element.
2151-
// (this assumes that A(*)[3][7] is converted to [3 x [7 x %A]]*)
2152-
if (DeleteTy->isConstantArrayType()) {
2153-
llvm::Value *Zero = Builder.getInt32(0);
2154-
SmallVector<llvm::Value*,8> GEP;
2155-
2156-
GEP.push_back(Zero); // point at the outermost array
2157-
2158-
// For each layer of array type we're pointing at:
2159-
while (const ConstantArrayType *Arr
2160-
= getContext().getAsConstantArrayType(DeleteTy)) {
2161-
// 1. Unpeel the array type.
2162-
DeleteTy = Arr->getElementType();
2163-
2164-
// 2. GEP to the first element of the array.
2165-
GEP.push_back(Zero);
2166-
}
2167-
2168-
Ptr = Builder.CreateInBoundsGEP(Ptr, GEP, ConvertTypeForMem(DeleteTy),
2169-
Ptr.getAlignment(), "del.first");
2170-
}
2171-
2172-
assert(ConvertTypeForMem(DeleteTy) == Ptr.getElementType());
2149+
// We might be deleting a pointer to array.
2150+
DeleteTy = getContext().getBaseElementType(DeleteTy);
2151+
Ptr = Ptr.withElementType(ConvertTypeForMem(DeleteTy));
21732152

21742153
if (E->isArrayForm()) {
21752154
EmitArrayDelete(*this, E, Ptr, DeleteTy);

clang/test/CodeGenCXX/delete.cpp

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,27 +76,45 @@ namespace test1 {
7676
~A();
7777
};
7878

79-
// CHECK-LABEL: define{{.*}} void @_ZN5test14testEPA10_A20_NS_1AE(
80-
void test(A (*arr)[10][20]) {
79+
// CHECK-LABEL: define{{.*}} void @_ZN5test11fEPA10_A20_NS_1AE(
80+
void f(A (*arr)[10][20]) {
8181
delete [] arr;
8282
// CHECK: icmp eq ptr [[PTR:%.*]], null
8383
// CHECK-NEXT: br i1
8484

85-
// CHECK: [[BEGIN:%.*]] = getelementptr inbounds [10 x [20 x [[A:%.*]]]], ptr [[PTR]], i32 0, i32 0, i32 0
86-
// CHECK-NEXT: [[ALLOC:%.*]] = getelementptr inbounds i8, ptr [[BEGIN]], i64 -8
85+
// CHECK: [[ALLOC:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 -8
8786
// CHECK-NEXT: [[COUNT:%.*]] = load i64, ptr [[ALLOC]]
88-
// CHECK: [[END:%.*]] = getelementptr inbounds [[A]], ptr [[BEGIN]], i64 [[COUNT]]
89-
// CHECK-NEXT: [[ISEMPTY:%.*]] = icmp eq ptr [[BEGIN]], [[END]]
87+
// CHECK: [[END:%.*]] = getelementptr inbounds [[A:%.*]], ptr [[PTR]], i64 [[COUNT]]
88+
// CHECK-NEXT: [[ISEMPTY:%.*]] = icmp eq ptr [[PTR]], [[END]]
9089
// CHECK-NEXT: br i1 [[ISEMPTY]],
9190
// CHECK: [[PAST:%.*]] = phi ptr [ [[END]], {{%.*}} ], [ [[CUR:%.*]], {{%.*}} ]
9291
// CHECK-NEXT: [[CUR:%.*]] = getelementptr inbounds [[A]], ptr [[PAST]], i64 -1
9392
// CHECK-NEXT: call void @_ZN5test11AD1Ev(ptr {{[^,]*}} [[CUR]])
94-
// CHECK-NEXT: [[ISDONE:%.*]] = icmp eq ptr [[CUR]], [[BEGIN]]
93+
// CHECK-NEXT: [[ISDONE:%.*]] = icmp eq ptr [[CUR]], [[PTR]]
9594
// CHECK-NEXT: br i1 [[ISDONE]]
9695
// CHECK: [[MUL:%.*]] = mul i64 4, [[COUNT]]
9796
// CHECK-NEXT: [[SIZE:%.*]] = add i64 [[MUL]], 8
9897
// CHECK-NEXT: call void @_ZdaPvm(ptr noundef [[ALLOC]], i64 noundef [[SIZE]])
9998
}
99+
100+
// CHECK-LABEL: define{{.*}} void @_ZN5test11gEPA_NS_1AE(
101+
void g(A (*arr)[]) {
102+
delete [] arr;
103+
// CHECK: icmp eq ptr [[PTR:%.*]], null
104+
// CHECK-NEXT: br i1
105+
106+
// CHECK: [[ALLOC:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 -8
107+
// CHECK-NEXT: [[COUNT:%.*]] = load i64, ptr [[ALLOC]]
108+
// CHECK: [[END:%.*]] = getelementptr inbounds [[A:%.*]], ptr [[PTR]], i64 [[COUNT]]
109+
// CHECK-NEXT: [[ISEMPTY:%.*]] = icmp eq ptr [[PTR]], [[END]]
110+
// CHECK-NEXT: br i1 [[ISEMPTY]],
111+
// CHECK: [[PAST:%.*]] = phi ptr [ [[END]], {{%.*}} ], [ [[CUR:%.*]], {{%.*}} ]
112+
// CHECK-NEXT: [[CUR:%.*]] = getelementptr inbounds [[A]], ptr [[PAST]], i64 -1
113+
// CHECK-NEXT: call void @_ZN5test11AD1Ev(ptr {{[^,]*}} [[CUR]])
114+
// CHECK-NEXT: [[ISDONE:%.*]] = icmp eq ptr [[CUR]], [[PTR]]
115+
// CHECK-NEXT: br i1 [[ISDONE]]
116+
// CHECK: call void @_ZdaPv(ptr noundef [[ALLOC]])
117+
}
100118
}
101119

102120
namespace test2 {

0 commit comments

Comments
 (0)