From: Eli Friedman Date: Sat, 7 Jan 2012 01:08:17 +0000 (+0000) Subject: Lambdas: semantic analysis of explicit captures. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e81d7e9810eed0d805263791d761ec545d2cf779;p=clang Lambdas: semantic analysis of explicit captures. This patch (and some of my other commits related to lambdas) is heavily based off of John Freeman's work-in-progress patches. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@147706 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index e15352bcd5..1663f12ae1 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -3992,6 +3992,18 @@ def err_return_in_constructor_handler : Error< "return in the catch of a function try block of a constructor is illegal">; def err_lambda_unsupported : Error<"lambda expressions are not supported yet">; +def err_capture_more_than_once : Error< + "%0 can appear only once in a capture list">; +def err_reference_capture_with_reference_default : Error< + "'&' cannot precede a capture when the capture default is '&'">; +def err_this_capture_with_copy_default : Error< + "'this' cannot appear in a capture list when the capture default is '='">; +def err_copy_capture_with_copy_default : Error< + "'&' must precede a capture when the capture default is '='">; +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_operator_arrow_circular : Error< "circular pointer delegation detected">; diff --git a/include/clang/Sema/ScopeInfo.h b/include/clang/Sema/ScopeInfo.h index 1edeff5b43..6a6f395c87 100644 --- a/include/clang/Sema/ScopeInfo.h +++ b/include/clang/Sema/ScopeInfo.h @@ -160,6 +160,29 @@ public: class LambdaScopeInfo : public FunctionScopeInfo { public: + + class Capture { + llvm::PointerIntPair InitAndKind; + + public: + Capture(VarDecl *Var, LambdaCaptureKind Kind) + : InitAndKind(Var, Kind) {} + + enum IsThisCapture { ThisCapture }; + Capture(IsThisCapture) + : InitAndKind(0, LCK_This) {} + + bool isThisCapture() const { return InitAndKind.getInt() == LCK_This; } + bool isVariableCapture() const { return !isThisCapture(); } + bool isCopyCapture() const { return InitAndKind.getInt() == LCK_ByCopy; } + bool isReferenceCapture() const { return InitAndKind.getInt() == LCK_ByRef; } + + VarDecl *getVariable() const { + return InitAndKind.getPointer(); + } + + }; + /// \brief The class that describes the lambda. CXXRecordDecl *Lambda; @@ -169,9 +192,7 @@ public: /// \brief The list of captured variables, starting with the explicit /// captures and then finishing with any implicit captures. - // TODO: This is commented out until an implementation of LambdaExpr is - // committed. - // llvm::SmallVector Captures; + llvm::SmallVector Captures; /// \brief The number of captures in the \c Captures list that are /// explicit captures. diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 0b1dcbda4f..7c5ad0097f 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -4793,6 +4793,84 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, Class->startDefinition(); CurContext->addDecl(Class); + // Introduce the lambda scope. + PushLambdaScope(Class); + + LambdaScopeInfo *LSI = getCurLambda(); + + QualType ThisCaptureType; + llvm::DenseMap CapturesSoFar; + for (llvm::SmallVector::const_iterator + C = Intro.Captures.begin(), E = Intro.Captures.end(); C != E; ++C) { + if (C->Kind == LCK_This) { + if (!ThisCaptureType.isNull()) { + Diag(C->Loc, diag::err_capture_more_than_once) << "'this'"; + continue; + } + + if (Intro.Default == LCD_ByCopy) { + Diag(C->Loc, diag::err_this_capture_with_copy_default); + continue; + } + + ThisCaptureType = getCurrentThisType(); + + if (ThisCaptureType.isNull()) { + Diag(C->Loc, diag::err_invalid_this_use); + continue; + } + LSI->Captures.push_back(LambdaScopeInfo::Capture::ThisCapture); + continue; + } + + assert(C->Id && "missing identifier for capture"); + + if (C->Kind == LCK_ByRef && Intro.Default == LCD_ByRef) { + Diag(C->Loc, diag::err_reference_capture_with_reference_default); + continue; + } else if (C->Kind == LCK_ByCopy && Intro.Default == LCD_ByCopy) { + Diag(C->Loc, diag::err_copy_capture_with_copy_default); + continue; + } + + llvm::DenseMap::iterator Appearance; + bool IsFirstAppearance; + llvm::tie(Appearance, IsFirstAppearance) + = CapturesSoFar.insert(std::make_pair(C->Id, C->Loc)); + + if (!IsFirstAppearance) { + Diag(C->Loc, diag::err_capture_more_than_once) << C->Id; + continue; + } + + DeclarationNameInfo Name(C->Id, C->Loc); + LookupResult R(*this, Name, LookupOrdinaryName); + CXXScopeSpec ScopeSpec; + LookupParsedName(R, CurScope, &ScopeSpec); + if (R.isAmbiguous()) + continue; + if (R.empty()) + if (DiagnoseEmptyLookup(CurScope, ScopeSpec, R, CTC_Unknown)) + continue; + + VarDecl *Var = R.getAsSingle(); + if (!Var) { + Diag(C->Loc, diag::err_capture_does_not_name_variable) << C->Id; + continue; + } + + if (!Var->hasLocalStorage()) { + Diag(C->Loc, diag::err_capture_non_automatic_variable) << C->Id; + continue; + } + + // FIXME: Actually capturing a variable is much more complicated than this + // 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)); + } + // 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; @@ -4837,17 +4915,12 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, ProcessDeclAttributes(CurScope, Method, ParamInfo); - // Introduce the lambda scope. - PushLambdaScope(Class); - // Enter a new evaluation context to insulate the block from any // cleanups from the enclosing full-expression. PushExpressionEvaluationContext(PotentiallyEvaluated); PushDeclContext(CurScope, Method); - LambdaScopeInfo *LSI = getCurLambda(); - // Set the parameters on the decl, if specified. if (isa(MethodTyInfo->getTypeLoc())) { FunctionProtoTypeLoc Proto = diff --git a/test/SemaCXX/lambda-expressions.cpp b/test/SemaCXX/lambda-expressions.cpp new file mode 100644 index 0000000000..50d0d6deb8 --- /dev/null +++ b/test/SemaCXX/lambda-expressions.cpp @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s + +namespace ExplicitCapture { + int GlobalVar; // expected-note {{declared here}} + + namespace N { + int AmbiguousVar; // expected-note {{candidate}} + } + int AmbiguousVar; // expected-note {{candidate}} + using namespace N; + + class C { + int x; + + void f(int); + void f() { + int foo; + + [foo, foo] () {}; // expected-error {{'foo' can appear only once}} expected-error {{not supported yet}} + [this, this] () {}; // expected-error {{'this' can appear only once}} expected-error {{not supported yet}} + [=, foo] () {}; // expected-error {{'&' must precede a capture when}} expected-error {{not supported yet}} + [=, &foo] () {}; // expected-error {{not supported yet}} + [=, this] () {}; // expected-error {{'this' cannot appear}} expected-error {{not supported yet}} + [&, 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}} + [&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 f() { + [this] () {}; // expected-error {{invalid use of 'this'}} expected-error {{not supported yet}} + } +}