r194188 - This patch implements capturing of variables within generic lambdas.
David Blaikie
dblaikie at gmail.com
Wed Nov 6 21:59:23 PST 2013
There were a couple of unused variables in this patch that caused the Clang
self-host -Werror build to fail. I've removed them in r194190 though it
looks like this might allow some simplification
of GetInnermostEnclosingCapturableLambda - instead of returning a pointer
that's not used, perhaps you could return a boolean or an
Optional<unsigned> (and move the non-const ref unsigned into the return
value)
Also:
There's a bunch of strangely wrapped lines and trailing whitespace in this
patch - it might be good to run clang-format over the bits you added and
recommit (& prefer formatting before committing in the future).
And possibly prefer the modern LLVM naming convention of lower-first for
function names (
http://llvm.org/docs/CodingStandards.html#name-types-functions-variables-and-enumerators-properly)
On Wed, Nov 6, 2013 at 9:17 PM, Faisal Vali <faisalv at yahoo.com> wrote:
> Author: faisalv
> Date: Wed Nov 6 23:17:06 2013
> New Revision: 194188
>
> URL: http://llvm.org/viewvc/llvm-project?rev=194188&view=rev
> Log:
> This patch implements capturing of variables within generic lambdas.
>
> Both Richard and I felt that the current wording in the working paper
> needed some tweaking - Please see http://llvm-reviews.chandlerc.com/D2035for additional context and references to core-reflector messages that
> discuss wording tweaks.
>
> What is implemented is what we had intended to specify in Bristol; but,
> recently felt that the specification might benefit from some tweaking and
> fleshing.
>
> As a rough attempt to explain the semantics: If a nested lambda with a
> default-capture names a variable within its body, and if the enclosing full
> expression that contains the name of that variable is
> instantiation-dependent - then an enclosing lambda that is capture-ready
> (i.e. within a non-dependent context) must capture that variable, if all
> intervening nested lambdas can potentially capture that variable if they
> need to, and all intervening parent lambdas of the capture-ready lambda can
> and do capture the variable.
>
> Of note, 'this' capturing is also currently underspecified in the working
> paper for generic lambdas. What is implemented here is if the set of
> candidate functions in a nested generic lambda includes both static and
> non-static member functions (regardless of viability checking - i.e. num
> and type of parameters/arguments) - and if all intervening nested-inner
> lambdas between the capture-ready lambda and the function-call containing
> nested lambda can capture 'this' and if all enclosing lambdas of the
> capture-ready lambda can capture 'this', then 'this' is speculatively
> captured by that capture-ready lambda.
>
> Hopefully a paper for the C++ committee (that Richard and I had started
> some preliminary work on) is forthcoming.
>
> This essentially makes generic lambdas feature complete, except for known
> bugs. The more prominent ones (and the ones I am currently aware of) being:
> - generic lambdas and init-captures are broken - but a patch that fixes
> this is already in the works ...
> - nested variadic expansions such as:
> auto K = [](auto ... OuterArgs) {
> vp([=](auto ... Is) {
> decltype(OuterArgs) OA = OuterArgs;
> return 0;
> }(5)...);
> return 0;
> };
> auto M = K('a', ' ', 1, " -- ", 3.14);
> currently cause crashes. I think I know how to fix this (since I had
> done so in my initial implementation) - but it will probably take some work
> and back & forth with Doug and Richard.
>
> A warm thanks to all who provided feedback - and especially to Doug Gregor
> and Richard Smith for their pivotal guidance: their insight and
> prestidigitation in such matters is boundless!
>
> Now let's hope this commit doesn't upset the buildbot gods ;)
>
> Thanks!
>
> Added:
> cfe/trunk/include/clang/Sema/SemaLambda.h
> cfe/trunk/test/SemaCXX/cxx1y-generic-lambdas-capturing.cpp
> Modified:
> cfe/trunk/include/clang/AST/ASTLambda.h
> cfe/trunk/include/clang/Sema/ScopeInfo.h
> cfe/trunk/include/clang/Sema/Sema.h
> cfe/trunk/include/clang/Sema/SemaInternal.h
> cfe/trunk/lib/Sema/ScopeInfo.cpp
> cfe/trunk/lib/Sema/SemaDecl.cpp
> cfe/trunk/lib/Sema/SemaExpr.cpp
> cfe/trunk/lib/Sema/SemaExprCXX.cpp
> cfe/trunk/lib/Sema/SemaExprMember.cpp
> cfe/trunk/lib/Sema/SemaLambda.cpp
>
> cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/generic-lambda-unimplemented-1y.cpp
>
> cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p2-generic-lambda-1y.cpp
>
> cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p5-generic-lambda-1y.cpp
> cfe/trunk/test/SemaCXX/cxx1y-generic-lambdas.cpp
>
> Modified: cfe/trunk/include/clang/AST/ASTLambda.h
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ASTLambda.h?rev=194188&r1=194187&r2=194188&view=diff
>
> ==============================================================================
> --- cfe/trunk/include/clang/AST/ASTLambda.h (original)
> +++ cfe/trunk/include/clang/AST/ASTLambda.h Wed Nov 6 23:17:06 2013
> @@ -65,6 +65,16 @@ inline bool isGenericLambdaCallOperatorS
> 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
>
> Modified: cfe/trunk/include/clang/Sema/ScopeInfo.h
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/ScopeInfo.h?rev=194188&r1=194187&r2=194188&view=diff
>
> ==============================================================================
> --- cfe/trunk/include/clang/Sema/ScopeInfo.h (original)
> +++ cfe/trunk/include/clang/Sema/ScopeInfo.h Wed Nov 6 23:17:06 2013
> @@ -18,8 +18,11 @@
> #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 {
>
> @@ -39,6 +42,7 @@ class TemplateTypeParmDecl;
> class TemplateParameterList;
> class VarDecl;
> class DeclRefExpr;
> +class MemberExpr;
> class ObjCIvarRefExpr;
> class ObjCPropertyRefExpr;
> class ObjCMessageExpr;
> @@ -614,13 +618,36 @@ public:
> /// 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;
> }
> @@ -635,6 +662,110 @@ public:
> 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.
> + ///
> + /// const int n = 0;
> + /// [&] (auto x) {
> + /// (void)+n + x;
> + /// };
> + /// 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
> + /// (since lambda layout can be made visible through inline functions
> and the
> + /// like), and there are no guarantees as to which cases we'll manage
> to build
> + /// an lvalue-to-rvalue conversion in, when parsing a template -- some
> + /// seemingly harmless change elsewhere in Sema could cause us to
> start or stop
> + /// building such a node. So we need a rule that anyone can implement
> and get
> + /// 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);
> +
> };
>
>
>
> Modified: cfe/trunk/include/clang/Sema/Sema.h
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=194188&r1=194187&r2=194188&view=diff
>
> ==============================================================================
> --- cfe/trunk/include/clang/Sema/Sema.h (original)
> +++ cfe/trunk/include/clang/Sema/Sema.h Wed Nov 6 23:17:06 2013
> @@ -3155,12 +3155,19 @@ public:
> /// 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,
> @@ -4082,7 +4089,17 @@ public:
> ///
> /// \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
>
> Modified: cfe/trunk/include/clang/Sema/SemaInternal.h
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/SemaInternal.h?rev=194188&r1=194187&r2=194188&view=diff
>
> ==============================================================================
> --- cfe/trunk/include/clang/Sema/SemaInternal.h (original)
> +++ cfe/trunk/include/clang/Sema/SemaInternal.h Wed Nov 6 23:17:06 2013
> @@ -24,7 +24,44 @@ namespace clang {
> inline PartialDiagnostic Sema::PDiag(unsigned DiagID) {
> return PartialDiagnostic(DiagID, Context.getDiagAllocator());
> }
> -
> +
> +
> +// This requires the variable to be non-dependent and the initializer
> +// to not be value dependent.
> +inline bool IsVariableAConstantExpression(VarDecl *Var, ASTContext
> &Context) {
> + const VarDecl *DefVD = 0;
> + return !isa<ParmVarDecl>(Var) &&
> + Var->isUsableInConstantExpressions(Context) &&
> + Var->getAnyInitializer(DefVD) && DefVD->checkInitIsICE();
> +}
> +
> +// Directly mark a variable odr-used. Given a choice, prefer to use
> +// MarkVariableReferenced since it does additional checks and then
> +// calls MarkVarDeclODRUsed.
> +// If the variable must be captured:
> +// - if FunctionScopeIndexToStopAt is null, capture it in the CurContext
> +// - else capture it in the DeclContext that maps to the
> +// *FunctionScopeIndexToStopAt on the FunctionScopeInfo stack.
> +inline void MarkVarDeclODRUsed(VarDecl *Var,
> + SourceLocation Loc, Sema &SemaRef,
> + const unsigned *const FunctionScopeIndexToStopAt) {
> + // 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;
> + }
> + QualType CaptureType, DeclRefType;
> + SemaRef.tryCaptureVariable(Var, Loc, Sema::TryCapture_Implicit,
> + /*EllipsisLoc*/ SourceLocation(),
> + /*BuildAndDiagnose*/ true,
> + CaptureType, DeclRefType,
> + FunctionScopeIndexToStopAt);
> +
> + Var->markUsed(SemaRef.Context);
> +}
> }
>
> #endif
>
> Added: cfe/trunk/include/clang/Sema/SemaLambda.h
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/SemaLambda.h?rev=194188&view=auto
>
> ==============================================================================
> --- cfe/trunk/include/clang/Sema/SemaLambda.h (added)
> +++ cfe/trunk/include/clang/Sema/SemaLambda.h Wed Nov 6 23:17:06 2013
> @@ -0,0 +1,39 @@
> +//===--- SemaLambda.h - Lambda Helper Functions --------------*- C++
> -*-===//
> +//
> +// The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
>
> +//===----------------------------------------------------------------------===//
> +///
> +/// \file
> +/// \brief This file provides some common utility functions for processing
> +/// Lambdas.
> +///
>
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef LLVM_CLANG_SEMA_LAMBDA_H
> +#define LLVM_CLANG_SEMA_LAMBDA_H
> +#include "clang/AST/ASTLambda.h"
> +#include "clang/Sema/ScopeInfo.h"
> +namespace clang {
> +
> +// 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)
> +//
> +// 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*
> +GetInnermostEnclosingCapturableLambda(
> + ArrayRef<sema::FunctionScopeInfo*> FunctionScopes,
> + unsigned &FunctionScopeIndex,
> + DeclContext *const CurContext, VarDecl *VD, Sema &S);
> +
> +} // clang
> +
> +#endif // LLVM_CLANG_SEMA_LAMBDA_H
>
> Modified: cfe/trunk/lib/Sema/ScopeInfo.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/ScopeInfo.cpp?rev=194188&r1=194187&r2=194188&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/Sema/ScopeInfo.cpp (original)
> +++ cfe/trunk/lib/Sema/ScopeInfo.cpp Wed Nov 6 23:17:06 2013
> @@ -184,6 +184,21 @@ void FunctionScopeInfo::markSafeWeakUse(
> 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() { }
>
> Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=194188&r1=194187&r2=194188&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaDecl.cpp Wed Nov 6 23:17:06 2013
> @@ -40,6 +40,7 @@
> #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>
> @@ -9428,6 +9429,8 @@ Sema::CheckForFunctionRedefinition(Funct
> Diag(Definition->getLocation(), diag::note_previous_definition);
> FD->setInvalidDecl();
> }
> +
> +
> static void RebuildLambdaScopeInfo(CXXMethodDecl *CallOperator,
> Sema &S) {
> CXXRecordDecl *const LambdaClass = CallOperator->getParent();
> @@ -9449,7 +9452,27 @@ static void RebuildLambdaScopeInfo(CXXMe
> 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) {
>
> Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=194188&r1=194187&r2=194188&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaExpr.cpp Wed Nov 6 23:17:06 2013
> @@ -15,6 +15,7 @@
> #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"
> @@ -11374,12 +11375,8 @@ static bool isVariableAlreadyCapturedInS
> 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);
> @@ -11815,12 +11812,24 @@ bool Sema::tryCaptureVariable(VarDecl *V
> 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;
> @@ -11855,7 +11864,23 @@ bool Sema::tryCaptureVariable(VarDecl *V
> 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.
> @@ -11871,6 +11896,17 @@ bool Sema::tryCaptureVariable(VarDecl *V
> << 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;
> }
> @@ -11920,7 +11956,7 @@ bool Sema::tryCaptureVariable(VarDecl *V
> QualType DeclRefType;
> return tryCaptureVariable(Var, Loc, Kind, EllipsisLoc,
> /*BuildAndDiagnose=*/true, CaptureType,
> - DeclRefType);
> + DeclRefType, 0);
> }
>
> QualType Sema::getCapturedDeclRefType(VarDecl *Var, SourceLocation Loc) {
> @@ -11929,28 +11965,36 @@ QualType Sema::getCapturedDeclRefType(Va
>
> // 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
> @@ -11958,6 +12002,22 @@ void Sema::UpdateMarkingForLValueToRValu
> // 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) {
> @@ -11988,20 +12048,56 @@ void Sema::CleanupVarDeclMarking() {
> 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);
> @@ -12051,7 +12147,6 @@ static void DoMarkVarDeclReferenced(Sema
> }
> }
> }
> -
> // 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)
> @@ -12060,14 +12155,16 @@ static void DoMarkVarDeclReferenced(Sema
> // 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
>
> Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=194188&r1=194187&r2=194188&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Wed Nov 6 23:17:06 2013
> @@ -21,6 +21,7 @@
> #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"
> @@ -31,6 +32,7 @@
> #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"
> @@ -751,21 +753,30 @@ static Expr *captureThis(ASTContext &Con
> 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 ||
> @@ -777,17 +788,18 @@ void Sema::CheckCXXThisCapture(SourceLoc
> 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();
> @@ -801,6 +813,7 @@ void Sema::CheckCXXThisCapture(SourceLoc
> bool isNested = NumClosures > 1;
> CSI->addThisCapture(isNested, Loc, ThisTy, ThisExpr);
> }
> + return false;
> }
>
> ExprResult Sema::ActOnCXXThis(SourceLocation Loc) {
> @@ -5778,7 +5791,7 @@ ExprResult Sema::IgnoredValueConversions
> if (Res.isInvalid())
> return Owned(E);
> E = Res.take();
> - }
> + }
> return Owned(E);
> }
>
> @@ -5802,6 +5815,123 @@ ExprResult Sema::IgnoredValueConversions
> 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) {
> @@ -5832,6 +5962,41 @@ ExprResult Sema::ActOnFinishFullExpr(Exp
> }
>
> 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);
> }
>
>
> Modified: cfe/trunk/lib/Sema/SemaExprMember.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprMember.cpp?rev=194188&r1=194187&r2=194188&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaExprMember.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaExprMember.cpp Wed Nov 6 23:17:06 2013
> @@ -11,6 +11,7 @@
> //
>
> //===----------------------------------------------------------------------===//
> #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"
> @@ -883,7 +884,54 @@ Sema::BuildMemberReferenceExpr(Expr *Bas
> 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();
>
> Modified: cfe/trunk/lib/Sema/SemaLambda.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaLambda.cpp?rev=194188&r1=194187&r2=194188&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaLambda.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaLambda.cpp Wed Nov 6 23:17:06 2013
> @@ -20,10 +20,117 @@
> #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) {
> @@ -1258,15 +1365,7 @@ ExprResult Sema::ActOnLambdaExpr(SourceL
> 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);
> }
>
>
> Modified:
> cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/generic-lambda-unimplemented-1y.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/generic-lambda-unimplemented-1y.cpp?rev=194188&r1=194187&r2=194188&view=diff
>
> ==============================================================================
> ---
> cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/generic-lambda-unimplemented-1y.cpp
> (original)
> +++
> cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/generic-lambda-unimplemented-1y.cpp
> Wed Nov 6 23:17:06 2013
> @@ -1,24 +1,24 @@
> // 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);
>
> Modified:
> cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p2-generic-lambda-1y.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p2-generic-lambda-1y.cpp?rev=194188&r1=194187&r2=194188&view=diff
>
> ==============================================================================
> ---
> cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p2-generic-lambda-1y.cpp
> (original)
> +++
> cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p2-generic-lambda-1y.cpp
> Wed Nov 6 23:17:06 2013
> @@ -14,10 +14,12 @@ struct P {
> virtual ~P();
> };
>
> -void unevaluated_operand(P &p, int i) {
> +void unevaluated_operand(P &p, int i) { //expected-note{{declared here}}
> // FIXME: this should only emit one error.
> int i2 = sizeof([](auto a, auto b)->void{}(3, '4')); //
> expected-error{{lambda expression in an unevaluated operand}} \
> //
> expected-error{{invalid application of 'sizeof'}}
> const std::type_info &ti1 = typeid([](auto &a) -> P& { static P p;
> return p; }(i));
> - const std::type_info &ti2 = typeid([](auto) -> int { return i; }(i));
> // expected-error{{lambda expression in an unevaluated operand}}
> + const std::type_info &ti2 = typeid([](auto) -> int { return i; }(i));
> // expected-error{{lambda expression in an unevaluated operand}}\
> +
> // expected-error{{cannot be implicitly captured}}\
> +
> // expected-note{{begins here}}
> }
>
> Modified:
> cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p5-generic-lambda-1y.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p5-generic-lambda-1y.cpp?rev=194188&r1=194187&r2=194188&view=diff
>
> ==============================================================================
> ---
> cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p5-generic-lambda-1y.cpp
> (original)
> +++
> cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p5-generic-lambda-1y.cpp
> Wed Nov 6 23:17:06 2013
> @@ -112,7 +112,7 @@ void test2() {
> namespace nested_lambdas {
> int test() {
> auto L = [](auto a) {
> - return [=](auto b) { //expected-error{{unimplemented}}
> + return [=](auto b) {
> return a + b;
> };
> };
>
> Added: cfe/trunk/test/SemaCXX/cxx1y-generic-lambdas-capturing.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx1y-generic-lambdas-capturing.cpp?rev=194188&view=auto
>
> ==============================================================================
> --- cfe/trunk/test/SemaCXX/cxx1y-generic-lambdas-capturing.cpp (added)
> +++ cfe/trunk/test/SemaCXX/cxx1y-generic-lambdas-capturing.cpp Wed Nov 6
> 23:17:06 2013
> @@ -0,0 +1,1363 @@
> +// 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
> +
>
> Modified: cfe/trunk/test/SemaCXX/cxx1y-generic-lambdas.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx1y-generic-lambdas.cpp?rev=194188&r1=194187&r2=194188&view=diff
>
> ==============================================================================
> --- cfe/trunk/test/SemaCXX/cxx1y-generic-lambdas.cpp (original)
> +++ cfe/trunk/test/SemaCXX/cxx1y-generic-lambdas.cpp Wed Nov 6 23:17:06
> 2013
> @@ -12,6 +12,108 @@ int test() {
> }
> } //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)) { }
> @@ -129,17 +231,26 @@ int test() {
> 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");
> return [](auto b) ->decltype(a) {
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20131106/3249389d/attachment.html>
More information about the cfe-commits
mailing list