/// \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;
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 {
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;
}
/// \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.
return cast<VarDecl>(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<FieldDecl>(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; }
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<LambdaExpr*>(this)->getInitCaptureInit(Capture);
- }
-
/// \brief Retrieve the set of index variables used in the capture
/// initializer of an array captured by copy.
///
bool RecursiveASTVisitor<Derived>::TraverseLambdaCapture(
LambdaExpr *LE, const LambdaExpr::Capture *C) {
if (C->isInitCapture())
- TRY_TO(TraverseStmt(LE->getInitCaptureInit(C)));
+ TRY_TO(TraverseDecl(C->getCapturedVar()));
return true;
}
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">;
}
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
// 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<VarDecl*, 1, bool> VarAndNested;
+ /// The variable being captured (if we are not capturing 'this') and whether
+ /// this is a nested capture.
+ llvm::PointerIntPair<VarDecl*, 1, bool> VarAndNested;
- // The variable being captured, or the implicitly-generated field for
- // an init-capture.
- llvm::PointerUnion<VarAndNested, FieldDecl*> 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<Expr*, 2, CaptureKind> InitExprAndCaptureKind;
/// \brief The source location at which the first capture occurred.
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) {}
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<VarAndNested>();
+ return InitExprAndCaptureKind.getInt() == Cap_This;
}
bool isVariableCapture() const {
- return InitExprAndCaptureKind.getInt() != Cap_ThisOrInit;
- }
- bool isInitCapture() const {
- return VarOrField.is<FieldDecl*>();
+ return InitExprAndCaptureKind.getInt() != Cap_This;
}
bool isCopyCapture() const {
return InitExprAndCaptureKind.getInt() == Cap_ByCopy;
bool isBlockCapture() const {
return InitExprAndCaptureKind.getInt() == Cap_Block;
}
- bool isNested() { return VarOrField.dyn_cast<VarAndNested>().getInt(); }
+ bool isNested() { return VarAndNested.getInt(); }
VarDecl *getVariable() const {
- return VarOrField.dyn_cast<VarAndNested>().getPointer();
- }
- FieldDecl *getInitCaptureField() const {
- return VarOrField.dyn_cast<FieldDecl*>();
+ return VarAndNested.getPointer();
}
/// \brief Retrieve the location at which this variable was captured.
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; }
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.
else if (C->capturesVariable())
Captures[C->getCapturedVar()] = *Field;
}
+ assert(Field == field_end());
}
TemplateParameterList *
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<FieldDecl>(D))
- return LCK_Init;
-
return (DeclAndBits.getInt() & Capture_ByCopy) ? LCK_ByCopy : LCK_ByRef;
}
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 << ']';
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
// 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();
}
}
-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);
}
TypeSourceInfo *TSI = TLB.getTypeSourceInfo(Context, DeductType);
- InitializationKind InitKind = InitializationKind::CreateDefault(Loc);
- Expr *Init = InitExpr;
- if (ParenListExpr *Parens = dyn_cast<ParenListExpr>(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<InitListExpr>(Init))
- // We do not need to distinguish between direct-list-initialization
- // and copy-list-initialization here, because we will always deduce
- // std::initializer_list<T>, 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<InitListExpr>(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<T>, 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<ParenListExpr>(Init) || isa<InitListExpr>(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,
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<VarDecl> 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<VarDecl> Validator;
+ if (DiagnoseEmptyLookup(CurScope, ScopeSpec, R, Validator))
+ continue;
+ }
- VarDecl *Var = R.getAsSingle<VarDecl>();
+ Var = R.getAsSingle<VarDecl>();
+ }
// C++11 [expr.prim.lambda]p8:
// An identifier or this shall not appear more than once in a
<< 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;
}
} 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);
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,
void Sema::DiagnoseAutoDeductionFailure(VarDecl *VDecl, Expr *Init) {
if (isa<InitListExpr>(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();
}
NewVar->setInitStyle(OldVar->getInitStyle());
NewVar->setCXXForRangeDecl(OldVar->isCXXForRangeDecl());
NewVar->setConstexpr(OldVar->isConstexpr());
+ NewVar->setInitCapture(OldVar->isInitCapture());
NewVar->setPreviousDeclInSameBlockScope(
OldVar->isPreviousDeclInSameBlockScope());
NewVar->setAccess(OldVar->getAccess());
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.
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;
}
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);
*ToCapture++ = Capture(Loc, IsImplicit, Kind, 0, SourceLocation());
break;
case LCK_ByCopy:
- case LCK_ByRef: {
+ case LCK_ByRef:
VarDecl *Var = ReadDeclAs<VarDecl>(Record, Idx);
SourceLocation EllipsisLoc = ReadSourceLocation(Record, Idx);
*ToCapture++ = Capture(Loc, IsImplicit, Kind, Var, EllipsisLoc);
break;
}
- case LCK_Init:
- FieldDecl *Field = ReadDeclAs<FieldDecl>(Record, Idx);
- *ToCapture++ = Capture(Field);
- break;
- }
}
}
}
case LCK_This:
break;
case LCK_ByCopy:
- case LCK_ByRef: {
+ case LCK_ByRef:
VarDecl *Var =
Capture.capturesVariable() ? Capture.getCapturedVar() : 0;
AddDeclRef(Var, Record);
Record);
break;
}
- case LCK_Init:
- FieldDecl *Field = Capture.getInitCaptureField();
- AddDeclRef(Field, Record);
- break;
- }
}
}
}
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());
!isa<ParmVarDecl>(D) &&
!isa<VarTemplateSpecializationDecl>(D) &&
!D->isConstexpr() &&
+ !D->isInitCapture() &&
!D->isPreviousDeclInSameBlockScope() &&
!D->getMemberSpecializationInfo())
AbbrevToUse = Writer.getDeclVarAbbrev();
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
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
// 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
// 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}}
};
}
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 '<overloaded function}}
auto bad_init_6 = [a{overload_fn}] {}; // expected-error {{cannot deduce type for lambda capture 'a' from initializer list}}
-template<typename...T> void pack_1(T...t) { [a(t...)] {}; } // expected-error {{initializer missing for lambda capture 'a'}}
+template<typename...T> 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<const int&&>(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<const int&&>(0)] {
static_assert(sizeof(a) == sizeof(int), "");
static_assert(sizeof(b) == sizeof(int), "");
using T = decltype(c);
template<typename T> struct remove_reference<T&> { typedef T type; };
template<typename T> decltype(auto) move(T &&t) { return static_cast<typename remove_reference<T>::type&&>(t); }
auto s = [s(move(S()))] {};
+
+template<typename T> 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}}
template<typename ...Args>
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);
--- /dev/null
+// 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(
--- /dev/null
+// 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<typename T> 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
// 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 {{<initializer_list>}}
- [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 {{<initializer_list>}}
[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;
} ();
}
};