[cfe-commits] r151117 - in /cfe/trunk: include/clang/AST/ExprCXX.h include/clang/Sema/Sema.h lib/Parse/ParseDeclCXX.cpp lib/Sema/Sema.cpp lib/Sema/SemaExpr.cpp lib/Sema/SemaExprCXX.cpp lib/Sema/SemaOverload.cpp lib/Sema/TreeTransform.h test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp test/SemaCXX/overload-call.cpp
Richard Smith
richard-llvm at metafoo.co.uk
Tue Feb 21 18:04:18 PST 2012
Author: rsmith
Date: Tue Feb 21 20:04:18 2012
New Revision: 151117
URL: http://llvm.org/viewvc/llvm-project?rev=151117&view=rev
Log:
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.
Added:
cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp
Modified:
cfe/trunk/include/clang/AST/ExprCXX.h
cfe/trunk/include/clang/Sema/Sema.h
cfe/trunk/lib/Parse/ParseDeclCXX.cpp
cfe/trunk/lib/Sema/Sema.cpp
cfe/trunk/lib/Sema/SemaExpr.cpp
cfe/trunk/lib/Sema/SemaExprCXX.cpp
cfe/trunk/lib/Sema/SemaOverload.cpp
cfe/trunk/lib/Sema/TreeTransform.h
cfe/trunk/test/SemaCXX/overload-call.cpp
Modified: cfe/trunk/include/clang/AST/ExprCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ExprCXX.h?rev=151117&r1=151116&r2=151117&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/ExprCXX.h (original)
+++ cfe/trunk/include/clang/AST/ExprCXX.h Tue Feb 21 20:04:18 2012
@@ -736,6 +736,9 @@
const CXXDestructorDecl *Destructor);
const CXXDestructorDecl *getDestructor() const { return Destructor; }
+ void setDestructor(const CXXDestructorDecl *Dtor) {
+ Destructor = Dtor;
+ }
};
/// \brief Represents binding an expression to a temporary.
Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=151117&r1=151116&r2=151117&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Tue Feb 21 20:04:18 2012
@@ -59,6 +59,7 @@
class BlockDecl;
class CXXBasePath;
class CXXBasePaths;
+ class CXXBindTemporaryExpr;
typedef SmallVector<CXXBaseSpecifier*, 4> CXXCastPath;
class CXXConstructorDecl;
class CXXConversionDecl;
@@ -562,6 +563,9 @@
/// \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 @@
/// 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 @@
Expr **Args, unsigned NumArgs);
void PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext,
- Decl *LambdaContextDecl = 0);
+ Decl *LambdaContextDecl = 0,
+ bool IsDecltype = false);
void PopExpressionEvaluationContext();
@@ -3446,6 +3460,8 @@
bool EnteringContext,
CXXScopeSpec &SS);
+ ExprResult ActOnDecltypeExpression(Expr *E);
+
bool ActOnCXXNestedNameSpecifierDecltype(CXXScopeSpec &SS,
const DeclSpec &DS,
SourceLocation ColonColonLoc);
@@ -6572,9 +6588,11 @@
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() {
Modified: cfe/trunk/lib/Parse/ParseDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDeclCXX.cpp?rev=151117&r1=151116&r2=151117&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseDeclCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp Tue Feb 21 20:04:18 2012
@@ -667,8 +667,8 @@
// 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 @@
return T.getCloseLocation();
}
+ Result = Actions.ActOnDecltypeExpression(Result.take());
+ if (Result.isInvalid()) {
+ DS.SetTypeSpecError();
+ return T.getCloseLocation();
+ }
+
EndLoc = T.getCloseLocation();
}
Modified: cfe/trunk/lib/Sema/Sema.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.cpp?rev=151117&r1=151116&r2=151117&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.cpp (original)
+++ cfe/trunk/lib/Sema/Sema.cpp Tue Feb 21 20:04:18 2012
@@ -111,7 +111,8 @@
&Context);
ExprEvalContexts.push_back(
- ExpressionEvaluationContextRecord(PotentiallyEvaluated, 0, false, 0));
+ ExpressionEvaluationContextRecord(PotentiallyEvaluated, 0,
+ false, 0, false));
FunctionScopes.push_back(new FunctionScopeInfo(Diags));
}
Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=151117&r1=151116&r2=151117&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Tue Feb 21 20:04:18 2012
@@ -9277,12 +9277,14 @@
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 @@
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();
Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=151117&r1=151116&r2=151117&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Tue Feb 21 20:04:18 2012
@@ -4315,11 +4315,14 @@
}
}
- // 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 @@
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 @@
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,
Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=151117&r1=151116&r2=151117&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Tue Feb 21 20:04:18 2012
@@ -740,6 +740,8 @@
/// 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.
Modified: cfe/trunk/lib/Sema/TreeTransform.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/TreeTransform.h?rev=151117&r1=151116&r2=151117&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/TreeTransform.h (original)
+++ cfe/trunk/lib/Sema/TreeTransform.h Tue Feb 21 20:04:18 2012
@@ -4305,12 +4305,17 @@
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()) {
Added: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp?rev=151117&view=auto
==============================================================================
--- cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp (added)
+++ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp Tue Feb 21 20:04:18 2012
@@ -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}}
+}
Modified: cfe/trunk/test/SemaCXX/overload-call.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/overload-call.cpp?rev=151117&r1=151116&r2=151117&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/overload-call.cpp (original)
+++ cfe/trunk/test/SemaCXX/overload-call.cpp Tue Feb 21 20:04:18 2012
@@ -534,3 +534,12 @@
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);
+}
More information about the cfe-commits
mailing list