Files
clang-p2996/clang/test/SemaCXX/cxx2a-destroying-delete.cpp
Richard Smith aab25fa7d8 Never call a destroying operator delete when cleaning up from an
exception thrown during construction in a new-expression.

Instead, when performing deallocation function lookup for a
new-expression, ignore all destroying operator delete candidates, and
fall back to global operator delete if there is no member operator
delete other than a destroying operator delete.

Use of destroying operator delete only makes sense when there is an
object to destroy, which there isn't in this case. The language wording
doesn't cover this case; this oversight has been reported to WG21, with
the approach in this patch as the proposed fix.
2021-01-08 16:51:47 -08:00

190 lines
6.7 KiB
C++

// RUN: %clang_cc1 -std=c++2a -fexceptions -verify %s
// RUN: %clang_cc1 -std=c++2a -verify %s
namespace std {
using size_t = decltype(sizeof(0));
enum class align_val_t : size_t;
struct destroying_delete_t {
struct __construct { explicit __construct() = default; };
explicit destroying_delete_t(__construct) {}
};
inline constexpr destroying_delete_t destroying_delete(destroying_delete_t::__construct());
}
void operator delete(void*, std::destroying_delete_t); // ok, just a placement delete
struct A;
void operator delete(A*, std::destroying_delete_t); // expected-error {{first parameter of 'operator delete' must have type 'void *'}}
struct A {
void operator delete(A*, std::destroying_delete_t);
void operator delete(A*, std::destroying_delete_t, std::size_t);
void operator delete(A*, std::destroying_delete_t, std::align_val_t);
void operator delete(A*, std::destroying_delete_t, std::size_t, std::align_val_t);
void operator delete(A*, std::destroying_delete_t, int); // expected-error {{destroying operator delete can have only an optional size and optional alignment parameter}}
// FIXME: It's probably a language defect that we permit usual operator delete to be variadic.
void operator delete(A*, std::destroying_delete_t, std::size_t, ...);
void operator delete(struct X*, std::destroying_delete_t, std::size_t, ...); // expected-error {{first parameter of 'operator delete' must have type 'A *'}}
void operator delete(void*, std::size_t);
};
void delete_A(A *a) { delete a; }
namespace convert_param {
struct A {
void operator delete(
A*,
std::destroying_delete_t);
};
struct B : private A { using A::operator delete; }; // expected-note 2{{declared private here}}
struct C : B {};
void delete_C(C *c) { delete c; } // expected-error {{cannot cast 'convert_param::C' to its private base class 'convert_param::A'}}
// expected-error@-7 {{cannot cast 'convert_param::D' to its private base class 'convert_param::A'}}
struct D : B { virtual ~D() {} }; // expected-note {{while checking implicit 'delete this' for virtual destructor}}
}
namespace delete_selection {
struct B {
void operator delete(void*) = delete;
void operator delete(B *, std::destroying_delete_t) = delete; // expected-note {{deleted}}
};
void delete_B(B *b) { delete b; } // expected-error {{deleted}}
struct C {
C();
void *operator new(std::size_t);
void operator delete(void*) = delete; // expected-note 0-1 {{deleted here}}
void operator delete(C *, std::destroying_delete_t) = delete;
};
// TODO: We only diagnose the use of a deleted operator delete when exceptions
// are enabled. Otherwise we don't bother doing the lookup.
#ifdef __EXCEPTIONS
// expected-error@+2 {{attempt to use a deleted function}}
#endif
C *new_C() { return new C; }
struct D {
void operator delete(D *, std::destroying_delete_t) = delete; // expected-note {{deleted}}
void operator delete(D *, std::destroying_delete_t, std::align_val_t) = delete;
};
void delete_D(D *d) { delete d; } // expected-error {{deleted}}
struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2) E {
void operator delete(E *, std::destroying_delete_t) = delete;
void operator delete(E *, std::destroying_delete_t, std::align_val_t) = delete; // expected-note {{deleted}}
};
void delete_E(E *e) { delete e; } // expected-error {{deleted}}
struct F {
void operator delete(F *, std::destroying_delete_t) = delete; // expected-note {{deleted}}
void operator delete(F *, std::destroying_delete_t, std::size_t) = delete;
};
void delete_F(F *f) { delete f; } // expected-error {{deleted}}
struct G {
void operator delete(G *, std::destroying_delete_t, std::align_val_t) = delete;
void operator delete(G *, std::destroying_delete_t, std::size_t) = delete; // expected-note {{deleted}}
};
void delete_G(G *g) { delete g; } // expected-error {{deleted}}
struct H {
void operator delete(H *, std::destroying_delete_t, std::align_val_t) = delete; // expected-note {{deleted}}
void operator delete(H *, std::destroying_delete_t, std::size_t, std::align_val_t) = delete;
};
void delete_H(H *h) { delete h; } // expected-error {{deleted}}
struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2) I {
void operator delete(I *, std::destroying_delete_t, std::size_t) = delete;
void operator delete(I *, std::destroying_delete_t, std::size_t, std::align_val_t) = delete; // expected-note {{deleted}}
};
void delete_I(I *i) { delete i; } // expected-error {{deleted}}
}
namespace first_param_conversion {
struct A {
void operator delete(A *, std::destroying_delete_t);
};
void f(const volatile A *a) {
delete a; // ok
}
struct B {
void operator delete(B *, std::destroying_delete_t);
};
struct C : B {};
struct D : B {};
struct E : C, D {};
void g(E *e) {
delete e; // expected-error {{ambiguous conversion from derived class 'first_param_conversion::E' to base class 'first_param_conversion::B':}}
}
}
namespace templated {
template<typename T> using id_alias = T;
template<typename T> struct id_struct { using type = T; };
template<typename T> struct A {
void operator delete(A *, std::destroying_delete_t);
};
template<typename T> struct B {
void operator delete(B<T> *, std::destroying_delete_t);
};
template<typename T> struct C {
void operator delete(id_alias<C> *, std::destroying_delete_t);
};
template<typename T> struct D {
void operator delete(typename id_struct<D>::type *, std::destroying_delete_t); // expected-error {{use 'D<T> *'}}
};
}
namespace dtor_access {
struct S {
void operator delete(S *p, std::destroying_delete_t);
private:
~S(); // expected-note {{here}}
};
// FIXME: PR47474: GCC accepts this, and it seems somewhat reasonable to
// allow, even though [expr.delete]p12 says this is ill-formed.
void f() { delete new S; } // expected-error {{calling a private destructor}}
struct T {
void operator delete(T *, std::destroying_delete_t);
protected:
virtual ~T(); // expected-note {{here}}
};
struct U : T {
void operator delete(void *);
private:
~U() override;
};
void g() { delete (T *)new U; } // expected-error {{calling a protected destructor}}
}
namespace delete_from_new {
struct A {
A(); // might throw
void operator delete(A *, std::destroying_delete_t) = delete;
};
struct B {
B(); // might throw
void operator delete(void *) = delete; // #member-delete-from-new
void operator delete(B *, std::destroying_delete_t) = delete;
};
void f() {
new A; // calls ::operator delete
new B; // calls B::operator delete
#ifdef __EXCEPTIONS
// expected-error@-2 {{attempt to use a deleted function}}
// expected-note@#member-delete-from-new {{deleted here}}
#endif
}
}