[cfe-commits] [PATCH] Parsing C++0x lambda expressions

Douglas Gregor dgregor at apple.com
Wed Jul 6 12:59:47 PDT 2011


On Jul 6, 2011, at 10:18 AM, John Freeman wrote:

> I'm submitting this patch mainly for review and feedback. It probably isn't ready for commit yet.
> 
> I've included a lot of FIXMEs to document different questions I have and missing pieces. I'll start working on empty Sema actions and lambda-specific Diagnostics while I wait for comments.


A few comments:

Index: include/clang/Parse/Parser.h
===================================================================
--- include/clang/Parse/Parser.h	(revision 134493)
+++ include/clang/Parse/Parser.h	(working copy)
@@ -1183,6 +1183,53 @@
                                       bool IsTypename = false);
 
   //===--------------------------------------------------------------------===//
+  // C++0x 5.1.2: Lambda expressions
+
+  enum CXX0XLambdaCaptureDefault {
+    DEFAULT_CAPTURE_NONE,
+    DEFAULT_CAPTURE_BY_COPY,
+    DEFAULT_CAPTURE_BY_REF
+  };
+
+  enum CXX0XLambdaCaptureKind {
+    CAPTURE_BY_COPY,
+    CAPTURE_BY_REF
+  };

Please see

  http://llvm.org/docs/CodingStandards.html#ll_naming

for enumerator naming conventions.

+  // FIXME: This struct is unnecessary at the moment, but if lambda parsing is
+  // divided among several functions, it raises a couple of questions: Should
+  // these be grouped in a struct, or passed as separate parameters? What is the
+  // policy on defining structs just to hold argument packs?
+  struct CXX0XLambdaCaptureList {
+    llvm::SmallVector<CXX0XLambdaCapture, 4> Captures;
+    SourceLocation ThisLoc;
+    CXX0XLambdaCaptureDefault Default;
+
+    CXX0XLambdaCaptureList()
+      : Default(DEFAULT_CAPTURE_NONE) {}
+  };

Why is the location of 'this' kept separately? Shouldn't it just be a CXX0XLambdaCapture, so that it's easier to keep track of the order of the captures?

As for the FIXME, just do whatever feels cleaner to you. There's no specific policy here.

-  case tok::caret:
-    return ParsePostfixExpressionSuffix(ParseBlockLiteralExpression());
-  case tok::code_completion:
+  case tok::caret: {
+    Res = ParseBlockLiteralExpression();
+    break;
+  }

This is a (separable) change.

