summaryrefslogtreecommitdiffstats
path: root/clang/lib
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/Sema/ScopeInfo.cpp15
-rw-r--r--clang/lib/Sema/SemaDecl.cpp25
-rw-r--r--clang/lib/Sema/SemaExpr.cpp167
-rw-r--r--clang/lib/Sema/SemaExprCXX.cpp187
-rw-r--r--clang/lib/Sema/SemaExprMember.cpp50
-rw-r--r--clang/lib/Sema/SemaLambda.cpp117
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);
}
OpenPOWER on IntegriCloud