Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,8 @@ Bug Fixes in This Version
- Fixed a crash when GNU statement expression contains invalid statement (#GH113468).
- Fixed a failed assertion when using ``__attribute__((noderef))`` on an
``_Atomic``-qualified type (#GH116124).
- No longer incorrectly diagnosing use of a deleted destructor when the
selected overload of ``operator delete`` for that type is a destroying delete.

Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
19 changes: 15 additions & 4 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3792,13 +3792,23 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
.HasSizeT;
}

if (!PointeeRD->hasIrrelevantDestructor())
// C++20 [expr.delete]p6: If the value of the operand of the delete-
// expression is not a null pointer value and the selected deallocation
// function (see below) is not a destroying operator delete, the delete-
// expression will invoke the destructor (if any) for the object or the
// elements of the array being deleted.
//
// This means we should not look at the destructor for a destroying
// delete operator, as that destructor is never called.
if (!PointeeRD->hasIrrelevantDestructor() &&
(!OperatorDelete || !OperatorDelete->isDestroyingOperatorDelete())) {
if (CXXDestructorDecl *Dtor = LookupDestructor(PointeeRD)) {
MarkFunctionReferenced(StartLoc,
const_cast<CXXDestructorDecl*>(Dtor));
const_cast<CXXDestructorDecl *>(Dtor));
if (DiagnoseUseOfDecl(Dtor, StartLoc))
return ExprError();
}
}

CheckVirtualDtorCall(PointeeRD->getDestructor(), StartLoc,
/*IsDelete=*/true, /*CallCanBeVirtual=*/true,
Expand Down Expand Up @@ -3829,9 +3839,10 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
MarkFunctionReferenced(StartLoc, OperatorDelete);

// Check access and ambiguity of destructor if we're going to call it.
// Note that this is required even for a virtual delete.
// Note that this is required even for a virtual delete, but not for a
// destroying delete operator.
bool IsVirtualDelete = false;
if (PointeeRD) {
if (PointeeRD && !OperatorDelete->isDestroyingOperatorDelete()) {
if (CXXDestructorDecl *Dtor = LookupDestructor(PointeeRD)) {
CheckDestructorAccess(Ex.get()->getExprLoc(), Dtor,
PDiag(diag::err_access_dtor) << PointeeElem);
Expand Down
32 changes: 30 additions & 2 deletions clang/test/CXX/expr/expr.unary/expr.delete/p10.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
// RUN: %clang_cc1 -std=c++1z -verify %s
// RUN: %clang_cc1 -std=c++20 -verify %s

using size_t = decltype(sizeof(0));
namespace std { enum class align_val_t : size_t {}; }
namespace std {
enum class align_val_t : size_t {};
struct destroying_delete_t {
explicit destroying_delete_t() = default;
};

inline constexpr destroying_delete_t destroying_delete{};
}

// Aligned version is preferred over unaligned version,
// unsized version is preferred over sized version.
Expand All @@ -23,3 +30,24 @@ struct alignas(Align) B {
};
void f(B<__STDCPP_DEFAULT_NEW_ALIGNMENT__> *p) { delete p; }
void f(B<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2> *p) { delete p; } // expected-error {{deleted}}

// Ensure that a deleted destructor is acceptable when the selected overload
// for operator delete is a destroying delete. See the comments in GH118660.
struct S {
~S() = delete;
void operator delete(S *, std::destroying_delete_t) noexcept {}
};

struct T {
void operator delete(T *, std::destroying_delete_t) noexcept {}
private:
~T();
};

void foo(S *s) {
delete s; // Was rejected, is intended to be accepted.
}

void bar(T *t) {
delete t; // Was rejected, is intended to be accepted.
}
Loading