зеркало из https://github.com/microsoft/clang-1.git
More lambda work: semantic analysis of capturing 'this'. It's a bit complicated, but we have to be careful about when exactly captures are marked given PotentiallyPotentiallyEvaluated contexts. (Actually, it's not 100% correct yet, but it's close enough for the moment.)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@147723 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
9f03b62036
Коммит
72899c34e3
|
@ -500,6 +500,9 @@ class CXXRecordDecl : public RecordDecl {
|
|||
/// declared but would have been deleted.
|
||||
bool FailedImplicitMoveAssignment : 1;
|
||||
|
||||
/// \brief Whether this class describes a C++ lambda.
|
||||
bool IsLambda : 1;
|
||||
|
||||
/// NumBases - The number of base class specifiers in Bases.
|
||||
unsigned NumBases;
|
||||
|
||||
|
@ -908,6 +911,11 @@ public:
|
|||
/// This value is used for lazy creation of destructors.
|
||||
bool hasDeclaredDestructor() const { return data().DeclaredDestructor; }
|
||||
|
||||
/// \brief Determine whether this class describes a lambda function object.
|
||||
bool isLambda() const { return data().IsLambda; }
|
||||
|
||||
void setLambda(bool Lambda = true) { data().IsLambda = Lambda; }
|
||||
|
||||
/// getConversions - Retrieve the overload set containing all of the
|
||||
/// conversion functions in this class.
|
||||
UnresolvedSetImpl *getConversionFunctions() {
|
||||
|
|
|
@ -4004,6 +4004,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_operator_arrow_circular : Error<
|
||||
"circular pointer delegation detected">;
|
||||
|
|
|
@ -189,15 +189,20 @@ public:
|
|||
/// \brief A mapping from the set of captured variables to the
|
||||
/// fields (within the lambda class) that represent the captured variables.
|
||||
llvm::DenseMap<VarDecl *, FieldDecl *> CapturedVariables;
|
||||
|
||||
|
||||
/// \brief The list of captured variables, starting with the explicit
|
||||
/// captures and then finishing with any implicit captures.
|
||||
llvm::SmallVector<Capture, 4> Captures;
|
||||
|
||||
|
||||
// \brief Whether we have already captured 'this'.
|
||||
bool CapturesCXXThis;
|
||||
|
||||
/// \brief The number of captures in the \c Captures list that are
|
||||
/// explicit captures.
|
||||
unsigned NumExplicitCaptures;
|
||||
|
||||
|
||||
LambdaCaptureDefault Default;
|
||||
|
||||
/// \brief The field associated with the captured 'this' pointer.
|
||||
FieldDecl *ThisCapture;
|
||||
|
||||
|
@ -208,8 +213,9 @@ public:
|
|||
QualType ReturnType;
|
||||
|
||||
LambdaScopeInfo(DiagnosticsEngine &Diag, CXXRecordDecl *Lambda)
|
||||
: FunctionScopeInfo(Diag), Lambda(Lambda),
|
||||
NumExplicitCaptures(0), ThisCapture(0) , HasImplicitReturnType(false)
|
||||
: FunctionScopeInfo(Diag), Lambda(Lambda), CapturesCXXThis(false),
|
||||
NumExplicitCaptures(0), Default(LCD_None), ThisCapture(0),
|
||||
HasImplicitReturnType(false)
|
||||
{
|
||||
Kind = SK_Lambda;
|
||||
}
|
||||
|
|
|
@ -3099,7 +3099,12 @@ public:
|
|||
/// \param Capture If true, capture 'this' in this context.
|
||||
///
|
||||
/// \returns The type of 'this', if possible. Otherwise, returns a NULL type.
|
||||
QualType getCurrentThisType(bool Capture = true);
|
||||
QualType getCurrentThisType();
|
||||
|
||||
/// \brief Make sure the value of 'this' is actually available in the current
|
||||
/// context, if it is a potentially evaluated context. This check can be
|
||||
/// delayed in PotentiallyPotentiallyEvaluated contexts.
|
||||
void CheckCXXThisCapture(SourceLocation Loc);
|
||||
|
||||
/// ActOnCXXBoolLiteral - Parse {true,false} literals.
|
||||
ExprResult ActOnCXXBoolLiteral(SourceLocation OpLoc, tok::TokenKind Kind);
|
||||
|
|
|
@ -57,8 +57,8 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
|
|||
DeclaredCopyConstructor(false), DeclaredMoveConstructor(false),
|
||||
DeclaredCopyAssignment(false), DeclaredMoveAssignment(false),
|
||||
DeclaredDestructor(false), FailedImplicitMoveConstructor(false),
|
||||
FailedImplicitMoveAssignment(false), NumBases(0), NumVBases(0), Bases(),
|
||||
VBases(), Definition(D), FirstFriend(0) {
|
||||
FailedImplicitMoveAssignment(false), IsLambda(false), NumBases(0),
|
||||
NumVBases(0), Bases(), VBases(), Definition(D), FirstFriend(0) {
|
||||
}
|
||||
|
||||
CXXRecordDecl::CXXRecordDecl(Kind K, TagKind TK, DeclContext *DC,
|
||||
|
|
|
@ -636,8 +636,16 @@ void Sema::ActOnEndOfTranslationUnit() {
|
|||
DeclContext *Sema::getFunctionLevelDeclContext() {
|
||||
DeclContext *DC = CurContext;
|
||||
|
||||
while (isa<BlockDecl>(DC) || isa<EnumDecl>(DC))
|
||||
DC = DC->getParent();
|
||||
while (true) {
|
||||
if (isa<BlockDecl>(DC) || isa<EnumDecl>(DC)) {
|
||||
DC = DC->getParent();
|
||||
} else if (isa<CXXMethodDecl>(DC) &&
|
||||
cast<CXXRecordDecl>(DC->getParent())->hasDefinition() &&
|
||||
cast<CXXRecordDecl>(DC->getParent())->isLambda()) {
|
||||
DC = DC->getParent()->getParent();
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
||||
return DC;
|
||||
}
|
||||
|
|
|
@ -1751,7 +1751,7 @@ static void AddOrdinaryNameResults(Sema::ParserCompletionContext CCC,
|
|||
case Sema::PCC_Expression: {
|
||||
if (SemaRef.getLangOptions().CPlusPlus) {
|
||||
// 'this', if we're in a non-static member function.
|
||||
QualType ThisTy = SemaRef.getCurrentThisType(false);
|
||||
QualType ThisTy = SemaRef.getCurrentThisType();
|
||||
if (!ThisTy.isNull()) {
|
||||
Builder.AddResultTypeChunk(GetCompletionTypeString(ThisTy,
|
||||
SemaRef.Context,
|
||||
|
|
|
@ -1544,6 +1544,7 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R,
|
|||
Diag(R.getNameLoc(), diagnostic) << Name
|
||||
<< FixItHint::CreateInsertion(R.getNameLoc(), "this->");
|
||||
QualType DepThisType = DepMethod->getThisType(Context);
|
||||
CheckCXXThisCapture(R.getNameLoc());
|
||||
CXXThisExpr *DepThis = new (Context) CXXThisExpr(
|
||||
R.getNameLoc(), DepThisType, false);
|
||||
TemplateArgumentListInfo TList;
|
||||
|
|
|
@ -653,22 +653,8 @@ ExprResult Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc, Expr *E,
|
|||
return Owned(E);
|
||||
}
|
||||
|
||||
QualType Sema::getCurrentThisType(bool Capture) {
|
||||
// Ignore block scopes: we can capture through them.
|
||||
// Ignore nested enum scopes: we'll diagnose non-constant expressions
|
||||
// where they're invalid, and other uses are legitimate.
|
||||
// Don't ignore nested class scopes: you can't use 'this' in a local class.
|
||||
DeclContext *DC = CurContext;
|
||||
unsigned NumBlocks = 0;
|
||||
while (true) {
|
||||
if (isa<BlockDecl>(DC)) {
|
||||
DC = cast<BlockDecl>(DC)->getDeclContext();
|
||||
++NumBlocks;
|
||||
} else if (isa<EnumDecl>(DC))
|
||||
DC = cast<EnumDecl>(DC)->getDeclContext();
|
||||
else break;
|
||||
}
|
||||
|
||||
QualType Sema::getCurrentThisType() {
|
||||
DeclContext *DC = getFunctionLevelDeclContext();
|
||||
QualType ThisTy;
|
||||
if (CXXMethodDecl *method = dyn_cast<CXXMethodDecl>(DC)) {
|
||||
if (method && method->isInstance())
|
||||
|
@ -683,17 +669,63 @@ QualType Sema::getCurrentThisType(bool Capture) {
|
|||
ThisTy = Context.getPointerType(Context.getRecordType(RD));
|
||||
}
|
||||
|
||||
if (!Capture || ThisTy.isNull())
|
||||
return ThisTy;
|
||||
|
||||
// Mark that we're closing on 'this' in all the block scopes we ignored.
|
||||
for (unsigned idx = FunctionScopes.size() - 1;
|
||||
NumBlocks; --idx, --NumBlocks)
|
||||
cast<BlockScopeInfo>(FunctionScopes[idx])->CapturesCXXThis = true;
|
||||
|
||||
return ThisTy;
|
||||
}
|
||||
|
||||
void Sema::CheckCXXThisCapture(SourceLocation Loc) {
|
||||
// We don't need to capture this in an unevaluated context.
|
||||
if (ExprEvalContexts.back().Context == Unevaluated)
|
||||
return;
|
||||
|
||||
// Otherwise, check that we can capture 'this'.
|
||||
unsigned NumClosures = 0;
|
||||
for (unsigned idx = FunctionScopes.size() - 1; idx != 0; idx--) {
|
||||
if (LambdaScopeInfo *LSI = dyn_cast<LambdaScopeInfo>(FunctionScopes[idx])) {
|
||||
if (LSI->CapturesCXXThis) {
|
||||
// This lambda already captures 'this'; there isn't anything more to do.
|
||||
break;
|
||||
}
|
||||
if (LSI->Default == LCD_ByRef) {
|
||||
// This lambda can implicitly capture 'this'; continue looking upwards.
|
||||
// FIXME: Is this check correct? The rules in the standard are a bit
|
||||
// unclear.
|
||||
NumClosures++;
|
||||
continue;
|
||||
}
|
||||
// This lambda can't implicitly capture 'this'; fail out.
|
||||
// (We need to delay the diagnostic in the
|
||||
// PotentiallyPotentiallyEvaluated case because it doesn't apply to
|
||||
// unevaluated contexts.)
|
||||
if (ExprEvalContexts.back().Context == PotentiallyPotentiallyEvaluated)
|
||||
ExprEvalContexts.back()
|
||||
.addDiagnostic(Loc, PDiag(diag::err_implicit_this_capture));
|
||||
else
|
||||
Diag(Loc, diag::err_implicit_this_capture);
|
||||
return;
|
||||
}
|
||||
if (isa<BlockScopeInfo>(FunctionScopes[idx])) {
|
||||
NumClosures++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if (BlockScopeInfo *BSI = dyn_cast<BlockScopeInfo>(FunctionScopes[idx])) {
|
||||
BSI->CapturesCXXThis = true;
|
||||
} else {
|
||||
LambdaScopeInfo *LSI = dyn_cast<LambdaScopeInfo>(FunctionScopes[idx]);
|
||||
assert(LSI && "Unexpected closure");
|
||||
LSI->CapturesCXXThis = true;
|
||||
LSI->Captures.push_back(LambdaScopeInfo::Capture::ThisCapture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExprResult Sema::ActOnCXXThis(SourceLocation Loc) {
|
||||
/// C++ 9.3.2: In the body of a non-static member function, the keyword this
|
||||
/// is a non-lvalue expression whose value is the address of the object for
|
||||
|
@ -702,6 +734,7 @@ ExprResult Sema::ActOnCXXThis(SourceLocation Loc) {
|
|||
QualType ThisTy = getCurrentThisType();
|
||||
if (ThisTy.isNull()) return Diag(Loc, diag::err_invalid_this_use);
|
||||
|
||||
CheckCXXThisCapture(Loc);
|
||||
return Owned(new (Context) CXXThisExpr(Loc, ThisTy, /*isImplicit=*/false));
|
||||
}
|
||||
|
||||
|
@ -4791,14 +4824,11 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
|
|||
/*IdLoc=*/SourceLocation(),
|
||||
/*Id=*/0);
|
||||
Class->startDefinition();
|
||||
Class->setLambda(true);
|
||||
CurContext->addDecl(Class);
|
||||
|
||||
// Introduce the lambda scope.
|
||||
PushLambdaScope(Class);
|
||||
|
||||
LambdaScopeInfo *LSI = getCurLambda();
|
||||
|
||||
QualType ThisCaptureType;
|
||||
llvm::SmallVector<LambdaScopeInfo::Capture, 4> Captures;
|
||||
llvm::DenseMap<const IdentifierInfo*, SourceLocation> CapturesSoFar;
|
||||
for (llvm::SmallVector<LambdaCapture, 4>::const_iterator
|
||||
C = Intro.Captures.begin(), E = Intro.Captures.end(); C != E; ++C) {
|
||||
|
@ -4814,12 +4844,13 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
|
|||
}
|
||||
|
||||
ThisCaptureType = getCurrentThisType();
|
||||
|
||||
if (ThisCaptureType.isNull()) {
|
||||
Diag(C->Loc, diag::err_invalid_this_use);
|
||||
continue;
|
||||
}
|
||||
LSI->Captures.push_back(LambdaScopeInfo::Capture::ThisCapture);
|
||||
CheckCXXThisCapture(C->Loc);
|
||||
|
||||
Captures.push_back(LambdaScopeInfo::Capture::ThisCapture);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -4868,7 +4899,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
|
|||
// in the general case; see shouldCaptureValueReference.
|
||||
// FIXME: Should we be building a DeclRefExpr here? We don't really need
|
||||
// it until the point where we're actually building the LambdaExpr.
|
||||
LSI->Captures.push_back(LambdaScopeInfo::Capture(Var, C->Kind));
|
||||
Captures.push_back(LambdaScopeInfo::Capture(Var, C->Kind));
|
||||
}
|
||||
|
||||
// Build the call operator; we don't really have all the relevant information
|
||||
|
@ -4944,6 +4975,15 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
|
|||
}
|
||||
}
|
||||
|
||||
// Introduce the lambda scope.
|
||||
PushLambdaScope(Class);
|
||||
|
||||
LambdaScopeInfo *LSI = getCurLambda();
|
||||
LSI->Default = Intro.Default;
|
||||
if (!ThisCaptureType.isNull())
|
||||
LSI->CapturesCXXThis = true;
|
||||
std::swap(LSI->Captures, Captures);
|
||||
|
||||
const FunctionType *Fn = MethodTy->getAs<FunctionType>();
|
||||
QualType RetTy = Fn->getResultType();
|
||||
if (RetTy != Context.DependentTy) {
|
||||
|
|
|
@ -705,6 +705,7 @@ Sema::BuildAnonymousStructUnionMemberReference(const CXXScopeSpec &SS,
|
|||
}
|
||||
|
||||
// Our base object expression is "this".
|
||||
CheckCXXThisCapture(loc);
|
||||
baseObjectExpr
|
||||
= new (Context) CXXThisExpr(loc, ThisTy, /*isImplicit=*/ true);
|
||||
baseObjectIsPointer = true;
|
||||
|
@ -854,6 +855,7 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
|
|||
SourceLocation Loc = R.getNameLoc();
|
||||
if (SS.getRange().isValid())
|
||||
Loc = SS.getRange().getBegin();
|
||||
CheckCXXThisCapture(Loc);
|
||||
BaseExpr = new (Context) CXXThisExpr(Loc, BaseExprType,/*isImplicit=*/true);
|
||||
}
|
||||
|
||||
|
@ -1556,6 +1558,7 @@ Sema::BuildImplicitMemberExpr(const CXXScopeSpec &SS,
|
|||
SourceLocation Loc = R.getNameLoc();
|
||||
if (SS.getRange().isValid())
|
||||
Loc = SS.getRange().getBegin();
|
||||
CheckCXXThisCapture(Loc);
|
||||
baseExpr = new (Context) CXXThisExpr(loc, ThisTy, /*isImplicit=*/true);
|
||||
}
|
||||
|
||||
|
|
|
@ -10401,6 +10401,7 @@ Expr *Sema::FixOverloadedFunctionReference(Expr *E, DeclAccessPair Found,
|
|||
SourceLocation Loc = MemExpr->getMemberLoc();
|
||||
if (MemExpr->getQualifier())
|
||||
Loc = MemExpr->getQualifierLoc().getBeginLoc();
|
||||
CheckCXXThisCapture(Loc);
|
||||
Base = new (Context) CXXThisExpr(Loc,
|
||||
MemExpr->getBaseType(),
|
||||
/*isImplicit=*/true);
|
||||
|
|
|
@ -14,7 +14,8 @@ class C {
|
|||
[] {}; // expected-error {{lambda expressions are not supported yet}}
|
||||
[=] (int i) {}; // expected-error {{lambda expressions are not supported yet}}
|
||||
[&] (int) mutable -> void {}; // expected-error {{lambda expressions are not supported yet}}
|
||||
[foo,bar] () { return 3; }; // expected-error {{lambda expressions are not supported yet}}
|
||||
// FIXME: Implicit return type deduction doesn't work yet.
|
||||
[foo,bar] () { return 3; }; // expected-error {{void function 'f' should not return a value}} expected-error {{lambda expressions are not supported yet}}
|
||||
[=,&foo] () {}; // expected-error {{lambda expressions are not supported yet}}
|
||||
[this] () {}; // expected-error {{lambda expressions are not supported yet}}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s
|
||||
|
||||
namespace std { class type_info; };
|
||||
|
||||
namespace ExplicitCapture {
|
||||
int GlobalVar; // expected-note {{declared here}}
|
||||
|
||||
|
@ -10,10 +12,13 @@ namespace ExplicitCapture {
|
|||
using namespace N;
|
||||
|
||||
class C {
|
||||
int x;
|
||||
int Member;
|
||||
|
||||
void f(int);
|
||||
void f() {
|
||||
static void Overload(int);
|
||||
void Overload();
|
||||
virtual C& Overload(float);
|
||||
|
||||
void ExplicitCapture() {
|
||||
int foo;
|
||||
|
||||
[foo, foo] () {}; // expected-error {{'foo' can appear only once}} expected-error {{not supported yet}}
|
||||
|
@ -24,11 +29,23 @@ namespace ExplicitCapture {
|
|||
[&, foo] () {}; // expected-error {{not supported yet}}
|
||||
[&, &foo] () {}; // expected-error {{'&' cannot precede a capture when}} expected-error {{not supported yet}}
|
||||
[&, this] () {}; // expected-error {{not supported yet}}
|
||||
[&f] () {}; // expected-error {{does not name a variable}} expected-error {{not supported yet}}
|
||||
[&Overload] () {}; // expected-error {{does not name a variable}} expected-error {{not supported yet}}
|
||||
[&GlobalVar] () {}; // expected-error {{does not have automatic storage duration}} expected-error {{not supported yet}}
|
||||
[&AmbiguousVar] () {} // expected-error {{reference to 'AmbiguousVar' is ambiguous}} expected-error {{not supported yet}}
|
||||
[&Globalvar] () {}; // expected-error {{use of undeclared identifier 'Globalvar'; did you mean 'GlobalVar}}
|
||||
}
|
||||
|
||||
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}}
|
||||
[this](){(void)Member;}; // expected-error {{not supported yet}}
|
||||
[this]{[this]{};};// expected-error 2 {{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}}
|
||||
[]{(void)typeid(Overload());};// expected-error {{not supported yet}}
|
||||
[]{(void)typeid(Overload(.5f));};// expected-error {{'this' cannot be implicitly captured in this context}} expected-error {{not supported yet}}
|
||||
}
|
||||
};
|
||||
|
||||
void f() {
|
||||
|
|
Загрузка…
Ссылка в новой задаче