Skip to content

Commit aab25fa

Browse files
committed
Never call a destroying operator delete when cleaning up from an
exception thrown during construction in a new-expression. Instead, when performing deallocation function lookup for a new-expression, ignore all destroying operator delete candidates, and fall back to global operator delete if there is no member operator delete other than a destroying operator delete. Use of destroying operator delete only makes sense when there is an object to destroy, which there isn't in this case. The language wording doesn't cover this case; this oversight has been reported to WG21, with the approach in this patch as the proposed fix.
1 parent b02ca09 commit aab25fa

File tree

3 files changed

+83
-12
lines changed

3 files changed

+83
-12
lines changed

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2630,8 +2630,24 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
26302630
if (FoundDelete.isAmbiguous())
26312631
return true; // FIXME: clean up expressions?
26322632

2633+
// Filter out any destroying operator deletes. We can't possibly call such a
2634+
// function in this context, because we're handling the case where the object
2635+
// was not successfully constructed.
2636+
// FIXME: This is not covered by the language rules yet.
2637+
{
2638+
LookupResult::Filter Filter = FoundDelete.makeFilter();
2639+
while (Filter.hasNext()) {
2640+
auto *FD = dyn_cast<FunctionDecl>(Filter.next()->getUnderlyingDecl());
2641+
if (FD && FD->isDestroyingOperatorDelete())
2642+
Filter.erase();
2643+
}
2644+
Filter.done();
2645+
}
2646+
26332647
bool FoundGlobalDelete = FoundDelete.empty();
26342648
if (FoundDelete.empty()) {
2649+
FoundDelete.clear(LookupOrdinaryName);
2650+
26352651
if (DeleteScope == AFS_Class)
26362652
return true;
26372653

clang/test/CodeGenCXX/cxx2a-destroying-delete.cpp

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
// RUN: %clang_cc1 -std=c++2a -emit-llvm %s -triple x86_64-linux-gnu -o - | FileCheck %s --check-prefixes=CHECK,CHECK-ITANIUM,CHECK-64BIT
2-
// RUN: %clang_cc1 -std=c++2a -emit-llvm %s -triple x86_64-windows -o - | FileCheck %s --check-prefixes=CHECK,CHECK-MSABI,CHECK-MSABI64,CHECK-64BIT
3-
// RUN: %clang_cc1 -std=c++2a -emit-llvm %s -triple i386-windows -o - | FileCheck %s --check-prefixes=CHECK,CHECK-MSABI,CHECK-MSABI32,CHECK-32BIT
1+
// RUN: %clang_cc1 -std=c++2a -fexceptions -emit-llvm %s -triple x86_64-linux-gnu -o - | FileCheck %s --check-prefixes=CHECK,CHECK-ITANIUM,CHECK-64BIT
2+
// RUN: %clang_cc1 -std=c++2a -fexceptions -emit-llvm %s -triple x86_64-windows -o - | FileCheck %s --check-prefixes=CHECK,CHECK-MSABI,CHECK-MSABI64,CHECK-64BIT
3+
// RUN: %clang_cc1 -std=c++2a -fexceptions -emit-llvm %s -triple i386-windows -o - | FileCheck %s --check-prefixes=CHECK,CHECK-MSABI,CHECK-MSABI32,CHECK-32BIT
44

55
// PR46908: ensure the IR passes the verifier with optimizations enabled.
6-
// RUN: %clang_cc1 -std=c++2a -emit-llvm-only %s -triple x86_64-linux-gnu -O2
7-
// RUN: %clang_cc1 -std=c++2a -emit-llvm-only %s -triple x86_64-windows -O2
8-
// RUN: %clang_cc1 -std=c++2a -emit-llvm-only %s -triple i386-windows -O2
6+
// RUN: %clang_cc1 -std=c++2a -fexceptions -emit-llvm-only %s -triple x86_64-linux-gnu -O2
7+
// RUN: %clang_cc1 -std=c++2a -fexceptions -emit-llvm-only %s -triple x86_64-windows -O2
8+
// RUN: %clang_cc1 -std=c++2a -fexceptions -emit-llvm-only %s -triple i386-windows -O2
99

1010
namespace std {
1111
using size_t = decltype(sizeof(0));
@@ -102,6 +102,41 @@ void delete_D(D *d) { delete d; }
102102
// CHECK-NOT: call
103103
// CHECK: }
104104

105+
struct J {
106+
J(); // might throw
107+
void operator delete(J *, std::destroying_delete_t);
108+
};
109+
110+
// CHECK-ITANIUM-LABEL: define {{.*}}@_Z1j
111+
// CHECK-MSABI-LABEL: define {{.*}}@"?j@@
112+
J *j() {
113+
// CHECK-ITANIUM: invoke {{.*}}@_ZN1JC1Ev(
114+
// CHECK-ITANIUM: call {{.*}}@_ZdlPv(
115+
// CHECK-NOT: }
116+
// CHECK-MSABI: invoke {{.*}}@"??0J@@Q{{AE|EAA}}@XZ"(
117+
// CHECK-MSABI: call {{.*}}@"??3@YAXP{{E?}}AX@Z"(
118+
return new J;
119+
// CHECK: }
120+
}
121+
122+
struct K {
123+
K(); // might throw
124+
void operator delete(void *);
125+
void operator delete(K *, std::destroying_delete_t);
126+
};
127+
128+
// CHECK-ITANIUM-LABEL: define {{.*}}@_Z1k
129+
// CHECK-MSABI-LABEL: define {{.*}}@"?k@@
130+
K *k() {
131+
// CHECK-ITANIUM: invoke {{.*}}@_ZN1KC1Ev(
132+
// CHECK-ITANIUM: call {{.*}}@_ZN1KdlEPv(
133+
// CHECK-NOT: }
134+
// CHECK-MSABI: invoke {{.*}}@"??0K@@Q{{AE|EAA}}@XZ"(
135+
// CHECK-MSABI: call {{.*}}@"??3K@@SAXP{{E?}}AX@Z"(
136+
return new K;
137+
// CHECK: }
138+
}
139+
105140
struct E { void *data; };
106141
struct F { void operator delete(F *, std::destroying_delete_t, std::size_t, std::align_val_t); void *data; };
107142
struct alignas(16) G : E, F { void *data; };
@@ -127,8 +162,8 @@ H::~H() { call_in_dtor(); }
127162
// CHECK-ITANIUM-NOT: call
128163
// CHECK-ITANIUM: }
129164

130-
// CHECK-MSABI64: define {{.*}} @"??_GH@@UEAAPEAXI@Z"(
131-
// CHECK-MSABI32: define {{.*}} @"??_GH@@UAEPAXI@Z"(
165+
// CHECK-MSABI64-LABEL: define {{.*}} @"??_GH@@UEAAPEAXI@Z"(
166+
// CHECK-MSABI32-LABEL: define {{.*}} @"??_GH@@UAEPAXI@Z"(
132167
// CHECK-MSABI-NOT: call{{ }}
133168
// CHECK-MSABI: load i32
134169
// CHECK-MSABI: icmp eq i32 {{.*}}, 0
@@ -158,8 +193,8 @@ I::~I() { call_in_dtor(); }
158193
// CHECK-ITANIUM-NOT: call
159194
// CHECK-ITANIUM: }
160195

161-
// CHECK-MSABI64: define {{.*}} @"??_GI@@UEAAPEAXI@Z"(
162-
// CHECK-MSABI32: define {{.*}} @"??_GI@@UAEPAXI@Z"(
196+
// CHECK-MSABI64-LABEL: define {{.*}} @"??_GI@@UEAAPEAXI@Z"(
197+
// CHECK-MSABI32-LABEL: define {{.*}} @"??_GI@@UAEPAXI@Z"(
163198
// CHECK-MSABI-NOT: call{{ }}
164199
// CHECK-MSABI: load i32
165200
// CHECK-MSABI: icmp eq i32 {{.*}}, 0

clang/test/SemaCXX/cxx2a-destroying-delete.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ namespace delete_selection {
5858
struct C {
5959
C();
6060
void *operator new(std::size_t);
61-
void operator delete(void*) = delete;
62-
void operator delete(C *, std::destroying_delete_t) = delete; // expected-note 0-1 {{deleted here}}
61+
void operator delete(void*) = delete; // expected-note 0-1 {{deleted here}}
62+
void operator delete(C *, std::destroying_delete_t) = delete;
6363
};
6464
// TODO: We only diagnose the use of a deleted operator delete when exceptions
6565
// are enabled. Otherwise we don't bother doing the lookup.
@@ -167,3 +167,23 @@ namespace dtor_access {
167167

168168
void g() { delete (T *)new U; } // expected-error {{calling a protected destructor}}
169169
}
170+
171+
namespace delete_from_new {
172+
struct A {
173+
A(); // might throw
174+
void operator delete(A *, std::destroying_delete_t) = delete;
175+
};
176+
struct B {
177+
B(); // might throw
178+
void operator delete(void *) = delete; // #member-delete-from-new
179+
void operator delete(B *, std::destroying_delete_t) = delete;
180+
};
181+
void f() {
182+
new A; // calls ::operator delete
183+
new B; // calls B::operator delete
184+
#ifdef __EXCEPTIONS
185+
// expected-error@-2 {{attempt to use a deleted function}}
186+
// expected-note@#member-delete-from-new {{deleted here}}
187+
#endif
188+
}
189+
}

0 commit comments

Comments
 (0)