forked from OSchip/llvm-project
PR49465: Disallow constant evaluation of a call to operator delete(nullptr).
The only time we would consider allowing this is inside a call to std::allocator<T>::deallocate, whose contract does not permit deletion of null pointers.
This commit is contained in:
parent
234f3211a3
commit
a892b0015e
|
@ -349,6 +349,8 @@ def note_constexpr_new_delete_mismatch : Note<
|
|||
"used to delete pointer to "
|
||||
"%select{array object of type %2|non-array object of type %2|"
|
||||
"object allocated with 'new'}0}1">;
|
||||
def note_constexpr_deallocate_null : Note<
|
||||
"'std::allocator<...>::deallocate' used to delete a null pointer">;
|
||||
def note_constexpr_delete_subobject : Note<
|
||||
"delete of pointer%select{ to subobject|}1 '%0' "
|
||||
"%select{|that does not point to complete object}1">;
|
||||
|
|
|
@ -6712,9 +6712,12 @@ bool HandleOperatorDeleteCall(EvalInfo &Info, const CallExpr *E) {
|
|||
if (Pointer.Designator.Invalid)
|
||||
return false;
|
||||
|
||||
// Deleting a null pointer has no effect.
|
||||
if (Pointer.isNullPointer())
|
||||
// Deleting a null pointer would have no effect, but it's not permitted by
|
||||
// std::allocator<T>::deallocate's contract.
|
||||
if (Pointer.isNullPointer()) {
|
||||
Info.CCEDiag(E->getExprLoc(), diag::note_constexpr_deallocate_null);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!CheckDeleteKind(Info, E, Pointer, DynAlloc::StdAllocator))
|
||||
return false;
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace std {
|
|||
return (T*)NEW(sizeof(T) * N); // expected-note 3{{heap allocation}} expected-note {{not deallocated}}
|
||||
}
|
||||
constexpr void deallocate(void *p) {
|
||||
DELETE(p); // expected-note 2{{'std::allocator<...>::deallocate' used to delete pointer to object allocated with 'new'}}
|
||||
DELETE(p); // #dealloc expected-note 2{{'std::allocator<...>::deallocate' used to delete pointer to object allocated with 'new'}}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -83,6 +83,11 @@ static_assert(mismatched(2, 2));
|
|||
constexpr int *escape = std::allocator<int>().allocate(3); // expected-error {{constant expression}} expected-note {{pointer to subobject of heap-allocated}}
|
||||
constexpr int leak = (std::allocator<int>().allocate(3), 0); // expected-error {{constant expression}}
|
||||
constexpr int no_lifetime_start = (*std::allocator<int>().allocate(1) = 1); // expected-error {{constant expression}} expected-note {{assignment to object outside its lifetime}}
|
||||
constexpr int no_deallocate_nullptr = (std::allocator<int>().deallocate(nullptr), 1); // expected-error {{constant expression}} expected-note {{in call}}
|
||||
// expected-note@#dealloc {{'std::allocator<...>::deallocate' used to delete a null pointer}}
|
||||
constexpr int no_deallocate_nonalloc = (std::allocator<int>().deallocate((int*)&no_deallocate_nonalloc), 1); // expected-error {{constant expression}} expected-note {{in call}}
|
||||
// expected-note@#dealloc {{delete of pointer '&no_deallocate_nonalloc' that does not point to a heap-allocated object}}
|
||||
// expected-note@-2 {{declared here}}
|
||||
|
||||
void *operator new(std::size_t, void *p) { return p; }
|
||||
constexpr bool no_placement_new_in_user_code() { // expected-error {{never produces a constant expression}}
|
||||
|
|
Loading…
Reference in New Issue