r182432 - PR16094: I should have known Obj-C init-capture disambiguation couldn't be

Richard Smith richard-llvm at metafoo.co.uk
Tue May 21 15:21:19 PDT 2013


Author: rsmith
Date: Tue May 21 17:21:19 2013
New Revision: 182432

URL: http://llvm.org/viewvc/llvm-project?rev=182432&view=rev
Log:
PR16094: I should have known Obj-C init-capture disambiguation couldn't be
*that* easy...

Try a bit harder to disambiguate. This is mostly straightforward, but for
=-style initializers, we actually need to know where an expression ends:

  [foo = bar baz]

is a message send, whereas

  [foo = bar + baz]

is a lambda-introducer. Handle this by parsing the expression eagerly, and
replacing it with an annotation token. By chance, we use the *exact same*
parsing rules in both cases (except that we need to assume we're inside a
message send for the parse, to turn off various forms of inapplicable
error recovery).

Modified:
    cfe/trunk/include/clang/Lex/Preprocessor.h
    cfe/trunk/include/clang/Parse/Parser.h
    cfe/trunk/lib/Parse/ParseExprCXX.cpp
    cfe/trunk/test/Parser/objcxx0x-lambda-expressions.mm

Modified: cfe/trunk/include/clang/Lex/Preprocessor.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Lex/Preprocessor.h?rev=182432&r1=182431&r2=182432&view=diff
==============================================================================
--- cfe/trunk/include/clang/Lex/Preprocessor.h (original)
+++ cfe/trunk/include/clang/Lex/Preprocessor.h Tue May 21 17:21:19 2013
@@ -828,6 +828,13 @@ public:
       AnnotatePreviousCachedTokens(Tok);
   }
 
+  /// Get the location of the last cached token, suitable for setting the end
+  /// location of an annotation token.
+  SourceLocation getLastCachedTokenLocation() const {
+    assert(CachedLexPos != 0);
+    return CachedTokens[CachedLexPos-1].getLocation();
+  }
+
   /// \brief Replace the last token with an annotation token.
   ///
   /// Like AnnotateCachedTokens(), this routine replaces an

Modified: cfe/trunk/include/clang/Parse/Parser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=182432&r1=182431&r2=182432&view=diff
==============================================================================
--- cfe/trunk/include/clang/Parse/Parser.h (original)
+++ cfe/trunk/include/clang/Parse/Parser.h Tue May 21 17:21:19 2013
@@ -1329,7 +1329,8 @@ private:
   // [...] () -> type {...}
   ExprResult ParseLambdaExpression();
   ExprResult TryParseLambdaExpression();
-  Optional<unsigned> ParseLambdaIntroducer(LambdaIntroducer &Intro);
+  Optional<unsigned> ParseLambdaIntroducer(LambdaIntroducer &Intro,
+                                           bool *SkippedInits = 0);
   bool TryParseLambdaIntroducer(LambdaIntroducer &Intro);
   ExprResult ParseLambdaExpressionAfterIntroducer(
                LambdaIntroducer &Intro);

Modified: cfe/trunk/lib/Parse/ParseExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseExprCXX.cpp?rev=182432&r1=182431&r2=182432&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseExprCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseExprCXX.cpp Tue May 21 17:21:19 2013
@@ -679,10 +679,17 @@ ExprResult Parser::TryParseLambdaExpress
   return ParseLambdaExpressionAfterIntroducer(Intro);
 }
 
