Author: Doug Wyatt Date: 2026-07-13T11:52:31-07:00 New Revision: cc615d117c7f942a508ceecedd519f2fe9921493
URL: https://github.com/llvm/llvm-project/commit/cc615d117c7f942a508ceecedd519f2fe9921493 DIFF: https://github.com/llvm/llvm-project/commit/cc615d117c7f942a508ceecedd519f2fe9921493.diff LOG: [clang][Sema] Function effect analysis was missing the implicit call to the destructor in a CXXDeleteExpr. (#184460) Co-authored-by: Doug Wyatt <[email protected]> Added: Modified: clang/docs/ReleaseNotes.md clang/lib/Sema/SemaFunctionEffects.cpp clang/test/Sema/attr-nonblocking-constraints.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 538c67e7e5d09..12fb2a1eb80b7 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -827,6 +827,7 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the would only use the first one). A new warning that diagnoses such declarations has been added to `-Wignored-attributes`. (#GH191829) - Fixed a crash in the constant evaluator when an ill-formed array new-expression whose bound could not be determined (e.g. `new int[]()`) was used in a constant expression. (#GH200139) +- Fixed a case where function effect analysis (`nonblocking` etc.) did not visit a destructor invoked from a `delete` expression. (#GH184460) - Clang now defines the GCC-compatible predefined macros `__WCHAR_MIN__`, `__WINT_MIN__`, and `__SIG_ATOMIC_MIN__`. (#GH199678) - Fix a crash in addUnsizedArray due assert not verifying we have a Base before doing checks on it. (#GH44212) diff --git a/clang/lib/Sema/SemaFunctionEffects.cpp b/clang/lib/Sema/SemaFunctionEffects.cpp index 124e23cc97556..975281175aebb 100644 --- a/clang/lib/Sema/SemaFunctionEffects.cpp +++ b/clang/lib/Sema/SemaFunctionEffects.cpp @@ -1255,15 +1255,23 @@ class Analyzer { } bool VisitCXXDeleteExpr(CXXDeleteExpr *Delete) override { + FunctionDecl *OpDelete = Delete->getOperatorDelete(); + + // RecursiveASTVisitor does not visit the called destructor. + // But a destroying operator delete means that no destructor is called. + if (OpDelete == nullptr || !OpDelete->isDestroyingOperatorDelete()) { + if (QualType QT = Delete->getDestroyedType(); !QT.isNull()) { + followTypeDtor(QT, Delete->getBeginLoc()); + } + } + // RecursiveASTVisitor does not visit the implicit call to operator // delete. - if (FunctionDecl *FD = Delete->getOperatorDelete()) { - CallableInfo CI(*FD, SpecialFuncType::OperatorDelete); + if (OpDelete != nullptr) { + CallableInfo CI(*OpDelete, SpecialFuncType::OperatorDelete); followCall(CI, Delete->getBeginLoc()); } - // It DOES however visit the called destructor - return true; } diff --git a/clang/test/Sema/attr-nonblocking-constraints.cpp b/clang/test/Sema/attr-nonblocking-constraints.cpp index 012c017798a1f..400057768523c 100644 --- a/clang/test/Sema/attr-nonblocking-constraints.cpp +++ b/clang/test/Sema/attr-nonblocking-constraints.cpp @@ -5,6 +5,8 @@ // This diagnostic is re-enabled and exercised in isolation later in this file. #pragma clang diagnostic ignored "-Wperf-constraint-implies-noexcept" +typedef __SIZE_TYPE__ size_t; + // --- CONSTRAINTS --- void nb1() [[clang::nonblocking]] @@ -430,6 +432,44 @@ struct HasDtor { ~HasDtor() {} }; +struct SafeDeleteUnsafeDestroy { + static unsigned char storage[256]; + + void* operator new(size_t) { return storage; } + void operator delete(void* ptr) {} + + void* operator new[](size_t sz) { return storage; } + void operator delete[](void* ptr) {} + + SafeDeleteUnsafeDestroy(); // expected-note 2 {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}} + ~SafeDeleteUnsafeDestroy(); // expected-note 2 {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}} +}; + +void testSafeDeleteUnsafeDestroy() [[clang::nonblocking]] +{ + auto *ptr = new SafeDeleteUnsafeDestroy; // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' constructor 'SafeDeleteUnsafeDestroy::SafeDeleteUnsafeDestroy'}} + delete ptr; // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' destructor 'SafeDeleteUnsafeDestroy::~SafeDeleteUnsafeDestroy'}} + + auto *arr = new SafeDeleteUnsafeDestroy[2]; // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' constructor 'SafeDeleteUnsafeDestroy::SafeDeleteUnsafeDestroy'}} + delete[] arr; // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' destructor 'SafeDeleteUnsafeDestroy::~SafeDeleteUnsafeDestroy'}} +} + +namespace std { +struct destroying_delete_t { explicit destroying_delete_t() = default; }; +inline constexpr destroying_delete_t destroying_delete{}; +} + +struct DestroyingDelete { + ~DestroyingDelete(); + void operator delete(DestroyingDelete*, std::destroying_delete_t) { + // do nothing + } +}; + +void testDestroyingDelete(DestroyingDelete* d) [[clang::nonblocking]] { + delete d; +} + template <typename T> struct Optional { union { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
