dyn_cast<CXXMethodDecl>(DC));
}
+
+// This returns the parent DeclContext ensuring that the correct
+// parent DeclContext is returned for Lambdas
+inline DeclContext *getLambdaAwareParentOfDeclContext(DeclContext *DC) {
+ if (isLambdaCallOperator(DC))
+ return DC->getParent()->getParent();
+ else
+ return DC->getParent();
+}
+
} // clang
#endif // LLVM_CLANG_AST_LAMBDA_H
#include "clang/AST/Type.h"
#include "clang/Basic/CapturedStmt.h"
#include "clang/Basic/PartialDiagnostic.h"
+#include "clang/Sema/Ownership.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
+#include <algorithm>
namespace clang {
class TemplateParameterList;
class VarDecl;
class DeclRefExpr;
+class MemberExpr;
class ObjCIvarRefExpr;
class ObjCPropertyRefExpr;
class ObjCMessageExpr;
/// list has been created (from the AutoTemplateParams) then
/// store a reference to it (cache it to avoid reconstructing it).
TemplateParameterList *GLTemplateParameterList;
+
+ /// \brief Contains all variable-referring-expressions (i.e. DeclRefExprs
+ /// or MemberExprs) that refer to local variables in a generic lambda
+ /// or a lambda in a potentially-evaluated-if-used context.
+ ///
+ /// Potentially capturable variables of a nested lambda that might need
+ /// to be captured by the lambda are housed here.
+ /// This is specifically useful for generic lambdas or
+ /// lambdas within a a potentially evaluated-if-used context.
+ /// If an enclosing variable is named in an expression of a lambda nested
+ /// within a generic lambda, we don't always know know whether the variable
+ /// will truly be odr-used (i.e. need to be captured) by that nested lambda,
+ /// until its instantiation. But we still need to capture it in the
+ /// enclosing lambda if all intervening lambdas can capture the variable.
+
+ llvm::SmallVector<Expr*, 4> PotentiallyCapturingExprs;
+
+ /// \brief Contains all variable-referring-expressions that refer
+ /// to local variables that are usable as constant expressions and
+ /// do not involve an odr-use (they may still need to be captured
+ /// if the enclosing full-expression is instantiation dependent).
+ llvm::SmallSet<Expr*, 8> NonODRUsedCapturingExprs;
+
+ SourceLocation PotentialThisCaptureLocation;
LambdaScopeInfo(DiagnosticsEngine &Diag)
: CapturingScopeInfo(Diag, ImpCap_None), Lambda(0),
CallOperator(0), NumExplicitCaptures(0), Mutable(false),
ExprNeedsCleanups(false), ContainsUnexpandedParameterPack(false),
- AutoTemplateParameterDepth(0),
- GLTemplateParameterList(0)
+ AutoTemplateParameterDepth(0), GLTemplateParameterList(0)
{
Kind = SK_Lambda;
}
static bool classof(const FunctionScopeInfo *FSI) {
return FSI->Kind == SK_Lambda;
}
+
+ ///
+ /// \brief Add a variable that might potentially be captured by the
+ /// lambda and therefore the enclosing lambdas.
+ ///
+ /// This is also used by enclosing lambda's to speculatively capture
+ /// variables that nested lambda's - depending on their enclosing
+ /// specialization - might need to capture.
+ /// Consider:
+ /// void f(int, int); <-- don't capture
+ /// void f(const int&, double); <-- capture
+ /// void foo() {
+ /// const int x = 10;
+ /// auto L = [=](auto a) { // capture 'x'
+ /// return [=](auto b) {
+ /// f(x, a); // we may or may not need to capture 'x'
+ /// };
+ /// };
+ /// }
+ void addPotentialCapture(Expr *VarExpr) {
+ assert(isa<DeclRefExpr>(VarExpr) || isa<MemberExpr>(VarExpr));
+ PotentiallyCapturingExprs.push_back(VarExpr);
+ }
+
+ void addPotentialThisCapture(SourceLocation Loc) {
+ PotentialThisCaptureLocation = Loc;
+ }
+ bool hasPotentialThisCapture() const {
+ return PotentialThisCaptureLocation.isValid();
+ }
+
+ /// \brief Mark a variable's reference in a lambda as non-odr using.
+ ///
+ /// For generic lambdas, if a variable is named in a potentially evaluated
+ /// expression, where the enclosing full expression is dependent then we
+ /// must capture the variable (given a default capture).
+ /// This is accomplished by recording all references to variables
+ /// (DeclRefExprs or MemberExprs) within said nested lambda in its array of
+ /// PotentialCaptures. All such variables have to be captured by that lambda,
+ /// except for as described below.
+ /// If that variable is usable as a constant expression and is named in a
+ /// manner that does not involve its odr-use (e.g. undergoes
+ /// lvalue-to-rvalue conversion, or discarded) record that it is so. Upon the
+ /// act of analyzing the enclosing full expression (ActOnFinishFullExpr)
+ /// if we can determine that the full expression is not instantiation-
+ /// dependent, then we can entirely avoid its capture.
+ ///\r
+ /// const int n = 0;\r
+ /// [&] (auto x) {\r
+ /// (void)+n + x;\r
+ /// };
+ /// Interestingly, this strategy would involve a capture of n, even though
+ /// it's obviously not odr-used here, because the full-expression is
+ /// instantiation-dependent. It could be useful to avoid capturing such
+ /// variables, even when they are referred to in an instantiation-dependent
+ /// expression, if we can unambiguously determine that they shall never be
+ /// odr-used. This would involve removal of the variable-referring-expression
+ /// from the array of PotentialCaptures during the lvalue-to-rvalue
+ /// conversions. But per the working draft N3797, (post-chicago 2013) we must
+ /// capture such variables.
+ /// Before anyone is tempted to implement a strategy for not-capturing 'n',
+ /// consider the insightful warning in:
+ /// /cfe-commits/Week-of-Mon-20131104/092596.html
+ /// "The problem is that the set of captures for a lambda is part of the ABI\r
+ /// (since lambda layout can be made visible through inline functions and the\r
+ /// like), and there are no guarantees as to which cases we'll manage to build\r
+ /// an lvalue-to-rvalue conversion in, when parsing a template -- some\r
+ /// seemingly harmless change elsewhere in Sema could cause us to start or stop\r
+ /// building such a node. So we need a rule that anyone can implement and get\r
+ /// exactly the same result".
+ ///
+ void markVariableExprAsNonODRUsed(Expr *CapturingVarExpr) {
+ assert(isa<DeclRefExpr>(CapturingVarExpr)
+ || isa<MemberExpr>(CapturingVarExpr));
+ NonODRUsedCapturingExprs.insert(CapturingVarExpr);
+ }
+ bool isVariableExprMarkedAsNonODRUsed(Expr *CapturingVarExpr) {
+ assert(isa<DeclRefExpr>(CapturingVarExpr)
+ || isa<MemberExpr>(CapturingVarExpr));
+ return NonODRUsedCapturingExprs.count(CapturingVarExpr);
+ }
+ void removePotentialCapture(Expr *E) {
+ PotentiallyCapturingExprs.erase(
+ std::remove(PotentiallyCapturingExprs.begin(),
+ PotentiallyCapturingExprs.end(), E),
+ PotentiallyCapturingExprs.end());
+ }
+ void clearPotentialCaptures() {
+ PotentiallyCapturingExprs.clear();
+ PotentialThisCaptureLocation = SourceLocation();
+ }
+ unsigned getNumPotentialVariableCaptures() const {
+ return PotentiallyCapturingExprs.size();
+ }
+
+ bool hasPotentialCaptures() const {
+ return getNumPotentialVariableCaptures() ||
+ PotentialThisCaptureLocation.isValid();
+ }
+
+ // When passed the index, returns the VarDecl and Expr associated
+ // with the index.
+ void getPotentialVariableCapture(unsigned Idx, VarDecl *&VD, Expr *&E);
+
};
/// from within the current scope. Only valid when the variable can be
/// captured.
///
+ /// \param FunctionScopeIndexToStopAt If non-null, it points to the index
+ /// of the FunctionScopeInfo stack beyond which we do not attempt to capture.
+ /// This is useful when enclosing lambdas must speculatively capture
+ /// variables that may or may not be used in certain specializations of
+ /// a nested generic lambda.
+ ///
/// \returns true if an error occurred (i.e., the variable cannot be
/// captured) and false if the capture succeeded.
bool tryCaptureVariable(VarDecl *Var, SourceLocation Loc, TryCaptureKind Kind,
SourceLocation EllipsisLoc, bool BuildAndDiagnose,
QualType &CaptureType,
- QualType &DeclRefType);
+ QualType &DeclRefType,
+ const unsigned *const FunctionScopeIndexToStopAt);
/// \brief Try to capture the given variable.
bool tryCaptureVariable(VarDecl *Var, SourceLocation Loc,
///
/// \param Explicit Whether 'this' is explicitly captured in a lambda
/// capture list.
- void CheckCXXThisCapture(SourceLocation Loc, bool Explicit = false);
+ ///
+ /// \param FunctionScopeIndexToStopAt If non-null, it points to the index
+ /// of the FunctionScopeInfo stack beyond which we do not attempt to capture.
+ /// This is useful when enclosing lambdas must speculatively capture
+ /// 'this' that may or may not be used in certain specializations of
+ /// a nested generic lambda (depending on whether the name resolves to
+ /// a non-static member function or a static function).
+ /// \return returns 'true' if failed, 'false' if success.
+ bool CheckCXXThisCapture(SourceLocation Loc, bool Explicit = false,
+ bool BuildAndDiagnose = true,
+ const unsigned *const FunctionScopeIndexToStopAt = 0);
/// \brief Determine whether the given type is the type of *this that is used
/// outside of the body of a member function for a type that is currently
inline PartialDiagnostic Sema::PDiag(unsigned DiagID) {
return PartialDiagnostic(DiagID, Context.getDiagAllocator());
}
-
+\r
+\r
+// This requires the variable to be non-dependent and the initializer\r
+// to not be value dependent.\r
+inline bool IsVariableAConstantExpression(VarDecl *Var, ASTContext &Context) {\r
+ const VarDecl *DefVD = 0;\r
+ return !isa<ParmVarDecl>(Var) &&\r
+ Var->isUsableInConstantExpressions(Context) &&\r
+ Var->getAnyInitializer(DefVD) && DefVD->checkInitIsICE(); \r
+}\r
+\r
+// Directly mark a variable odr-used. Given a choice, prefer to use \r
+// MarkVariableReferenced since it does additional checks and then \r
+// calls MarkVarDeclODRUsed.\r
+// If the variable must be captured:\r
+// - if FunctionScopeIndexToStopAt is null, capture it in the CurContext\r
+// - else capture it in the DeclContext that maps to the \r
+// *FunctionScopeIndexToStopAt on the FunctionScopeInfo stack. \r
+inline void MarkVarDeclODRUsed(VarDecl *Var,\r
+ SourceLocation Loc, Sema &SemaRef,\r
+ const unsigned *const FunctionScopeIndexToStopAt) {\r
+ // Keep track of used but undefined variables.\r
+ // FIXME: We shouldn't suppress this warning for static data members.\r
+ if (Var->hasDefinition(SemaRef.Context) == VarDecl::DeclarationOnly &&\r
+ !Var->isExternallyVisible() &&\r
+ !(Var->isStaticDataMember() && Var->hasInit())) {\r
+ SourceLocation &old = SemaRef.UndefinedButUsed[Var->getCanonicalDecl()];\r
+ if (old.isInvalid()) old = Loc;\r
+ }\r
+ QualType CaptureType, DeclRefType;\r
+ SemaRef.tryCaptureVariable(Var, Loc, Sema::TryCapture_Implicit, \r
+ /*EllipsisLoc*/ SourceLocation(),\r
+ /*BuildAndDiagnose*/ true, \r
+ CaptureType, DeclRefType, \r
+ FunctionScopeIndexToStopAt);\r
+\r
+ Var->markUsed(SemaRef.Context);\r
+}
}
#endif
--- /dev/null
+//===--- SemaLambda.h - Lambda Helper Functions --------------*- C++ -*-===//\r
+//\r
+// The LLVM Compiler Infrastructure\r
+//\r
+// This file is distributed under the University of Illinois Open Source\r
+// License. See LICENSE.TXT for details.\r
+//\r
+//===----------------------------------------------------------------------===//\r
+///\r
+/// \file\r
+/// \brief This file provides some common utility functions for processing\r
+/// Lambdas.\r
+///\r
+//===----------------------------------------------------------------------===//\r
+\r
+#ifndef LLVM_CLANG_SEMA_LAMBDA_H\r
+#define LLVM_CLANG_SEMA_LAMBDA_H\r
+#include "clang/AST/ASTLambda.h"\r
+#include "clang/Sema/ScopeInfo.h"\r
+namespace clang {\r
+ \r
+// Given a lambda's call operator and a variable (or null for 'this'), \r
+// compute the nearest enclosing lambda that is capture-ready (i.e \r
+// the enclosing context is not dependent, and all intervening lambdas can \r
+// either implicitly or explicitly capture Var)\r
+// \r
+// Return the CallOperator of the capturable lambda and set function scope \r
+// index to the correct index within the function scope stack to correspond \r
+// to the capturable lambda.\r
+// If VarDecl *VD is null, we check for 'this' capture.\r
+CXXMethodDecl* \r
+GetInnermostEnclosingCapturableLambda( \r
+ ArrayRef<sema::FunctionScopeInfo*> FunctionScopes,\r
+ unsigned &FunctionScopeIndex,\r
+ DeclContext *const CurContext, VarDecl *VD, Sema &S);\r
+\r
+} // clang\r
+\r
+#endif // LLVM_CLANG_SEMA_LAMBDA_H\r
ThisUse->markSafe();
}
+void LambdaScopeInfo::getPotentialVariableCapture(unsigned Idx, VarDecl *&VD, Expr *&E) {
+ assert((Idx >= 0 && Idx < getNumPotentialVariableCaptures()) &&
+ "Index of potential capture must be within 0 to less than the "
+ "number of captures!");
+ E = PotentiallyCapturingExprs[Idx];
+ if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
+ VD = dyn_cast<VarDecl>(DRE->getFoundDecl());
+ else if (MemberExpr *ME = dyn_cast<MemberExpr>(E))
+ VD = dyn_cast<VarDecl>(ME->getMemberDecl());
+ else
+ llvm_unreachable("Only DeclRefExprs or MemberExprs should be added for "
+ "potential captures");
+ assert(VD);
+}
+
FunctionScopeInfo::~FunctionScopeInfo() { }
BlockScopeInfo::~BlockScopeInfo() { }
LambdaScopeInfo::~LambdaScopeInfo() { }
#include "clang/Sema/ParsedTemplate.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/ScopeInfo.h"
+#include "clang/Sema/Template.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/Triple.h"
#include <algorithm>
Diag(Definition->getLocation(), diag::note_previous_definition);
FD->setInvalidDecl();
}
+
+
static void RebuildLambdaScopeInfo(CXXMethodDecl *CallOperator,
Sema &S) {
CXXRecordDecl *const LambdaClass = CallOperator->getParent();
LSI->IntroducerRange = DNI.getCXXOperatorNameRange();
LSI->Mutable = !CallOperator->isConst();
- // FIXME: Add the captures to the LSI.
+ // Add the captures to the LSI so they can be noted as already
+ // captured within tryCaptureVar.
+ for (LambdaExpr::capture_iterator C = LambdaClass->captures_begin(),
+ CEnd = LambdaClass->captures_end(); C != CEnd; ++C) {
+ if (C->capturesVariable()) {
+ VarDecl *VD = C->getCapturedVar();
+ if (VD->isInitCapture())
+ S.CurrentInstantiationScope->InstantiatedLocal(VD, VD);
+ QualType CaptureType = VD->getType();
+ const bool ByRef = C->getCaptureKind() == LCK_ByRef;
+ LSI->addCapture(VD, /*IsBlock*/false, ByRef,
+ /*RefersToEnclosingLocal*/true, C->getLocation(),
+ /*EllipsisLoc*/C->isPackExpansion()
+ ? C->getEllipsisLoc() : SourceLocation(),
+ CaptureType, /*Expr*/ 0);
+
+ } else if (C->capturesThis()) {
+ LSI->addThisCapture(/*Nested*/ false, C->getLocation(),
+ S.getCurrentThisType(), /*Expr*/ 0);
+ }
+ }
}
Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D) {
#include "TreeTransform.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTLambda.h"
#include "clang/AST/ASTMutationListener.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/DeclObjC.h"
static DeclContext *getParentOfCapturingContextOrNull(DeclContext *DC, VarDecl *Var,
SourceLocation Loc,
const bool Diagnose, Sema &S) {
- if (isa<BlockDecl>(DC) || isa<CapturedDecl>(DC))
- return DC->getParent();
- else if (isa<CXXMethodDecl>(DC) &&
- cast<CXXMethodDecl>(DC)->getOverloadedOperator() == OO_Call &&
- cast<CXXRecordDecl>(DC->getParent())->isLambda())
- return DC->getParent()->getParent();
+ if (isa<BlockDecl>(DC) || isa<CapturedDecl>(DC) || isLambdaCallOperator(DC))
+ return getLambdaAwareParentOfDeclContext(DC);
else {
if (Diagnose)
diagnoseUncapturableValueReference(S, Loc, Var, DC);
TryCaptureKind Kind, SourceLocation EllipsisLoc,
bool BuildAndDiagnose,
QualType &CaptureType,
- QualType &DeclRefType) {
+ QualType &DeclRefType,
+ const unsigned *const FunctionScopeIndexToStopAt) {
bool Nested = false;
DeclContext *DC = CurContext;
- const unsigned MaxFunctionScopesIndex = FunctionScopes.size() - 1;
+ const unsigned MaxFunctionScopesIndex = FunctionScopeIndexToStopAt
+ ? *FunctionScopeIndexToStopAt : FunctionScopes.size() - 1;
+ // We need to sync up the Declaration Context with the
+ // FunctionScopeIndexToStopAt
+ if (FunctionScopeIndexToStopAt) {
+ unsigned FSIndex = FunctionScopes.size() - 1;
+ while (FSIndex != MaxFunctionScopesIndex) {
+ DC = getLambdaAwareParentOfDeclContext(DC);
+ --FSIndex;
+ }
+ }
+
// If the variable is declared in the current context (and is not an
// init-capture), there is no need to capture it.
if (!Var->isInitCapture() && Var->getDeclContext() == DC) return true;
if (isVariableAlreadyCapturedInScopeInfo(CSI, Var, Nested, CaptureType,
DeclRefType))
break;
-
+ // If we are instantiating a generic lambda call operator body,
+ // we do not want to capture new variables. What was captured
+ // during either a lambdas transformation or initial parsing
+ // should be used.
+ if (isGenericLambdaCallOperatorSpecialization(DC)) {
+ if (BuildAndDiagnose) {
+ LambdaScopeInfo *LSI = cast<LambdaScopeInfo>(CSI);
+ if (LSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_None) {
+ Diag(ExprLoc, diag::err_lambda_impcap) << Var->getDeclName();
+ Diag(Var->getLocation(), diag::note_previous_decl)
+ << Var->getDeclName();
+ Diag(LSI->Lambda->getLocStart(), diag::note_lambda_decl);
+ } else
+ diagnoseUncapturableValueReference(*this, ExprLoc, Var, DC);
+ }
+ return true;
+ }
// Certain capturing entities (lambdas, blocks etc.) are not allowed to capture
// certain types of variables (unnamed, variably modified types etc.)
// so check for eligibility.
<< Var->getDeclName();
Diag(cast<LambdaScopeInfo>(CSI)->Lambda->getLocStart(),
diag::note_lambda_decl);
+ // FIXME: If we error out because an outer lambda can not implicitly
+ // capture a variable that an inner lambda explicitly captures, we
+ // should have the inner lambda do the explicit capture - because
+ // it makes for cleaner diagnostics later. This would purely be done
+ // so that the diagnostic does not misleadingly claim that a variable
+ // can not be captured by a lambda implicitly even though it is captured
+ // explicitly. Suggestion:
+ // - create const bool VariableCaptureWasInitiallyExplicit = Explicit
+ // at the function head
+ // - cache the StartingDeclContext - this must be a lambda
+ // - captureInLambda in the innermost lambda the variable.
}
return true;
}
QualType DeclRefType;
return tryCaptureVariable(Var, Loc, Kind, EllipsisLoc,
/*BuildAndDiagnose=*/true, CaptureType,
- DeclRefType);
+ DeclRefType, 0);
}
QualType Sema::getCapturedDeclRefType(VarDecl *Var, SourceLocation Loc) {
// Determine whether we can capture this variable.
if (tryCaptureVariable(Var, Loc, TryCapture_Implicit, SourceLocation(),
- /*BuildAndDiagnose=*/false, CaptureType, DeclRefType))
+ /*BuildAndDiagnose=*/false, CaptureType,
+ DeclRefType, 0))
return QualType();
return DeclRefType;
}
-static void MarkVarDeclODRUsed(Sema &SemaRef, VarDecl *Var,
- SourceLocation Loc) {
- // Keep track of used but undefined variables.
- // FIXME: We shouldn't suppress this warning for static data members.
- if (Var->hasDefinition(SemaRef.Context) == VarDecl::DeclarationOnly &&
- !Var->isExternallyVisible() &&
- !(Var->isStaticDataMember() && Var->hasInit())) {
- SourceLocation &old = SemaRef.UndefinedButUsed[Var->getCanonicalDecl()];
- if (old.isInvalid()) old = Loc;
- }
- SemaRef.tryCaptureVariable(Var, Loc);
- Var->markUsed(SemaRef.Context);
+// If either the type of the variable or the initializer is dependent,
+// return false. Otherwise, determine whether the variable is a constant
+// expression. Use this if you need to know if a variable that might or
+// might not be dependent is truly a constant expression.
+static inline bool IsVariableNonDependentAndAConstantExpression(VarDecl *Var,
+ ASTContext &Context) {
+
+ if (Var->getType()->isDependentType())
+ return false;
+ const VarDecl *DefVD = 0;
+ Var->getAnyInitializer(DefVD);
+ if (!DefVD)
+ return false;
+ EvaluatedStmt *Eval = DefVD->ensureEvaluatedStmt();
+ Expr *Init = cast<Expr>(Eval->Value);
+ if (Init->isValueDependent())
+ return false;
+ return IsVariableAConstantExpression(Var, Context);
}
+
void Sema::UpdateMarkingForLValueToRValue(Expr *E) {
// Per C++11 [basic.def.odr], a variable is odr-used "unless it is
// an object that satisfies the requirements for appearing in a
// is immediately applied." This function handles the lvalue-to-rvalue
// conversion part.
MaybeODRUseExprs.erase(E->IgnoreParens());
+
+ // If we are in a lambda, check if this DeclRefExpr or MemberExpr refers
+ // to a variable that is a constant expression, and if so, identify it as
+ // a reference to a variable that does not involve an odr-use of that
+ // variable.
+ if (LambdaScopeInfo *LSI = getCurLambda()) {
+ Expr *SansParensExpr = E->IgnoreParens();
+ VarDecl *Var = 0;
+ if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(SansParensExpr))
+ Var = dyn_cast<VarDecl>(DRE->getFoundDecl());
+ else if (MemberExpr *ME = dyn_cast<MemberExpr>(SansParensExpr))
+ Var = dyn_cast<VarDecl>(ME->getMemberDecl());
+
+ if (Var && IsVariableNonDependentAndAConstantExpression(Var, Context))
+ LSI->markVariableExprAsNonODRUsed(SansParensExpr);
+ }
}
ExprResult Sema::ActOnConstantExpression(ExprResult Res) {
llvm_unreachable("Unexpcted expression");
}
- MarkVarDeclODRUsed(*this, Var, Loc);
+ MarkVarDeclODRUsed(Var, Loc, *this, /*MaxFunctionScopeIndex Pointer*/ 0);
}
MaybeODRUseExprs.clear();
}
-// Mark a VarDecl referenced, and perform the necessary handling to compute
-// odr-uses.
+
static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
VarDecl *Var, Expr *E) {
+ assert(!E || isa<DeclRefExpr>(E) || isa<MemberExpr>(E)
+ && "Invalid Expr argument to DoMarkVarDeclReferenced");
Var->setReferenced();
- if (!IsPotentiallyEvaluatedContext(SemaRef))
- return;
+ // If the context is not PotentiallyEvaluated and not Unevaluated
+ // (i.e PotentiallyEvaluatedIfUsed) do not bother to consider variables
+ // in this context for odr-use unless we are within a lambda.
+ // If we don't know whether the context is potentially evaluated or not
+ // (for e.g., if we're in a generic lambda), we want to add a potential
+ // capture and eventually analyze for odr-use.
+ // We should also be able to analyze certain constructs in a non-generic
+ // lambda setting for potential odr-use and capture violation:
+ // template<class T> void foo(T t) {
+ // auto L = [](int i) { return t; };
+ // }
+ //
+ if (!IsPotentiallyEvaluatedContext(SemaRef)) {
+
+ if (SemaRef.isUnevaluatedContext()) return;
+
+ const bool refersToEnclosingScope =
+ (SemaRef.CurContext != Var->getDeclContext() &&
+ Var->getDeclContext()->isFunctionOrMethod());
+ if (!refersToEnclosingScope) return;
+
+ if (LambdaScopeInfo *const LSI = SemaRef.getCurLambda()) {
+ // If a variable could potentially be odr-used, defer marking it so
+ // until we finish analyzing the full expression for any lvalue-to-rvalue
+ // or discarded value conversions that would obviate odr-use.
+ // Add it to the list of potential captures that will be analyzed
+ // later (ActOnFinishFullExpr) for eventual capture and odr-use marking
+ // unless the variable is a reference that was initialized by a constant
+ // expression (this will never need to be captured or odr-used).
+ const bool IsConstantExpr = IsVariableNonDependentAndAConstantExpression(
+ Var, SemaRef.Context);
+ assert(E && "Capture variable should be used in an expression.");
+ if (!IsConstantExpr || !Var->getType()->isReferenceType())
+ LSI->addPotentialCapture(E->IgnoreParens());
+ }
+ return;
+ }
VarTemplateSpecializationDecl *VarSpec =
dyn_cast<VarTemplateSpecializationDecl>(Var);
}
}
}
-
// Per C++11 [basic.def.odr], a variable is odr-used "unless it satisfies
// the requirements for appearing in a constant expression (5.19) and, if
// it is an object, the lvalue-to-rvalue conversion (4.1)
// Note that we use the C++11 definition everywhere because nothing in
// C++03 depends on whether we get the C++03 version correct. The second
// part does not apply to references, since they are not objects.
- const VarDecl *DefVD;
- if (E && !isa<ParmVarDecl>(Var) &&
- Var->isUsableInConstantExpressions(SemaRef.Context) &&
- Var->getAnyInitializer(DefVD) && DefVD->checkInitIsICE()) {
+ if (E && IsVariableAConstantExpression(Var, SemaRef.Context)) {
+ // A reference initialized by a constant expression can never be
+ // odr-used, so simply ignore it.
+ // But a non-reference might get odr-used if it doesn't undergo
+ // an lvalue-to-rvalue or is discarded, so track it.
if (!Var->getType()->isReferenceType())
SemaRef.MaybeODRUseExprs.insert(E);
- } else
- MarkVarDeclODRUsed(SemaRef, Var, Loc);
+ }
+ else
+ MarkVarDeclODRUsed(Var, Loc, SemaRef, /*MaxFunctionScopeIndex ptr*/0);
}
/// \brief Mark a variable referenced, and check whether it is odr-used
#include "clang/AST/EvaluatedExprVisitor.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
+#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Sema/ParsedTemplate.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/ScopeInfo.h"
+#include "clang/Sema/SemaLambda.h"
#include "clang/Sema/TemplateDeduction.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/STLExtras.h"
return new (Context) CXXThisExpr(Loc, ThisTy, /*isImplicit*/true);
}
-void Sema::CheckCXXThisCapture(SourceLocation Loc, bool Explicit) {
+bool Sema::CheckCXXThisCapture(SourceLocation Loc, bool Explicit,
+ bool BuildAndDiagnose, const unsigned *const FunctionScopeIndexToStopAt) {
// We don't need to capture this in an unevaluated context.
if (isUnevaluatedContext() && !Explicit)
- return;
+ return true;
- // Otherwise, check that we can capture 'this'.
+ const unsigned MaxFunctionScopesIndex = FunctionScopeIndexToStopAt ?
+ *FunctionScopeIndexToStopAt : FunctionScopes.size() - 1;
+ // Otherwise, check that we can capture 'this'.
unsigned NumClosures = 0;
- for (unsigned idx = FunctionScopes.size() - 1; idx != 0; idx--) {
+ for (unsigned idx = MaxFunctionScopesIndex; idx != 0; idx--) {
if (CapturingScopeInfo *CSI =
dyn_cast<CapturingScopeInfo>(FunctionScopes[idx])) {
if (CSI->CXXThisCaptureIndex != 0) {
// 'this' is already being captured; there isn't anything more to do.
break;
}
-
+ LambdaScopeInfo *LSI = dyn_cast<LambdaScopeInfo>(CSI);
+ if (LSI && isGenericLambdaCallOperatorSpecialization(LSI->CallOperator)) {
+ // This context can't implicitly capture 'this'; fail out.
+ if (BuildAndDiagnose)
+ Diag(Loc, diag::err_this_capture) << Explicit;
+ return true;
+ }
if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByref ||
CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByval ||
CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_Block ||
continue;
}
// This context can't implicitly capture 'this'; fail out.
- Diag(Loc, diag::err_this_capture) << Explicit;
- return;
+ if (BuildAndDiagnose)
+ Diag(Loc, diag::err_this_capture) << Explicit;
+ return true;
}
break;
}
-
+ if (!BuildAndDiagnose) return false;
// Mark that we're implicitly capturing 'this' in all the scopes we skipped.
// FIXME: We need to delay this marking in PotentiallyPotentiallyEvaluated
// contexts.
- for (unsigned idx = FunctionScopes.size() - 1;
- NumClosures; --idx, --NumClosures) {
+ for (unsigned idx = MaxFunctionScopesIndex; NumClosures;
+ --idx, --NumClosures) {
CapturingScopeInfo *CSI = cast<CapturingScopeInfo>(FunctionScopes[idx]);
Expr *ThisExpr = 0;
QualType ThisTy = getCurrentThisType();
bool isNested = NumClosures > 1;
CSI->addThisCapture(isNested, Loc, ThisTy, ThisExpr);
}
+ return false;
}
ExprResult Sema::ActOnCXXThis(SourceLocation Loc) {
if (Res.isInvalid())
return Owned(E);
E = Res.take();
- }
+ }
return Owned(E);
}
return Owned(E);
}
+// If we can unambiguously determine whether Var can never be used
+// in a constant expression, return true.
+// - if the variable and its initializer are non-dependent, then
+// we can unambiguously check if the variable is a constant expression.
+// - if the initializer is not value dependent - we can determine whether
+// it can be used to initialize a constant expression. If Init can not
+// be used to initialize a constant expression we conclude that Var can
+// never be a constant expression.
+// - FXIME: if the initializer is dependent, we can still do some analysis and
+// identify certain cases unambiguously as non-const by using a Visitor:
+// - such as those that involve odr-use of a ParmVarDecl, involve a new
+// delete, lambda-expr, dynamic-cast, reinterpret-cast etc...
+static inline bool VariableCanNeverBeAConstantExpression(VarDecl *Var,
+ ASTContext &Context) {
+ if (isa<ParmVarDecl>(Var)) return true;
+ const VarDecl *DefVD = 0;
+
+ // If there is no initializer - this can not be a constant expression.
+ if (!Var->getAnyInitializer(DefVD)) return true;
+ assert(DefVD);
+ if (DefVD->isWeak()) return false;
+ EvaluatedStmt *Eval = DefVD->ensureEvaluatedStmt();
+
+ Expr *Init = cast<Expr>(Eval->Value);
+
+ if (Var->getType()->isDependentType() || Init->isValueDependent()) {
+ if (!Init->isValueDependent())
+ return !DefVD->checkInitIsICE();
+ // FIXME: We might still be able to do some analysis of Init here
+ // to conclude that even in a dependent setting, Init can never
+ // be a constexpr - but for now admit agnosticity.
+ return false;
+ }
+ return !IsVariableAConstantExpression(Var, Context);
+}
+
+/// \brief Check if the current lambda scope has any potential captures, and
+/// whether they can be captured by any of the enclosing lambdas that are
+/// ready to capture. If there is a lambda that can capture a nested
+/// potential-capture, go ahead and do so. Also, check to see if any
+/// variables are uncaptureable or do not involve an odr-use so do not
+/// need to be captured.
+
+static void CheckLambdaCaptures(Expr *const FE,
+ LambdaScopeInfo *const CurrentLSI, Sema &S) {
+
+ assert(!S.isUnevaluatedContext());
+ assert(S.CurContext->isDependentContext());
+ const bool IsFullExprInstantiationDependent =
+ FE->isInstantiationDependent();
+ // All the potentially captureable variables in the current nested
+ // lambda (within a generic outer lambda), must be captured by an
+ // outer lambda that is enclosed within a non-dependent context.
+
+ for (size_t I = 0, N = CurrentLSI->getNumPotentialVariableCaptures();
+ I != N; ++I) {
+ Expr *VarExpr = 0;
+ VarDecl *Var = 0;
+ CurrentLSI->getPotentialVariableCapture(I, Var, VarExpr);
+ //
+ if (CurrentLSI->isVariableExprMarkedAsNonODRUsed(VarExpr) &&
+ !IsFullExprInstantiationDependent)
+ continue;
+ // Climb up until we find a lambda that can capture:
+ // - a generic-or-non-generic lambda call operator that is enclosed
+ // within a non-dependent context.
+ unsigned FunctionScopeIndexOfCapturableLambda = 0;
+ CXXMethodDecl *NearestCapturableCallOp = 0;
+ if (NearestCapturableCallOp =
+ GetInnermostEnclosingCapturableLambda(
+ S.FunctionScopes,
+ FunctionScopeIndexOfCapturableLambda,
+ S.CurContext, Var, S)) {
+ MarkVarDeclODRUsed(Var, VarExpr->getExprLoc(),
+ S, &FunctionScopeIndexOfCapturableLambda);
+ }
+ const bool IsVarNeverAConstantExpression =
+ VariableCanNeverBeAConstantExpression(Var, S.Context);
+ if (!IsFullExprInstantiationDependent || IsVarNeverAConstantExpression) {
+ // This full expression is not instantiation dependent or the variable
+ // can not be used in a constant expression - which means
+ // this variable must be odr-used here, so diagnose a
+ // capture violation early, if the variable is un-captureable.
+ // This is purely for diagnosing errors early. Otherwise, this
+ // error would get diagnosed when the lambda becomes capture ready.
+ QualType CaptureType, DeclRefType;
+ SourceLocation ExprLoc = VarExpr->getExprLoc();
+ if (S.tryCaptureVariable(Var, ExprLoc, S.TryCapture_Implicit,
+ /*EllipsisLoc*/ SourceLocation(),
+ /*BuildAndDiagnose*/false, CaptureType,
+ DeclRefType, 0)) {
+ // We will never be able to capture this variable, and we need
+ // to be able to in any and all instantiations, so diagnose it.
+ S.tryCaptureVariable(Var, ExprLoc, S.TryCapture_Implicit,
+ /*EllipsisLoc*/ SourceLocation(),
+ /*BuildAndDiagnose*/true, CaptureType,
+ DeclRefType, 0);
+ }
+ }
+ }
+
+ if (CurrentLSI->hasPotentialThisCapture()) {
+ unsigned FunctionScopeIndexOfCapturableLambda = 0;
+ if (CXXMethodDecl *NearestCapturableCallOp =
+ GetInnermostEnclosingCapturableLambda(
+ S.FunctionScopes,
+ FunctionScopeIndexOfCapturableLambda,
+ S.CurContext, /*0 is 'this'*/ 0, S)) {
+ S.CheckCXXThisCapture(CurrentLSI->PotentialThisCaptureLocation,
+ /*Explicit*/false, /*BuildAndDiagnose*/true,
+ &FunctionScopeIndexOfCapturableLambda);
+ }
+ }
+ CurrentLSI->clearPotentialCaptures();
+}
+
+
ExprResult Sema::ActOnFinishFullExpr(Expr *FE, SourceLocation CC,
bool DiscardedValue,
bool IsConstexpr) {
}
CheckCompletedExpr(FullExpr.get(), CC, IsConstexpr);
+
+ // At the end of this full expression (which could be a deeply nested lambda),
+ // if there is a potential capture within the nested lambda, have the outer
+ // capture-able lambda try and capture it.
+ // Consider the following code:
+ // void f(int, int);
+ // void f(const int&, double);
+ // void foo() {
+ // const int x = 10, y = 20;
+ // auto L = [=](auto a) {
+ // auto M = [=](auto b) {
+ // f(x, b); <-- requires x to be captured by L and M
+ // f(y, a); <-- requires y to be captured by L, but not all Ms
+ // };
+ // };
+ // }
+
+ // FIXME: Also consider what happens for something like this that involves
+ // the gnu-extension statement-expressions or even lambda-init-captures:
+ // void f() {
+ // const int n = 0;
+ // auto L = [&](auto a) {
+ // +n + ({ 0; a; });
+ // };
+ // }
+ //
+ // Here, we see +n, and then the full-expression 0; ends, so we don't capture n
+ // (and instead remove it from our list of potential captures), and then the
+ // full-expression +n + ({ 0; }); ends, but it's too late for us to see that
+ // we need to capture n after all.
+
+ LambdaScopeInfo *const CurrentLSI = getCurLambda();
+ if (CurrentLSI && CurrentLSI->hasPotentialCaptures() &&
+ !FullExpr.isInvalid())
+ CheckLambdaCaptures(FE, CurrentLSI, *this);
return MaybeCreateExprWithCleanups(FullExpr);
}
//
//===----------------------------------------------------------------------===//
#include "clang/Sema/SemaInternal.h"
+#include "clang/AST/ASTLambda.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
BaseType = BaseType->castAs<PointerType>()->getPointeeType();
}
R.setBaseObjectType(BaseType);
-
+
+ LambdaScopeInfo *const CurLSI = getCurLambda();
+ // If this is an implicit member reference and the overloaded
+ // name refers to both static and non-static member functions
+ // (i.e. BaseExpr is null) and if we are currently processing a lambda,
+ // check if we should/can capture 'this'...
+ // Keep this example in mind:
+ // struct X {
+ // void f(int) { }
+ // static void f(double) { }
+ //
+ // int g() {
+ // auto L = [=](auto a) {
+ // return [](int i) {
+ // return [=](auto b) {
+ // f(b);
+ // //f(decltype(a){});
+ // };
+ // };
+ // };
+ // auto M = L(0.0);
+ // auto N = M(3);
+ // N(5.32); // OK, must not error.
+ // return 0;
+ // }
+ // };
+ //
+ if (!BaseExpr && CurLSI) {
+ SourceLocation Loc = R.getNameLoc();
+ if (SS.getRange().isValid())
+ Loc = SS.getRange().getBegin();
+ DeclContext *EnclosingFunctionCtx = CurContext->getParent()->getParent();
+ // If the enclosing function is not dependent, then this lambda is
+ // capture ready, so if we can capture this, do so.
+ if (!EnclosingFunctionCtx->isDependentContext()) {
+ // If the current lambda and all enclosing lambdas can capture 'this' -
+ // then go ahead and capture 'this' (since our unresolved overload set
+ // contains both static and non-static member functions).
+ if (!CheckCXXThisCapture(Loc, /*Explcit*/false, /*Diagnose*/false))
+ CheckCXXThisCapture(Loc);
+ } else if (CurContext->isDependentContext()) {
+ // ... since this is an implicit member reference, that might potentially
+ // involve a 'this' capture, mark 'this' for potential capture in
+ // enclosing lambdas.
+ if (CurLSI->ImpCaptureStyle != CurLSI->ImpCap_None)
+ CurLSI->addPotentialThisCapture(Loc);
+ }
+ }
const DeclarationNameInfo &MemberNameInfo = R.getLookupNameInfo();
DeclarationName MemberName = MemberNameInfo.getName();
SourceLocation MemberLoc = MemberNameInfo.getLoc();
#include "clang/Sema/Scope.h"
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/SemaInternal.h"
+#include "clang/Sema/SemaLambda.h"
#include "TypeLocBuilder.h"
using namespace clang;
using namespace sema;
+// returns -1 if none of the lambdas on the scope stack can capture.
+// A lambda 'L' is capture-ready for a certain variable 'V' if,
+// - its enclosing context is non-dependent
+// - and if the chain of lambdas between L and the lambda in which
+// V is potentially used, call all capture or have captured V.
+static inline int GetScopeIndexOfNearestCaptureReadyLambda(
+ ArrayRef<clang::sema::FunctionScopeInfo*> FunctionScopes,
+ DeclContext *const CurContext, VarDecl *VD) {
+
+ DeclContext *EnclosingDC = CurContext;
+ // If VD is null, we are attempting to capture 'this'
+ const bool IsCapturingThis = !VD;
+ const bool IsCapturingVariable = !IsCapturingThis;
+ int RetIndex = -1;
+ unsigned CurScopeIndex = FunctionScopes.size() - 1;
+ while (!EnclosingDC->isTranslationUnit() &&
+ EnclosingDC->isDependentContext() && isLambdaCallOperator(EnclosingDC)) {
+ RetIndex = CurScopeIndex;
+ clang::sema::LambdaScopeInfo *LSI =
+ cast<sema::LambdaScopeInfo>(FunctionScopes[CurScopeIndex]);
+ // We have crawled up to an intervening lambda that contains the
+ // variable declaration - so not only does it not need to capture;
+ // none of the enclosing lambdas need to capture it, and since all
+ // other nested lambdas are dependent (otherwise we wouldn't have
+ // arrived here) - we don't yet have a lambda that can capture the
+ // variable.
+ if (IsCapturingVariable && VD->getDeclContext()->Equals(EnclosingDC))
+ return -1;
+ // All intervening lambda call operators have to be able to capture.
+ // If they do not have a default implicit capture, check to see
+ // if the entity has already been explicitly captured.
+ // If even a single dependent enclosing lambda lacks the capability
+ // to ever capture this variable, there is no further enclosing
+ // non-dependent lambda that can capture this variable.
+ if (LSI->ImpCaptureStyle == sema::LambdaScopeInfo::ImpCap_None) {
+ if (IsCapturingVariable && !LSI->isCaptured(VD))
+ return -1;
+ if (IsCapturingThis && !LSI->isCXXThisCaptured())
+ return -1;
+ }
+ EnclosingDC = getLambdaAwareParentOfDeclContext(EnclosingDC);
+ --CurScopeIndex;
+ }
+ // If the enclosingDC is not dependent, then the immediately nested lambda
+ // is capture-ready.
+ if (!EnclosingDC->isDependentContext())
+ return RetIndex;
+ return -1;
+}
+// Given a lambda's call operator and a variable (or null for 'this'),
+// compute the nearest enclosing lambda that is capture-ready (i.e
+// the enclosing context is not dependent, and all intervening lambdas can
+// either implicitly or explicitly capture Var)
+//
+// The approach is as follows, for the entity VD ('this' if null):
+// - start with the current lambda
+// - if it is non-dependent and can capture VD, return it.
+// - if it is dependent and has an implicit or explicit capture, check its parent
+// whether the parent is non-depdendent and all its intervening lambdas
+// can capture, if so return the child.
+// [Note: When we hit a generic lambda specialization, do not climb up
+// the scope stack any further since not only do we not need to,
+// the scope stack will often not be synchronized with any lambdas
+// enclosing the specialized generic lambda]
+//
+// Return the CallOperator of the capturable lambda and set function scope
+// index to the correct index within the function scope stack to correspond
+// to the capturable lambda.
+// If VarDecl *VD is null, we check for 'this' capture.
+CXXMethodDecl* clang::GetInnermostEnclosingCapturableLambda(
+ ArrayRef<sema::FunctionScopeInfo*> FunctionScopes,
+ unsigned &FunctionScopeIndex,
+ DeclContext *const CurContext, VarDecl *VD,
+ Sema &S) {
+
+ const int IndexOfCaptureReadyLambda =
+ GetScopeIndexOfNearestCaptureReadyLambda(FunctionScopes,CurContext, VD);
+ if (IndexOfCaptureReadyLambda == -1) return 0;
+ assert(IndexOfCaptureReadyLambda >= 0);
+ const unsigned IndexOfCaptureReadyLambdaU =
+ static_cast<unsigned>(IndexOfCaptureReadyLambda);
+ sema::LambdaScopeInfo *const CaptureReadyLambdaLSI =
+ cast<sema::LambdaScopeInfo>(FunctionScopes[IndexOfCaptureReadyLambdaU]);
+ // If VD is null, we are attempting to capture 'this'
+ const bool IsCapturingThis = !VD;
+ const bool IsCapturingVariable = !IsCapturingThis;
+
+ if (IsCapturingVariable) {
+ // Now check to see if this lambda can truly capture, and also
+ // if all enclosing lambdas of this lambda allow this capture.
+ QualType CaptureType, DeclRefType;
+ const bool CanCaptureVariable = !S.tryCaptureVariable(VD,
+ /*ExprVarIsUsedInLoc*/SourceLocation(), clang::Sema::TryCapture_Implicit,
+ /*EllipsisLoc*/ SourceLocation(),
+ /*BuildAndDiagnose*/false, CaptureType, DeclRefType,
+ &IndexOfCaptureReadyLambdaU);
+ if (!CanCaptureVariable) return 0;
+ } else {
+ const bool CanCaptureThis = !S.CheckCXXThisCapture(
+ CaptureReadyLambdaLSI->PotentialThisCaptureLocation, false, false,
+ &IndexOfCaptureReadyLambdaU);
+ if (!CanCaptureThis) return 0;
+ } // end 'this' capture test
+ FunctionScopeIndex = IndexOfCaptureReadyLambdaU;
+ return CaptureReadyLambdaLSI->CallOperator;
+}
static inline TemplateParameterList *
getGenericLambdaTemplateParameterList(LambdaScopeInfo *LSI, Sema &SemaRef) {
break;
}
}
- // TODO: Implement capturing.
- if (Lambda->isGenericLambda()) {
- if (!Captures.empty() || Lambda->getCaptureDefault() != LCD_None) {
- Diag(Lambda->getIntroducerRange().getBegin(),
- diag::err_glambda_not_fully_implemented)
- << " capturing not implemented yet";
- return ExprError();
- }
- }
+
return MaybeBindToTemporary(Lambda);
}
// RUN: %clang_cc1 -fsyntax-only -std=c++1y %s -verify
-
+//expected-no-diagnostics
namespace lambda_capturing {
// FIXME: Once return type deduction is implemented for generic lambdas
// this will need to be updated.
void test() {
int i = 10;
{
- auto L = [=](auto a) -> int { //expected-error{{unimplemented}}
+ auto L = [=](auto a) -> int {
return i + a;
};
L(3);
}
{
- auto L = [i](auto a) -> int { //expected-error{{unimplemented}}
+ auto L = [i](auto a) -> int {
return i + a;
};
L(3);
}
{
- auto L = [i = i](auto a) -> int { //expected-error{{unimplemented}}
+ auto L = [i=i](auto a) -> int {
return i + a;
};
L(3);
virtual ~P();\r
};\r
\r
-void unevaluated_operand(P &p, int i) {\r
+void unevaluated_operand(P &p, int i) { //expected-note{{declared here}}\r
// FIXME: this should only emit one error.\r
int i2 = sizeof([](auto a, auto b)->void{}(3, '4')); // expected-error{{lambda expression in an unevaluated operand}} \\r
// expected-error{{invalid application of 'sizeof'}}\r
const std::type_info &ti1 = typeid([](auto &a) -> P& { static P p; return p; }(i));\r
- const std::type_info &ti2 = typeid([](auto) -> int { return i; }(i)); // expected-error{{lambda expression in an unevaluated operand}}\r
+ const std::type_info &ti2 = typeid([](auto) -> int { return i; }(i)); // expected-error{{lambda expression in an unevaluated operand}}\\r
+ // expected-error{{cannot be implicitly captured}}\\r
+ // expected-note{{begins here}}\r
}\r
namespace nested_lambdas {\r
int test() {\r
auto L = [](auto a) {\r
- return [=](auto b) { //expected-error{{unimplemented}}\r
+ return [=](auto b) { \r
return a + b;\r
};\r
};\r
--- /dev/null
+// RUN: %clang_cc1 -std=c++1y -verify -fsyntax-only -fblocks -emit-llvm-only %s
+// DONTRUNYET: %clang_cc1 -std=c++1y -verify -fsyntax-only -fblocks -fdelayed-template-parsing %s -DDELAYED_TEMPLATE_PARSING
+// DONTRUNYET: %clang_cc1 -std=c++1y -verify -fsyntax-only -fblocks -fms-extensions %s -DMS_EXTENSIONS
+// DONTRUNYET: %clang_cc1 -std=c++1y -verify -fsyntax-only -fblocks -fdelayed-template-parsing -fms-extensions %s -DMS_EXTENSIONS -DDELAYED_TEMPLATE_PARSING
+
+constexpr int ODRUSE_SZ = sizeof(char);
+
+template<class T, int N>
+void f(T, const int (&)[N]) { }
+
+template<class T>
+void f(const T&, const int (&)[ODRUSE_SZ]) { }
+
+#define DEFINE_SELECTOR(x) \
+ int selector_ ## x[sizeof(x) == ODRUSE_SZ ? ODRUSE_SZ : ODRUSE_SZ + 5]
+
+#define F_CALL(x, a) f(x, selector_ ## a)
+
+// This is a risky assumption, because if an empty class gets captured by value
+// the lambda's size will still be '1'
+#define ASSERT_NO_CAPTURES(L) static_assert(sizeof(L) == 1, "size of closure with no captures must be 1")
+#define ASSERT_CLOSURE_SIZE_EXACT(L, N) static_assert(sizeof(L) == (N), "size of closure must be " #N)
+#define ASSERT_CLOSURE_SIZE(L, N) static_assert(sizeof(L) >= (N), "size of closure must be >=" #N)
+
+
+namespace sample {
+ struct X {
+ int i;
+ X(int i) : i(i) { }
+ };
+}
+
+namespace test_transformations_in_templates {
+template<class T> void foo(T t) {
+ auto L = [](auto a) { return a; };
+}
+template<class T> void foo2(T t) {
+ auto L = [](auto a) -> void {
+ auto M = [](char b) -> void {
+ auto N = [](auto c) -> void {
+ int selector[sizeof(c) == 1 ?
+ (sizeof(b) == 1 ? 1 : 2)
+ : 2
+ ]{};
+ };
+ N('a');
+ };
+ };
+ L(3.14);
+}
+
+void doit() {
+ foo(3);
+ foo('a');
+ foo2('A');
+}
+}
+
+namespace test_return_type_deduction {
+
+void doit() {
+
+ auto L = [](auto a, auto b) {
+ if ( a > b ) return a;
+ return b;
+ };
+ L(2, 4);
+ {
+ auto L2 = [](auto a, int i) {
+ return a + i;
+ };
+ L2(3.14, 2);
+ }
+ {
+ int a; //expected-note{{declared here}}
+ auto B = []() { return ^{ return a; }; }; //expected-error{{cannot be implicitly capture}}\
+ //expected-note{{begins here}}
+ //[](){ return ({int b = 5; return 'c'; 'x';}); };
+
+ //auto X = ^{ return a; };
+
+ //auto Y = []() -> auto { return 3; return 'c'; };
+
+ }
+}
+}
+
+
+namespace test_no_capture{
+void doit() {
+ const int x = 10; //expected-note{{declared here}}
+ {
+ // should not capture 'x' - variable undergoes lvalue-to-rvalue
+ auto L = [=](auto a) {
+ int y = x;
+ return a + y;
+ };
+ ASSERT_NO_CAPTURES(L);
+ }
+ {
+ // should not capture 'x' - even though certain instantiations require
+ auto L = [](auto a) { //expected-note{{begins here}}
+ DEFINE_SELECTOR(a);
+ F_CALL(x, a); //expected-error{{'x' cannot be implicitly captured}}
+ };
+ ASSERT_NO_CAPTURES(L);
+ L('s'); //expected-note{{in instantiation of}}
+ }
+ {
+ // Does not capture because no default capture in inner most lambda 'b'
+ auto L = [=](auto a) {
+ return [=](int p) {
+ return [](auto b) {
+ DEFINE_SELECTOR(a);
+ F_CALL(x, a);
+ return 0;
+ };
+ };
+ };
+ ASSERT_NO_CAPTURES(L);
+ }
+} // doit
+} // namespace
+
+namespace test_capture_of_potentially_evaluated_expression {
+void doit() {
+ const int x = 5;
+ {
+ auto L = [=](auto a) {
+ DEFINE_SELECTOR(a);
+ F_CALL(x, a);
+ };
+ static_assert(sizeof(L) == 4, "Must be captured");
+ }
+ {
+ int j = 0; //expected-note{{declared}}
+ auto L = [](auto a) { //expected-note{{begins here}}
+ return j + 1; //expected-error{{cannot be implicitly captured}}
+ };
+ }
+ {
+ const int x = 10;
+ auto L = [](auto a) {
+ //const int y = 20;
+ return [](int p) {
+ return [](auto b) {
+ DEFINE_SELECTOR(a);
+ F_CALL(x, a);
+ return 0;
+ };
+ };
+ };
+ auto M = L(3);
+ auto N = M(5);
+
+ }
+
+ { // if the nested capture does not implicitly or explicitly allow any captures
+ // nothing should capture - and instantiations will create errors if needed.
+ const int x = 0;
+ auto L = [=](auto a) { // <-- #A
+ const int y = 0;
+ return [](auto b) { // <-- #B
+ int c[sizeof(b)];
+ f(x, c);
+ f(y, c);
+ int i = x;
+ };
+ };
+ ASSERT_NO_CAPTURES(L);
+ auto M_int = L(2);
+ ASSERT_NO_CAPTURES(M_int);
+ }
+ { // Permutations of this example must be thoroughly tested!
+ const int x = 0;
+ sample::X cx{5};
+ auto L = [=](auto a) {
+ const int z = 3;
+ return [&,a](auto b) {
+ const int y = 5;
+ return [=](auto c) {
+ int d[sizeof(a) == sizeof(c) || sizeof(c) == sizeof(b) ? 2 : 1];
+ f(x, d);
+ f(y, d);
+ f(z, d);
+ decltype(a) A = a;
+ decltype(b) B = b;
+ const int &i = cx.i;
+ };
+ };
+ };
+ auto M = L(3)(3.5);
+ M(3.14);
+ }
+}
+namespace Test_no_capture_of_clearly_no_odr_use {
+auto foo() {
+ const int x = 10;
+ auto L = [=](auto a) {
+ return [=](auto b) {
+ return [=](auto c) {
+ int A = x;
+ return A;
+ };
+ };
+ };
+ auto M = L(1);
+ auto N = M(2.14);
+ ASSERT_NO_CAPTURES(L);
+ ASSERT_NO_CAPTURES(N);
+
+ return 0;
+}
+}
+
+namespace Test_capture_of_odr_use_var {
+auto foo() {
+ const int x = 10;
+ auto L = [=](auto a) {
+ return [=](auto b) {
+ return [=](auto c) {
+ int A = x;
+ const int &i = x;
+ decltype(a) A2 = a;
+ return A;
+ };
+ };
+ };
+ auto M_int = L(1);
+ auto N_int_int = M_int(2);
+ ASSERT_CLOSURE_SIZE_EXACT(L, sizeof(x));
+ // M_int captures both a & x
+ ASSERT_CLOSURE_SIZE_EXACT(M_int, sizeof(x) + sizeof(int));
+ // N_int_int captures both a & x
+ ASSERT_CLOSURE_SIZE_EXACT(N_int_int, sizeof(x) + sizeof(int));
+ auto M_double = L(3.14);
+ ASSERT_CLOSURE_SIZE(M_double, sizeof(x) + sizeof(double));
+
+ return 0;
+}
+auto run = foo();
+}
+
+}
+namespace more_nested_captures_1 {
+template<class T> struct Y {
+ static void f(int, double, ...) { }
+ template<class R>
+ static void f(const int&, R, ...) { }
+ template<class R>
+ void foo(R t) {
+ const int x = 10; //expected-note{{declared here}}
+ auto L = [](auto a) {
+ return [=](auto b) {
+ return [=](auto c) {
+ f(x, c, b, a); //expected-error{{reference to local variable 'x'}}
+ return 0;
+ };
+ };
+ };
+ auto M = L(t);
+ auto N = M('b');
+ N(3.14);
+ N(5); //expected-note{{in instantiation of}}
+ }
+};
+Y<int> yi;
+int run = (yi.foo(3.14), 0); //expected-note{{in instantiation of}}
+}
+
+
+namespace more_nested_captures_1_1 {
+template<class T> struct Y {
+ static void f(int, double, ...) { }
+ template<class R>
+ static void f(const int&, R, ...) { }
+ template<class R>
+ void foo(R t) {
+ const int x = 10; //expected-note{{declared here}}
+ auto L = [](auto a) {
+ return [=](char b) {
+ return [=](auto c) {
+ f(x, c, b, a); //expected-error{{reference to local variable 'x'}}
+ return 0;
+ };
+ };
+ };
+ auto M = L(t);
+ auto N = M('b');
+ N(3.14);
+ N(5); //expected-note{{in instantiation of}}
+ }
+};
+Y<int> yi;
+int run = (yi.foo(3.14), 0); //expected-note{{in instantiation of}}
+}
+namespace more_nested_captures_1_2 {
+template<class T> struct Y {
+ static void f(int, double, ...) { }
+ template<class R>
+ static void f(const int&, R, ...) { }
+ template<class R>
+ void foo(R t) {
+ const int x = 10;
+ auto L = [=](auto a) {
+ return [=](char b) {
+ return [=](auto c) {
+ f(x, c, b, a);
+ return 0;
+ };
+ };
+ };
+ auto M = L(t);
+ auto N = M('b');
+ N(3.14);
+ N(5);
+ }
+};
+Y<int> yi;
+int run = (yi.foo(3.14), 0);
+}
+
+namespace more_nested_captures_1_3 {
+template<class T> struct Y {
+ static void f(int, double, ...) { }
+ template<class R>
+ static void f(const int&, R, ...) { }
+ template<class R>
+ void foo(R t) {
+ const int x = 10; //expected-note{{declared here}}
+ auto L = [=](auto a) {
+ return [](auto b) {
+ const int y = 0;
+ return [=](auto c) {
+ f(x, c, b); //expected-error{{reference to local variable 'x'}}
+ f(y, b, c);
+ return 0;
+ };
+ };
+ };
+ auto M = L(t);
+ auto N = M('b');
+ N(3.14);
+ N(5); //expected-note{{in instantiation of}}
+ }
+};
+Y<int> yi;
+int run = (yi.foo(3.14), 0); //expected-note{{in instantiation of}}
+}
+
+
+namespace more_nested_captures_1_4 {
+template<class T> struct Y {
+ static void f(int, double, ...) { }
+ template<class R>
+ static void f(const int&, R, ...) { }
+ template<class R>
+ void foo(R t) {
+ const int x = 10; //expected-note{{declared here}}
+ auto L = [=](auto a) {
+ T t2{t};
+ return [](auto b) {
+ const int y = 0; //expected-note{{declared here}}
+ return [](auto c) { //expected-note 2{{lambda expression begins here}}
+ f(x, c); //expected-error{{variable 'x'}}
+ f(y, c); //expected-error{{variable 'y'}}
+ return 0;
+ };
+ };
+ };
+ auto M = L(t);
+ auto N_char = M('b');
+ N_char(3.14);
+ auto N_double = M(3.14);
+ N_double(3.14);
+ N_char(3); //expected-note{{in instantiation of}}
+ }
+};
+Y<int> yi;
+int run = (yi.foo('a'), 0); //expected-note{{in instantiation of}}
+}
+
+
+namespace more_nested_captures_2 {
+template<class T> struct Y {
+ static void f(int, double) { }
+ template<class R>
+ static void f(const int&, R) { }
+ template<class R>
+ void foo(R t) {
+ const int x = 10;
+ auto L = [=](auto a) {
+ return [=](auto b) {
+ return [=](auto c) {
+ f(x, c);
+ return 0;
+ };
+ };
+ };
+ auto M = L(t);
+ auto N = M('b');
+ N(3);
+ N(3.14);
+ }
+};
+Y<int> yi;
+int run = (yi.foo(3.14), 0);
+
+}
+
+namespace more_nested_captures_3 {
+template<class T> struct Y {
+ static void f(int, double) { }
+ template<class R>
+ static void f(const int&, R) { }
+ template<class R>
+ void foo(R t) {
+ const int x = 10; //expected-note{{declared here}}
+ auto L = [](auto a) {
+ return [=](auto b) {
+ return [=](auto c) {
+ f(x, c); //expected-error{{reference to local variable 'x'}}
+ return 0;
+ };
+ };
+ };
+ auto M = L(t);
+ auto N = M('b');
+ N(3); //expected-note{{in instantiation of}}
+ N(3.14);
+ }
+};
+Y<int> yi;
+int run = (yi.foo(3.14), 0); //expected-note{{in instantiation of}}
+
+}
+
+namespace more_nested_captures_4 {
+template<class T> struct Y {
+ static void f(int, double) { }
+ template<class R>
+ static void f(const int&, R) { }
+ template<class R>
+ void foo(R t) {
+ const int x = 10; //expected-note{{'x' declared here}}
+ auto L = [](auto a) {
+ return [=](char b) {
+ return [=](auto c) {
+ f(x, c); //expected-error{{reference to local variable 'x'}}
+ return 0;
+ };
+ };
+ };
+ auto M = L(t);
+ auto N = M('b');
+ N(3); //expected-note{{in instantiation of}}
+ N(3.14);
+ }
+};
+Y<int> yi;
+int run = (yi.foo(3.14), 0); //expected-note{{in instantiation of}}
+
+}
+
+namespace more_nested_captures_5 {
+template<class T> struct Y {
+ static void f(int, double) { }
+ template<class R>
+ static void f(const int&, R) { }
+ template<class R>
+ void foo(R t) {
+ const int x = 10;
+ auto L = [=](auto a) {
+ return [=](char b) {
+ return [=](auto c) {
+ f(x, c);
+ return 0;
+ };
+ };
+ };
+ auto M = L(t);
+ auto N = M('b');
+ N(3);
+ N(3.14);
+ }
+};
+Y<int> yi;
+int run = (yi.foo(3.14), 0);
+
+}
+
+namespace lambdas_in_NSDMIs {
+template<class T>
+ struct L {
+ T t{};
+ T t2 = ([](auto a) { return [](auto b) { return b; };})(t)(t);
+ T t3 = ([](auto a) { return a; })(t);
+ };
+ L<int> l;
+ int run = l.t2;
+}
+namespace test_nested_decltypes_in_trailing_return_types {
+int foo() {
+ auto L = [](auto a) {
+ return [](auto b, decltype(a) b2) -> decltype(a) {
+ return decltype(a){};
+ };
+ };
+ auto M = L(3.14);
+ M('a', 6.26);
+ return 0;
+}
+}
+
+namespace more_this_capture_1 {
+struct X {
+ void f(int) { }
+ static void f(double) { }
+ void foo() {
+ {
+ auto L = [=](auto a) {
+ f(a);
+ };
+ L(3);
+ L(3.13);
+ }
+ {
+ auto L = [](auto a) {
+ f(a); //expected-error{{this}}
+ };
+ L(3.13);
+ L(2); //expected-note{{in instantiation}}
+ }
+ }
+
+ int g() {
+ auto L = [=](auto a) {
+ return [](int i) {
+ return [=](auto b) {
+ f(b);
+ int x = i;
+ };
+ };
+ };
+ auto M = L(0.0);
+ auto N = M(3);
+ N(5.32); // OK
+ return 0;
+ }
+};
+int run = X{}.g();
+}
+namespace more_this_capture_1_1 {
+struct X {
+ void f(int) { }
+ static void f(double) { }
+
+ int g() {
+ auto L = [=](auto a) {
+ return [](int i) {
+ return [=](auto b) {
+ f(decltype(a){}); //expected-error{{this}}
+ int x = i;
+ };
+ };
+ };
+ auto M = L(0.0);
+ auto N = M(3);
+ N(5.32); // OK
+ L(3); // expected-note{{instantiation}}
+ return 0;
+ }
+};
+int run = X{}.g();
+}
+
+namespace more_this_capture_1_1_1 {
+struct X {
+ void f(int) { }
+ static void f(double) { }
+
+ int g() {
+ auto L = [=](auto a) {
+ return [](auto b) {
+ return [=](int i) {
+ f(b);
+ f(decltype(a){}); //expected-error{{this}}
+ };
+ };
+ };
+ auto M = L(0.0); // OK
+ auto N = M(3.3); //OK
+ auto M_int = L(0); //expected-note{{instantiation}}
+ return 0;
+ }
+};
+int run = X{}.g();
+}
+
+
+namespace more_this_capture_1_1_1_1 {
+struct X {
+ void f(int) { }
+ static void f(double) { }
+
+ int g() {
+ auto L = [=](auto a) {
+ return [](auto b) {
+ return [=](int i) {
+ f(b); //expected-error{{this}}
+ f(decltype(a){});
+ };
+ };
+ };
+ auto M_double = L(0.0); // OK
+ auto N = M_double(3); //expected-note{{instantiation}}
+
+ return 0;
+ }
+};
+int run = X{}.g();
+}
+
+namespace more_this_capture_2 {
+struct X {
+ void f(int) { }
+ static void f(double) { }
+
+ int g() {
+ auto L = [=](auto a) {
+ return [](int i) {
+ return [=](auto b) {
+ f(b); //expected-error{{'this' cannot}}
+ int x = i;
+ };
+ };
+ };
+ auto M = L(0.0);
+ auto N = M(3);
+ N(5); // NOT OK expected-note{{in instantiation of}}
+ return 0;
+ }
+};
+int run = X{}.g();
+}
+namespace diagnose_errors_early_in_generic_lambdas {
+
+int foo()
+{
+
+ { // This variable is used and must be caught early, do not need instantiation
+ const int x = 0; //expected-note{{declared}}
+ auto L = [](auto a) { //expected-note{{begins}}
+ const int &r = x; //expected-error{{variable}}
+ };
+ }
+ { // This variable is not used
+ const int x = 0;
+ auto L = [](auto a) {
+ int i = x;
+ };
+ }
+ {
+
+ const int x = 0; //expected-note{{declared}}
+ auto L = [=](auto a) { // <-- #A
+ const int y = 0;
+ return [](auto b) { //expected-note{{begins}}
+ int c[sizeof(b)];
+ f(x, c);
+ f(y, c);
+ int i = x;
+ // This use will always be an error regardless of instantatiation
+ // so diagnose this early.
+ const int &r = x; //expected-error{{variable}}
+ };
+ };
+
+ }
+ return 0;
+}
+
+int run = foo();
+}
+
+namespace generic_nongenerics_interleaved_1 {
+int foo() {
+ {
+ auto L = [](int a) {
+ int y = 10;
+ return [=](auto b) {
+ return a + y;
+ };
+ };
+ auto M = L(3);
+ M(5);
+ }
+ {
+ int x;
+ auto L = [](int a) {
+ int y = 10;
+ return [=](auto b) {
+ return a + y;
+ };
+ };
+ auto M = L(3);
+ M(5);
+ }
+ {
+ // FIXME: why are there 2 error messages here?
+ int x;
+ auto L = [](auto a) { //expected-note {{declared here}}
+ int y = 10; //expected-note {{declared here}}
+ return [](int b) { //expected-note 2{{expression begins here}}
+ return [=] (auto c) {
+ return a + y; //expected-error 2{{cannot be implicitly captured}}
+ };
+ };
+ };
+ }
+ {
+ int x;
+ auto L = [](auto a) {
+ int y = 10;
+ return [=](int b) {
+ return [=] (auto c) {
+ return a + y;
+ };
+ };
+ };
+ }
+ return 1;
+}
+
+int run = foo();
+}
+namespace dont_capture_refs_if_initialized_with_constant_expressions {
+
+auto foo(int i) {
+ // This is surprisingly not odr-used within the lambda!
+ static int j;
+ j = i;
+ int &ref_j = j;
+ return [](auto a) { return ref_j; }; // ok
+}
+
+template<class T>
+auto foo2(T t) {
+ // This is surprisingly not odr-used within the lambda!
+ static T j;
+ j = t;
+ T &ref_j = j;
+ return [](auto a) { return ref_j; }; // ok
+}
+
+int do_test() {
+ auto L = foo(3);
+ auto L_int = L(3);
+ auto L_char = L('a');
+ auto L1 = foo2(3.14);
+ auto L1_int = L1(3);
+ auto L1_char = L1('a');
+ return 0;
+}
+
+} // dont_capture_refs_if_initialized_with_constant_expressions
+
+namespace test_conversion_to_fptr {
+
+template<class T> struct X {
+
+ T (*fp)(T) = [](auto a) { return a; };
+
+};
+
+X<int> xi;
+
+template<class T>
+void fooT(T t, T (*fp)(T) = [](auto a) { return a; }) {
+ fp(t);
+}
+
+int test() {
+{
+ auto L = [](auto a) { return a; };
+ int (*fp)(int) = L;
+ fp(5);
+ L(3);
+ char (*fc)(char) = L;
+ fc('b');
+ L('c');
+ double (*fd)(double) = L;
+ fd(3.14);
+ fd(6.26);
+ L(4.25);
+}
+{
+ auto L = [](auto a) ->int { return a; }; //expected-note 2{{candidate template ignored}}
+ int (*fp)(int) = L;
+ char (*fc)(char) = L; //expected-error{{no viable conversion}}
+ double (*fd)(double) = L; //expected-error{{no viable conversion}}
+}
+{
+ int x = 5;
+ auto L = [=](auto b, char c = 'x') {
+ int i = x;
+ return [](auto a) ->decltype(a) { return a; };
+ };
+ int (*fp)(int) = L(8);
+ fp(5);
+ L(3);
+ char (*fc)(char) = L('a');
+ fc('b');
+ L('c');
+ double (*fd)(double) = L(3.14);
+ fd(3.14);
+ fd(6.26);
+
+}
+{
+ auto L = [=](auto b) {
+ return [](auto a) ->decltype(b)* { return (decltype(b)*)0; };
+ };
+ int* (*fp)(int) = L(8);
+ fp(5);
+ L(3);
+ char* (*fc)(char) = L('a');
+ fc('b');
+ L('c');
+ double* (*fd)(double) = L(3.14);
+ fd(3.14);
+ fd(6.26);
+}
+{
+ auto L = [=](auto b) {
+ return [](auto a) ->decltype(b)* { return (decltype(b)*)0; }; //expected-note{{candidate template ignored}}
+ };
+ char* (*fp)(int) = L('8');
+ fp(5);
+ char* (*fc)(char) = L('a');
+ fc('b');
+ double* (*fi)(int) = L(3.14);
+ fi(5);
+ int* (*fi2)(int) = L(3.14); //expected-error{{no viable conversion}}
+}
+
+{
+ auto L = [=](auto b) {
+ return [](auto a) {
+ return [=](auto c) {
+ return [](auto d) ->decltype(a + b + c + d) { return d; };
+ };
+ };
+ };
+ int (*fp)(int) = L('8')(3)(short{});
+ double (*fs)(char) = L(3.14)(short{})('4');
+}
+
+ fooT(3);
+ fooT('a');
+ fooT(3.14);
+ fooT("abcdefg");
+ return 0;
+}
+int run2 = test();
+
+}
+
+
+namespace this_capture {
+void f(char, int) { }
+template<class T>
+void f(T, const int&) { }
+
+struct X {
+ int x = 0;
+ void foo() {
+ auto L = [=](auto a) {
+ return [=](auto b) {
+ //f(a, x++);
+ x++;
+ };
+ };
+ L('a')(5);
+ L('b')(4);
+ L(3.14)('3');
+
+ }
+
+};
+
+int run = (X{}.foo(), 0);
+
+namespace this_capture_unresolvable {
+struct X {
+ void f(int) { }
+ static void f(double) { }
+
+ int g() {
+ auto lam = [=](auto a) { f(a); }; // captures 'this'
+ lam(0); // ok.
+ lam(0.0); // ok.
+ return 0;
+ }
+ int g2() {
+ auto lam = [](auto a) { f(a); }; // expected-error{{'this'}}
+ lam(0); // expected-note{{in instantiation of}}
+ lam(0.0); // ok.
+ return 0;
+ }
+ double (*fd)(double) = [](auto a) { f(a); return a; };
+
+};
+
+int run = X{}.g();
+
+}
+
+namespace check_nsdmi_and_this_capture_of_member_functions {
+
+struct FunctorDouble {
+ template<class T> FunctorDouble(T t) { t(2.14); };
+};
+struct FunctorInt {
+ template<class T> FunctorInt(T t) { t(2); }; //expected-note{{in instantiation of}}
+};
+
+template<class T> struct YUnresolvable {
+ void f(int) { }
+ static void f(double) { }
+
+ T t = [](auto a) { f(a); return a; };
+ T t2 = [=](auto b) { f(b); return b; };
+};
+
+template<class T> struct YUnresolvable2 {
+ void f(int) { }
+ static void f(double) { }
+
+ T t = [](auto a) { f(a); return a; }; //expected-error{{'this'}} \
+ //expected-note{{in instantiation of}}
+ T t2 = [=](auto b) { f(b); return b; };
+};
+
+
+YUnresolvable<FunctorDouble> yud;
+// This will cause an error since it call's with an int and calls a member function.
+YUnresolvable2<FunctorInt> yui;
+
+
+template<class T> struct YOnlyStatic {
+ static void f(double) { }
+
+ T t = [](auto a) { f(a); return a; };
+};
+YOnlyStatic<FunctorDouble> yos;
+template<class T> struct YOnlyNonStatic {
+ void f(int) { }
+
+ T t = [](auto a) { f(a); return a; }; //expected-error{{'this'}}
+};
+
+
+}
+
+
+namespace check_nsdmi_and_this_capture_of_data_members {
+
+struct FunctorDouble {
+ template<class T> FunctorDouble(T t) { t(2.14); };
+};
+struct FunctorInt {
+ template<class T> FunctorInt(T t) { t(2); };
+};
+
+template<class T> struct YThisCapture {
+ const int x = 10;
+ static double d;
+ T t = [](auto a) { return x; }; //expected-error{{'this'}}
+ T t2 = [](auto b) { return d; };
+ T t3 = [this](auto a) {
+ return [=](auto b) {
+ return x;
+ };
+ };
+ T t4 = [=](auto a) {
+ return [=](auto b) {
+ return x;
+ };
+ };
+ T t5 = [](auto a) {
+ return [=](auto b) {
+ return x; //expected-error{{'this'}}
+ };
+ };
+};
+
+template<class T> double YThisCapture<T>::d = 3.14;
+
+
+}
+
+
+#ifdef DELAYED_TEMPLATE_PARSING
+template<class T> void foo_no_error(T t) {
+ auto L = []()
+ { return t; };
+}
+template<class T> void foo(T t) { //expected-note 2{{declared here}}
+ auto L = []() //expected-note 2{{begins here}}
+ { return t; }; //expected-error 2{{cannot be implicitly captured}}
+}
+template void foo(int); //expected-note{{in instantiation of}}
+
+#else
+
+template<class T> void foo(T t) { //expected-note{{declared here}}
+ auto L = []() //expected-note{{begins here}}
+ { return t; }; //expected-error{{cannot be implicitly captured}}
+}
+
+#endif
+}
+
+namespace no_this_capture_for_static {
+
+struct X {
+ static void f(double) { }
+
+ int g() {
+ auto lam = [=](auto a) { f(a); };
+ lam(0); // ok.
+ ASSERT_NO_CAPTURES(lam);
+ return 0;
+ }
+};
+
+int run = X{}.g();
+}
+
+namespace this_capture_for_non_static {
+
+struct X {
+ void f(double) { }
+
+ int g() {
+ auto L = [=](auto a) { f(a); };
+ L(0);
+ auto L2 = [](auto a) { f(a); }; //expected-error {{cannot be implicitly captured}}
+ return 0;
+ }
+};
+
+int run = X{}.g();
+}
+
+namespace this_captures_with_num_args_disambiguation {
+
+struct X {
+ void f(int) { }
+ static void f(double, int i) { }
+ int g() {
+ auto lam = [](auto a) { f(a, a); };
+ lam(0);
+ return 0;
+ }
+};
+
+int run = X{}.g();
+}
+namespace enclosing_function_is_template_this_capture {
+// Only error if the instantiation tries to use the member function.
+struct X {
+ void f(int) { }
+ static void f(double) { }
+ template<class T>
+ int g(T t) {
+ auto L = [](auto a) { f(a); }; //expected-error{{'this'}}
+ L(t); // expected-note{{in instantiation of}}
+ return 0;
+ }
+};
+
+int run = X{}.g(0.0); // OK.
+int run2 = X{}.g(0); // expected-note{{in instantiation of}}
+
+
+}
+
+namespace enclosing_function_is_template_this_capture_2 {
+// This should error, even if not instantiated, since
+// this would need to be captured.
+struct X {
+ void f(int) { }
+ template<class T>
+ int g(T t) {
+ auto L = [](auto a) { f(a); }; //expected-error{{'this'}}
+ L(t);
+ return 0;
+ }
+};
+
+}
+
+
+namespace enclosing_function_is_template_this_capture_3 {
+// This should not error, this does not need to be captured.
+struct X {
+ static void f(int) { }
+ template<class T>
+ int g(T t) {
+ auto L = [](auto a) { f(a); };
+ L(t);
+ return 0;
+ }
+};
+
+int run = X{}.g(0.0); // OK.
+int run2 = X{}.g(0); // OK.
+
+}
+
+namespace nested_this_capture_1 {
+struct X {
+ void f(int) { }
+ static void f(double) { }
+
+ int g() {
+ auto L = [=](auto a) {
+ return [this]() {
+ return [=](auto b) {
+ f(b);
+ };
+ };
+ };
+ auto M = L(0);
+ auto N = M();
+ N(5);
+ return 0;
+ }
+};
+
+int run = X{}.g();
+
+}
+
+
+namespace nested_this_capture_2 {
+struct X {
+ void f(int) { }
+ static void f(double) { }
+
+ int g() {
+ auto L = [=](auto a) {
+ return [&]() {
+ return [=](auto b) {
+ f(b);
+ };
+ };
+ };
+ auto M = L(0);
+ auto N = M();
+ N(5);
+ N(3.14);
+ return 0;
+ }
+};
+
+int run = X{}.g();
+
+}
+
+namespace nested_this_capture_3_1 {
+struct X {
+ template<class T>
+ void f(int, T t) { }
+ template<class T>
+ static void f(double, T t) { }
+
+ int g() {
+ auto L = [=](auto a) {
+ return [&](auto c) {
+ return [=](auto b) {
+ f(b, c);
+ };
+ };
+ };
+ auto M = L(0);
+ auto N = M('a');
+ N(5);
+ N(3.14);
+ return 0;
+ }
+};
+
+int run = X{}.g();
+
+}
+
+
+namespace nested_this_capture_3_2 {
+struct X {
+ void f(int) { }
+ static void f(double) { }
+
+ int g() {
+ auto L = [=](auto a) {
+ return [](int i) {
+ return [=](auto b) {
+ f(b); //expected-error {{'this' cannot}}
+ int x = i;
+ };
+ };
+ };
+ auto M = L(0.0);
+ auto N = M(3);
+ N(5); //expected-note {{in instantiation of}}
+ N(3.14); // OK.
+ return 0;
+ }
+};
+
+int run = X{}.g();
+
+}
+
+namespace nested_this_capture_4 {
+struct X {
+ void f(int) { }
+ static void f(double) { }
+
+ int g() {
+ auto L = [](auto a) {
+ return [=](auto i) {
+ return [=](auto b) {
+ f(b); //expected-error {{'this' cannot}}
+ int x = i;
+ };
+ };
+ };
+ auto M = L(0.0);
+ auto N = M(3);
+ N(5); //expected-note {{in instantiation of}}
+ N(3.14); // OK.
+ return 0;
+ }
+};
+
+int run = X{}.g();
+
+}
+namespace capture_enclosing_function_parameters {
+
+
+inline auto foo(int x) {
+ int i = 10;
+ auto lambda = [=](auto z) { return x + z; };
+ return lambda;
+}
+
+int foo2() {
+ auto L = foo(3);
+ L(4);
+ L('a');
+ L(3.14);
+ return 0;
+}
+
+inline auto foo3(int x) {
+ int local = 1;
+ auto L = [=](auto a) {
+ int i = a[local];
+ return [=](auto b) mutable {
+ auto n = b;
+ return [&, n](auto c) mutable {
+ ++local;
+ return ++x;
+ };
+ };
+ };
+ auto M = L("foo-abc");
+ auto N = M("foo-def");
+ auto O = N("foo-ghi");
+
+ return L;
+}
+
+int main() {
+ auto L3 = foo3(3);
+ auto M3 = L3("L3-1");
+ auto N3 = M3("M3-1");
+ auto O3 = N3("N3-1");
+ N3("N3-2");
+ M3("M3-2");
+ M3("M3-3");
+ L3("L3-2");
+}
+} // end ns
+
+namespace capture_arrays {
+
+inline int sum_array(int n) {
+ int array2[5] = { 1, 2, 3, 4, 5};
+
+ auto L = [=](auto N) -> int {
+ int sum = 0;
+ int array[5] = { 1, 2, 3, 4, 5 };
+ sum += array2[sum];
+ sum += array2[N];
+ return 0;
+ };
+ L(2);
+ return L(n);
+}
+}
+
+namespace capture_non_odr_used_variable_because_named_in_instantiation_dependent_expressions {
+
+// even though 'x' is not odr-used, it should be captured.
+
+int test() {
+ const int x = 10;
+ auto L = [=](auto a) {
+ (void) +x + a;
+ };
+ ASSERT_CLOSURE_SIZE_EXACT(L, sizeof(x));
+}
+
+} //end ns
+#ifdef MS_EXTENSIONS
+namespace explicit_spec {
+template<class R> struct X {
+ template<class T> int foo(T t) {
+ auto L = [](auto a) { return a; };
+ L(&t);
+ return 0;
+ }
+
+ template<> int foo<char>(char c) { //expected-warning{{explicit specialization}}
+ const int x = 10;
+ auto LC = [](auto a) { return a; };
+ R r;
+ LC(&r);
+ auto L = [=](auto a) {
+ return [=](auto b) {
+ int d[sizeof(a)];
+ f(x, d);
+ };
+ };
+ auto M = L(1);
+
+ ASSERT_NO_CAPTURES(M);
+ return 0;
+ }
+
+};
+
+int run_char = X<int>{}.foo('a');
+int run_int = X<double>{}.foo(4);
+}
+
+#endif // MS_EXTENSIONS
+
}
} //end ns
+namespace test_conversion_to_fptr_2 {
+
+template<class T> struct X {
+
+ T (*fp)(T) = [](auto a) { return a; };
+
+};
+
+X<int> xi;
+
+template<class T>
+void fooT(T t, T (*fp)(T) = [](auto a) { return a; }) {
+ fp(t);
+}
+
+int test() {
+{
+ auto L = [](auto a) { return a; };
+ int (*fp)(int) = L;
+ fp(5);
+ L(3);
+ char (*fc)(char) = L;
+ fc('b');
+ L('c');
+ double (*fd)(double) = L;
+ fd(3.14);
+ fd(6.26);
+ L(4.25);
+}
+{
+ auto L = [](auto a) ->int { return a; }; //expected-note 2{{candidate template ignored}}
+ int (*fp)(int) = L;
+ char (*fc)(char) = L; //expected-error{{no viable conversion}}
+ double (*fd)(double) = L; //expected-error{{no viable conversion}}
+}
+{
+ int x = 5;
+ auto L = [=](auto b, char c = 'x') {
+ int i = x;
+ return [](auto a) ->decltype(a) { return a; };
+ };
+ int (*fp)(int) = L(8);
+ fp(5);
+ L(3);
+ char (*fc)(char) = L('a');
+ fc('b');
+ L('c');
+ double (*fd)(double) = L(3.14);
+ fd(3.14);
+ fd(6.26);
+
+}
+{
+ auto L = [=](auto b) {
+ return [](auto a) ->decltype(b)* { return (decltype(b)*)0; };
+ };
+ int* (*fp)(int) = L(8);
+ fp(5);
+ L(3);
+ char* (*fc)(char) = L('a');
+ fc('b');
+ L('c');
+ double* (*fd)(double) = L(3.14);
+ fd(3.14);
+ fd(6.26);
+}
+{
+ auto L = [=](auto b) {
+ return [](auto a) ->decltype(b)* { return (decltype(b)*)0; }; //expected-note{{candidate template ignored}}
+ };
+ char* (*fp)(int) = L('8');
+ fp(5);
+ char* (*fc)(char) = L('a');
+ fc('b');
+ double* (*fi)(int) = L(3.14);
+ fi(5);
+ int* (*fi2)(int) = L(3.14); //expected-error{{no viable conversion}}
+}
+
+{
+ auto L = [=](auto b) {
+ return [](auto a) {
+ return [=](auto c) {
+ return [](auto d) ->decltype(a + b + c + d) { return d; };
+ };
+ };
+ };
+ int (*fp)(int) = L('8')(3)(short{});
+ double (*fs)(char) = L(3.14)(short{})('4');
+}
+
+ fooT(3);
+ fooT('a');
+ fooT(3.14);
+ fooT("abcdefg");
+ return 0;
+}
+int run2 = test();
+
+}
+
+
namespace test_conversion_to_fptr {
void f1(int (*)(int)) { }
M(4.15);
}
{
- int i = 10; //expected-note{{declared here}}
+ int i = 10; //expected-note 3{{declared here}}
auto L = [](auto a) {
- return [](auto b) { //expected-note{{begins here}}
- i = b; //expected-error{{cannot be implicitly captured}}
+ return [](auto b) { //expected-note 3{{begins here}}
+ i = b; //expected-error 3{{cannot be implicitly captured}}
return b;
};
};
- auto M = L(3);
+ auto M = L(3); //expected-note{{instantiation}}
M(4.15); //expected-note{{instantiation}}
}
+ {
+ int i = 10;
+ auto L = [](auto a) {
+ return [](auto b) {
+ b = sizeof(i); //ok
+ return b;
+ };
+ };
+ }
{
auto L = [](auto a) {
print("a = ", a, "\n");