Implement C++11 [expr.call]p11: If the operand to a decltype-specifier is a

function call (or a comma expression with a function call on its right-hand
side), possibly parenthesized, then the return type is not required to be
complete and a temporary is not bound. Other subexpressions inside a decltype
expression do not get this treatment.

This is implemented by deferring the relevant checks for all calls immediately
within a decltype expression, then, when the expression is fully-parsed,
checking the relevant constraints and stripping off any top-level temporary
binding.

Deferring the completion of the return type exposed a bug in overload
resolution where completion of the argument types was not attempted, which
is also fixed by this change.

llvm-svn: 151117
This commit is contained in:
Richard Smith
2012-02-22 02:04:18 +00:00
parent 3715c691b3
commit fd555f6b1f
10 changed files with 276 additions and 19 deletions

View File

@@ -736,6 +736,9 @@ public:
const CXXDestructorDecl *Destructor);
const CXXDestructorDecl *getDestructor() const { return Destructor; }
void setDestructor(const CXXDestructorDecl *Dtor) {
Destructor = Dtor;
}
};
/// \brief Represents binding an expression to a temporary.

View File

@@ -59,6 +59,7 @@ namespace clang {
class BlockDecl;
class CXXBasePath;
class CXXBasePaths;
class CXXBindTemporaryExpr;
typedef SmallVector<CXXBaseSpecifier*, 4> CXXCastPath;
class CXXConstructorDecl;
class CXXConversionDecl;
@@ -562,6 +563,9 @@ public:
/// \brief Whether the enclosing context needed a cleanup.
bool ParentNeedsCleanups;
/// \brief Whether we are in a decltype expression.
bool IsDecltype;
/// \brief The number of active cleanup objects when we entered
/// this expression evaluation context.
unsigned NumCleanupObjects;
@@ -583,13 +587,22 @@ public:
/// This mangling information is allocated lazily, since most contexts
/// do not have lambda expressions.
LambdaMangleContext *LambdaMangle;
/// \brief If we are processing a decltype type, a set of call expressions
/// for which we have deferred checking the completeness of the return type.
llvm::SmallVector<CallExpr*, 8> DelayedDecltypeCalls;
/// \brief If we are processing a decltype type, a set of temporary binding
/// expressions for which we have deferred checking the destructor.
llvm::SmallVector<CXXBindTemporaryExpr*, 8> DelayedDecltypeBinds;
ExpressionEvaluationContextRecord(ExpressionEvaluationContext Context,
unsigned NumCleanupObjects,
bool ParentNeedsCleanups,
Decl *LambdaContextDecl)
Decl *LambdaContextDecl,
bool IsDecltype)
: Context(Context), ParentNeedsCleanups(ParentNeedsCleanups),
NumCleanupObjects(NumCleanupObjects),
IsDecltype(IsDecltype), NumCleanupObjects(NumCleanupObjects),
LambdaContextDecl(LambdaContextDecl), LambdaMangle() { }
~ExpressionEvaluationContextRecord() {
@@ -2315,7 +2328,8 @@ public:
Expr **Args, unsigned NumArgs);
void PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext,
Decl *LambdaContextDecl = 0);
Decl *LambdaContextDecl = 0,
bool IsDecltype = false);
void PopExpressionEvaluationContext();
@@ -3446,6 +3460,8 @@ public:
bool EnteringContext,
CXXScopeSpec &SS);
ExprResult ActOnDecltypeExpression(Expr *E);
bool ActOnCXXNestedNameSpecifierDecltype(CXXScopeSpec &SS,
const DeclSpec &DS,
SourceLocation ColonColonLoc);
@@ -6572,9 +6588,11 @@ class EnterExpressionEvaluationContext {
public:
EnterExpressionEvaluationContext(Sema &Actions,
Sema::ExpressionEvaluationContext NewContext,
Decl *LambdaContextDecl = 0)
Decl *LambdaContextDecl = 0,
bool IsDecltype = false)
: Actions(Actions) {
Actions.PushExpressionEvaluationContext(NewContext, LambdaContextDecl);
Actions.PushExpressionEvaluationContext(NewContext, LambdaContextDecl,
IsDecltype);
}
~EnterExpressionEvaluationContext() {

View File

@@ -667,8 +667,8 @@ SourceLocation Parser::ParseDecltypeSpecifier(DeclSpec &DS) {
// C++0x [dcl.type.simple]p4:
// The operand of the decltype specifier is an unevaluated operand.
EnterExpressionEvaluationContext Unevaluated(Actions,
Sema::Unevaluated);
EnterExpressionEvaluationContext Unevaluated(Actions, Sema::Unevaluated,
0, /*IsDecltype=*/true);
Result = ParseExpression();
if (Result.isInvalid()) {
SkipUntil(tok::r_paren, true, true);
@@ -685,6 +685,12 @@ SourceLocation Parser::ParseDecltypeSpecifier(DeclSpec &DS) {
return T.getCloseLocation();
}
Result = Actions.ActOnDecltypeExpression(Result.take());
if (Result.isInvalid()) {
DS.SetTypeSpecError();
return T.getCloseLocation();
}
EndLoc = T.getCloseLocation();
}

View File

@@ -111,7 +111,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
&Context);
ExprEvalContexts.push_back(
ExpressionEvaluationContextRecord(PotentiallyEvaluated, 0, false, 0));
ExpressionEvaluationContextRecord(PotentiallyEvaluated, 0,
false, 0, false));
FunctionScopes.push_back(new FunctionScopeInfo(Diags));
}

