Lambdas: semantic analysis of explicit captures.
authorEli Friedman <eli.friedman@gmail.com>
Sat, 7 Jan 2012 01:08:17 +0000 (01:08 +0000)
committerEli Friedman <eli.friedman@gmail.com>
Sat, 7 Jan 2012 01:08:17 +0000 (01:08 +0000)
This patch (and some of my other commits related to lambdas) is heavily based off of John Freeman's work-in-progress patches.

llvm-svn: 147706

clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/ScopeInfo.h
clang/lib/Sema/SemaExprCXX.cpp
clang/test/SemaCXX/lambda-expressions.cpp [new file with mode: 0644]

index e15352b..1663f12 100644 (file)
@@ -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">;
index 1edeff5..6a6f395 100644 (file)
@@ -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.
index 0b1dcbd..7c5ad00 100644 (file)
@@ -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/clang/test/SemaCXX/lambda-expressions.cpp b/clang/test/SemaCXX/lambda-expressions.cpp
new file mode 100644 (file)
index 0000000..50d0d6d
--- /dev/null
@@ -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}}
+  }
+}