+/// isCXX0XLambdaExpression - Make sure we are looking at a C++ lambda
+/// expression and not a (possibly nested) Objective-C++ message expression.
+bool Parser::isCXX0XLambdaExpression() {
+  assert(getLang().CPlusPlus0x
+         && Tok.is(tok::l_square)
+         && "why would you call me?");
+  // FIXME: Is there a smaller set of conditions we can check?
+  return !getLang().ObjC1
+         || NextToken().is(tok::r_square)
+         || NextToken().is(tok::equal)
+         || NextToken().is(tok::amp)
+         || (NextToken().is(tok::identifier)
+             && (GetLookAheadToken(2).is(tok::comma)
+                 || GetLookAheadToken(2).is(tok::r_square)));

This isn't quite enough, because of the comma operator:

	[x,y method:17]

To catch this evil case, you're going to need tentative parsing… but only after you've done a quick check for the common/easy cases.

+
+/// ParseCXX0XLambdaExpression - Handle a C++0x lambda expression.
+///
+/// FIXME: Split this into functions for introducer, declarator, and whole
+/// expression?


Only if it becomes unwieldy.

+  bool first = true;
+  while (Tok.isNot(tok::r_square)) {
+    if (first) {
+      first = false;
+
+      // Parse capture-default.
+      if (Tok.is(tok::amp) && NextToken().isNot(tok::identifier)) {
+        List.Default = DEFAULT_CAPTURE_BY_REF;
+      } else if (Tok.is(tok::equal)) {
+        List.Default = DEFAULT_CAPTURE_BY_COPY;
+      }
+
+      if (List.Default != DEFAULT_CAPTURE_NONE) {
+        // Consume '&' or '='.
+        ConsumeToken();
+        continue;
+      }
+    } else {
+      if (ExpectAndConsume(tok::comma,
+                           diag::err_expected_comma,
+                           "",
+                           tok::r_square)) {
+        // FIXME: We could also expect end of capture list. Should the
+        // diagnostic indicate this?
+        return ExprError();
+      }
+    }

I find this logic to be a bit twisty. Why not do an initial check for & (followed by non-identifier) or = to set the default capture kind, then go into the loop that handles individual captures?

+    // Parse capture.
+    CXX0XLambdaCaptureKind Kind = CAPTURE_BY_COPY;
+    if (Tok.is(tok::amp)) {
+      Kind = CAPTURE_BY_REF;
+      ConsumeToken();
+    }
+
+    if (Tok.is(tok::kw_this)) {
+      if (Kind == CAPTURE_BY_REF) {
+        // FIXME: Need proper diagnostic.
+        //fprintf(stderr, "error: 'this' is always captured by copy'");
+      } 
+      if (List.ThisLoc.isValid()) {
+        // FIXME: Need proper diagnostic.
+        //fprintf(stderr,
+                //"error: 'this' may appear only once in a capture list");
+      }
+      List.ThisLoc = Tok.getLocation();
+      ConsumeToken();
+    } else if (Tok.is(tok::identifier)) {
+      List.Captures.push_back(CXX0XLambdaCapture(Tok.getLocation(),
+                                                 Kind,
+                                                 Tok.getIdentifierInfo()));
+      ConsumeToken();
+    } else {
+      // FIXME: Need proper diagnostic.
+      //fprintf(stderr, "error: expected capture in capture list\n");
+      SkipUntil(tok::r_square);
+      return ExprError();
+    }
+  }

Definitely needs some nice diagnostics here :)

+  MatchRHSPunctuation(tok::r_square, IntroLoc);
+

It's probably worth saving the result of MatchRHSPunctuation.

+  // Parse lambda-declarator[opt].
+  // FIXME: Should we default the type quals, or store mutability in this?
+  DeclSpec DS(AttrFactory);

Parse as it's written, and let Sema/AST deal with the mutability.

+  if (Tok.is(tok::l_paren)) {
+    // FIXME: Should we have separate or same scopes for the declarator and
+    // body?
+    ParseScope PrototypeScope(this,
+                              Scope::FunctionPrototypeScope |
+                              Scope::DeclScope);
+

Separate scopes, just like we do with functions.

+    // Parse parameter-declaration-clause.
+    // FIXME: Is "Attr" the correct naming convention for ParsedAttributes?
+    ParsedAttributes Attr(AttrFactory);
+    llvm::SmallVector<DeclaratorChunk::ParamInfo, 16> ParamInfo;
+    SourceLocation EllipsisLoc;


Yes, that's fine.

+    // Parse 'mutable'[opt].
+    if (Tok.is(tok::kw_mutable)) {
+      // FIXME: How should we add this to the declarator? Should we instead
+      // add const-ness if we do not see 'mutable'?
+      ConsumeToken();
+    }

Just extend the Declarator class with a location for the lambda 'mutable' and Sema will deal with it.

+  // Parse compound-statement.
+  if (Tok.is(tok::l_brace)) {
+    // FIXME: Do we need a separate scope type for lambdas, or can we reuse
+    // BlockScope?
+    ParseScope BodyScope(this, Scope::BlockScope | Scope::FnScope |
+                               Scope::BreakScope | Scope::ContinueScope |
+                               Scope::DeclScope);
+
+    StmtResult Stmt(ParseCompoundStatementBody());
+
+    BodyScope.Exit();
+  } else {
+    Diag(Tok, diag::err_expected_fn_body);


Most likely, you can just re-use BlockScope, but it will likely need to be renamed to ClosureScope.

	- Doug





More information about the cfe-commits mailing list