View File

@@ -9277,12 +9277,14 @@ ExprResult Sema::TranformToPotentiallyEvaluated(Expr *E) {
void
Sema::PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext,
Decl *LambdaContextDecl) {
Decl *LambdaContextDecl,
bool IsDecltype) {
ExprEvalContexts.push_back(
ExpressionEvaluationContextRecord(NewContext,
ExprCleanupObjects.size(),
ExprNeedsCleanups,
LambdaContextDecl));
LambdaContextDecl,
IsDecltype));
ExprNeedsCleanups = false;
if (!MaybeODRUseExprs.empty())
std::swap(MaybeODRUseExprs, ExprEvalContexts.back().SavedMaybeODRUseExprs);
@@ -10280,6 +10282,13 @@ bool Sema::CheckCallReturnType(QualType ReturnType, SourceLocation Loc,
if (ReturnType->isVoidType() || !ReturnType->isIncompleteType())
return false;
// If we're inside a decltype's expression, don't check for a valid return
// type or construct temporaries until we know whether this is the last call.
if (ExprEvalContexts.back().IsDecltype) {
ExprEvalContexts.back().DelayedDecltypeCalls.push_back(CE);
return false;
}
PartialDiagnostic Note =
FD ? PDiag(diag::note_function_with_incomplete_return_type_declared_here)
<< FD->getDeclName() : PDiag();

View File

@@ -4315,11 +4315,14 @@ ExprResult Sema::MaybeBindToTemporary(Expr *E) {
}
}
// That should be enough to guarantee that this type is complete.
// That should be enough to guarantee that this type is complete, if we're
// not processing a decltype expression.
CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
if (RD->isInvalidDecl() || RD->isDependentContext())
return Owned(E);
CXXDestructorDecl *Destructor = LookupDestructor(RD);
bool IsDecltype = ExprEvalContexts.back().IsDecltype;
CXXDestructorDecl *Destructor = IsDecltype ? 0 : LookupDestructor(RD);
if (Destructor) {
MarkFunctionReferenced(E->getExprLoc(), Destructor);
@@ -4327,18 +4330,22 @@ ExprResult Sema::MaybeBindToTemporary(Expr *E) {
PDiag(diag::err_access_dtor_temp)
<< E->getType());
DiagnoseUseOfDecl(Destructor, E->getExprLoc());
}
// If destructor is trivial, we can avoid the extra copy.
if (Destructor->isTrivial())
return Owned(E);
// If destructor is trivial, we can avoid the extra copy.
if (Destructor->isTrivial())
return Owned(E);
if (Destructor)
// We need a cleanup, but we don't need to remember the temporary.
ExprNeedsCleanups = true;
}
CXXTemporary *Temp = CXXTemporary::Create(Context, Destructor);
return Owned(CXXBindTemporaryExpr::Create(Context, Temp, E));
CXXBindTemporaryExpr *Bind = CXXBindTemporaryExpr::Create(Context, Temp, E);
if (IsDecltype)
ExprEvalContexts.back().DelayedDecltypeBinds.push_back(Bind);
return Owned(Bind);
}
ExprResult
@@ -4390,6 +4397,95 @@ Stmt *Sema::MaybeCreateStmtWithCleanups(Stmt *SubStmt) {
return MaybeCreateExprWithCleanups(E);
}
/// Process the expression contained within a decltype. For such expressions,
/// certain semantic checks on temporaries are delayed until this point, and
/// are omitted for the 'topmost' call in the decltype expression. If the
/// topmost call bound a temporary, strip that temporary off the expression.
ExprResult Sema::ActOnDecltypeExpression(Expr *E) {
ExpressionEvaluationContextRecord &Rec = ExprEvalContexts.back();
assert(Rec.IsDecltype && "not in a decltype expression");
// C++11 [expr.call]p11:
// If a function call is a prvalue of object type,
// -- if the function call is either
// -- the operand of a decltype-specifier, or
// -- the right operand of a comma operator that is the operand of a
// decltype-specifier,
// a temporary object is not introduced for the prvalue.
// Recursively rebuild ParenExprs and comma expressions to strip out the
// outermost CXXBindTemporaryExpr, if any.
if (ParenExpr *PE = dyn_cast<ParenExpr>(E)) {
ExprResult SubExpr = ActOnDecltypeExpression(PE->getSubExpr());
if (SubExpr.isInvalid())
return ExprError();
if (SubExpr.get() == PE->getSubExpr())
return Owned(E);
return ActOnParenExpr(PE->getLParen(), PE->getRParen(), SubExpr.take());
}
if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) {
if (BO->getOpcode() == BO_Comma) {
ExprResult RHS = ActOnDecltypeExpression(BO->getRHS());
if (RHS.isInvalid())
return ExprError();
if (RHS.get() == BO->getRHS())
return Owned(E);
return Owned(new (Context) BinaryOperator(BO->getLHS(), RHS.take(),
BO_Comma, BO->getType(),
BO->getValueKind(),
BO->getObjectKind(),
BO->getOperatorLoc()));
}
}
CXXBindTemporaryExpr *TopBind = dyn_cast<CXXBindTemporaryExpr>(E);
if (TopBind)
E = TopBind->getSubExpr();
// Disable the special decltype handling now.
Rec.IsDecltype = false;
// Perform the semantic checks we delayed until this point.
CallExpr *TopCall = dyn_cast<CallExpr>(E);
for (unsigned I = 0, N = Rec.DelayedDecltypeCalls.size(); I != N; ++I) {
CallExpr *Call = Rec.DelayedDecltypeCalls[I];
if (Call == TopCall)
continue;
if (CheckCallReturnType(Call->getCallReturnType(),
Call->getSourceRange().getBegin(),
Call, Call->getDirectCallee()))
return ExprError();
}
// Now all relevant types are complete, check the destructors are accessible
// and non-deleted, and annotate them on the temporaries.
for (unsigned I = 0, N = Rec.DelayedDecltypeBinds.size(); I != N; ++I) {
CXXBindTemporaryExpr *Bind = Rec.DelayedDecltypeBinds[I];
if (Bind == TopBind)
continue;
CXXTemporary *Temp = Bind->getTemporary();
CXXRecordDecl *RD =
Bind->getType()->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();
CXXDestructorDecl *Destructor = LookupDestructor(RD);
Temp->setDestructor(Destructor);
MarkFunctionReferenced(E->getExprLoc(), Destructor);
CheckDestructorAccess(E->getExprLoc(), Destructor,
PDiag(diag::err_access_dtor_temp)
<< E->getType());
DiagnoseUseOfDecl(Destructor, E->getExprLoc());
// We need a cleanup, but we don't need to remember the temporary.
ExprNeedsCleanups = true;
}
// Possibly strip off the top CXXBindTemporaryExpr.
return Owned(E);
}
ExprResult
Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, SourceLocation OpLoc,
tok::TokenKind OpKind, ParsedType &ObjectType,

