diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 395fdcd10f651e2bd640ec015b08263fb78009b7..f21ba1f3b75e8c588bb256f1d6129982c0678ccb 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -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. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 7e3103cfa205e355339fdf37a9fbbaee418c9aa8..d908937b631d250025a9ee9fdab0d3d9b1a50e20 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -59,6 +59,7 @@ namespace clang { class BlockDecl; class CXXBasePath; class CXXBasePaths; + class CXXBindTemporaryExpr; typedef SmallVector 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 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 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() { diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 5edc30be92b4fa36b572de11ee4831a4aafa5e1b..61ca84136e3d473b14e0225ebad881dc032fb874 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -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(); } diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 6a83518bc027fa8d1e49f91bffdb66b15175b6f8..cfb4e4c91b9ee57630efbaf18343dcf6b5cbd8da 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -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)); } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index be3fa6c0dc6aac8dd9ac18feba0cb92e1ac2801b..c7738a5626a05d8d56d37ea5568390c6444652e3 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -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(); diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index c33ba7558b8ec801a4871f90e72e50021abcbdac..c8e640f1917733dce72716b596cc582dcf4e3f82 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -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(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(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(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(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(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, diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index bc48f08a34085d9c463a94bcb2e87b1201955865..e5db60b2002b12fa59920924e68bb566b9b045e6 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -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. diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 7e775f55e0a9fc4cc9d05ba0607dbfd714776d7e..58d6a22a288297966e92cde7b8ce8b578949e76a 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -4305,12 +4305,17 @@ QualType TreeTransform::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()) { diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2bd5d234ce7f2f9bb77415dcb2cd150b4b68d325 --- /dev/null +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp @@ -0,0 +1,108 @@ +// RUN: %clang_cc1 -std=c++11 -verify %s + +namespace std_example { + +template struct A { ~A() = delete; }; // expected-note {{deleted here}} +template auto h() -> A; +template auto i(T) -> T; +template auto f(T) -> decltype(i(h())); // #1 +template auto f(T) -> void; // #2 +auto g() -> void { + f(42); // ok, calls #2, since #1 is not viable. +} +template auto q(T) -> decltype((h())); +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 struct is_same { static const bool value = false; }; + template struct is_same { static const bool value = true; }; + + template T declval(); + + void swap(int &a, int &b); + nat swap(any, any); + + template struct swappable { + typedef decltype(swap(declval(), declval())) type; + static const bool value = !is_same::value; + constexpr operator bool() { return value; } + }; + + static_assert(swappable(), ""); + static_assert(!swappable(), ""); +} + +namespace RequireCompleteType { + template struct S { + static_assert(OK, "boom!"); // expected-error 2{{boom!}} + }; + + template T make(); + template S 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())>()) *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}} +} diff --git a/clang/test/SemaCXX/overload-call.cpp b/clang/test/SemaCXX/overload-call.cpp index 1bbe00fcd4013093f3403e5f7b2b2393e6d69ec2..501e2d6fb2f2dcd5a7a64f168bd66873f0f81129 100644 --- a/clang/test/SemaCXX/overload-call.cpp +++ b/clang/test/SemaCXX/overload-call.cpp @@ -534,3 +534,12 @@ namespace rdar9803316 { int &ir = (&foo)(0); } } + +namespace IncompleteArg { + // Ensure that overload resolution attempts to complete argument types. + template struct S { + friend int f(const S&); + }; + extern S s; + int k = f(s); +}