From 04fa7a33279808dc3e5117c41b5f84c40eeb7362 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Sat, 28 Sep 2013 04:02:39 +0000 Subject: [PATCH] Per latest drafting, switch to implementing init-captures as if by declaring and capturing a variable declaration, and complete the implementation of them. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@191605 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Decl.h | 10 +- include/clang/AST/ExprCXX.h | 27 +-- include/clang/AST/RecursiveASTVisitor.h | 2 +- include/clang/Basic/DiagnosticSemaKinds.td | 2 +- include/clang/Basic/Lambda.h | 3 +- include/clang/Sema/ScopeInfo.h | 49 ++-- include/clang/Sema/Sema.h | 11 +- lib/AST/DeclCXX.cpp | 1 + lib/AST/ExprCXX.cpp | 11 - lib/AST/StmtPrinter.cpp | 14 +- lib/AST/StmtProfile.cpp | 3 - lib/Sema/SemaDecl.cpp | 7 +- lib/Sema/SemaLambda.cpp | 215 +++++++----------- lib/Sema/SemaTemplateDeduction.cpp | 8 +- lib/Sema/SemaTemplateInstantiateDecl.cpp | 1 + lib/Sema/TreeTransform.h | 17 +- lib/Serialization/ASTReaderDecl.cpp | 8 +- lib/Serialization/ASTWriter.cpp | 7 +- lib/Serialization/ASTWriterDecl.cpp | 4 + test/Analysis/lambdas.cpp | 2 +- .../expr.prim/expr.prim.lambda/p11-1y.cpp | 57 ++--- .../expr/expr.prim/expr.prim.lambda/p23.cpp | 2 +- test/CodeGenCXX/cxx1y-init-captures.cpp | 98 ++++++++ test/PCH/cxx1y-init-captures.cpp | 28 +++ test/Parser/cxx0x-lambda-expressions.cpp | 10 +- 25 files changed, 316 insertions(+), 281 deletions(-) create mode 100644 test/CodeGenCXX/cxx1y-init-captures.cpp create mode 100644 test/PCH/cxx1y-init-captures.cpp diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 654209c7ee..c68416b5cd 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -707,12 +707,16 @@ private: /// \brief Whether this variable is (C++0x) constexpr. unsigned IsConstexpr : 1; + /// \brief Whether this variable is the implicit variable for a lambda + /// init-capture. + unsigned IsInitCapture : 1; + /// \brief Whether this local extern variable's previous declaration was /// declared in the same block scope. This controls whether we should merge /// the type of this declaration with its previous declaration. unsigned PreviousDeclInSameBlockScope : 1; }; - enum { NumVarDeclBits = 13 }; + enum { NumVarDeclBits = 14 }; friend class ASTDeclReader; friend class StmtIteratorBase; @@ -1132,6 +1136,10 @@ public: bool isConstexpr() const { return VarDeclBits.IsConstexpr; } void setConstexpr(bool IC) { VarDeclBits.IsConstexpr = IC; } + /// Whether this variable is the implicit variable for a lambda init-capture. + bool isInitCapture() const { return VarDeclBits.IsInitCapture; } + void setInitCapture(bool IC) { VarDeclBits.IsInitCapture = IC; } + /// Whether this local extern variable declaration's previous declaration /// was declared in the same block scope. Only correct in C++. bool isPreviousDeclInSameBlockScope() const { diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index 731d47f723..f59f232d8f 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -1388,9 +1388,6 @@ public: LambdaCaptureKind Kind, VarDecl *Var = 0, SourceLocation EllipsisLoc = SourceLocation()); - /// \brief Create a new init-capture. - Capture(FieldDecl *Field); - /// \brief Determine the kind of capture. LambdaCaptureKind getCaptureKind() const; @@ -1404,7 +1401,9 @@ public: } /// \brief Determine whether this is an init-capture. - bool isInitCapture() const { return getCaptureKind() == LCK_Init; } + bool isInitCapture() const { + return capturesVariable() && getCapturedVar()->isInitCapture(); + } /// \brief Retrieve the declaration of the local variable being /// captured. @@ -1416,16 +1415,6 @@ public: return cast(DeclAndBits.getPointer()); } - /// \brief Retrieve the field for an init-capture. - /// - /// This works only for an init-capture. To retrieve the FieldDecl for - /// a captured variable or for a capture of \c this, use - /// LambdaExpr::getLambdaClass and CXXRecordDecl::getCaptureFields. - FieldDecl *getInitCaptureField() const { - assert(getCaptureKind() == LCK_Init && "no field for non-init-capture"); - return cast(DeclAndBits.getPointer()); - } - /// \brief Determine whether this was an implicit capture (not /// written between the square brackets introducing the lambda). bool isImplicit() const { return DeclAndBits.getInt() & Capture_Implicit; } @@ -1573,16 +1562,6 @@ public: return capture_init_begin() + NumCaptures; } - /// \brief Retrieve the initializer for an init-capture. - Expr *getInitCaptureInit(capture_iterator Capture) { - assert(Capture >= explicit_capture_begin() && - Capture <= explicit_capture_end() && Capture->isInitCapture()); - return capture_init_begin()[Capture - capture_begin()]; - } - const Expr *getInitCaptureInit(capture_iterator Capture) const { - return const_cast(this)->getInitCaptureInit(Capture); - } - /// \brief Retrieve the set of index variables used in the capture /// initializer of an array captured by copy. /// diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index 8c047d3b7e..3400e1f323 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -825,7 +825,7 @@ template bool RecursiveASTVisitor::TraverseLambdaCapture( LambdaExpr *LE, const LambdaExpr::Capture *C) { if (C->isInitCapture()) - TRY_TO(TraverseStmt(LE->getInitCaptureInit(C))); + TRY_TO(TraverseDecl(C->getCapturedVar())); return true; } diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index f3456f45f4..3746252833 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -5074,7 +5074,7 @@ let CategoryName = "Lambda Issue" in { def err_init_capture_multiple_expressions : Error< "initializer for lambda capture %0 contains multiple expressions">; def err_init_capture_deduction_failure : Error< - "cannot deduce type for lambda capture %0 from initializer of type %1">; + "cannot deduce type for lambda capture %0 from initializer of type %2">; def err_init_capture_deduction_failure_from_init_list : Error< "cannot deduce type for lambda capture %0 from initializer list">; } diff --git a/include/clang/Basic/Lambda.h b/include/clang/Basic/Lambda.h index 29ecb3a167..280ae94fed 100644 --- a/include/clang/Basic/Lambda.h +++ b/include/clang/Basic/Lambda.h @@ -34,8 +34,7 @@ enum LambdaCaptureDefault { enum LambdaCaptureKind { LCK_This, ///< Capturing the \c this pointer LCK_ByCopy, ///< Capturing by copy (a.k.a., by value) - LCK_ByRef, ///< Capturing by reference - LCK_Init ///< C++1y "init-capture", value specified by an expression + LCK_ByRef ///< Capturing by reference }; } // end namespace clang diff --git a/include/clang/Sema/ScopeInfo.h b/include/clang/Sema/ScopeInfo.h index 4853f46f5f..4bfffae4a8 100644 --- a/include/clang/Sema/ScopeInfo.h +++ b/include/clang/Sema/ScopeInfo.h @@ -347,21 +347,17 @@ public: // variables of reference type are captured by reference, and other // variables are captured by copy. enum CaptureKind { - Cap_ByCopy, Cap_ByRef, Cap_Block, Cap_ThisOrInit + Cap_ByCopy, Cap_ByRef, Cap_Block, Cap_This }; - // The variable being captured (if we are not capturing 'this', and whether - // this is a nested capture; the expression is only required if we are - // capturing ByVal and the variable's type has a non-trivial copy - // constructor, or for an initialized capture. - typedef llvm::PointerIntPair VarAndNested; + /// The variable being captured (if we are not capturing 'this') and whether + /// this is a nested capture. + llvm::PointerIntPair VarAndNested; - // The variable being captured, or the implicitly-generated field for - // an init-capture. - llvm::PointerUnion VarOrField; - - // Expression to initialize a field of the given type, and the kind of - // capture (if this is a capture and not an init-capture). + /// Expression to initialize a field of the given type, and the kind of + /// capture (if this is a capture and not an init-capture). The expression + /// is only required if we are capturing ByVal and the variable's type has + /// a non-trivial copy constructor. llvm::PointerIntPair InitExprAndCaptureKind; /// \brief The source location at which the first capture occurred. @@ -378,7 +374,7 @@ public: Capture(VarDecl *Var, bool Block, bool ByRef, bool IsNested, SourceLocation Loc, SourceLocation EllipsisLoc, QualType CaptureType, Expr *Cpy) - : VarOrField(VarAndNested(Var, IsNested)), + : VarAndNested(Var, IsNested), InitExprAndCaptureKind(Cpy, Block ? Cap_Block : ByRef ? Cap_ByRef : Cap_ByCopy), Loc(Loc), EllipsisLoc(EllipsisLoc), CaptureType(CaptureType) {} @@ -386,23 +382,15 @@ public: enum IsThisCapture { ThisCapture }; Capture(IsThisCapture, bool IsNested, SourceLocation Loc, QualType CaptureType, Expr *Cpy) - : VarOrField(VarAndNested(0, IsNested)), - InitExprAndCaptureKind(Cpy, Cap_ThisOrInit), + : VarAndNested(0, IsNested), + InitExprAndCaptureKind(Cpy, Cap_This), Loc(Loc), EllipsisLoc(), CaptureType(CaptureType) {} - Capture(FieldDecl *Field, Expr *Init) - : VarOrField(Field), InitExprAndCaptureKind(Init, Cap_ThisOrInit), - Loc(), EllipsisLoc(), CaptureType() {} - bool isThisCapture() const { - return InitExprAndCaptureKind.getInt() == Cap_ThisOrInit && - VarOrField.is(); + return InitExprAndCaptureKind.getInt() == Cap_This; } bool isVariableCapture() const { - return InitExprAndCaptureKind.getInt() != Cap_ThisOrInit; - } - bool isInitCapture() const { - return VarOrField.is(); + return InitExprAndCaptureKind.getInt() != Cap_This; } bool isCopyCapture() const { return InitExprAndCaptureKind.getInt() == Cap_ByCopy; @@ -413,13 +401,10 @@ public: bool isBlockCapture() const { return InitExprAndCaptureKind.getInt() == Cap_Block; } - bool isNested() { return VarOrField.dyn_cast().getInt(); } + bool isNested() { return VarAndNested.getInt(); } VarDecl *getVariable() const { - return VarOrField.dyn_cast().getPointer(); - } - FieldDecl *getInitCaptureField() const { - return VarOrField.dyn_cast(); + return VarAndNested.getPointer(); } /// \brief Retrieve the location at which this variable was captured. @@ -473,10 +458,6 @@ public: void addThisCapture(bool isNested, SourceLocation Loc, QualType CaptureType, Expr *Cpy); - void addInitCapture(FieldDecl *Field, Expr *Init) { - Captures.push_back(Capture(Field, Init)); - } - /// \brief Determine whether the C++ 'this' is captured. bool isCXXThisCaptured() const { return CXXThisCaptureIndex != 0; } diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 6ac19ae5f7..66772c07ae 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -4458,10 +4458,13 @@ public: bool ExplicitResultType, bool Mutable); - /// \brief Check and build an init-capture with the specified name and - /// initializer. - FieldDecl *checkInitCapture(SourceLocation Loc, bool ByRef, - IdentifierInfo *Id, Expr *Init); + /// \brief Check an init-capture and build the implied variable declaration + /// with the specified name and initializer. + VarDecl *checkInitCapture(SourceLocation Loc, bool ByRef, + IdentifierInfo *Id, Expr *Init); + + /// \brief Build the implicit field for an init-capture. + FieldDecl *buildInitCaptureField(sema::LambdaScopeInfo *LSI, VarDecl *Var); /// \brief Note that we have finished the explicit captures for the /// given lambda. diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 09ab8c916a..33c7ff99b0 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -992,6 +992,7 @@ void CXXRecordDecl::getCaptureFields( else if (C->capturesVariable()) Captures[C->getCapturedVar()] = *Field; } + assert(Field == field_end()); } TemplateParameterList * diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index a13d6f79c8..069404aa7c 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -905,26 +905,15 @@ LambdaExpr::Capture::Capture(SourceLocation Loc, bool Implicit, case LCK_ByRef: assert(Var && "capture must have a variable!"); break; - - case LCK_Init: - llvm_unreachable("don't use this constructor for an init-capture"); } DeclAndBits.setInt(Bits); } -LambdaExpr::Capture::Capture(FieldDecl *Field) - : DeclAndBits(Field, - Field->getType()->isReferenceType() ? 0 : Capture_ByCopy), - Loc(Field->getLocation()), EllipsisLoc() {} - LambdaCaptureKind LambdaExpr::Capture::getCaptureKind() const { Decl *D = DeclAndBits.getPointer(); if (!D) return LCK_This; - if (isa(D)) - return LCK_Init; - return (DeclAndBits.getInt() & Capture_ByCopy) ? LCK_ByCopy : LCK_ByRef; } diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index c94ffab971..1bbd4667fc 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -1460,24 +1460,18 @@ void StmtPrinter::VisitLambdaExpr(LambdaExpr *Node) { break; case LCK_ByRef: - if (Node->getCaptureDefault() != LCD_ByRef) + if (Node->getCaptureDefault() != LCD_ByRef || C->isInitCapture()) OS << '&'; OS << C->getCapturedVar()->getName(); break; case LCK_ByCopy: - if (Node->getCaptureDefault() != LCD_ByCopy) - OS << '='; OS << C->getCapturedVar()->getName(); break; - - case LCK_Init: - if (C->getInitCaptureField()->getType()->isReferenceType()) - OS << '&'; - OS << C->getInitCaptureField()->getName(); - PrintExpr(Node->getInitCaptureInit(C)); - break; } + + if (C->isInitCapture()) + PrintExpr(C->getCapturedVar()->getInit()); } OS << ']'; diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp index 7612bef58a..35f37ddaa7 100644 --- a/lib/AST/StmtProfile.cpp +++ b/lib/AST/StmtProfile.cpp @@ -881,9 +881,6 @@ StmtProfiler::VisitLambdaExpr(const LambdaExpr *S) { VisitDecl(C->getCapturedVar()); ID.AddBoolean(C->isPackExpansion()); break; - case LCK_Init: - VisitDecl(C->getInitCaptureField()); - break; } } // Note: If we actually needed to be able to match lambda diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index ccfbeeb1a6..b15208bc3a 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -7971,14 +7971,17 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, // It isn't possible to write this directly, but it is possible to // end up in this situation with "auto x(some_pack...);" Diag(CXXDirectInit->getLocStart(), - diag::err_auto_var_init_no_expression) + VDecl->isInitCapture() ? diag::err_init_capture_no_expression + : diag::err_auto_var_init_no_expression) << VDecl->getDeclName() << VDecl->getType() << VDecl->getSourceRange(); RealDecl->setInvalidDecl(); return; } else if (CXXDirectInit->getNumExprs() > 1) { Diag(CXXDirectInit->getExpr(1)->getLocStart(), - diag::err_auto_var_init_multiple_expressions) + VDecl->isInitCapture() + ? diag::err_init_capture_multiple_expressions + : diag::err_auto_var_init_multiple_expressions) << VDecl->getDeclName() << VDecl->getType() << VDecl->getSourceRange(); RealDecl->setInvalidDecl(); diff --git a/lib/Sema/SemaLambda.cpp b/lib/Sema/SemaLambda.cpp index 569bfdfce2..08048d54d9 100644 --- a/lib/Sema/SemaLambda.cpp +++ b/lib/Sema/SemaLambda.cpp @@ -494,13 +494,12 @@ void Sema::deduceClosureReturnType(CapturingScopeInfo &CSI) { } } -FieldDecl *Sema::checkInitCapture(SourceLocation Loc, bool ByRef, - IdentifierInfo *Id, Expr *InitExpr) { - LambdaScopeInfo *LSI = getCurLambda(); - +VarDecl *Sema::checkInitCapture(SourceLocation Loc, bool ByRef, + IdentifierInfo *Id, Expr *Init) { // C++1y [expr.prim.lambda]p11: - // The type of [the] member corresponds to the type of a hypothetical - // variable declaration of the form "auto init-capture;" + // An init-capture behaves as if it declares and explicitly captures + // a variable of the form + // "auto init-capture;" QualType DeductType = Context.getAutoDeductType(); TypeLocBuilder TLB; TLB.pushTypeSpec(DeductType).setNameLoc(Loc); @@ -511,69 +510,38 @@ FieldDecl *Sema::checkInitCapture(SourceLocation Loc, bool ByRef, } TypeSourceInfo *TSI = TLB.getTypeSourceInfo(Context, DeductType); - InitializationKind InitKind = InitializationKind::CreateDefault(Loc); - Expr *Init = InitExpr; - if (ParenListExpr *Parens = dyn_cast(Init)) { - if (Parens->getNumExprs() == 1) { - Init = Parens->getExpr(0); - InitKind = InitializationKind::CreateDirect( - Loc, Parens->getLParenLoc(), Parens->getRParenLoc()); - } else { - // C++1y [dcl.spec.auto]p3: - // In an initializer of the form ( expression-list ), the - // expression-list shall be a single assignment-expression. - if (Parens->getNumExprs() == 0) - Diag(Parens->getLocStart(), diag::err_init_capture_no_expression) - << Id; - else if (Parens->getNumExprs() > 1) - Diag(Parens->getExpr(1)->getLocStart(), - diag::err_init_capture_multiple_expressions) - << Id; - return 0; - } - } else if (isa(Init)) - // We do not need to distinguish between direct-list-initialization - // and copy-list-initialization here, because we will always deduce - // std::initializer_list, and direct- and copy-list-initialization - // always behave the same for such a type. - // FIXME: We should model whether an '=' was present. - InitKind = InitializationKind::CreateDirectList(Loc); - else - InitKind = InitializationKind::CreateCopy(Loc, Loc); - QualType DeducedType; - if (DeduceAutoType(TSI, Init, DeducedType) == DAR_Failed) { - if (isa(Init)) - Diag(Loc, diag::err_init_capture_deduction_failure_from_init_list) - << Id << Init->getSourceRange(); - else - Diag(Loc, diag::err_init_capture_deduction_failure) - << Id << Init->getType() << Init->getSourceRange(); - } - if (DeducedType.isNull()) - return 0; - - // [...] a non-static data member named by the identifier is declared in - // the closure type. This member is not a bit-field and not mutable. - // Core issue: the member is (probably...) public. - FieldDecl *NewFD = CheckFieldDecl( - Id, DeducedType, TSI, LSI->Lambda, - Loc, /*Mutable*/ false, /*BitWidth*/ 0, ICIS_NoInit, - Loc, AS_public, /*PrevDecl*/ 0, /*Declarator*/ 0); - LSI->Lambda->addDecl(NewFD); - - if (CurContext->isDependentContext()) { - LSI->addInitCapture(NewFD, InitExpr); - } else { - InitializedEntity Entity = InitializedEntity::InitializeMember(NewFD); - InitializationSequence InitSeq(*this, Entity, InitKind, Init); - if (!InitSeq.Diagnose(*this, Entity, InitKind, Init)) { - ExprResult InitResult = InitSeq.Perform(*this, Entity, InitKind, Init); - if (!InitResult.isInvalid()) - LSI->addInitCapture(NewFD, InitResult.take()); - } - } + // Create a dummy variable representing the init-capture. This is not actually + // used as a variable, and only exists as a way to name and refer to the + // init-capture. + // FIXME: Pass in separate source locations for '&' and identifier. + VarDecl *NewVD = VarDecl::Create(Context, CurContext->getLexicalParent(), Loc, + Loc, Id, TSI->getType(), TSI, SC_Auto); + NewVD->setInitCapture(true); + NewVD->setReferenced(true); + NewVD->markUsed(Context); + + // We do not need to distinguish between direct-list-initialization + // and copy-list-initialization here, because we will always deduce + // std::initializer_list, and direct- and copy-list-initialization + // always behave the same for such a type. + // FIXME: We should model whether an '=' was present. + bool DirectInit = isa(Init) || isa(Init); + AddInitializerToDecl(NewVD, Init, DirectInit, /*ContainsAuto*/true); + return NewVD; +} - return NewFD; +FieldDecl *Sema::buildInitCaptureField(LambdaScopeInfo *LSI, VarDecl *Var) { + FieldDecl *Field = FieldDecl::Create( + Context, LSI->Lambda, Var->getLocation(), Var->getLocation(), + 0, Var->getType(), Var->getTypeSourceInfo(), 0, false, ICIS_NoInit); + Field->setImplicit(true); + Field->setAccess(AS_private); + LSI->Lambda->addDecl(Field); + + LSI->addCapture(Var, /*isBlock*/false, Var->getType()->isReferenceType(), + /*isNested*/false, Var->getLocation(), SourceLocation(), + Var->getType(), Var->getInit()); + return Field; } void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, @@ -732,62 +700,56 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, if (C->Init.isInvalid()) continue; - if (C->Init.isUsable()) { - // C++11 [expr.prim.lambda]p8: - // An identifier or this shall not appear more than once in a - // lambda-capture. - if (!CaptureNames.insert(C->Id)) - Diag(C->Loc, diag::err_capture_more_than_once) << C->Id; + VarDecl *Var; + if (C->Init.isUsable()) { if (C->Init.get()->containsUnexpandedParameterPack()) ContainsUnexpandedParameterPack = true; - FieldDecl *NewFD = checkInitCapture(C->Loc, C->Kind == LCK_ByRef, - C->Id, C->Init.take()); + Var = checkInitCapture(C->Loc, C->Kind == LCK_ByRef, + C->Id, C->Init.take()); // C++1y [expr.prim.lambda]p11: - // Within the lambda-expression's lambda-declarator and - // compound-statement, the identifier in the init-capture - // hides any declaration of the same name in scopes enclosing - // the lambda-expression. - if (NewFD) - PushOnScopeChains(NewFD, CurScope, false); - continue; - } - - // C++11 [expr.prim.lambda]p8: - // If a lambda-capture includes a capture-default that is &, the - // identifiers in the lambda-capture shall not be preceded by &. - // If a lambda-capture includes a capture-default that is =, [...] - // each identifier it contains shall be preceded by &. - if (C->Kind == LCK_ByRef && Intro.Default == LCD_ByRef) { - Diag(C->Loc, diag::err_reference_capture_with_reference_default) - << FixItHint::CreateRemoval( - SourceRange(PP.getLocForEndOfToken(PrevCaptureLoc), C->Loc)); - continue; - } else if (C->Kind == LCK_ByCopy && Intro.Default == LCD_ByCopy) { - Diag(C->Loc, diag::err_copy_capture_with_copy_default) - << FixItHint::CreateRemoval( - SourceRange(PP.getLocForEndOfToken(PrevCaptureLoc), C->Loc)); - continue; - } + // An init-capture behaves as if it declares and explicitly + // captures a variable [...] whose declarative region is the + // lambda-expression's compound-statement + if (Var) + PushOnScopeChains(Var, CurScope, false); + } else { + // C++11 [expr.prim.lambda]p8: + // If a lambda-capture includes a capture-default that is &, the + // identifiers in the lambda-capture shall not be preceded by &. + // If a lambda-capture includes a capture-default that is =, [...] + // each identifier it contains shall be preceded by &. + if (C->Kind == LCK_ByRef && Intro.Default == LCD_ByRef) { + Diag(C->Loc, diag::err_reference_capture_with_reference_default) + << FixItHint::CreateRemoval( + SourceRange(PP.getLocForEndOfToken(PrevCaptureLoc), C->Loc)); + continue; + } else if (C->Kind == LCK_ByCopy && Intro.Default == LCD_ByCopy) { + Diag(C->Loc, diag::err_copy_capture_with_copy_default) + << FixItHint::CreateRemoval( + SourceRange(PP.getLocForEndOfToken(PrevCaptureLoc), C->Loc)); + continue; + } - // C++11 [expr.prim.lambda]p10: - // The identifiers in a capture-list are looked up using the usual - // rules for unqualified name lookup (3.4.1) - DeclarationNameInfo Name(C->Id, C->Loc); - LookupResult R(*this, Name, LookupOrdinaryName); - LookupName(R, CurScope); - if (R.isAmbiguous()) - continue; - if (R.empty()) { - // FIXME: Disable corrections that would add qualification? - CXXScopeSpec ScopeSpec; - DeclFilterCCC Validator; - if (DiagnoseEmptyLookup(CurScope, ScopeSpec, R, Validator)) + // C++11 [expr.prim.lambda]p10: + // The identifiers in a capture-list are looked up using the usual + // rules for unqualified name lookup (3.4.1) + DeclarationNameInfo Name(C->Id, C->Loc); + LookupResult R(*this, Name, LookupOrdinaryName); + LookupName(R, CurScope); + if (R.isAmbiguous()) continue; - } + if (R.empty()) { + // FIXME: Disable corrections that would add qualification? + CXXScopeSpec ScopeSpec; + DeclFilterCCC Validator; + if (DiagnoseEmptyLookup(CurScope, ScopeSpec, R, Validator)) + continue; + } - VarDecl *Var = R.getAsSingle(); + Var = R.getAsSingle(); + } // C++11 [expr.prim.lambda]p8: // An identifier or this shall not appear more than once in a @@ -799,7 +761,8 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, << FixItHint::CreateRemoval( SourceRange(PP.getLocForEndOfToken(PrevCaptureLoc), C->Loc)); } else - // Previous capture was an init-capture: no fixit. + // Previous capture captured something different (one or both was + // an init-cpature): no fixit. Diag(C->Loc, diag::err_capture_more_than_once) << C->Id; continue; } @@ -838,10 +801,14 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, } else if (Var->isParameterPack()) { ContainsUnexpandedParameterPack = true; } - - TryCaptureKind Kind = C->Kind == LCK_ByRef ? TryCapture_ExplicitByRef : - TryCapture_ExplicitByVal; - tryCaptureVariable(Var, C->Loc, Kind, EllipsisLoc); + + if (C->Init.isUsable()) { + buildInitCaptureField(LSI, Var); + } else { + TryCaptureKind Kind = C->Kind == LCK_ByRef ? TryCapture_ExplicitByRef : + TryCapture_ExplicitByVal; + tryCaptureVariable(Var, C->Loc, Kind, EllipsisLoc); + } } finishLambdaExplicitCaptures(LSI); @@ -1042,12 +1009,6 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body, continue; } - if (From.isInitCapture()) { - Captures.push_back(LambdaExpr::Capture(From.getInitCaptureField())); - CaptureInits.push_back(From.getInitExpr()); - continue; - } - VarDecl *Var = From.getVariable(); LambdaCaptureKind Kind = From.isCopyCapture()? LCK_ByCopy : LCK_ByRef; Captures.push_back(LambdaExpr::Capture(From.getLocation(), IsImplicit, diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 5510bc2040..b71aafe868 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -3928,10 +3928,14 @@ TypeSourceInfo* Sema::SubstAutoTypeSourceInfo(TypeSourceInfo *TypeWithAuto, void Sema::DiagnoseAutoDeductionFailure(VarDecl *VDecl, Expr *Init) { if (isa(Init)) Diag(VDecl->getLocation(), - diag::err_auto_var_deduction_failure_from_init_list) + VDecl->isInitCapture() + ? diag::err_init_capture_deduction_failure_from_init_list + : diag::err_auto_var_deduction_failure_from_init_list) << VDecl->getDeclName() << VDecl->getType() << Init->getSourceRange(); else - Diag(VDecl->getLocation(), diag::err_auto_var_deduction_failure) + Diag(VDecl->getLocation(), + VDecl->isInitCapture() ? diag::err_init_capture_deduction_failure + : diag::err_auto_var_deduction_failure) << VDecl->getDeclName() << VDecl->getType() << Init->getType() << Init->getSourceRange(); } diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 8f4c009073..8d384b915b 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3369,6 +3369,7 @@ void Sema::BuildVariableInstantiation( NewVar->setInitStyle(OldVar->getInitStyle()); NewVar->setCXXForRangeDecl(OldVar->isCXXForRangeDecl()); NewVar->setConstexpr(OldVar->isConstexpr()); + NewVar->setInitCapture(OldVar->isInitCapture()); NewVar->setPreviousDeclInSameBlockScope( OldVar->isPreviousDeclInSameBlockScope()); NewVar->setAccess(OldVar->getAccess()); diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 6559dede1c..5b019a96c0 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -8313,7 +8313,9 @@ TreeTransform::TransformLambdaScope(LambdaExpr *E, if (!C->isInitCapture()) continue; InitCaptureExprs[C - E->capture_begin()] = - getDerived().TransformExpr(E->getInitCaptureInit(C)); + getDerived().TransformInitializer( + C->getCapturedVar()->getInit(), + C->getCapturedVar()->getInitStyle() == VarDecl::CallInit); } // Introduce the context of the call operator. @@ -8353,14 +8355,15 @@ TreeTransform::TransformLambdaScope(LambdaExpr *E, Invalid = true; continue; } - FieldDecl *OldFD = C->getInitCaptureField(); - FieldDecl *NewFD = getSema().checkInitCapture( - C->getLocation(), OldFD->getType()->isReferenceType(), - OldFD->getIdentifier(), Init.take()); - if (!NewFD) + VarDecl *OldVD = C->getCapturedVar(); + VarDecl *NewVD = getSema().checkInitCapture( + C->getLocation(), OldVD->getType()->isReferenceType(), + OldVD->getIdentifier(), Init.take()); + if (!NewVD) Invalid = true; else - getDerived().transformedLocalDecl(OldFD, NewFD); + getDerived().transformedLocalDecl(OldVD, NewVD); + getSema().buildInitCaptureField(LSI, NewVD); continue; } diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index eaa5c5500b..a5fb21cb66 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -945,6 +945,7 @@ ASTDeclReader::RedeclarableResult ASTDeclReader::VisitVarDeclImpl(VarDecl *VD) { VD->VarDeclBits.CXXForRangeDecl = Record[Idx++]; VD->VarDeclBits.ARCPseudoStrong = Record[Idx++]; VD->VarDeclBits.IsConstexpr = Record[Idx++]; + VD->VarDeclBits.IsInitCapture = Record[Idx++]; VD->VarDeclBits.PreviousDeclInSameBlockScope = Record[Idx++]; Linkage VarLinkage = Linkage(Record[Idx++]); VD->setCachedLinkage(VarLinkage); @@ -1223,17 +1224,12 @@ void ASTDeclReader::ReadCXXDefinitionData( *ToCapture++ = Capture(Loc, IsImplicit, Kind, 0, SourceLocation()); break; case LCK_ByCopy: - case LCK_ByRef: { + case LCK_ByRef: VarDecl *Var = ReadDeclAs(Record, Idx); SourceLocation EllipsisLoc = ReadSourceLocation(Record, Idx); *ToCapture++ = Capture(Loc, IsImplicit, Kind, Var, EllipsisLoc); break; } - case LCK_Init: - FieldDecl *Field = ReadDeclAs(Record, Idx); - *ToCapture++ = Capture(Field); - break; - } } } } diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 1826ad8312..d23267059c 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -5144,7 +5144,7 @@ void ASTWriter::AddCXXDefinitionData(const CXXRecordDecl *D, RecordDataImpl &Rec case LCK_This: break; case LCK_ByCopy: - case LCK_ByRef: { + case LCK_ByRef: VarDecl *Var = Capture.capturesVariable() ? Capture.getCapturedVar() : 0; AddDeclRef(Var, Record); @@ -5153,11 +5153,6 @@ void ASTWriter::AddCXXDefinitionData(const CXXRecordDecl *D, RecordDataImpl &Rec Record); break; } - case LCK_Init: - FieldDecl *Field = Capture.getInitCaptureField(); - AddDeclRef(Field, Record); - break; - } } } } diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 252569b2ca..5931af3926 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -705,6 +705,7 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) { Record.push_back(D->isCXXForRangeDecl()); Record.push_back(D->isARCPseudoStrong()); Record.push_back(D->isConstexpr()); + Record.push_back(D->isInitCapture()); Record.push_back(D->isPreviousDeclInSameBlockScope()); Record.push_back(D->getLinkageInternal()); @@ -747,6 +748,7 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) { !isa(D) && !isa(D) && !D->isConstexpr() && + !D->isInitCapture() && !D->isPreviousDeclInSameBlockScope() && !D->getMemberSpecializationInfo()) AbbrevToUse = Writer.getDeclVarAbbrev(); @@ -1633,6 +1635,7 @@ void ASTWriter::WriteDeclsBlockAbbrevs() { Abv->Add(BitCodeAbbrevOp(0)); // isCXXForRangeDecl Abv->Add(BitCodeAbbrevOp(0)); // isARCPseudoStrong Abv->Add(BitCodeAbbrevOp(0)); // isConstexpr + Abv->Add(BitCodeAbbrevOp(0)); // isInitCapture Abv->Add(BitCodeAbbrevOp(0)); // isPrevDeclInSameScope Abv->Add(BitCodeAbbrevOp(0)); // Linkage Abv->Add(BitCodeAbbrevOp(0)); // HasInit @@ -1713,6 +1716,7 @@ void ASTWriter::WriteDeclsBlockAbbrevs() { Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isCXXForRangeDecl Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isARCPseudoStrong Abv->Add(BitCodeAbbrevOp(0)); // isConstexpr + Abv->Add(BitCodeAbbrevOp(0)); // isInitCapture Abv->Add(BitCodeAbbrevOp(0)); // isPrevDeclInSameScope Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Linkage Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // HasInit diff --git a/test/Analysis/lambdas.cpp b/test/Analysis/lambdas.cpp index 77b36c42cf..33e216b57e 100644 --- a/test/Analysis/lambdas.cpp +++ b/test/Analysis/lambdas.cpp @@ -10,7 +10,7 @@ void f(X x) { (void) [x]{}; } // CHECK: 1: x // CHECK: 2: [B1.1] (ImplicitCastExpr, NoOp, const struct X) // CHECK: 3: [B1.2] (CXXConstructExpr, struct X) -// CHECK: 4: [=x] { +// CHECK: 4: [x] { // CHECK: } // CHECK: 5: (void)[B1.4] (CStyleCastExpr, ToVoid, void) // CHECK: Preds (1): B2 diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/p11-1y.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/p11-1y.cpp index be39ded6ff..6be200dd54 100644 --- a/test/CXX/expr/expr.prim/expr.prim.lambda/p11-1y.cpp +++ b/test/CXX/expr/expr.prim/expr.prim.lambda/p11-1y.cpp @@ -1,34 +1,26 @@ // RUN: %clang_cc1 -std=c++1y %s -verify -// For every init-capture a non-static data member named by the identifier of -// the init-capture is declared in the closure type. -const char *has_member_x = [x("hello")] {}.x; -// This member is not a bit-field... -auto capturing_lambda = [n(0)] {}; -int decltype(capturing_lambda)::*mem_ptr = &decltype(capturing_lambda)::n; -// ... and not mutable. -const auto capturing_lambda_copy = capturing_lambda; -int &n = capturing_lambda_copy.n; // expected-error {{drops qualifiers}} +const char *has_no_member = [x("hello")] {}.x; // expected-error {{no member named 'x'}} -// The type of that member [...is that of a...] variable declaration of the form -// "auto init-capture ;"... -auto with_float = [f(1.0f)] {}; -float &f = with_float.f; -// ... except that the variable name is replaced by a unique identifier. -auto with_float_2 = [&f(f)] {}; // ok, refers to outer f -float &f2 = with_float_2.f; +double f; +auto with_float = [f(1.0f)] { + using T = decltype(f); + using T = float; +}; +auto with_float_2 = [&f(f)] { // ok, refers to outer f + using T = decltype(f); + using T = double&; +}; -// Within the lambda-expression's lambda-declarator (FIXME) and -// compound-statement, the identifier in the init-capture hides any declaration +// Within the lambda-expression's compound-statement, +// the identifier in the init-capture hides any declaration // of the same name in scopes enclosing the lambda-expression. void hiding() { char c; (void) [c("foo")] { static_assert(sizeof(c) == sizeof(const char*), ""); }; - (void) [c("bar")] () -> decltype(c) { - // FIXME: the 'c' in the return type should be the init-capture, not the - // outer c. + (void) [c("bar")] () -> decltype(c) { // outer c, not init-capture return "baz"; // expected-error {{cannot initialize}} }; } @@ -54,22 +46,16 @@ int overload_fn(int); auto bad_init_1 = [a()] {}; // expected-error {{expected expression}} auto bad_init_2 = [a(1, 2)] {}; // expected-error {{initializer for lambda capture 'a' contains multiple expressions}} auto bad_init_3 = [&a(void_fn())] {}; // expected-error {{cannot form a reference to 'void'}} -auto bad_init_4 = [a(void_fn())] {}; // expected-error {{field has incomplete type 'void'}} +auto bad_init_4 = [a(void_fn())] {}; // expected-error {{has incomplete type 'void'}} auto bad_init_5 = [a(overload_fn)] {}; // expected-error {{cannot deduce type for lambda capture 'a' from initializer of type ' void pack_1(T...t) { [a(t...)] {}; } // expected-error {{initializer missing for lambda capture 'a'}} +template void pack_1(T...t) { (void)[a(t...)] {}; } // expected-error {{initializer missing for lambda capture 'a'}} template void pack_1<>(); // expected-note {{instantiation of}} -auto multi_return(int a, int b) { - return [n(a + 2*b), m(a - 2*b)] {}; -} -auto use_multi_return() { - auto nm = multi_return(5, 9); - return nm.n + nm.m; -} - -auto a = [a(4), b = 5, &c = static_cast(0)] { // expected-warning {{binding reference member 'c' to a temporary value}} expected-note {{here}} +// FIXME: Might need lifetime extension for the temporary here. +// See DR1695. +auto a = [a(4), b = 5, &c = static_cast(0)] { static_assert(sizeof(a) == sizeof(int), ""); static_assert(sizeof(b) == sizeof(int), ""); using T = decltype(c); @@ -82,3 +68,10 @@ template struct remove_reference { typedef T type; }; template struct remove_reference { typedef T type; }; template decltype(auto) move(T &&t) { return static_cast::type&&>(t); } auto s = [s(move(S()))] {}; + +template T instantiate_test(T t) { + [x(&t)]() { *x = 1; } (); // expected-error {{assigning to 'const char *'}} + return t; +} +int instantiate_test_1 = instantiate_test(0); +const char *instantiate_test_2 = instantiate_test("foo"); // expected-note {{here}} diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp index 174ae6d5b0..083ca1bdd3 100644 --- a/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp +++ b/test/CXX/expr/expr.prim/expr.prim.lambda/p23.cpp @@ -66,7 +66,7 @@ void init_capture_pack_err(Args ...args) { template void init_capture_pack_multi(Args ...args) { - [as(args...)] {} (); // expected-error {{initializer missing}} expected-error {{multiple}} + [as(args...)] {} (); // expected-error {{initializer missing for lambda capture 'as'}} expected-error {{multiple}} } template void init_capture_pack_multi(); // expected-note {{instantiation}} template void init_capture_pack_multi(int); diff --git a/test/CodeGenCXX/cxx1y-init-captures.cpp b/test/CodeGenCXX/cxx1y-init-captures.cpp new file mode 100644 index 0000000000..6258cda3b0 --- /dev/null +++ b/test/CodeGenCXX/cxx1y-init-captures.cpp @@ -0,0 +1,98 @@ +// RUN: %clang_cc1 -std=c++1y -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s + +struct S { + S(); + S(S &&); + ~S(); +}; + +void f() { + (void) [s(S{})] {}; +} + +// CHECK-LABEL: define void @_Z1fv( +// CHECK: call void @_ZN1SC1Ev( +// CHECK: call void @"_ZZ1fvEN3$_0D1Ev"( + +// CHECK-LABEL: define internal void @"_ZZ1fvEN3$_0D1Ev"( +// CHECK: @"_ZZ1fvEN3$_0D2Ev"( + +// D2 at end of file. + +void g() { + [a(1), b(2)] { return a + b; } (); +} + +// CHECK-LABEL: define void @_Z1gv( +// CHECK: getelementptr inbounds {{.*}}, i32 0, i32 0 +// CHECK: store i32 1, i32* +// CHECK: getelementptr inbounds {{.*}}, i32 0, i32 1 +// CHECK: store i32 2, i32* +// CHECK: call i32 @"_ZZ1gvENK3$_1clEv"( + +// CHECK-LABEL: define internal i32 @"_ZZ1gvENK3$_1clEv"( +// CHECK: getelementptr inbounds {{.*}}, i32 0, i32 0 +// CHECK: load i32* +// CHECK: getelementptr inbounds {{.*}}, i32 0, i32 1 +// CHECK: load i32* +// CHECK: add nsw i32 + +int h(int a) { + // CHECK-LABEL: define i32 @_Z1hi( + // CHECK: %[[A_ADDR:.*]] = alloca i32, + // CHECK: %[[OUTER:.*]] = alloca + // CHECK: store i32 {{.*}}, i32* %[[A_ADDR]], + // + // Initialize init-capture 'b(a)' by reference. + // CHECK: getelementptr inbounds {{.*}}* %[[OUTER]], i32 0, i32 0 + // CHECK: store i32* %[[A_ADDR]], i32** {{.*}}, + // + // Initialize init-capture 'c(a)' by copy. + // CHECK: getelementptr inbounds {{.*}}* %[[OUTER]], i32 0, i32 1 + // CHECK: load i32* %[[A_ADDR]], + // CHECK: store i32 + // + // CHECK: call i32 @"_ZZ1hiENK3$_2clEv"({{.*}}* %[[OUTER]]) + return [&b(a), c(a)] { + // CHECK-LABEL: define internal i32 @"_ZZ1hiENK3$_2clEv"( + // CHECK: %[[OUTER_ADDR:.*]] = alloca + // CHECK: %[[INNER:.*]] = alloca + // CHECK: store {{.*}}, {{.*}}** %[[OUTER_ADDR]], + // + // Capture outer 'c' by reference. + // CHECK: %[[OUTER:.*]] = load {{.*}}** %[[OUTER_ADDR]] + // CHECK: getelementptr inbounds {{.*}}* %[[INNER]], i32 0, i32 0 + // CHECK-NEXT: getelementptr inbounds {{.*}}* %[[OUTER]], i32 0, i32 1 + // CHECK-NEXT: store i32* % + // + // Capture outer 'b' by copy. + // CHECK: getelementptr inbounds {{.*}}* %[[INNER]], i32 0, i32 1 + // CHECK-NEXT: getelementptr inbounds {{.*}}* %[[OUTER]], i32 0, i32 0 + // CHECK-NEXT: load i32** % + // CHECK-NEXT: load i32* % + // CHECK-NEXT: store i32 + // + // CHECK: call i32 @"_ZZZ1hiENK3$_2clEvENKUlvE_clEv"({{.*}}* %[[INNER]]) + return [=, &c] { + // CHECK-LABEL: define internal i32 @"_ZZZ1hiENK3$_2clEvENKUlvE_clEv"( + // CHECK: %[[INNER_ADDR:.*]] = alloca + // CHECK: store {{.*}}, {{.*}}** %[[INNER_ADDR]], + // CHECK: %[[INNER:.*]] = load {{.*}}** %[[INNER_ADDR]] + // + // Load capture of 'b' + // CHECK: getelementptr inbounds {{.*}}* %[[INNER]], i32 0, i32 1 + // CHECK: load i32* % + // + // Load capture of 'c' + // CHECK: getelementptr inbounds {{.*}}* %[[INNER]], i32 0, i32 0 + // CHECK: load i32** % + // CHECK: load i32* % + // + // CHECK: add nsw i32 + return b + c; + } (); + } (); +} + +// CHECK-LABEL: define internal void @"_ZZ1fvEN3$_0D2Ev"( +// CHECK: call void @_ZN1SD1Ev( diff --git a/test/PCH/cxx1y-init-captures.cpp b/test/PCH/cxx1y-init-captures.cpp new file mode 100644 index 0000000000..3c8fc149d8 --- /dev/null +++ b/test/PCH/cxx1y-init-captures.cpp @@ -0,0 +1,28 @@ +// No PCH: +// RUN: %clang_cc1 -pedantic -std=c++1y -include %s -verify %s +// +// With PCH: +// RUN: %clang_cc1 -pedantic -std=c++1y -emit-pch %s -o %t +// RUN: %clang_cc1 -pedantic -std=c++1y -include-pch %t -verify %s + +#ifndef HEADER +#define HEADER + +auto counter = [a(0)] () mutable { return a++; }; +int x = counter(); + +template void f(T t) { + [t(t)] { int n = t; } (); +} + +#else + +int y = counter(); + +void g() { + f(0); // ok + // expected-error@15 {{lvalue of type 'const char *const'}} + f("foo"); // expected-note {{here}} +} + +#endif diff --git a/test/Parser/cxx0x-lambda-expressions.cpp b/test/Parser/cxx0x-lambda-expressions.cpp index 76c1e0e7ce..426e530251 100644 --- a/test/Parser/cxx0x-lambda-expressions.cpp +++ b/test/Parser/cxx0x-lambda-expressions.cpp @@ -52,18 +52,16 @@ class C { // We support init-captures in C++11 as an extension. int z; void init_capture() { - // FIXME: These diagnostics should all disappear once semantic analysis - // for init-captures is complete. - [n(0)] () -> int { return ++n; }; // expected-error {{non-static data member}} + [n(0)] () mutable -> int { return ++n; }; [n{0}] { return; }; // expected-error {{}} - [n = 0] { return ++n; }; // expected-error {{non-static data member}} + [n = 0] { return ++n; }; // expected-error {{captured by copy in a non-mutable}} [n = {0}] { return; }; // expected-error {{}} [a([&b = z]{})](){}; int x = 4; auto y = [&r = x, x = x + 1]() -> int { - r += 2; // expected-error {{non-static data member}} - return x + 2; // expected-error {{non-static data member}} + r += 2; + return x + 2; } (); } }; -- 2.40.0