Introduce the lambda scope before determining explicit captures, which
cleans up and improves a few things: - We get rid of the ugly dance of computing all of the captures in data structures that clone those of CapturingScopeInfo, centralizing the logic for accessing/updating these data structures - We re-use the existing capture logic for 'this', which actually works now. Cleaned up some diagnostic wording in minor ways as well. llvm-svn: 149516
This commit is contained in:
parent
62efe0b062
commit
cdd11d4e7e
|
@ -4066,8 +4066,8 @@ def err_capture_does_not_name_variable : Error<
|
|||
"%0 in capture list does not name a variable">;
|
||||
def err_capture_non_automatic_variable : Error<
|
||||
"%0 cannot be captured because it does not have automatic storage duration">;
|
||||
def err_implicit_this_capture : Error<
|
||||
"'this' cannot be implicitly captured in this context">;
|
||||
def err_this_capture : Error<
|
||||
"'this' cannot be %select{implicitly |}0captured in this context">;
|
||||
def err_lambda_capture_block : Error<
|
||||
"__block variable %0 cannot be captured in a lambda">;
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#include "clang/Basic/PartialDiagnostic.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/SetVector.h"
|
||||
|
||||
namespace clang {
|
||||
|
||||
|
@ -206,6 +205,34 @@ public:
|
|||
CXXThisCaptureIndex = Captures.size();
|
||||
}
|
||||
|
||||
/// \brief Determine whether the C++ 'this' is captured.
|
||||
bool isCXXThisCaptured() const { return CXXThisCaptureIndex != 0; }
|
||||
|
||||
/// \brief Retrieve the capture of C++ 'this', if it has been captured.
|
||||
Capture &getCXXThisCapture() {
|
||||
assert(isCXXThisCaptured() && "this has not been captured");
|
||||
return Captures[CXXThisCaptureIndex - 1];
|
||||
}
|
||||
|
||||
/// \brief Determine whether the given variable has been captured.
|
||||
bool isCaptured(VarDecl *Var) const {
|
||||
return CaptureMap.count(Var) > 0;
|
||||
}
|
||||
|
||||
/// \brief Retrieve the capture of the given variable, if it has been
|
||||
/// captured already.
|
||||
Capture &getCapture(VarDecl *Var) {
|
||||
assert(isCaptured(Var) && "Variable has not been captured");
|
||||
return Captures[CaptureMap[Var] - 1];
|
||||
}
|
||||
|
||||
const Capture &getCapture(VarDecl *Var) const {
|
||||
llvm::DenseMap<VarDecl*, unsigned>::const_iterator Known
|
||||
= CaptureMap.find(Var);
|
||||
assert(Known != CaptureMap.end() && "Variable has not been captured");
|
||||
return Captures[Known->second - 1];
|
||||
}
|
||||
|
||||
static bool classof(const FunctionScopeInfo *FSI) {
|
||||
return FSI->Kind == SK_Block || FSI->Kind == SK_Lambda;
|
||||
}
|
||||
|
@ -258,6 +285,11 @@ public:
|
|||
|
||||
virtual ~LambdaScopeInfo();
|
||||
|
||||
/// \brief Note when
|
||||
void finishedExplicitCaptures() {
|
||||
NumExplicitCaptures = Captures.size();
|
||||
}
|
||||
|
||||
static bool classof(const FunctionScopeInfo *FSI) {
|
||||
return FSI->Kind == SK_Lambda;
|
||||
}
|
||||
|
|
|
@ -3085,7 +3085,12 @@ public:
|
|||
|
||||
/// \brief Make sure the value of 'this' is actually available in the current
|
||||
/// context, if it is a potentially evaluated context.
|
||||
void CheckCXXThisCapture(SourceLocation Loc);
|
||||
///
|
||||
/// \param Loc The location at which the capture of 'this' occurs.
|
||||
///
|
||||
/// \param Explicit Whether 'this' is explicitly captured in a lambda
|
||||
/// capture list.
|
||||
void CheckCXXThisCapture(SourceLocation Loc, bool Explicit = false);
|
||||
|
||||
/// ActOnCXXBoolLiteral - Parse {true,false} literals.
|
||||
ExprResult ActOnCXXBoolLiteral(SourceLocation OpLoc, tok::TokenKind Kind);
|
||||
|
|
|
@ -670,9 +670,9 @@ QualType Sema::getCurrentThisType() {
|
|||
return ThisTy;
|
||||
}
|
||||
|
||||
void Sema::CheckCXXThisCapture(SourceLocation Loc) {
|
||||
void Sema::CheckCXXThisCapture(SourceLocation Loc, bool Explicit) {
|
||||
// We don't need to capture this in an unevaluated context.
|
||||
if (ExprEvalContexts.back().Context == Unevaluated)
|
||||
if (ExprEvalContexts.back().Context == Unevaluated && !Explicit)
|
||||
return;
|
||||
|
||||
// Otherwise, check that we can capture 'this'.
|
||||
|
@ -684,16 +684,19 @@ void Sema::CheckCXXThisCapture(SourceLocation Loc) {
|
|||
// 'this' is already being captured; there isn't anything more to do.
|
||||
break;
|
||||
}
|
||||
|
||||
if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByref ||
|
||||
CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_Block) {
|
||||
// This closure can implicitly capture 'this'; continue looking upwards.
|
||||
CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_Block ||
|
||||
Explicit) {
|
||||
// This closure can capture 'this'; continue looking upwards.
|
||||
// FIXME: Is this check correct? The rules in the standard are a bit
|
||||
// unclear.
|
||||
NumClosures++;
|
||||
Explicit = false;
|
||||
continue;
|
||||
}
|
||||
// This context can't implicitly capture 'this'; fail out.
|
||||
Diag(Loc, diag::err_implicit_this_capture);
|
||||
Diag(Loc, diag::err_this_capture) << Explicit;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
@ -4862,20 +4865,90 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
|
|||
Class->setLambda(true);
|
||||
CurContext->addDecl(Class);
|
||||
|
||||
QualType ThisCaptureType;
|
||||
llvm::DenseMap<VarDecl*, unsigned> CaptureMap;
|
||||
unsigned CXXThisCaptureIndex = 0;
|
||||
llvm::SmallVector<LambdaScopeInfo::Capture, 4> Captures;
|
||||
// Build the call operator; we don't really have all the relevant information
|
||||
// at this point, but we need something to attach child declarations to.
|
||||
QualType MethodTy;
|
||||
TypeSourceInfo *MethodTyInfo;
|
||||
if (ParamInfo.getNumTypeObjects() == 0) {
|
||||
// C++11 [expr.prim.lambda]p4:
|
||||
// If a lambda-expression does not include a lambda-declarator, it is as
|
||||
// if the lambda-declarator were ().
|
||||
FunctionProtoType::ExtProtoInfo EPI;
|
||||
EPI.TypeQuals |= DeclSpec::TQ_const;
|
||||
MethodTy = Context.getFunctionType(Context.DependentTy,
|
||||
/*Args=*/0, /*NumArgs=*/0, EPI);
|
||||
MethodTyInfo = Context.getTrivialTypeSourceInfo(MethodTy);
|
||||
} else {
|
||||
assert(ParamInfo.isFunctionDeclarator() &&
|
||||
"lambda-declarator is a function");
|
||||
DeclaratorChunk::FunctionTypeInfo &FTI = ParamInfo.getFunctionTypeInfo();
|
||||
|
||||
// C++11 [expr.prim.lambda]p5:
|
||||
// This function call operator is declared const (9.3.1) if and only if
|
||||
// the lambda-expression’s parameter-declaration-clause is not followed
|
||||
// by mutable. It is neither virtual nor declared volatile.
|
||||
if (!FTI.hasMutableQualifier())
|
||||
FTI.TypeQuals |= DeclSpec::TQ_const;
|
||||
MethodTyInfo = GetTypeForDeclarator(ParamInfo, CurScope);
|
||||
// FIXME: Can these asserts actually fail?
|
||||
assert(MethodTyInfo && "no type from lambda-declarator");
|
||||
MethodTy = MethodTyInfo->getType();
|
||||
assert(!MethodTy.isNull() && "no type from lambda declarator");
|
||||
}
|
||||
|
||||
// C++11 [expr.prim.lambda]p5:
|
||||
// The closure type for a lambda-expression has a public inline function
|
||||
// call operator (13.5.4) whose parameters and return type are described by
|
||||
// the lambda-expression’s parameter-declaration-clause and
|
||||
// trailing-return-type respectively.
|
||||
DeclarationName MethodName
|
||||
= Context.DeclarationNames.getCXXOperatorName(OO_Call);
|
||||
CXXMethodDecl *Method
|
||||
= CXXMethodDecl::Create(Context,
|
||||
Class,
|
||||
ParamInfo.getSourceRange().getEnd(),
|
||||
DeclarationNameInfo(MethodName,
|
||||
/*NameLoc=*/SourceLocation()),
|
||||
MethodTy,
|
||||
MethodTyInfo,
|
||||
/*isStatic=*/false,
|
||||
SC_None,
|
||||
/*isInline=*/true,
|
||||
/*isConstExpr=*/false,
|
||||
ParamInfo.getSourceRange().getEnd());
|
||||
Method->setAccess(AS_public);
|
||||
Class->addDecl(Method);
|
||||
Method->setLexicalDeclContext(DC); // FIXME: Minor hack.
|
||||
|
||||
ProcessDeclAttributes(CurScope, Method, ParamInfo);
|
||||
|
||||
// Enter a new evaluation context to insulate the block from any
|
||||
// cleanups from the enclosing full-expression.
|
||||
PushExpressionEvaluationContext(PotentiallyEvaluated);
|
||||
|
||||
PushDeclContext(CurScope, Method);
|
||||
|
||||
// Introduce the lambda scope.
|
||||
PushLambdaScope(Class);
|
||||
LambdaScopeInfo *LSI = getCurLambda();
|
||||
if (Intro.Default == LCD_ByCopy)
|
||||
LSI->ImpCaptureStyle = LambdaScopeInfo::ImpCap_LambdaByval;
|
||||
else if (Intro.Default == LCD_ByRef)
|
||||
LSI->ImpCaptureStyle = LambdaScopeInfo::ImpCap_LambdaByref;
|
||||
|
||||
// Handle explicit captures.
|
||||
for (llvm::SmallVector<LambdaCapture, 4>::const_iterator
|
||||
C = Intro.Captures.begin(), E = Intro.Captures.end(); C != E; ++C) {
|
||||
C = Intro.Captures.begin(),
|
||||
E = Intro.Captures.end();
|
||||
C != E; ++C) {
|
||||
if (C->Kind == LCK_This) {
|
||||
// C++11 [expr.prim.lambda]p8:
|
||||
// An identifier or this shall not appear more than once in a
|
||||
// lambda-capture.
|
||||
if (!ThisCaptureType.isNull()) {
|
||||
if (LSI->isCXXThisCaptured()) {
|
||||
Diag(C->Loc, diag::err_capture_more_than_once)
|
||||
<< "'this'"
|
||||
<< SourceRange(Captures[CXXThisCaptureIndex].getLocation());
|
||||
<< SourceRange(LSI->getCXXThisCapture().getLocation());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -4890,19 +4963,13 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
|
|||
// C++11 [expr.prim.lambda]p12:
|
||||
// If this is captured by a local lambda expression, its nearest
|
||||
// enclosing function shall be a non-static member function.
|
||||
ThisCaptureType = getCurrentThisType();
|
||||
QualType ThisCaptureType = getCurrentThisType();
|
||||
if (ThisCaptureType.isNull()) {
|
||||
Diag(C->Loc, diag::err_invalid_this_use);
|
||||
Diag(C->Loc, diag::err_this_capture) << true;
|
||||
continue;
|
||||
}
|
||||
CheckCXXThisCapture(C->Loc);
|
||||
|
||||
// FIXME: Need getCurCapture().
|
||||
bool isNested = getCurBlock() || getCurLambda();
|
||||
CXXThisCaptureIndex = Captures.size();
|
||||
CapturingScopeInfo::Capture Cap(CapturingScopeInfo::Capture::ThisCapture,
|
||||
isNested, C->Loc);
|
||||
Captures.push_back(Cap);
|
||||
|
||||
CheckCXXThisCapture(C->Loc, /*Explicit=*/true);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -4939,7 +5006,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
|
|||
// for unqualified name lookup (3.4.1); each such lookup shall find a
|
||||
// variable with automatic storage duration declared in the reaching
|
||||
// scope of the local lambda expression.
|
||||
// FIXME: Check reaching scope.
|
||||
// FIXME: Check reaching scope.
|
||||
VarDecl *Var = R.getAsSingle<VarDecl>();
|
||||
if (!Var) {
|
||||
Diag(C->Loc, diag::err_capture_does_not_name_variable) << C->Id;
|
||||
|
@ -4961,119 +5028,44 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
|
|||
// C++11 [expr.prim.lambda]p8:
|
||||
// An identifier or this shall not appear more than once in a
|
||||
// lambda-capture.
|
||||
if (CaptureMap.count(Var)) {
|
||||
if (LSI->isCaptured(Var)) {
|
||||
Diag(C->Loc, diag::err_capture_more_than_once)
|
||||
<< C->Id
|
||||
<< SourceRange(Captures[CaptureMap[Var]].getLocation());
|
||||
<< SourceRange(LSI->getCapture(Var).getLocation());
|
||||
continue;
|
||||
}
|
||||
|
||||
// FIXME: If this is capture by copy, make sure that we can in fact copy
|
||||
// the variable.
|
||||
CaptureMap[Var] = Captures.size();
|
||||
Captures.push_back(LambdaScopeInfo::Capture(Var, C->Kind == LCK_ByRef,
|
||||
/*isNested*/false, C->Loc, 0));
|
||||
// FIXME: Unify with normal capture path, so we get all of the necessary
|
||||
// nested captures.
|
||||
LSI->AddCapture(Var, C->Kind == LCK_ByRef, /*isNested=*/false, C->Loc, 0);
|
||||
}
|
||||
|
||||
// Build the call operator; we don't really have all the relevant information
|
||||
// at this point, but we need something to attach child declarations to.
|
||||
QualType MethodTy;
|
||||
TypeSourceInfo *MethodTyInfo;
|
||||
if (ParamInfo.getNumTypeObjects() == 0) {
|
||||
// C++11 [expr.prim.lambda]p4:
|
||||
// If a lambda-expression does not include a lambda-declarator, it is as
|
||||
// if the lambda-declarator were ().
|
||||
FunctionProtoType::ExtProtoInfo EPI;
|
||||
EPI.TypeQuals |= DeclSpec::TQ_const;
|
||||
MethodTy = Context.getFunctionType(Context.DependentTy,
|
||||
/*Args=*/0, /*NumArgs=*/0, EPI);
|
||||
MethodTyInfo = Context.getTrivialTypeSourceInfo(MethodTy);
|
||||
} else {
|
||||
assert(ParamInfo.isFunctionDeclarator() &&
|
||||
"lambda-declarator is a function");
|
||||
DeclaratorChunk::FunctionTypeInfo &FTI = ParamInfo.getFunctionTypeInfo();
|
||||
|
||||
// C++11 [expr.prim.lambda]p5:
|
||||
// This function call operator is declared const (9.3.1) if and only if
|
||||
// the lambda- expression’s parameter-declaration-clause is not followed
|
||||
// by mutable. It is neither virtual nor declared volatile.
|
||||
if (!FTI.hasMutableQualifier())
|
||||
FTI.TypeQuals |= DeclSpec::TQ_const;
|
||||
MethodTyInfo = GetTypeForDeclarator(ParamInfo, CurScope);
|
||||
// FIXME: Can these asserts actually fail?
|
||||
assert(MethodTyInfo && "no type from lambda-declarator");
|
||||
MethodTy = MethodTyInfo->getType();
|
||||
assert(!MethodTy.isNull() && "no type from lambda declarator");
|
||||
}
|
||||
|
||||
// C++11 [expr.prim.lambda]p5:
|
||||
// The closure type for a lambda-expression has a public inline function
|
||||
// call operator (13.5.4) whose parameters and return type are described by
|
||||
// the lambda-expression’s parameter-declaration-clause and
|
||||
// trailing-return-type respectively.
|
||||
DeclarationName MethodName
|
||||
= Context.DeclarationNames.getCXXOperatorName(OO_Call);
|
||||
CXXMethodDecl *Method
|
||||
= CXXMethodDecl::Create(Context,
|
||||
Class,
|
||||
ParamInfo.getSourceRange().getEnd(),
|
||||
DeclarationNameInfo(MethodName,
|
||||
/*NameLoc=*/SourceLocation()),
|
||||
MethodTy,
|
||||
MethodTyInfo,
|
||||
/*isStatic=*/false,
|
||||
SC_None,
|
||||
/*isInline=*/true,
|
||||
/*isConstExpr=*/false,
|
||||
ParamInfo.getSourceRange().getEnd());
|
||||
Method->setAccess(AS_public);
|
||||
Class->addDecl(Method);
|
||||
Method->setLexicalDeclContext(DC); // FIXME: Is this really correct?
|
||||
|
||||
ProcessDeclAttributes(CurScope, Method, ParamInfo);
|
||||
|
||||
// Enter a new evaluation context to insulate the block from any
|
||||
// cleanups from the enclosing full-expression.
|
||||
PushExpressionEvaluationContext(PotentiallyEvaluated);
|
||||
|
||||
PushDeclContext(CurScope, Method);
|
||||
LSI->finishedExplicitCaptures();
|
||||
|
||||
// Set the parameters on the decl, if specified.
|
||||
if (isa<FunctionProtoTypeLoc>(MethodTyInfo->getTypeLoc())) {
|
||||
FunctionProtoTypeLoc Proto =
|
||||
cast<FunctionProtoTypeLoc>(MethodTyInfo->getTypeLoc());
|
||||
cast<FunctionProtoTypeLoc>(MethodTyInfo->getTypeLoc());
|
||||
Method->setParams(Proto.getParams());
|
||||
CheckParmsForFunctionDef(Method->param_begin(),
|
||||
Method->param_end(),
|
||||
/*CheckParameterNames=*/false);
|
||||
|
||||
|
||||
// Introduce our parameters into the function scope
|
||||
for (unsigned p = 0, NumParams = Method->getNumParams(); p < NumParams; ++p) {
|
||||
ParmVarDecl *Param = Method->getParamDecl(p);
|
||||
Param->setOwningFunction(Method);
|
||||
|
||||
|
||||
// If this has an identifier, add it to the scope stack.
|
||||
if (Param->getIdentifier()) {
|
||||
CheckShadow(CurScope, Param);
|
||||
|
||||
|
||||
PushOnScopeChains(Param, CurScope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Introduce the lambda scope.
|
||||
PushLambdaScope(Class);
|
||||
|
||||
LambdaScopeInfo *LSI = getCurLambda();
|
||||
LSI->CXXThisCaptureIndex = CXXThisCaptureIndex;
|
||||
std::swap(LSI->CaptureMap, CaptureMap);
|
||||
std::swap(LSI->Captures, Captures);
|
||||
LSI->NumExplicitCaptures = Captures.size();
|
||||
if (Intro.Default == LCD_ByCopy)
|
||||
LSI->ImpCaptureStyle = LambdaScopeInfo::ImpCap_LambdaByval;
|
||||
else if (Intro.Default == LCD_ByRef)
|
||||
LSI->ImpCaptureStyle = LambdaScopeInfo::ImpCap_LambdaByref;
|
||||
|
||||
const FunctionType *Fn = MethodTy->getAs<FunctionType>();
|
||||
QualType RetTy = Fn->getResultType();
|
||||
if (RetTy != Context.DependentTy) {
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "clang/Basic/Builtins.h"
|
||||
#include "clang/Basic/LangOptions.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/SetVector.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
|
|
|
@ -13,9 +13,9 @@ namespace ExplicitCapture {
|
|||
void ImplicitThisCapture() {
|
||||
[](){(void)Member;}; // expected-error {{'this' cannot be implicitly captured in this context}} expected-error {{not supported yet}}
|
||||
[&](){(void)Member;}; // expected-error {{not supported yet}}
|
||||
// FIXME: 'this' captures below don't actually work yet
|
||||
// FIXME: [this](){(void)Member;};
|
||||
// FIXME: [this]{[this]{};};
|
||||
// 'this' captures below don't actually work yet
|
||||
[this](){(void)Member;}; // expected-error{{lambda expressions are not supported yet}}
|
||||
[this]{[this]{};}; // expected-error 2{{lambda expressions are not supported yet}}
|
||||
[]{[this]{};};// expected-error {{'this' cannot be implicitly captured in this context}} expected-error 2 {{not supported yet}}
|
||||
[]{Overload(3);}; // expected-error {{not supported yet}}
|
||||
[]{Overload();}; // expected-error {{'this' cannot be implicitly captured in this context}} expected-error {{not supported yet}}
|
||||
|
@ -25,7 +25,7 @@ namespace ExplicitCapture {
|
|||
};
|
||||
|
||||
void f() {
|
||||
[this] () {}; // expected-error {{invalid use of 'this'}} expected-error {{not supported yet}}
|
||||
[this] () {}; // expected-error {{'this' cannot be captured in this context}} expected-error {{not supported yet}}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue