Perform lvalue conversions on the left of a pseudo-destructor call 'p->~T()'.

Previously we failed to convert 'p' from array/function to pointer type,
and to represent the load of 'p' in the AST. The latter causes problems
for constant evaluation.
This commit is contained in:
Richard Smith 2020-10-14 22:05:30 -07:00
parent 0065198166
commit 9dbb0886ea
3 changed files with 35 additions and 3 deletions

View File

@ -7249,8 +7249,8 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
return Base;
}
static bool CheckArrow(Sema& S, QualType& ObjectType, Expr *&Base,
tok::TokenKind& OpKind, SourceLocation OpLoc) {
static bool CheckArrow(Sema &S, QualType &ObjectType, Expr *&Base,
tok::TokenKind &OpKind, SourceLocation OpLoc) {
if (Base->hasPlaceholderType()) {
ExprResult result = S.CheckPlaceholderExpr(Base);
if (result.isInvalid()) return true;
@ -7265,6 +7265,18 @@ static bool CheckArrow(Sema& S, QualType& ObjectType, Expr *&Base,
// Note that this is rather different from the normal handling for the
// arrow operator.
if (OpKind == tok::arrow) {
// The operator requires a prvalue, so perform lvalue conversions.
// Only do this if we might plausibly end with a pointer, as otherwise
// this was likely to be intended to be a '.'.
if (ObjectType->isPointerType() || ObjectType->isArrayType() ||
ObjectType->isFunctionType()) {
ExprResult BaseResult = S.DefaultFunctionArrayLvalueConversion(Base);
if (BaseResult.isInvalid())
return true;
Base = BaseResult.get();
ObjectType = Base->getType();
}
if (const PointerType *Ptr = ObjectType->getAs<PointerType>()) {
ObjectType = Ptr->getPointeeType();
} else if (!Base->isTypeDependent()) {

View File

@ -1061,9 +1061,10 @@ namespace memory_leaks {
static_assert(h({new bool(true)})); // ok
}
void *operator new(std::size_t, void*);
constexpr void *operator new(std::size_t, void *p) { return p; }
namespace std {
template<typename T> constexpr T *construct(T *p) { return new (p) T; }
template<typename T> constexpr void destroy(T *p) { p->~T(); }
}
namespace dtor_call {
@ -1428,3 +1429,11 @@ namespace PR47805 {
constexpr bool g(B b) { return &b == b.p; }
static_assert(g({}));
}
constexpr bool destroy_at_test() {
int n = 0;
std::destroy(&n);
std::construct(&n);
return true;
}
static_assert(destroy_at_test());

View File

@ -183,3 +183,14 @@ namespace TwoPhaseLookup {
template<typename T> void f6(int *p) { p->TemplateNamesNonTemplate::C::~C<int>(); } // expected-error {{'C' does not refer to a template}}
}
}
void destroy_array_element() {
int arr[5];
using T = int;
arr->~T(); // ok, destroy arr[0].
}
void destroy_function() {
using T = void();
destroy_function->~T(); // expected-error {{object expression of non-scalar type 'void ()' cannot be used in a pseudo-destructor expression}}
}