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 e15352b..1663f12 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3992,6 +3992,18 @@
"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 1edeff5..6a6f395 100644
--- a/include/clang/Sema/ScopeInfo.h
+++ b/include/clang/Sema/ScopeInfo.h
@@ -160,6 +160,29 @@
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 @@
/// \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 0b1dcbd..7c5ad00 100644
--- a/lib/Sema/SemaExprCXX.cpp
+++ b/lib/Sema/SemaExprCXX.cpp
@@ -4793,6 +4793,84 @@
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 @@
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 0000000..50d0d6d
--- /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}}
+ }
+}