diff options
Diffstat (limited to 'clang')
-rw-r--r-- | clang/include/clang/AST/ASTLambda.h | 10 | ||||
-rw-r--r-- | clang/include/clang/Sema/ScopeInfo.h | 135 | ||||
-rw-r--r-- | clang/include/clang/Sema/Sema.h | 21 | ||||
-rw-r--r-- | clang/include/clang/Sema/SemaInternal.h | 39 | ||||
-rw-r--r-- | clang/include/clang/Sema/SemaLambda.h | 39 | ||||
-rw-r--r-- | clang/lib/Sema/ScopeInfo.cpp | 15 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 25 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 167 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExprCXX.cpp | 187 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExprMember.cpp | 50 | ||||
-rw-r--r-- | clang/lib/Sema/SemaLambda.cpp | 117 | ||||
-rw-r--r-- | clang/test/CXX/expr/expr.prim/expr.prim.lambda/generic-lambda-unimplemented-1y.cpp | 8 | ||||
-rw-r--r-- | clang/test/CXX/expr/expr.prim/expr.prim.lambda/p2-generic-lambda-1y.cpp | 6 | ||||
-rw-r--r-- | clang/test/CXX/expr/expr.prim/expr.prim.lambda/p5-generic-lambda-1y.cpp | 2 | ||||
-rw-r--r-- | clang/test/SemaCXX/cxx1y-generic-lambdas-capturing.cpp | 1363 | ||||
-rw-r--r-- | clang/test/SemaCXX/cxx1y-generic-lambdas.cpp | 119 |
16 files changed, 2230 insertions, 73 deletions
diff --git a/clang/include/clang/AST/ASTLambda.h b/clang/include/clang/AST/ASTLambda.h index ec0a3ce830f..358ac7131e4 100644 --- a/clang/include/clang/AST/ASTLambda.h +++ b/clang/include/clang/AST/ASTLambda.h @@ -65,6 +65,16 @@ inline bool isGenericLambdaCallOperatorSpecialization(DeclContext *DC) { 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 diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h index 4bfffae4a81..06afe1a8ae9 100644 --- a/clang/include/clang/Sema/ScopeInfo.h +++ b/clang/include/clang/Sema/ScopeInfo.h @@ -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); + }; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index fdeb89acbbf..f80d11ce1e2 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -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 diff --git a/clang/include/clang/Sema/SemaInternal.h b/clang/include/clang/Sema/SemaInternal.h index bbf42721ba6..01d4cc9679e 100644 --- a/clang/include/clang/Sema/SemaInternal.h +++ b/clang/include/clang/Sema/SemaInternal.h @@ -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 diff --git a/clang/include/clang/Sema/SemaLambda.h b/clang/include/clang/Sema/SemaLambda.h new file mode 100644 index 00000000000..cf9fff1f1a2 --- /dev/null +++ b/clang/include/clang/Sema/SemaLambda.h @@ -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
diff --git a/clang/lib/Sema/ScopeInfo.cpp b/clang/lib/Sema/ScopeInfo.cpp index 2f48bec123b..f3eb8fcebab 100644 --- a/clang/lib/Sema/ScopeInfo.cpp +++ b/clang/lib/Sema/ScopeInfo.cpp @@ -184,6 +184,21 @@ void FunctionScopeInfo::markSafeWeakUse(const Expr *E) { 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() { } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 222b70cdab8..0f6818cf5ae 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -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(FunctionDecl *FD, 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(CXXMethodDecl *CallOperator, 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) { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 80ebfe71629..9b40afeb7c5 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -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 isVariableAlreadyCapturedInScopeInfo(CapturingScopeInfo *CSI, VarDec 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 *Var, SourceLocation ExprLoc, 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 *Var, SourceLocation ExprLoc, 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 *Var, SourceLocation ExprLoc, << 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 *Var, SourceLocation Loc, 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(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 @@ -11958,6 +12002,22 @@ void Sema::UpdateMarkingForLValueToRValue(Expr *E) { // 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 &SemaRef, SourceLocation Loc, } } } - // 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 &SemaRef, SourceLocation Loc, // 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 diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 4c3b49ad245..d8d05c78e2d 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -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 &Context, RecordDecl *RD, 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(SourceLocation Loc, bool Explicit) { 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(SourceLocation Loc, bool Explicit) { bool isNested = NumClosures > 1; CSI->addThisCapture(isNested, Loc, ThisTy, ThisExpr); } + return false; } ExprResult Sema::ActOnCXXThis(SourceLocation Loc) { @@ -5778,7 +5791,7 @@ ExprResult Sema::IgnoredValueConversions(Expr *E) { if (Res.isInvalid()) return Owned(E); E = Res.take(); - } + } return Owned(E); } @@ -5802,6 +5815,123 @@ ExprResult Sema::IgnoredValueConversions(Expr *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) { @@ -5832,6 +5962,41 @@ ExprResult Sema::ActOnFinishFullExpr(Expr *FE, SourceLocation CC, } 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); } diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index 5d133233609..f6accb15bf9 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -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 *BaseExpr, QualType BaseExprType, 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(); diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index 3fff7465bca..6db37ecf1b5 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -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(SourceLocation StartLoc, Stmt *Body, 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); } diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/generic-lambda-unimplemented-1y.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/generic-lambda-unimplemented-1y.cpp index 92dd7ad1876..7f42c396ee2 100644 --- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/generic-lambda-unimplemented-1y.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/generic-lambda-unimplemented-1y.cpp @@ -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); diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p2-generic-lambda-1y.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p2-generic-lambda-1y.cpp index 44656a37e31..d41c4507a85 100644 --- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p2-generic-lambda-1y.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p2-generic-lambda-1y.cpp @@ -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}}
}
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p5-generic-lambda-1y.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p5-generic-lambda-1y.cpp index ba41cb47f59..c5d3bf6d1bc 100644 --- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p5-generic-lambda-1y.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p5-generic-lambda-1y.cpp @@ -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;
};
};
diff --git a/clang/test/SemaCXX/cxx1y-generic-lambdas-capturing.cpp b/clang/test/SemaCXX/cxx1y-generic-lambdas-capturing.cpp new file mode 100644 index 00000000000..8bd4f4242c1 --- /dev/null +++ b/clang/test/SemaCXX/cxx1y-generic-lambdas-capturing.cpp @@ -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 + diff --git a/clang/test/SemaCXX/cxx1y-generic-lambdas.cpp b/clang/test/SemaCXX/cxx1y-generic-lambdas.cpp index a7ec6755d60..4d0e27ec1cd 100644 --- a/clang/test/SemaCXX/cxx1y-generic-lambdas.cpp +++ b/clang/test/SemaCXX/cxx1y-generic-lambdas.cpp @@ -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) { |