diff options
-rw-r--r-- | include/clang/AST/DeclCXX.h | 8 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 2 | ||||
-rw-r--r-- | include/clang/Sema/ScopeInfo.h | 16 | ||||
-rw-r--r-- | include/clang/Sema/Sema.h | 7 | ||||
-rw-r--r-- | lib/AST/DeclCXX.cpp | 4 | ||||
-rw-r--r-- | lib/Sema/Sema.cpp | 12 | ||||
-rw-r--r-- | lib/Sema/SemaCodeComplete.cpp | 2 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 1 | ||||
-rw-r--r-- | lib/Sema/SemaExprCXX.cpp | 104 | ||||
-rw-r--r-- | lib/Sema/SemaExprMember.cpp | 3 | ||||
-rw-r--r-- | lib/Sema/SemaOverload.cpp | 1 | ||||
-rw-r--r-- | test/Parser/objcxx0x-lambda-expressions.mm | 3 | ||||
-rw-r--r-- | test/SemaCXX/lambda-expressions.cpp | 25 |
13 files changed, 140 insertions, 48 deletions
diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index dcf8c17bcd..588b1993d9 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -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() { diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 1663f12ae1..441f14d113 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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">; diff --git a/include/clang/Sema/ScopeInfo.h b/include/clang/Sema/ScopeInfo.h index 6a6f395c87..4d49a5a88a 100644 --- a/include/clang/Sema/ScopeInfo.h +++ b/include/clang/Sema/ScopeInfo.h @@ -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; } diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 6d20480b57..389cf8ac72 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -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); diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 0f83c560b0..ba369bbe99 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -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, diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 4ab960de0e..72073a1038 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -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; } diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index 78e3c355d6..1888d65ad9 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -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, diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 14d3c045ce..f9fb3bc9fc 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -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; diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 7c5ad0097f..5e7fb33d05 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -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) { diff --git a/lib/Sema/SemaExprMember.cpp b/lib/Sema/SemaExprMember.cpp index 3e7f8314d5..e05360b87e 100644 --- a/lib/Sema/SemaExprMember.cpp +++ b/lib/Sema/SemaExprMember.cpp @@ -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); } diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index ee846c3fc3..be89ed4462 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -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); diff --git a/test/Parser/objcxx0x-lambda-expressions.mm b/test/Parser/objcxx0x-lambda-expressions.mm index 5cfb4f8c5a..06573b9446 100644 --- a/test/Parser/objcxx0x-lambda-expressions.mm +++ b/test/Parser/objcxx0x-lambda-expressions.mm @@ -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}} } diff --git a/test/SemaCXX/lambda-expressions.cpp b/test/SemaCXX/lambda-expressions.cpp index 50d0d6deb8..9411c34ec8 100644 --- a/test/SemaCXX/lambda-expressions.cpp +++ b/test/SemaCXX/lambda-expressions.cpp @@ -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; + + static void Overload(int); + void Overload(); + virtual C& Overload(float); - void f(int); - void f() { + 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() { |