diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index 453db168b461..45bb647a65d8 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -560,18 +560,28 @@ class CXXRecordDecl : public RecordDecl { struct LambdaDefinitionData : public DefinitionData { typedef LambdaExpr::Capture Capture; - LambdaDefinitionData(CXXRecordDecl *D) - : DefinitionData(D), NumCaptures(0), NumExplicitCaptures(0), - ContextDecl(0), Captures(0) + LambdaDefinitionData(CXXRecordDecl *D, bool Dependent) + : DefinitionData(D), Dependent(Dependent), NumCaptures(0), + NumExplicitCaptures(0), ManglingNumber(0), ContextDecl(0), Captures(0) { IsLambda = true; } + /// \brief Whether this lambda is known to be dependent, even if its + /// context isn't dependent. + /// + /// A lambda with a non-dependent context can be dependent if it occurs + /// within the default argument of a function template, because the + /// lambda will have been created with the enclosing context as its + /// declaration context, rather than function. This is an unfortunate + /// artifact of having to parse the default arguments before + unsigned Dependent : 1; + /// \brief The number of captures in this lambda. unsigned NumCaptures : 16; /// \brief The number of explicit captures in this lambda. - unsigned NumExplicitCaptures : 16; + unsigned NumExplicitCaptures : 15; /// \brief The number used to indicate this lambda expression for name /// mangling in the Itanium C++ ABI. @@ -689,7 +699,7 @@ public: IdentifierInfo *Id, CXXRecordDecl* PrevDecl=0, bool DelayTypeCreation = false); static CXXRecordDecl *CreateLambda(const ASTContext &C, DeclContext *DC, - SourceLocation Loc); + SourceLocation Loc, bool DependentLambda); static CXXRecordDecl *CreateDeserialized(const ASTContext &C, unsigned ID); bool isDynamicClass() const { @@ -1478,6 +1488,21 @@ public: return getLambdaData().ContextDecl; } + /// \brief Determine whether this lambda expression was known to be dependent + /// at the time it was created, even if its context does not appear to be + /// dependent. + /// + /// This flag is a workaround for an issue with parsing, where default + /// arguments are parsed before their enclosing function declarations have + /// been created. This means that any lambda expressions within those + /// default arguments will have as their DeclContext the context enclosing + /// the function declaration, which may be non-dependent even when the + /// function declaration itself is dependent. This flag indicates when we + /// know that the lambda is dependent despite that. + bool isDependentLambda() const { + return isLambda() && getLambdaData().Dependent; + } + static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K >= firstCXXRecord && K <= lastCXXRecord; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 29d3862b4b6c..7e3103cfa205 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2386,7 +2386,8 @@ public: QualType getCapturedDeclRefType(VarDecl *Var, SourceLocation Loc); void MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T); - void MarkDeclarationsReferencedInExpr(Expr *E); + void MarkDeclarationsReferencedInExpr(Expr *E, + bool SkipLocalVariables = false); /// \brief Try to recover by turning the given expression into a /// call. Returns true if recovery was attempted or an error was @@ -3543,7 +3544,8 @@ public: void ActOnCXXExitDeclInitializer(Scope *S, Decl *Dcl); /// \brief Create a new lambda closure type. - CXXRecordDecl *createLambdaClosureType(SourceRange IntroducerRange); + CXXRecordDecl *createLambdaClosureType(SourceRange IntroducerRange, + bool KnownDependent = false); /// \brief Start the definition of a lambda expression. CXXMethodDecl *startLambdaDefinition(CXXRecordDecl *Class, diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index a507215aa843..efbc5f992381 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -741,10 +741,14 @@ bool DeclContext::isDependentContext() const { if (isa(this)) return true; - if (const CXXRecordDecl *Record = dyn_cast(this)) + if (const CXXRecordDecl *Record = dyn_cast(this)) { if (Record->getDescribedClassTemplate()) return true; - + + if (Record->isDependentLambda()) + return true; + } + if (const FunctionDecl *Function = dyn_cast(this)) { if (Function->getDescribedFunctionTemplate()) return true; diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index a4d82206258d..9840cc7820fa 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -83,11 +83,11 @@ CXXRecordDecl *CXXRecordDecl::Create(const ASTContext &C, TagKind TK, } CXXRecordDecl *CXXRecordDecl::CreateLambda(const ASTContext &C, DeclContext *DC, - SourceLocation Loc) { + SourceLocation Loc, bool Dependent) { CXXRecordDecl* R = new (C) CXXRecordDecl(CXXRecord, TTK_Class, DC, Loc, Loc, 0, 0); R->IsBeingDefined = true; - R->DefinitionData = new (C) struct LambdaDefinitionData(R); + R->DefinitionData = new (C) struct LambdaDefinitionData(R, Dependent); C.getTypeDeclType(R, /*PrevDecl=*/0); return R; } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 1008a3ab431e..be3fa6c0dc6a 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -3146,7 +3146,8 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, // We already type-checked the argument, so we know it works. // Just mark all of the declarations in this potentially-evaluated expression // as being "referenced". - MarkDeclarationsReferencedInExpr(Param->getDefaultArg()); + MarkDeclarationsReferencedInExpr(Param->getDefaultArg(), + /*SkipLocalVariables=*/true); return Owned(CXXDefaultArgExpr::Create(Context, CallLoc, Param)); } @@ -10145,13 +10146,22 @@ namespace { /// potentially-evaluated subexpressions as "referenced". class EvaluatedExprMarker : public EvaluatedExprVisitor { Sema &S; + bool SkipLocalVariables; public: typedef EvaluatedExprVisitor Inherited; - explicit EvaluatedExprMarker(Sema &S) : Inherited(S.Context), S(S) { } + EvaluatedExprMarker(Sema &S, bool SkipLocalVariables) + : Inherited(S.Context), S(S), SkipLocalVariables(SkipLocalVariables) { } void VisitDeclRefExpr(DeclRefExpr *E) { + // If we were asked not to visit local variables, don't. + if (SkipLocalVariables) { + if (VarDecl *VD = dyn_cast(E->getDecl())) + if (VD->hasLocalStorage()) + return; + } + S.MarkDeclRefReferenced(E); } @@ -10193,6 +10203,10 @@ namespace { } void VisitBlockDeclRefExpr(BlockDeclRefExpr *E) { + // If we were asked not to visit local variables, don't. + if (SkipLocalVariables && E->getDecl()->hasLocalStorage()) + return; + S.MarkBlockDeclRefReferenced(E); } @@ -10211,8 +10225,12 @@ namespace { /// \brief Mark any declarations that appear within this expression or any /// potentially-evaluated subexpressions as "referenced". -void Sema::MarkDeclarationsReferencedInExpr(Expr *E) { - EvaluatedExprMarker(*this).Visit(E); +/// +/// \param SkipLocalVariables If true, don't mark local variables as +/// 'referenced'. +void Sema::MarkDeclarationsReferencedInExpr(Expr *E, + bool SkipLocalVariables) { + EvaluatedExprMarker(*this, SkipLocalVariables).Visit(E); } /// \brief Emit a diagnostic that describes an effect on the run-time behavior diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index 694fee677623..42040084e10e 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -21,14 +21,16 @@ using namespace clang; using namespace sema; -CXXRecordDecl *Sema::createLambdaClosureType(SourceRange IntroducerRange) { +CXXRecordDecl *Sema::createLambdaClosureType(SourceRange IntroducerRange, + bool KnownDependent) { DeclContext *DC = CurContext; while (!(DC->isFunctionOrMethod() || DC->isRecord() || DC->isFileContext())) DC = DC->getParent(); // Start constructing the lambda class. CXXRecordDecl *Class = CXXRecordDecl::CreateLambda(Context, DC, - IntroducerRange.getBegin()); + IntroducerRange.getBegin(), + KnownDependent); DC->addDecl(Class); return Class; @@ -142,7 +144,14 @@ void Sema::addLambdaParameters(CXXMethodDecl *CallOperator, Scope *CurScope) { void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, Declarator &ParamInfo, Scope *CurScope) { - CXXRecordDecl *Class = createLambdaClosureType(Intro.Range); + // Determine if we're within a context where we know that the lambda will + // be dependent, because there are template parameters in scope. + bool KnownDependent = false; + if (Scope *TmplScope = CurScope->getTemplateParamParent()) + if (!TmplScope->decl_empty()) + KnownDependent = true; + + CXXRecordDecl *Class = createLambdaClosureType(Intro.Range, KnownDependent); // Determine the signature of the call operator. TypeSourceInfo *MethodTyInfo; diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index c2a93b99fa1e..fe09cdafa7c0 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1107,6 +1107,7 @@ void ASTDeclReader::ReadCXXDefinitionData( typedef LambdaExpr::Capture Capture; CXXRecordDecl::LambdaDefinitionData &Lambda = static_cast(Data); + Lambda.Dependent = Record[Idx++]; Lambda.NumCaptures = Record[Idx++]; Lambda.NumExplicitCaptures = Record[Idx++]; Lambda.ManglingNumber = Record[Idx++]; @@ -1134,7 +1135,7 @@ void ASTDeclReader::VisitCXXRecordDecl(CXXRecordDecl *D) { // allocate the appropriate DefinitionData structure. bool IsLambda = Record[Idx++]; if (IsLambda) - D->DefinitionData = new (C) CXXRecordDecl::LambdaDefinitionData(D); + D->DefinitionData = new (C) CXXRecordDecl::LambdaDefinitionData(D, false); else D->DefinitionData = new (C) struct CXXRecordDecl::DefinitionData(D); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 5c3a1e93f69c..8dbd6e447150 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -4330,6 +4330,7 @@ void ASTWriter::AddCXXDefinitionData(const CXXRecordDecl *D, RecordDataImpl &Rec // Add lambda-specific data. if (Data.IsLambda) { CXXRecordDecl::LambdaDefinitionData &Lambda = D->getLambdaData(); + Record.push_back(Lambda.Dependent); Record.push_back(Lambda.NumCaptures); Record.push_back(Lambda.NumExplicitCaptures); Record.push_back(Lambda.ManglingNumber); diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp index 53f458a824d7..8bb707e0dbc7 100644 --- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp @@ -8,3 +8,9 @@ void f2() { void g4(int = ([=]{ return 0; })()); void g5(int = ([]{ return sizeof i; })()); } + +namespace lambda_in_default_args { + int f(int = [] () -> int { int n; return ++n; } ()); + template T g(T = [] () -> T { T n; return ++n; } ()); + int k = f() + g(); +}