Skip to content

Commit cc615d1

Browse files
dougsonosDoug Wyatt
andauthored
[clang][Sema] Function effect analysis was missing the implicit call to the destructor in a CXXDeleteExpr. (#184460)
Co-authored-by: Doug Wyatt <dwyatt@apple.com>
1 parent 7633d1e commit cc615d1

3 files changed

Lines changed: 53 additions & 4 deletions

File tree

clang/docs/ReleaseNotes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,7 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
827827
would only use the first one). A new warning that diagnoses such declarations has been added to `-Wignored-attributes`.
828828
(#GH191829)
829829
- 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)
830+
- Fixed a case where function effect analysis (`nonblocking` etc.) did not visit a destructor invoked from a `delete` expression. (#GH184460)
830831
- Clang now defines the GCC-compatible predefined macros `__WCHAR_MIN__`, `__WINT_MIN__`, and `__SIG_ATOMIC_MIN__`. (#GH199678)
831832
- Fix a crash in addUnsizedArray due assert not verifying we have a Base before doing checks on it. (#GH44212)
832833

clang/lib/Sema/SemaFunctionEffects.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,15 +1255,23 @@ class Analyzer {
12551255
}
12561256

12571257
bool VisitCXXDeleteExpr(CXXDeleteExpr *Delete) override {
1258+
FunctionDecl *OpDelete = Delete->getOperatorDelete();
1259+
1260+
// RecursiveASTVisitor does not visit the called destructor.
1261+
// But a destroying operator delete means that no destructor is called.
1262+
if (OpDelete == nullptr || !OpDelete->isDestroyingOperatorDelete()) {
1263+
if (QualType QT = Delete->getDestroyedType(); !QT.isNull()) {
1264+
followTypeDtor(QT, Delete->getBeginLoc());
1265+
}
1266+
}
1267+
12581268
// RecursiveASTVisitor does not visit the implicit call to operator
12591269
// delete.
1260-
if (FunctionDecl *FD = Delete->getOperatorDelete()) {
1261-
CallableInfo CI(*FD, SpecialFuncType::OperatorDelete);
1270+
if (OpDelete != nullptr) {
1271+
CallableInfo CI(*OpDelete, SpecialFuncType::OperatorDelete);
12621272
followCall(CI, Delete->getBeginLoc());
12631273
}
12641274

1265-
// It DOES however visit the called destructor
1266-
12671275
return true;
12681276
}
12691277

clang/test/Sema/attr-nonblocking-constraints.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
// This diagnostic is re-enabled and exercised in isolation later in this file.
66
#pragma clang diagnostic ignored "-Wperf-constraint-implies-noexcept"
77

8+
typedef __SIZE_TYPE__ size_t;
9+
810
// --- CONSTRAINTS ---
911

1012
void nb1() [[clang::nonblocking]]
@@ -430,6 +432,44 @@ struct HasDtor {
430432
~HasDtor() {}
431433
};
432434

435+
struct SafeDeleteUnsafeDestroy {
436+
static unsigned char storage[256];
437+
438+
void* operator new(size_t) { return storage; }
439+
void operator delete(void* ptr) {}
440+
441+
void* operator new[](size_t sz) { return storage; }
442+
void operator delete[](void* ptr) {}
443+
444+
SafeDeleteUnsafeDestroy(); // expected-note 2 {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}}
445+
~SafeDeleteUnsafeDestroy(); // expected-note 2 {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}}
446+
};
447+
448+
void testSafeDeleteUnsafeDestroy() [[clang::nonblocking]]
449+
{
450+
auto *ptr = new SafeDeleteUnsafeDestroy; // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' constructor 'SafeDeleteUnsafeDestroy::SafeDeleteUnsafeDestroy'}}
451+
delete ptr; // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' destructor 'SafeDeleteUnsafeDestroy::~SafeDeleteUnsafeDestroy'}}
452+
453+
auto *arr = new SafeDeleteUnsafeDestroy[2]; // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' constructor 'SafeDeleteUnsafeDestroy::SafeDeleteUnsafeDestroy'}}
454+
delete[] arr; // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' destructor 'SafeDeleteUnsafeDestroy::~SafeDeleteUnsafeDestroy'}}
455+
}
456+
457+
namespace std {
458+
struct destroying_delete_t { explicit destroying_delete_t() = default; };
459+
inline constexpr destroying_delete_t destroying_delete{};
460+
}
461+
462+
struct DestroyingDelete {
463+
~DestroyingDelete();
464+
void operator delete(DestroyingDelete*, std::destroying_delete_t) {
465+
// do nothing
466+
}
467+
};
468+
469+
void testDestroyingDelete(DestroyingDelete* d) [[clang::nonblocking]] {
470+
delete d;
471+
}
472+
433473
template <typename T>
434474
struct Optional {
435475
union {

0 commit comments

Comments
 (0)