diff options
Diffstat (limited to 'clang/lib')
-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 |
6 files changed, 504 insertions, 57 deletions
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); } |