Skip to content

Commit 10985bc

Browse files
committed
[C++20] Deleting destructors can be noexcept
Given a `noexcept` operator with an operand that calls `delete`, Clang was not considering whether the selected `operator delete` function was a destroying delete or not when inspecting whether the deleted object type has a throwing destructor. Thus, the operator would return `false` for a type with a potentially throwing destructor even though that destructor would not be called due to the destroying delete. Clang now takes the kind of delete operator into consideration. Fixes #118660
1 parent a88653a commit 10985bc

File tree

3 files changed

+27
-2
lines changed

3 files changed

+27
-2
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,9 @@ Bug Fixes in This Version
654654
- Fixed a crash when GNU statement expression contains invalid statement (#GH113468).
655655
- Fixed a failed assertion when using ``__attribute__((noderef))`` on an
656656
``_Atomic``-qualified type (#GH116124).
657+
- No longer return ``false`` for ``noexcept`` expressions involving a
658+
``delete`` which resolves to a destroying delete but the type of the object
659+
being deleted as a potentially throwing destructor (#GH118660).
657660

658661
Bug Fixes to Compiler Builtins
659662
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

clang/lib/Sema/SemaExceptionSpec.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,11 +1205,12 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
12051205
if (DTy.isNull() || DTy->isDependentType()) {
12061206
CT = CT_Dependent;
12071207
} else {
1208-
CT = canCalleeThrow(*this, DE, DE->getOperatorDelete());
1208+
const FunctionDecl *OperatorDelete = DE->getOperatorDelete();
1209+
CT = canCalleeThrow(*this, DE, OperatorDelete);
12091210
if (const RecordType *RT = DTy->getAs<RecordType>()) {
12101211
const CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
12111212
const CXXDestructorDecl *DD = RD->getDestructor();
1212-
if (DD)
1213+
if (DD && !OperatorDelete->isDestroyingOperatorDelete())
12131214
CT = mergeCanThrow(CT, canCalleeThrow(*this, DE, DD));
12141215
}
12151216
if (CT == CT_Can)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify -fcxx-exceptions -Wno-unevaluated-expression -std=c++20 %s
2+
// expected-no-diagnostics
3+
4+
namespace std {
5+
struct destroying_delete_t {
6+
explicit destroying_delete_t() = default;
7+
};
8+
9+
inline constexpr destroying_delete_t destroying_delete{};
10+
}
11+
12+
struct Explicit {
13+
~Explicit() noexcept(false) {}
14+
15+
void operator delete(Explicit*, std::destroying_delete_t) noexcept {
16+
}
17+
};
18+
19+
Explicit *qn = nullptr;
20+
// This assertion used to fail, see GH118660
21+
static_assert(noexcept(delete(qn)));

0 commit comments

Comments
 (0)