mirror of
https://github.com/intel/llvm.git
synced 2026-01-25 19:44:38 +08:00
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:
@@ -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.
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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}}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user