From: Douglas Gregor Date: Wed, 1 Feb 2012 17:04:21 +0000 (+0000) Subject: Introduce the lambda scope before determining explicit captures, which X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a1f2114d9e81923c750f6b439302ac03552c37db;p=clang 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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@149516 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 8cf83fba6a..84f67cdb90 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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">; diff --git a/include/clang/Sema/ScopeInfo.h b/include/clang/Sema/ScopeInfo.h index bff2cf8ad2..2015ce7fc4 100644 --- a/include/clang/Sema/ScopeInfo.h +++ b/include/clang/Sema/ScopeInfo.h @@ -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::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; } diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 4d15ec4b1c..5b87bb98fa 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -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); diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 0a987511fb..c7daa80761 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -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 CaptureMap; - unsigned CXXThisCaptureIndex = 0; - llvm::SmallVector 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::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(); 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(MethodTyInfo->getTypeLoc())) { FunctionProtoTypeLoc Proto = - cast(MethodTyInfo->getTypeLoc()); + cast(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(); QualType RetTy = Fn->getResultType(); if (RetTy != Context.DependentTy) { diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index f2185157ad..610b790d2c 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -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" diff --git a/test/SemaCXX/lambda-expressions.cpp b/test/SemaCXX/lambda-expressions.cpp index cacd4b680c..a2dc7070dc 100644 --- a/test/SemaCXX/lambda-expressions.cpp +++ b/test/SemaCXX/lambda-expressions.cpp @@ -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}} } }