Attached is a patch that begins building an AST for lambda expressions. It is still incomplete (notably no LambdaExpr yet), but I feel like I reached a stopping point and would like a review before I get too deep.

There are quite a few FIXMEs sprinkled throughout. Not all of them indicate problems; many just mark design/API/coding convention questions for the reviewer. When it came to decisions, I just went with my gut figuring I could always fix it later if I was wrong.

Thank you,
John
diff --git include/clang/AST/DeclBase.h include/clang/AST/DeclBase.h
index 8355e36..ed7ce88 100644
--- include/clang/AST/DeclBase.h
+++ include/clang/AST/DeclBase.h
@@ -39,6 +39,7 @@ class ObjCCategoryImplDecl;
 class ObjCImplDecl;
 class LinkageSpecDecl;
 class BlockDecl;
+class LambdaDecl;
 class DeclarationName;
 class CompoundStmt;
 class StoredDeclsMap;
diff --git include/clang/AST/DeclCXX.h include/clang/AST/DeclCXX.h
index 01ca2e3..5e68b96 100644
--- include/clang/AST/DeclCXX.h
+++ include/clang/AST/DeclCXX.h
@@ -16,6 +16,8 @@
 #define LLVM_CLANG_AST_DECLCXX_H
 
 #include "clang/AST/Expr.h"
+// FIXME: Including this only for CXXThisExpr. Is that bad?
+#include "clang/AST/ExprCXX.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/TypeLoc.h"
 #include "clang/AST/UnresolvedSet.h"
@@ -2566,6 +2568,53 @@ public:
   friend class ASTDeclReader;
 };
 
+/// LambdaDecl - Represents a C++0x lambda expression.
+class LambdaDecl : public Decl, public DeclContext {
+public:
+  class Capture {
+    enum {
+      CaptureThis,
+      CaptureByRef,
+      CaptureByCopy
+    } Kind;
+
+    Expr* Init;
+
+  public:
+    Capture(CXXThisExpr *Init)
+      : Kind(CaptureThis), Init(Init) {}
+
+    Capture(DeclRefExpr *Init, bool ByRef)
+      : Kind(ByRef ? CaptureByRef : CaptureByCopy), Init(Init) {}
+
+    bool isThisCapture() const { return Kind == CaptureThis; }
+    bool isReferenceCapture() const { return Kind == CaptureByRef; }
+    bool isCopyCapture() const { return Kind == CaptureByCopy; }
+
+    /// The initializing Expr for the captured entity. Will be either
+    /// CXXThisExpr or DeclRefExpr.
+    Expr* getInit() const { return Init; }
+
+  };
+
+  Capture *Explicits;
+  unsigned NumExplicits;
+
+  /// Declaration for the function object class implementing the lambda
+  /// expression.
+  CXXRecordDecl *D;
+
+protected:
+  LambdaDecl(DeclContext *DC, SourceLocation L)
+    : Decl(Lambda, DC, L), DeclContext(Lambda) {}
+
+public:
+  static LambdaDecl *Create(ASTContext &C, DeclContext *DC, SourceLocation L) {
+    return new (C) LambdaDecl(DC, L);
+  }
+
+};
+
 /// Insertion operator for diagnostics.  This allows sending AccessSpecifier's
 /// into a diagnostic with <<.
 const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
diff --git include/clang/AST/RecursiveASTVisitor.h 
include/clang/AST/RecursiveASTVisitor.h
index 85c5c08..ccdb202 100644
--- include/clang/AST/RecursiveASTVisitor.h
+++ include/clang/AST/RecursiveASTVisitor.h
@@ -1114,6 +1114,8 @@ DEF_TRAVERSE_DECL(FriendTemplateDecl, {
     }
   })
 
+DEF_TRAVERSE_DECL(LambdaDecl, { })
+
 DEF_TRAVERSE_DECL(LinkageSpecDecl, { })
 
 DEF_TRAVERSE_DECL(ObjCClassDecl, {
diff --git include/clang/Basic/DeclNodes.td include/clang/Basic/DeclNodes.td
index ddd0827..3c3f590 100644
--- include/clang/Basic/DeclNodes.td
+++ include/clang/Basic/DeclNodes.td
@@ -74,3 +74,4 @@ def Friend : Decl;
 def FriendTemplate : Decl;
 def StaticAssert : Decl;
 def Block : Decl, DeclContext;
+def Lambda : Decl, DeclContext;
diff --git include/clang/Basic/DiagnosticSemaKinds.td 
include/clang/Basic/DiagnosticSemaKinds.td
index 81fb8b6..a598f6b 100644
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -1151,6 +1151,19 @@ def err_for_range_begin_end_types_differ : Error<
 def note_for_range_type : Note<"range has type %0">;
 def note_for_range_begin_end : Note<
   "selected '%select{begin|end}0' %select{function|template }1%2 with iterator 
type %3">;
+
+// C++0x lambda expressions
+def err_capture_more_than_once : Error<
+  "%0 can appear only once in a capture list">;
+// FIXME: Is this note overkill? It will almost always appear on the same line
+// as the error.
+def note_previously_captured_here : Note<"previously captured here">;
+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 '='">;
   
 // Objective-C++
 def err_objc_decls_may_only_appear_in_global_scope : Error<
diff --git include/clang/Sema/Sema.h include/clang/Sema/Sema.h
index 1258d59..36c6b52 100644
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -4940,6 +4940,18 @@ public:
   DeclContext *FindInstantiatedContext(SourceLocation Loc, DeclContext *DC,
                           const MultiLevelTemplateArgumentList &TemplateArgs);
 
+  
//===--------------------------------------------------------------------===//
+  // C++ Lambda Expressions (C++0x [expr.prim.lambda])
+
+  // FIXME: Change to DeclResult later? Is there a better way to pass the 
Lambda
+  // through each action? The introducer does not begin a function scope,
+  // so cannot mimic blocks. Should we condense these into one action, or 
should
+  // we try semantic checking as early as possible?
+  LambdaDecl *ActOnLambdaIntroducer(const LambdaIntroducer &Intro);
+  void ActOnLambdaDeclarator(const Declarator &D);
+  void ActOnLambdaBody(LambdaDecl *Lambda, Stmt *Body);
+
+  
//===--------------------------------------------------------------------===//
   // Objective-C declarations.
   Decl *ActOnStartClassInterface(SourceLocation AtInterfaceLoc,
                                  IdentifierInfo *ClassName,
diff --git lib/Parse/ParseExprCXX.cpp lib/Parse/ParseExprCXX.cpp
index 757d86e..20ee777 100644
--- lib/Parse/ParseExprCXX.cpp
+++ lib/Parse/ParseExprCXX.cpp
@@ -672,6 +672,10 @@ bool Parser::TryParseLambdaIntroducer(LambdaIntroducer 
&Intro) {
 /// expression.
 ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
                      LambdaIntroducer &Intro) {
+  LambdaDecl *Lambda = Actions.ActOnLambdaIntroducer(Intro);
+  if (!Lambda)
+    return ExprError();
+
   // Parse lambda-declarator[opt].
   DeclSpec DS(AttrFactory);
   Declarator D(DS, Declarator::PrototypeContext);
@@ -747,6 +751,8 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
                   Attr, DeclEndLoc);
   }
 
+  Actions.ActOnLambdaDeclarator(D);
+
   // Parse compound-statement.
   if (Tok.is(tok::l_brace)) {
     // FIXME: Rename BlockScope -> ClosureScope if we decide to continue using
@@ -758,6 +764,9 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
     StmtResult Stmt(ParseCompoundStatementBody());
 
     BodyScope.Exit();
+
+    if (!Stmt.isInvalid())
+      Actions.ActOnLambdaBody(Lambda, Stmt.take());
   } else {
     Diag(Tok, diag::err_expected_lambda_body);
   }
diff --git lib/Sema/SemaExprCXX.cpp lib/Sema/SemaExprCXX.cpp
index 4f49463..781d6fc 100644
--- lib/Sema/SemaExprCXX.cpp
+++ lib/Sema/SemaExprCXX.cpp
@@ -4655,3 +4655,264 @@ bool Sema::CheckMicrosoftIfExistsSymbol(CXXScopeSpec 
&SS,
   LookupParsedName(R, getCurScope(), &SS);
   return !R.empty(); 
 }
+
+// FIXME: Several areas in the below ActOnLambda* functions may benefit from
+// calling existing Sema functions, or from factoring out some essential bits 
to
+// share. These are marked with FIXMEs that begin "Compare with ...".
+//
+// Additionally, there are a few checks sprinkled throughout these actions
+// merely to ensure their correct implementation. I did not think it worth
+// wrapping them in separate functions just so they can be asserted. They 
should
+// be removed after code review for performance reasons. They are marked with
+// FIXMEs stating "Remove this check later.".
+LambdaDecl *Sema::ActOnLambdaIntroducer(const LambdaIntroducer &Intro) {
+  // Diagnose the explicit capture list.
+  SourceLocation CapturesThis;
+  // FIXME: Are typedefs encouraged? What is the naming convention?
+  llvm::DenseMap<const IdentifierInfo*, SourceLocation> CapturesSoFar;
+
+  // Check captures before we allocate a LambdaDecl.
+  // FIXME: If we hit an error, should we continue as if it didn't exist, or
+  // immediately return failure? For now, return failure.
+  for (llvm::SmallVector<LambdaCapture, 4>::const_iterator
+       C = Intro.Captures.begin(), E = Intro.Captures.end(); C != E; ++C) {
+    if (C->Kind == LCK_This) {
+      if (CapturesThis.isValid()) {
+        Diag(C->Loc, diag::err_capture_more_than_once) << "'this'";
+        Diag(CapturesThis, diag::note_previously_captured_here);
+        return NULL;
+      }
+
+      if (Intro.Default == LCD_ByCopy) {
+        Diag(C->Loc, diag::err_this_capture_with_copy_default);
+        return NULL;
+      }
+
+      CapturesThis = C->Loc;
+      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);
+      return NULL;
+    } else if (C->Kind == LCK_ByCopy && Intro.Default == LCD_ByCopy) {
+      Diag(C->Loc, diag::err_copy_capture_with_copy_default);
+      return NULL;
+    }
+
+    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;
+      Diag(Appearance->second, diag::note_previously_captured_here);
+      return NULL;
+    }
+  }
+
+  // Avoid new Capture[] because we don't want to provide a default 
constructor.
+  // FIXME: Do we need to align?
+  LambdaDecl::Capture *Explicits
+    = static_cast<LambdaDecl::Capture*>(
+        Context.Allocate(Intro.Captures.size() * sizeof(LambdaDecl::Capture)));
+
+  // FIXME: Try to merge with the above loop somehow?
+  LambdaDecl::Capture *Explicit = Explicits;
+  for (llvm::SmallVector<LambdaCapture, 4>::const_iterator
+       C = Intro.Captures.begin(), E = Intro.Captures.end(); C != E; ++C) {
+    if (C->Kind == LCK_This) {
+      CXXThisExpr *This = 
static_cast<CXXThisExpr*>(ActOnCXXThis(C->Loc).take());
+      new (Explicit) LambdaDecl::Capture(This);
+      ++Explicit;
+      continue;
+    }
+
+    UnqualifiedId Name;
+    CXXScopeSpec ScopeSpec;
+    Name.setIdentifier(C->Id, C->Loc);
+    // FIXME: Can we do better than ActOnIdExpression?
+    DeclRefExpr *Var
+      = static_cast<DeclRefExpr*>(
+          ActOnIdExpression(getCurScope(),
+                            ScopeSpec, Name,
+                            /*HasTrailingLParen=*/false,
+                            /*IsAddressOfOperand=*/false).take());
+
+    // FIXME: Check that we have a local variable with automatic storage
+    // duration.
+
+    new (Explicit) LambdaDecl::Capture(Var, C->Kind == LCK_ByRef);
+    ++Explicit;
+  }
+
+  // Find the nearest block, class, or namespace scope.
+  DeclContext *DC = CurContext;
+  while (!(DC->isFunctionOrMethod() || DC->isRecord() || DC->isNamespace()))
+    DC = DC->getParent();
+
+  LambdaDecl *Lambda = LambdaDecl::Create(Context, DC,
+                                          Intro.Range.getBegin());
+  Lambda->Explicits = Explicits;
+  Lambda->NumExplicits = Intro.Captures.size();
+
+  return Lambda;
+}
+
+void Sema::ActOnLambdaDeclarator(const Declarator &D) {}
+void Sema::ActOnLambdaBody(LambdaDecl *Lambda, Stmt *Body) {
+  SourceLocation ClassLoc;
+  CXXRecordDecl *Class = CXXRecordDecl::Create(Context,
+                                               TTK_Class,
+                                               Lambda->getDeclContext(),
+                                               /*StartLoc=*/ClassLoc,
+                                               /*IdLoc=*/SourceLocation(),
+                                               /*Id=*/0);
+  Class->startDefinition();
+
+  CanQualType ClassType
+    = Context.getCanonicalType(Context.getTypeDeclType(Class));
+  DeclarationName CtorName
+    = Context.DeclarationNames.getCXXConstructorName(ClassType);
+
+  CXXConstructorDecl *Ctor
+    = CXXConstructorDecl::Create(Context,
+                                 Class,
+                                 ClassLoc,
+                                 DeclarationNameInfo(CtorName, ClassLoc),
+                                 // Chicken and egg problem: we need the Ctor 
to
+                                 // build the CtorParams and the CtorParams to
+                                 // build the Ctor. Favor one call on the Ctor
+                                 // to set its type instead of one call on each
+                                 // CtorParam to set their DeclContext.
+                                 QualType(),
+                                 /*TInfo=*/0,
+                                 /*isExplicit=*/true,
+                                 /*isInline=*/true,
+                                 /*isImplicitlyDeclared=*/true);
+
+  // FIXME: When we support implicit captures later, this number will need to 
be
+  // updated.
+  unsigned NumCaptures = Lambda->NumExplicits;
+
+  llvm::SmallVector<QualType, 4> CtorParamTys;
+  CtorParamTys.reserve(NumCaptures);
+  llvm::SmallVector<ParmVarDecl*, 4> CtorParams;
+  CtorParams.reserve(NumCaptures);
+  CXXCtorInitializer **CtorInits
+    = new (Context) CXXCtorInitializer*[NumCaptures];
+
+  CXXCtorInitializer **CtorInit = CtorInits;
+  for (LambdaDecl::Capture *C = Lambda->Explicits,
+       *E = Lambda->Explicits + Lambda->NumExplicits; C != E; ++C) {
+
+    Expr *Init = C->getInit();
+
+    IdentifierInfo *Id;
+    if (C->isThisCapture()) {
+      Id = PP.getIdentifierInfo("__this");
+    } else {
+      Id = cast<DeclRefExpr>(Init)->getDecl()->getIdentifier();
+    }
+
+    DeclarationName CaptureName
+      = Context.DeclarationNames.getIdentifier(Id);
+
+    // FIXME: Find the correct type. There are capture type transformations
+    // remaining to be implemented. What is the difference between CanQualType
+    // and QualType? How is template instantiation handled? If some of these
+    // have dependent type, how is that treated?
+    QualType CaptureType = Init->getType();
+    if (C->isReferenceCapture()) {
+      // FIXME: Compare with Sema::BuildReferenceType.
+      CaptureType = Context.getLValueReferenceType(CaptureType,
+                                                   /*SpelledAsLValue=*/true);
+    }
+
+    FieldDecl *Field = CheckFieldDecl(CaptureName,
+                                      CaptureType,
+                                      /*TInfo=*/0,
+                                      Class,
+                                      /*StartLoc=*/SourceLocation(),
+                                      /*Mutable=*/false,
+                                      /*BitWidth=*/0,
+                                      /*HasInit=*/false,
+                                      /*IdLoc=*/SourceLocation(),
+                                      AS_private,
+                                      /*PrevDecl=*/0);
+
+    Class->addDecl(Field);
+
+    CtorParamTys.push_back(CaptureType);
+
+    // FIXME: Compare with Sema::CheckParameter.
+    ParmVarDecl *CtorParam = ParmVarDecl::Create(Context,
+                                                 Ctor,
+                                                 /*StartLoc=*/SourceLocation(),
+                                                 /*IdLoc=*/SourceLocation(),
+                                                 Id,
+                                  
Context.getAdjustedParameterType(CaptureType),
+                                                 /*TInfo=*/0,
+                                                 SC_None,
+                                                 SC_None,
+                                                 /*DefArg=*/0);
+    CtorParams.push_back(CtorParam);
+
+    DeclRefExpr *CtorInitArg
+      = DeclRefExpr::Create(Context,
+                            /*QualifierLoc=*/NestedNameSpecifierLoc(),
+                            CtorParam,
+                            /*NameLoc=*/SourceLocation(),
+                            CaptureType.getNonReferenceType(),
+                            VK_LValue);
+    // FIXME: Compare with Sema::BuildMemberInitializer.
+    *CtorInit = new (Context) CXXCtorInitializer(Context,
+                                                 Field,
+                                                 
/*MemberLoc=*/SourceLocation(),
+                                                 /*L=*/SourceLocation(),
+                                                 CtorInitArg,
+                                                 /*R=*/SourceLocation());
+    ++CtorInit;
+
+  }
+
+  // FIXME: Compare with Sema::BuildFunctionType.
+  QualType CtorType = Context.getFunctionType(Context.VoidTy,
+                                              CtorParamTys.data(),
+                                              CtorParamTys.size(),
+                                              
FunctionProtoType::ExtProtoInfo());
+
+  Ctor->setAccess(AS_public);
+  Ctor->setImplicit();
+  Ctor->setType(CtorType);
+  // FIXME: Compare with Sema::CheckParmsForFunctionDef.
+  Ctor->setParams(CtorParams.data(), CtorParams.size());
+  // FIXME: Compare with Sema::ActOnMemInitializers.
+  // FIXME: Compare with Sema::SetCtorInitializers.
+  Ctor->setNumCtorInitializers(NumCaptures);
+  Ctor->setCtorInitializers(CtorInits);
+
+  // FIXME: Remove this check later.
+  bool Redeclaration = false;
+  LookupResult Previous(*this,
+                        CtorName,
+                        SourceLocation(),
+                        LookupOrdinaryName,
+                        ForRedeclaration);
+  CheckFunctionDeclaration(getCurScope(),
+                           Ctor,
+                           Previous,
+                           /*IsExplicitSpecialization=*/false,
+                           Redeclaration);
+  assert(!Ctor->isInvalidDecl() && "bad lambda constructor");
+
+  Class->addDecl(Ctor);
+
+  Class->completeDefinition();
+  // FIXME: Remove this check later.
+  CheckCompletedCXXClass(Class);
+}
+
diff --git test/Parser/objcxx0x-lambda-expressions.mm 
test/Parser/objcxx0x-lambda-expressions.mm
index d100e2e..8d2d9f6 100644
--- test/Parser/objcxx0x-lambda-expressions.mm
+++ test/Parser/objcxx0x-lambda-expressions.mm
@@ -9,7 +9,7 @@ class C {
     [foo,+] {}; // expected-error {{expected expression}}
 
     []; // expected-error {{expected body of lambda expression}}
-    [=,foo+] {}; // expected-error {{expected ',' or ']' in lambda capture 
list}}
+    [=,&foo+] {}; // expected-error {{expected ',' or ']' in lambda capture 
list}}
     [&this] {}; // expected-error {{address expression must be an lvalue}}
     [] {};
     [=] (int i) {};
diff --git test/SemaCXX/lambda-expressions.cpp 
test/SemaCXX/lambda-expressions.cpp
new file mode 100644
index 0000000..c9427aa
--- /dev/null
+++ test/SemaCXX/lambda-expressions.cpp
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s
+
+class C {
+  void f() {
+    int foo;
+
+    [foo, foo] () {}; // expected-error {{'foo' can appear only once}} 
expected-note {{previously captured here}}
+    [this, this] () {}; // expected-error {{'this' can appear only once}} 
expected-note {{previously captured here}}
+    [=, foo] () {}; // expected-error {{'&' must precede a capture when}}
+    [=, &foo] () {};
+    [=, this] () {} // expected-error {{'this' cannot appear}}
+    [&, foo] () {};
+    [&, &foo] () {}; // expected-error {{'&' cannot precede a capture when}}
+    [&, this] () {};
+  }
+};
+
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to