-/// ParseLambdaExpression - Parse a lambda introducer.
-///
-/// Returns a DiagnosticID if it hit something unexpected.
-Optional<unsigned> Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro) {
+/// \brief Parse a lambda introducer.
+/// \param Intro A LambdaIntroducer filled in with information about the
+///        contents of the lambda-introducer.
+/// \param SkippedInits If non-null, we are disambiguating between an Obj-C
+///        message send and a lambda expression. In this mode, we will
+///        sometimes skip the initializers for init-captures and not fully
+///        populate \p Intro. This flag will be set to \c true if we do so.
+/// \return A DiagnosticID if it hit something unexpected. The location for
+///         for the diagnostic is that of the current token.
+Optional<unsigned> Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro,
+                                                 bool *SkippedInits) {
   typedef Optional<unsigned> DiagResult;
 
   assert(Tok.is(tok::l_square) && "Lambda expressions begin with '['.");
@@ -781,7 +788,10 @@ Optional<unsigned> Parser::ParseLambdaIn
 
         ExprVector Exprs;
         CommaLocsTy Commas;
-        if (ParseExpressionList(Exprs, Commas)) {
+        if (SkippedInits) {
+          Parens.skipToEnd();
+          *SkippedInits = true;
+        } else if (ParseExpressionList(Exprs, Commas)) {
           Parens.skipToEnd();
           Init = ExprError();
         } else {
@@ -794,7 +804,53 @@ Optional<unsigned> Parser::ParseLambdaIn
         if (Tok.is(tok::equal))
           ConsumeToken();
 
-        Init = ParseInitializer();
+        if (!SkippedInits)
+          Init = ParseInitializer();
+        else if (Tok.is(tok::l_brace)) {
+          BalancedDelimiterTracker Braces(*this, tok::l_brace);
+          Braces.consumeOpen();
+          Braces.skipToEnd();
+          *SkippedInits = true;
+        } else {
+          // We're disambiguating this:
+          //
+          //   [..., x = expr
+          //
+          // We need to find the end of the following expression in order to
+          // determine whether this is an Obj-C message send's receiver, or a
+          // lambda init-capture.
+          //
+          // Parse the expression to find where it ends, and annotate it back
+          // onto the tokens. We would have parsed this expression the same way
+          // in either case: both the RHS of an init-capture and the RHS of an
+          // assignment expression are parsed as an initializer-clause, and in
+          // neither case can anything be added to the scope between the '[' and
+          // here.
+          //
+          // FIXME: This is horrible. Adding a mechanism to skip an expression
+          // would be much cleaner.
+          // FIXME: If there is a ',' before the next ']' or ':', we can skip to
+          // that instead. (And if we see a ':' with no matching '?', we can
+          // classify this as an Obj-C message send.)
+          SourceLocation StartLoc = Tok.getLocation();
+          InMessageExpressionRAIIObject MaybeInMessageExpression(*this, true);
+          Init = ParseInitializer();
+
+          if (Tok.getLocation() != StartLoc) {
+            // Back out the lexing of the token after the initializer.
+            PP.RevertCachedTokens(1);
+
+            // Replace the consumed tokens with an appropriate annotation.
+            Tok.setLocation(StartLoc);
+            Tok.setKind(tok::annot_primary_expr);
+            setExprAnnotation(Tok, Init);
+            Tok.setAnnotationEndLoc(PP.getLastCachedTokenLocation());
+            PP.AnnotateCachedTokens(Tok);
+
+            // Consume the annotated initializer.
+            ConsumeToken();
+          }
+        }
       } else if (Tok.is(tok::ellipsis))
         EllipsisLoc = ConsumeToken();
     }
@@ -814,13 +870,23 @@ Optional<unsigned> Parser::ParseLambdaIn
 bool Parser::TryParseLambdaIntroducer(LambdaIntroducer &Intro) {
   TentativeParsingAction PA(*this);
 
-  Optional<unsigned> DiagID(ParseLambdaIntroducer(Intro));
+  bool SkippedInits = false;
+  Optional<unsigned> DiagID(ParseLambdaIntroducer(Intro, &SkippedInits));
 
   if (DiagID) {
     PA.Revert();
     return true;
   }
 
+  if (SkippedInits) {
+    // Parse it again, but this time parse the init-captures too.
+    PA.Revert();
+    Intro = LambdaIntroducer();
+    DiagID = ParseLambdaIntroducer(Intro);
+    assert(!DiagID && "parsing lambda-introducer failed on reparse");
+    return false;
+  }
+
   PA.Commit();
   return false;
 }

Modified: cfe/trunk/test/Parser/objcxx0x-lambda-expressions.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/objcxx0x-lambda-expressions.mm?rev=182432&r1=182431&r2=182432&view=diff
==============================================================================
--- cfe/trunk/test/Parser/objcxx0x-lambda-expressions.mm (original)
+++ cfe/trunk/test/Parser/objcxx0x-lambda-expressions.mm Tue May 21 17:21:19 2013
@@ -1,9 +1,10 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -Wno-unused-value -std=c++11 %s
 
 class C {
+  id get(int);
 
   void f() {
-    int foo, bar;
+    int foo, bar, baz;
 
     // fail to parse as a lambda introducer, so we get objc message parsing errors instead
     [foo,+] {}; // expected-error {{expected expression}}
@@ -24,9 +25,18 @@ class C {
     [foo = {bar}] () {}; // expected-error {{<initializer_list>}}
 
     [foo(bar) baz] () {}; // expected-error {{called object type 'int' is not a function}}
+    [foo(bar), baz] () {}; // ok
 
-    // FIXME: These are some appalling diagnostics.
-    [foo = bar baz]; // expected-error {{missing '['}} expected-warning 2{{receiver type 'int'}} expected-warning 2{{instance method '-baz'}}
+    [foo = bar baz]; // expected-warning {{receiver type 'int'}} expected-warning {{instance method '-baz'}}
+
+    [get(bar) baz]; // expected-warning {{instance method '-baz'}}
+    [get(bar), baz]; // expected-error {{expected body of lambda}}
+
+    [foo = bar ++ baz]; // expected-warning {{receiver type 'int'}} expected-warning {{instance method '-baz'}}
+    [foo = bar + baz]; // expected-error {{expected body of lambda}}
+    [foo = { bar, baz }]; // expected-error {{<initializer_list>}} expected-error {{expected body of lambda}}
+    [foo = { bar } baz ]; // expected-warning {{receiver type 'int'}} expected-warning {{instance method '-baz'}}
+    [foo = { bar }, baz ]; // expected-error {{<initializer_list>}} expected-error {{expected body of lambda}}
   }
 
 };





More information about the cfe-commits mailing list