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:
parent
0065198166
commit
9dbb0886ea
|
@ -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()) {
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue