Hi rsmith, doug.gregor,
Currently clang does not seem to get capturing of init-captures correctly.
Consider the following code:
void test() {
const int x = 10;
const int y = 12;
auto L = [&k = x, j = x](int a) {
return [&l = x, m = y] (int b) { };
};
}
The outer lambda needs to capture 'x' but not 'y'. And the inner lambda should
not capture either x or y.
This patch fixes this by:
- moving up the checking of the init capture before its lambda call
operator's declcontext, scope, scopeinfo are pushed on (so that
lvalue-to-rvalue conversions can be checked for const variables in the
init-expr)
- using the declcontext of the enclosing scope temporarily when the
init-variable is created, and then fixing it once its call-operator has been
created. (this does require fixing the linkage computation of init-vars to
return none, so that it isn't perceived as ever being declared at namespace
scope).
Tests added - and regressions tests all seem to pass.
Certain aspects of this patch might emanate a bad smell - lets see if anyone
else feels the same ;)
Thanks!
http://llvm-reviews.chandlerc.com/D2092
Files:
include/clang/AST/Decl.h
include/clang/Sema/DeclSpec.h
include/clang/Sema/Sema.h
lib/AST/Decl.cpp
lib/Parse/ParseExprCXX.cpp
lib/Sema/Sema.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaLambda.cpp
lib/Sema/SemaTemplateInstantiate.cpp
lib/Sema/SemaTemplateVariadic.cpp
lib/Sema/TreeTransform.h
test/SemaCXX/cxx1y-init-captures.cpp
Index: include/clang/AST/Decl.h
===================================================================
--- include/clang/AST/Decl.h
+++ include/clang/AST/Decl.h
@@ -884,8 +884,11 @@
bool isLocalVarDecl() const {
if (getKind() != Decl::Var)
return false;
+ // Lambda init-captures behave as if declared within the body of lambdas.
+ if (isInitCapture()) return true;
if (const DeclContext *DC = getLexicalDeclContext())
return DC->getRedeclContext()->isFunctionOrMethod();
+
return false;
}
@@ -909,7 +912,8 @@
/// \endcode
bool isStaticDataMember() const {
// If it wasn't static, it would be a FieldDecl.
- return getKind() != Decl::ParmVar && getDeclContext()->isRecord();
+ return getKind() != Decl::ParmVar && !isInitCapture() &&
+ getDeclContext()->isRecord();
}
virtual VarDecl *getCanonicalDecl();
Index: include/clang/Sema/DeclSpec.h
===================================================================
--- include/clang/Sema/DeclSpec.h
+++ include/clang/Sema/DeclSpec.h
@@ -50,6 +50,7 @@
class Sema;
class Declarator;
struct TemplateIdAnnotation;
+ class VarDecl;
/// \brief Represents a C++ nested-name-specifier or a global scope specifier.
///
@@ -2143,12 +2144,13 @@
IdentifierInfo *Id;
SourceLocation EllipsisLoc;
ExprResult Init;
-
+ VarDecl *InitVar;
LambdaCapture(LambdaCaptureKind Kind, SourceLocation Loc,
IdentifierInfo* Id = 0,
SourceLocation EllipsisLoc = SourceLocation(),
ExprResult Init = ExprResult())
- : Kind(Kind), Loc(Loc), Id(Id), EllipsisLoc(EllipsisLoc), Init(Init)
+ : Kind(Kind), Loc(Loc), Id(Id), EllipsisLoc(EllipsisLoc), Init(Init),
+ InitVar(0)
{}
};
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -281,8 +281,36 @@
/// element type here is ExprWithCleanups::Object.
SmallVector<BlockDecl*, 8> ExprCleanupObjects;
+ /// \brief When non-zero, this represents a lambda's init-capture
+ /// being processed after initial parsing, or during template
+ /// transformation. This is set in Sema::checkInitCapture and
+ /// updated in Sema::AddInitializerToDecl. It is used mainly
+ /// to inform SemaTemplateVariadic that we are in a lambda and
+ /// its ok to allow an unexpanded parameter pack here.
+ const Expr *CurLambdaInitCapture;
+
+ /// \brief Store a list of either DeclRefExprs or MemberExprs
+ /// that contain a reference to a variable (constant) that may or may not
+ /// be odr-used in this Expr, and we won't know until all lvalue-to-rvalue
+ /// and discarded value conversions have been applied to all subexpressions
+ /// of the enclosing full expression. This is cleared at the end of each
+ /// full expression.
llvm::SmallPtrSet<Expr*, 2> MaybeODRUseExprs;
+
+ typedef SmallVector<llvm::SmallPtrSet<Expr*, 2>, 4> MaybeODRUseExprsStackTy;
+
+ /// \brief A stack to push and pop off the current MaybeODRUseExprs set.
+ MaybeODRUseExprsStackTy MaybeODRUseExprsStack;
+ void SaveMaybeODRUseExprs() {
+ MaybeODRUseExprsStack.push_back(MaybeODRUseExprsStackTy::value_type());
+ MaybeODRUseExprs.swap(MaybeODRUseExprsStack.back());
+ }
+ void RestoreMaybeODRUseExprs() {
+ MaybeODRUseExprs.swap(MaybeODRUseExprsStack.back());
+ MaybeODRUseExprsStack.pop_back();
+ }
+
/// \brief Stack containing information about each of the nested
/// function, block, and method scopes that are currently active.
///
Index: lib/AST/Decl.cpp
===================================================================
--- lib/AST/Decl.cpp
+++ lib/AST/Decl.cpp
@@ -1141,7 +1141,18 @@
case Decl::ObjCPropertyImpl:
case Decl::ObjCProtocol:
return LinkageInfo::external();
-
+ case Decl::Var:
+ // Lambda init-capture dummy VarDecls behave as if the variable
+ // was declared locally in its lambda's call-operator's body.
+ // When they are initially created, their corresponding call-operator
+ // has not been. So the DeclContext is not always correct, and may
+ // temporarily incorrectly refer to the enclosing DeclContext - which
+ // can result in incorrect linkage computations for lambdas with
+ // init-captures at namespace scope. So nip this in the bud and always
+ // return no linkage.
+ if (cast<VarDecl>(D)->isInitCapture())
+ return LinkageInfo::none();
+ break;
case Decl::CXXRecord: {
const CXXRecordDecl *Record = cast<CXXRecordDecl>(D);
if (Record->isLambda()) {
Index: lib/Parse/ParseExprCXX.cpp
===================================================================
--- lib/Parse/ParseExprCXX.cpp
+++ lib/Parse/ParseExprCXX.cpp
@@ -590,7 +590,71 @@
return Actions.ActOnIdExpression(getCurScope(), SS, TemplateKWLoc, Name,
Tok.is(tok::l_paren), isAddressOfOperand);
}
+namespace {
+// For lambda init-captures such as the following:
+// const int x = 10;
+// auto L = [i = x+1](int a) {
+// return [j = x+2,
+// &k = x](char b) { };
+// };
+// keep in mind that each lambda init-capture has to have:
+// - its initialization expression executed in the context
+// of the enclosing/parent decl-context.
+// - but the variable itself has to be 'injected' into the
+// decl-context of its lambda's call-operator (which has
+// not yet been created).
+// So the init-variables (i, j, k) above are created temporarily
+// in their enclosing DeclContext.
+// Each init-expression is a full-expression that has to get
+// Sema-analyzed (for capturing etc.) before its lambda's
+// call-operator's decl-context, scope & scopeinfo are pushed on their
+// respective stacks. Thus if any variable is odr-used in the init-capture
+// it will correctly get captured in the enclosing lambda, if one exists.
+//
+struct InitCaptureCheckerAndBuilder {
+ LambdaIntroducer &Intro;
+ Sema &Actions;
+ InitCaptureCheckerAndBuilder(LambdaIntroducer &Intro, Sema &S) :
+ Intro(Intro), Actions(S) {
+ // Each lambda init-capture forms its own full expression, which clears
+ // MaybeODRUseExprs. So save it on the stack, and avoid clobbering by a
+ // premature 'clear'.
+ Actions.SaveMaybeODRUseExprs();
+ }
+ ~InitCaptureCheckerAndBuilder() {
+ // The lambda init-capture's initializer expression occurs in the context of
+ // the enclosing function or lambda. Therefore we can not wait till a lambda
+ // scope has been pushed on before deciding whether the variable needs to be
+ // captured. We also need to process all lvalue-to-rvalue conversions and
+ // discarded-value conversions, so that we can avoid capturing certain
+ // constant variables.
+ // For e.g.,
+ // void test() {
+ // const int x = 10;
+ // auto L = [&z = x](char a) { <-- don't capture by the current lambda
+ // return [y = x](int i) { <-- don't capture by enclosing lambda
+ // return y;
+ // }
+ // };
+ // If x was not const, the second use would require 'L' to capture, and
+ // that would be an error.
+ for (SmallVectorImpl<LambdaCapture>::iterator
+ C = Intro.Captures.begin(),
+ E = Intro.Captures.end();
+ C != E; ++C) {
+ if (C->Init.isUsable()) {
+ // This also performs any lvalue-to-rvalue conversions if necessary.
+ VarDecl *Var = Actions.checkInitCapture(C->Loc, C->Kind == LCK_ByRef,
+ C->Id, C->Init.take());
+ C->InitVar = Var;
+ }
+
+ }
+ Actions.RestoreMaybeODRUseExprs();
+ }
+};
+}
/// ParseLambdaExpression - Parse a C++11 lambda expression.
///
/// lambda-expression:
@@ -633,8 +697,13 @@
ExprResult Parser::ParseLambdaExpression() {
// Parse lambda-introducer.
LambdaIntroducer Intro;
-
- Optional<unsigned> DiagID(ParseLambdaIntroducer(Intro));
+ Optional<unsigned> DiagID;
+ // Use a new block to ensure that ~ICAB gets called
+ // appropriately.
+ {
+ InitCaptureCheckerAndBuilder ICAB(Intro, Actions);
+ DiagID = ParseLambdaIntroducer(Intro);
+ }
if (DiagID) {
Diag(Tok, DiagID.getValue());
SkipUntil(tok::r_square);
@@ -674,7 +743,7 @@
if (Next.is(tok::identifier) && After.is(tok::identifier)) {
return ExprEmpty();
}
-
+
// Here, we're stuck: lambda introducers and Objective-C message sends are
// unambiguous, but it requires arbitrary lookhead. [a,b,c,d,e,f,g] is a
// lambda, and [a,b,c,d,e,f,g h] is a Objective-C message send. Instead of
@@ -682,8 +751,11 @@
// a lambda introducer first, and fall back if that fails.
// (TryParseLambdaIntroducer never produces any diagnostic output.)
LambdaIntroducer Intro;
- if (TryParseLambdaIntroducer(Intro))
- return ExprEmpty();
+ {
+ InitCaptureCheckerAndBuilder ICAB(Intro, Actions);
+ if (TryParseLambdaIntroducer(Intro))
+ return ExprEmpty();
+ }
return ParseLambdaExpressionAfterIntroducer(Intro);
}
Index: lib/Sema/Sema.cpp
===================================================================
--- lib/Sema/Sema.cpp
+++ lib/Sema/Sema.cpp
@@ -78,7 +78,8 @@
CurContext(0), OriginalLexicalContext(0),
PackContext(0), MSStructPragmaOn(false), VisContext(0),
IsBuildingRecoveryCallExpr(false),
- ExprNeedsCleanups(false), LateTemplateParser(0), OpaqueParser(0),
+ ExprNeedsCleanups(false), CurLambdaInitCapture(0),
+ LateTemplateParser(0), OpaqueParser(0),
IdResolver(pp), StdInitializerList(0), CXXTypeInfoDecl(0), MSVCGuidDecl(0),
NSNumberDecl(0),
NSStringDecl(0), StringWithUTF8StringMethod(0),
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -7996,11 +7996,31 @@
}
}
+// Intercept assignment of the InitExpr so that we can update
+// CurLambdaInitCapture at the same time.
+class AssignmentInterceptorProxyForInitExpr {
+ Expr *&Init;
+ Sema &S;
+ const bool IsInitCapture;
+public:
+ AssignmentInterceptorProxyForInitExpr(Expr *&Init, Sema &S)
+ : Init(Init), S(S), IsInitCapture(Init == S.CurLambdaInitCapture)
+ { }
+ void operator=(Expr *E) {
+ Init = E;
+ if (IsInitCapture)
+ S.CurLambdaInitCapture = E;
+ }
+ operator Expr*() const { return Init; }
+ Expr* operator->() const { return Init; }
+ Expr*& get() { return Init; }
+};
/// AddInitializerToDecl - Adds the initializer Init to the
/// declaration dcl. If DirectInit is true, this is C++ direct
/// initialization rather than copy initialization.
-void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
+void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *InitExpr,
bool DirectInit, bool TypeMayContainAuto) {
+ AssignmentInterceptorProxyForInitExpr Init(InitExpr, *this);
// If there is no declaration, there was an error parsing it. Just ignore
// the initializer.
if (RealDecl == 0 || RealDecl->isInvalidDecl())
@@ -8011,7 +8031,7 @@
// distinguish between a normal initializer and a pure-specifier.
// Thus this grotesque test.
IntegerLiteral *IL;
- if ((IL = dyn_cast<IntegerLiteral>(Init)) && IL->getValue() == 0 &&
+ if ((IL = dyn_cast<IntegerLiteral>(Init.get())) && IL->getValue() == 0 &&
Context.getCanonicalType(IL->getType()) == Context.IntTy)
CheckPureMethod(Method, Init->getSourceRange());
else {
@@ -8029,7 +8049,7 @@
RealDecl->setInvalidDecl();
return;
}
- ParenListExpr *CXXDirectInit = dyn_cast<ParenListExpr>(Init);
+ ParenListExpr *CXXDirectInit = dyn_cast<ParenListExpr>(Init.get());
// C++11 [decl.spec.auto]p6. Deduce the type which 'auto' stands in for.
if (TypeMayContainAuto && VDecl->getType()->isUndeducedType()) {
@@ -8218,7 +8238,7 @@
: InitializationKind::CreateCopy(VDecl->getLocation(),
Init->getLocStart());
- MultiExprArg Args = Init;
+ MultiExprArg Args = Init.get();
if (CXXDirectInit)
Args = MultiExprArg(CXXDirectInit->getExprs(),
CXXDirectInit->getNumExprs());
@@ -8305,7 +8325,7 @@
// for an object that has aggregate or union type shall be
// constant expressions.
else if (!getLangOpts().C99 && VDecl->getType()->isAggregateType() &&
- isa<InitListExpr>(Init) &&
+ isa<InitListExpr>(Init.get()) &&
!Init->isConstantInitializer(Context, false))
Diag(Init->getExprLoc(),
diag::ext_aggregate_init_not_constant)
@@ -8744,7 +8764,7 @@
void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
if (var->isInvalidDecl()) return;
-
+
// In ARC, don't allow jumps past the implicit initialization of a
// local retaining variable.
if (getLangOpts().ObjCAutoRefCount &&
Index: lib/Sema/SemaLambda.cpp
===================================================================
--- lib/Sema/SemaLambda.cpp
+++ lib/Sema/SemaLambda.cpp
@@ -508,6 +508,8 @@
// An init-capture behaves as if it declares and explicitly captures
// a variable of the form
// "auto init-capture;"
+ const Expr *const PreviousLambdaInitCapture = CurLambdaInitCapture;
+ CurLambdaInitCapture = Init;
QualType DeductType = Context.getAutoDeductType();
TypeLocBuilder TLB;
TLB.pushTypeSpec(DeductType).setNameLoc(Loc);
@@ -522,7 +524,7 @@
// 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, Loc,
+ VarDecl *NewVD = VarDecl::Create(Context, CurContext /*CurContext*/, Loc,
Loc, Id, TSI->getType(), TSI, SC_Auto);
NewVD->setInitCapture(true);
NewVD->setReferenced(true);
@@ -535,6 +537,7 @@
// FIXME: We should model whether an '=' was present.
bool DirectInit = isa<ParenListExpr>(Init) || isa<InitListExpr>(Init);
AddInitializerToDecl(NewVD, Init, DirectInit, /*ContainsAuto*/true);
+ CurLambdaInitCapture = PreviousLambdaInitCapture;
return NewVD;
}
@@ -718,14 +721,16 @@
if (C->Init.get()->containsUnexpandedParameterPack())
ContainsUnexpandedParameterPack = true;
- Var = checkInitCapture(C->Loc, C->Kind == LCK_ByRef,
- C->Id, C->Init.take());
+ Var = C->InitVar;
// C++1y [expr.prim.lambda]p11:
// 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)
+
+ if (Var) {
+ Var->setDeclContext(Method);
PushOnScopeChains(Var, CurScope, false);
+ }
} else {
// C++11 [expr.prim.lambda]p8:
// If a lambda-capture includes a capture-default that is &, the
Index: lib/Sema/SemaTemplateInstantiate.cpp
===================================================================
--- lib/Sema/SemaTemplateInstantiate.cpp
+++ lib/Sema/SemaTemplateInstantiate.cpp
@@ -917,7 +917,8 @@
}
ExprResult TransformLambdaScope(LambdaExpr *E,
- CXXMethodDecl *NewCallOperator) {
+ CXXMethodDecl *NewCallOperator,
+ ArrayRef<InitExprVarPairTy> InitCaptureExprsAndVars) {
CXXMethodDecl *const OldCallOperator = E->getCallOperator();
// In the generic lambda case, we set the NewTemplate to be considered
// an "instantiation" of the OldTemplate.
@@ -936,7 +937,8 @@
NewCallOperator->setInstantiationOfMemberFunction(OldCallOperator,
TSK_ImplicitInstantiation);
- return inherited::TransformLambdaScope(E, NewCallOperator);
+ return inherited::TransformLambdaScope(E, NewCallOperator,
+ InitCaptureExprsAndVars);
}
TemplateParameterList *TransformTemplateParameterList(
TemplateParameterList *OrigTPL) {
Index: lib/Sema/SemaTemplateVariadic.cpp
===================================================================
--- lib/Sema/SemaTemplateVariadic.cpp
+++ lib/Sema/SemaTemplateVariadic.cpp
@@ -275,6 +275,14 @@
// ill-formed.
if (!E->containsUnexpandedParameterPack())
return false;
+ // If we are an init-expression in a lambdas init-capture, we are done.
+ // template<class ... Ts> void test(Ts ... t) {
+ // test([&a(t)]() { <-- (t) is an init-expr that shouldn't be diagnosed now.
+ // return a;
+ // }() ...);
+ // }
+ if (E == CurLambdaInitCapture)
+ return false;
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseStmt(E);
Index: lib/Sema/TreeTransform.h
===================================================================
--- lib/Sema/TreeTransform.h
+++ lib/Sema/TreeTransform.h
@@ -589,9 +589,10 @@
StmtResult TransformCompoundStmt(CompoundStmt *S, bool IsStmtExpr);
ExprResult TransformCXXNamedCastExpr(CXXNamedCastExpr *E);
-
+ typedef std::pair<ExprResult, VarDecl*> InitExprVarPairTy;
/// \brief Transform the captures and body of a lambda expression.
- ExprResult TransformLambdaScope(LambdaExpr *E, CXXMethodDecl *CallOperator);
+ ExprResult TransformLambdaScope(LambdaExpr *E, CXXMethodDecl *CallOperator,
+ ArrayRef<InitExprVarPairTy> InitCaptureExprsAndVars);
TemplateParameterList *TransformTemplateParameterList(
TemplateParameterList *TPL) {
@@ -8272,6 +8273,32 @@
ExprResult
TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
+ // Transform any init-capture expressions before entering the scope of the
+ // lambda to avoid confusing tryCaptureVar during the transformation.
+ SmallVector<InitExprVarPairTy, 8> InitCaptureExprsAndVars;
+ InitCaptureExprsAndVars.resize(E->explicit_capture_end() -
+ E->explicit_capture_begin());
+ getSema().SaveMaybeODRUseExprs();
+ for (LambdaExpr::capture_iterator C = E->capture_begin(),
+ CEnd = E->capture_end();
+ C != CEnd; ++C) {
+ if (!C->isInitCapture())
+ continue;
+
+ ExprResult NewExprInit = getDerived().TransformInitializer(
+ C->getCapturedVar()->getInit(),
+ C->getCapturedVar()->getInitStyle() == VarDecl::CallInit);
+ VarDecl *OldVD = C->getCapturedVar();
+ VarDecl *NewVD = getSema().checkInitCapture(
+ C->getLocation(), OldVD->getType()->isReferenceType(),
+ OldVD->getIdentifier(), NewExprInit.take());
+
+ InitCaptureExprsAndVars[C - E->capture_begin()] =
+ std::make_pair(NewExprInit, NewVD);
+
+ }
+ getSema().RestoreMaybeODRUseExprs();
+
getSema().PushLambdaScope();
LambdaScopeInfo *LSI = getSema().getCurLambda();
// Transform the template parameters, and add them to the current
@@ -8367,31 +8394,17 @@
getDerived().transformAttrs(E->getCallOperator(), NewCallOperator);
- return getDerived().TransformLambdaScope(E, NewCallOperator);
+ return getDerived().TransformLambdaScope(E, NewCallOperator,
+ InitCaptureExprsAndVars);
}
template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformLambdaScope(LambdaExpr *E,
- CXXMethodDecl *CallOperator) {
+ CXXMethodDecl *CallOperator,
+ ArrayRef<InitExprVarPairTy> InitCaptureExprsAndVars) {
bool Invalid = false;
- // Transform any init-capture expressions before entering the scope of the
- // lambda.
- SmallVector<ExprResult, 8> InitCaptureExprs;
- InitCaptureExprs.resize(E->explicit_capture_end() -
- E->explicit_capture_begin());
- for (LambdaExpr::capture_iterator C = E->capture_begin(),
- CEnd = E->capture_end();
- C != CEnd; ++C) {
- if (!C->isInitCapture())
- continue;
- InitCaptureExprs[C - E->capture_begin()] =
- getDerived().TransformInitializer(
- C->getCapturedVar()->getInit(),
- C->getCapturedVar()->getInitStyle() == VarDecl::CallInit);
- }
-
// Introduce the context of the call operator.
Sema::ContextRAII SavedContext(getSema(), CallOperator);
@@ -8424,19 +8437,22 @@
// Rebuild init-captures, including the implied field declaration.
if (C->isInitCapture()) {
- ExprResult Init = InitCaptureExprs[C - E->capture_begin()];
+
+ InitExprVarPairTy InitExprVarPair =
+ InitCaptureExprsAndVars[C - E->capture_begin()];
+ ExprResult Init = InitExprVarPair.first;
if (Init.isInvalid()) {
Invalid = true;
continue;
}
VarDecl *OldVD = C->getCapturedVar();
- VarDecl *NewVD = getSema().checkInitCapture(
- C->getLocation(), OldVD->getType()->isReferenceType(),
- OldVD->getIdentifier(), Init.take());
+ VarDecl *NewVD = InitExprVarPair.second;
if (!NewVD)
Invalid = true;
- else
+ else {
+ NewVD->setDeclContext(CallOperator);
getDerived().transformedLocalDecl(OldVD, NewVD);
+ }
getSema().buildInitCaptureField(LSI, NewVD);
continue;
}
Index: test/SemaCXX/cxx1y-init-captures.cpp
===================================================================
--- test/SemaCXX/cxx1y-init-captures.cpp
+++ test/SemaCXX/cxx1y-init-captures.cpp
@@ -1,14 +1,169 @@
-// RUN: %clang_cc1 -std=c++1y %s -verify
+// RUN: %clang_cc1 -std=c++1y %s -verify -emit-llvm-only
-// expected-no-diagnostics
namespace variadic_expansion {
- void f(int &, char &);
-
- template <typename ... T> void g(T &... t) {
+ int f(int &, char &) { return 0; }
+ template<class ... Ts> char fv(Ts ... ts) { return 0; }
+ // FIXME: why do we get 2 error messages
+ template <typename ... T> void g(T &... t) { //expected-note2{{declared here}}
f([&a(t)]()->decltype(auto) {
return a;
}() ...);
+
+ auto L = [x = f([&a(t)]()->decltype(auto) { return a; }()...)]() { return x; };
+ const int y = 10;
+ auto M = [x = y,
+ &z = y](T& ... t) { };
+ auto N = [x = y,
+ &z = y, n = f(t...),
+ o = f([&a(t)](T& ... t)->decltype(auto) { return a; }(t...)...), t...](T& ... s) {
+ fv([&a(t)]()->decltype(auto) {
+ return a;
+ }() ...);
+ };
+ auto N2 = [x = y, //expected-note2{{begins here}}
+ &z = y, n = f(t...),
+ o = f([&a(t)](T& ... t)->decltype(auto) { return a; }(t...)...)](T& ... s) {
+ fv([&a(t)]()->decltype(auto) { //expected-error 2{{captured}}
+ return a;
+ }() ...);
+ };
+
}
- void h(int i, char c) { g(i, c); }
+ void h(int i, char c) { g(i, c); } //expected-note{{in instantiation}}
}
+
+namespace odr_use_within_init_capture {
+
+int test() {
+
+ { // no captures
+ const int x = 10;
+ auto L = [z = x + 2](int a) {
+ auto M = [y = x - 2](char b) {
+ return y;
+ };
+ return M;
+ };
+
+ }
+ { // should not capture
+ const int x = 10;
+ auto L = [&z = x](int a) {
+ return a;;
+ };
+
+ }
+ {
+ const int x = 10;
+ auto L = [k = x](char a) { //expected-note {{declared}}
+ return [](int b) { //expected-note {{begins}}
+ return [j = k](int c) { //expected-error {{cannot be implicitly captured}}
+ return c;
+ };
+ };
+ };
+ }
+ {
+ const int x = 10;
+ auto L = [k = x](char a) {
+ return [=](int b) {
+ return [j = k](int c) {
+ return c;
+ };
+ };
+ };
+ }
+ {
+ const int x = 10;
+ auto L = [k = x](char a) {
+ return [k](int b) {
+ return [j = k](int c) {
+ return c;
+ };
+ };
+ };
+ }
+
+ return 0;
+}
+
+int run = test();
+
+}
+
+namespace odr_use_within_init_capture_template {
+
+template<class T = int>
+int test(T t = T{}) {
+
+ { // no captures
+ const T x = 10;
+ auto L = [z = x](char a) {
+ auto M = [y = x](T b) {
+ return y;
+ };
+ return M;
+ };
+
+ }
+ { // should not capture
+ const T x = 10;
+ auto L = [&z = x](T a) {
+ return a;;
+ };
+
+ }
+ { // will need to capture x in outer lambda
+ const T x = 10; //expected-note {{declared}}
+ auto L = [z = x](char a) { //expected-note {{begins}}
+ auto M = [&y = x](T b) { //expected-error {{cannot be implicitly captured}}
+ return y;
+ };
+ return M;
+ };
+
+ }
+ { // will need to capture x in outer lambda
+ const T x = 10;
+ auto L = [=,z = x](char a) {
+ auto M = [&y = x](T b) {
+ return y;
+ };
+ return M;
+ };
+
+ }
+ { // will need to capture x in outer lambda
+ const T x = 10;
+ auto L = [x, z = x](char a) {
+ auto M = [&y = x](T b) {
+ return y;
+ };
+ return M;
+ };
+ }
+ { // will need to capture x in outer lambda
+ const int x = 10; //expected-note {{declared}}
+ auto L = [z = x](char a) { //expected-note {{begins}}
+ auto M = [&y = x](T b) { //expected-error {{cannot be implicitly captured}}
+ return y;
+ };
+ return M;
+ };
+ }
+ {
+ // no captures
+ const T x = 10;
+ auto L = [z =
+ [z = x, &y = x](char a) { return z + y; }('a')](char a)
+ { return z; };
+
+ }
+
+ return 0;
+}
+
+int run = test(); //expected-note {{instantiation}}
+
+}
\ No newline at end of file
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits