diff options
author | Eli Friedman <eli.friedman@gmail.com> | 2012-01-07 01:08:17 +0000 |
---|---|---|
committer | Eli Friedman <eli.friedman@gmail.com> | 2012-01-07 01:08:17 +0000 |
commit | e81d7e9810eed0d805263791d761ec545d2cf779 (patch) | |
tree | a48a534d55c3e1430f8475c8b9219d1052c6d669 | |
parent | 3070e13dca5bbefa32acb80ce4a7b217a6220983 (diff) |
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
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 12 | ||||
-rw-r--r-- | include/clang/Sema/ScopeInfo.h | 27 | ||||
-rw-r--r-- | lib/Sema/SemaExprCXX.cpp | 83 | ||||
-rw-r--r-- | test/SemaCXX/lambda-expressions.cpp | 37 |
4 files changed, 151 insertions, 8 deletions
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<VarDecl*, 2, LambdaCaptureKind> 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<LambdaExpr::Capture, 4> Captures; + llvm::SmallVector<Capture, 4> 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<const IdentifierInfo*, SourceLocation> CapturesSoFar; + for (llvm::SmallVector<LambdaCapture, 4>::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<const IdentifierInfo*, SourceLocation>::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<VarDecl>(); + 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<FunctionProtoTypeLoc>(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}} + } +} |