View File

@@ -740,6 +740,8 @@ namespace {
/// Return true on unrecoverable error.
static bool checkPlaceholderForOverload(Sema &S, Expr *&E,
UnbridgedCastsSet *unbridgedCasts = 0) {
S.RequireCompleteType(E->getExprLoc(), E->getType(), 0);
if (const BuiltinType *placeholder = E->getType()->getAsPlaceholderType()) {
// We can't handle overloaded expressions here because overload
// resolution might reasonably tweak them.

View File

@@ -4305,12 +4305,17 @@ QualType TreeTransform<Derived>::TransformDecltypeType(TypeLocBuilder &TLB,
const DecltypeType *T = TL.getTypePtr();
// decltype expressions are not potentially evaluated contexts
EnterExpressionEvaluationContext Unevaluated(SemaRef, Sema::Unevaluated);
EnterExpressionEvaluationContext Unevaluated(SemaRef, Sema::Unevaluated, 0,
/*IsDecltype=*/ true);
ExprResult E = getDerived().TransformExpr(T->getUnderlyingExpr());
if (E.isInvalid())
return QualType();
E = getSema().ActOnDecltypeExpression(E.take());
if (E.isInvalid())
return QualType();
QualType Result = TL.getType();
if (getDerived().AlwaysRebuild() ||
E.get() != T->getUnderlyingExpr()) {

View File

@@ -0,0 +1,108 @@
// RUN: %clang_cc1 -std=c++11 -verify %s
namespace std_example {
template<class T> struct A { ~A() = delete; }; // expected-note {{deleted here}}
template<class T> auto h() -> A<T>;
template<class T> auto i(T) -> T;
template<class T> auto f(T) -> decltype(i(h<T>())); // #1
template<class T> auto f(T) -> void; // #2
auto g() -> void {
f(42); // ok, calls #2, since #1 is not viable.
}
template<class T> auto q(T) -> decltype((h<T>()));
void r() {
// Deduction against q succeeds, but results in a temporary which can't be
// destroyed.
q(42); // expected-error {{attempt to use a deleted function}}
}
}
class PD {
friend struct A;
~PD(); // expected-note 4{{here}}
public:
typedef int n;
};
struct DD {
~DD() = delete; // expected-note 2{{here}}
typedef int n;
};
struct A {
decltype(PD()) s; // ok
decltype(PD())::n n; // ok
decltype(DD()) *p = new decltype(DD()); // ok
};
// Two errors here: one for the decltype, one for the variable.
decltype(PD(), PD()) pd1; // expected-error 2{{private destructor}}
decltype(DD(), DD()) dd1; // expected-error 2{{deleted function}}
decltype(((13, ((DD())))))::n dd_parens; // ok
decltype(((((42)), PD())))::n pd_parens_comma; // ok
// Ensure parens aren't stripped from a decltype node.
extern decltype(PD()) pd_ref; // ok
decltype((pd_ref)) pd_ref3 = pd_ref; // ok, PD &
decltype(pd_ref) pd_ref2 = pd_ref; // expected-error {{private destructor}}
namespace libcxx_example {
struct nat {
nat() = delete;
nat(const nat&) = delete;
nat &operator=(const nat&) = delete;
~nat() = delete;
};
struct any {
any(...);
};
template<typename T, typename U> struct is_same { static const bool value = false; };
template<typename T> struct is_same<T, T> { static const bool value = true; };
template<typename T> T declval();
void swap(int &a, int &b);
nat swap(any, any);
template<typename T> struct swappable {
typedef decltype(swap(declval<T&>(), declval<T&>())) type;
static const bool value = !is_same<type, nat>::value;
constexpr operator bool() { return value; }
};
static_assert(swappable<int>(), "");
static_assert(!swappable<const int>(), "");
}
namespace RequireCompleteType {
template<int N, bool OK> struct S {
static_assert(OK, "boom!"); // expected-error 2{{boom!}}
};
template<typename T> T make();
template<int N, bool OK> S<N, OK> make();
void consume(...);
decltype(make<0, false>()) *p1; // ok
decltype((make<1, false>())) *p2; // ok
// A complete type is required here in order to detect an overloaded 'operator,'.
decltype(123, make<2, false>()) *p3; // expected-note {{here}}
decltype(consume(make<3, false>())) *p4; // expected-note {{here}}
decltype(make<decltype(make<4, false>())>()) *p5; // ok
}
namespace Overload {
DD operator+(PD &a, PD &b);
decltype(PD()) *pd_ptr;
decltype(*pd_ptr + *pd_ptr) *dd_ptr; // ok
decltype(0, *pd_ptr) pd_ref2 = pd_ref; // ok
DD operator,(int a, PD b);
decltype(0, *pd_ptr) *dd_ptr2; // expected-error {{private destructor}}
}

View File

@@ -534,3 +534,12 @@ namespace rdar9803316 {
int &ir = (&foo)(0);
}
}
namespace IncompleteArg {
// Ensure that overload resolution attempts to complete argument types.
template<typename T> struct S {
friend int f(const S&);
};
extern S<int> s;
int k = f(s);
}