[prev in list] [next in list] [prev in thread] [next in thread] 

List:       cfe-commits
Subject:    [PATCH] Fix parsing comma in default arguments.
From:       Olivier Goffart <ogoffart () kde ! org>
Date:       2013-05-19 9:45:57
Message-ID: 4362263.gOp8vEDqoB () gargamel
[Download RAW message or body]

Hi,

I attached a patch to the bug http://llvm.org/bugs/show_bug.cgi?id=14486
The problem is that a comma can be both the separation between template 
parameter, or the separation between function arguments.

GCC tries to fully parse the part after the comma to see if it can be the 
separation between function argument:
http://code.woboq.org/gcc/gcc/cp/parser.c.html#23442

But I was too lazy to do the same (because the function like 
TryParseParameterDeclarationClause or TryParseDeclarationSpecifier are not good 
enough and stop too early.)
So Instead I used some heuristics in the existence of a '=' before the 
possible closing '>' as further argument need to find a comma.

I am not 100% sure that this cover all the cases, but i could not find an 
example that breaks.

-- 
Olivier
["0001-Fix-parsing-comma-in-default-argument.patch" (0001-Fix-parsing-comma-in-default-argument.patch)]

From a1bfaeb798c376a2b322a35c955c2f126e697717 Mon Sep 17 00:00:00 2001
From: Olivier Goffart <ogoffart@woboq.com>
Date: Sun, 19 May 2013 11:24:54 +0200
Subject: [PATCH] Fix parsing comma in default argument

http://llvm.org/bugs/show_bug.cgi?id=13657
---
 include/clang/Parse/Parser.h              |  1 +
 lib/Parse/ParseCXXInlineMethods.cpp       | 44 +++++++++++++++++--
 lib/Parse/ParseTentative.cpp              | 70 +++++++++++++++++++++++++++++++
 test/Parser/cxx-default-args.cpp          | 17 ++++++++
 test/Parser/cxx0x-member-initializers.cpp | 10 +++++
 5 files changed, 138 insertions(+), 4 deletions(-)

diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index 1029a90..f2afa06 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -1821,6 +1821,7 @@ private:
   TPResult TryParseParameterDeclarationClause(bool *InvalidAsDeclaration = 0);
   TPResult TryParseFunctionDeclarator();
   TPResult TryParseBracketDeclarator();
+  TPResult TryParseParameterDeclarationWithDefaultArgument();
 
 public:
   TypeResult ParseTypeName(SourceRange *Range = 0,
diff --git a/lib/Parse/ParseCXXInlineMethods.cpp b/lib/Parse/ParseCXXInlineMethods.cpp
index 5fc4189..2868bfc 100644
--- a/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/lib/Parse/ParseCXXInlineMethods.cpp
@@ -563,14 +563,32 @@ bool Parser::ConsumeAndStoreUntil(tok::TokenKind T1, tok::TokenKind T2,
   // We always want this function to consume at least one token if the first
   // token isn't T and if not at EOF.
   bool isFirstTokenConsumed = true;
+  int TemplateLevel = 0;
   while (1) {
     // If we found one of the tokens, stop and return true.
     if (Tok.is(T1) || Tok.is(T2)) {
-      if (ConsumeFinalToken) {
-        Toks.push_back(Tok);
-        ConsumeAnyToken();
+      bool Done = true;
+      if (Tok.is(tok::comma) && TemplateLevel > 0) {
+        // Special case for comma: is it the separation of template parameters,
+        // or is it the separation of function parameters?
+        TentativeParsingAction TPA(*this);
+        ConsumeToken();
+        if (T2 == tok::r_paren) {
+          // We are currently parsing the default argument of a function
+          Done = TryParseParameterDeclarationWithDefaultArgument() == TPResult::True();
+        } else {
+          // We are currently parsing a NSDMI
+          Done = TryParseDeclarator(false) == TPResult::Ambiguous();
+        }
+        TPA.Revert();
+      }
+      if (Done) {
+        if (ConsumeFinalToken) {
+          Toks.push_back(Tok);
+          ConsumeAnyToken();
+        }
+        return true;
       }
-      return true;
     }
 
     switch (Tok.getKind()) {
@@ -597,6 +615,24 @@ bool Parser::ConsumeAndStoreUntil(tok::TokenKind T1, tok::TokenKind T2,
       ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false);
       break;
 
+    case tok::less:
+      TemplateLevel++;
+      Toks.push_back(Tok);
+      ConsumeToken();
+      break;
+    case tok::greater:
+      if (TemplateLevel)
+        TemplateLevel--;
+      Toks.push_back(Tok);
+      ConsumeToken();
+      break;
+    case tok::greatergreater:
+      if (TemplateLevel)
+        TemplateLevel-=2;
+      Toks.push_back(Tok);
+      ConsumeToken();
+      break;
+
     // Okay, we found a ']' or '}' or ')', which we think should be balanced.
     // Since the user wasn't looking for this token (if they were, it would
     // already be handled), this isn't balanced.  If there is a LHS token at a
diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp
index dff3b64..59842db 100644
--- a/lib/Parse/ParseTentative.cpp
+++ b/lib/Parse/ParseTentative.cpp
@@ -1530,6 +1530,76 @@ Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration) {
   return TPResult::Ambiguous();
 }
 
+/// try to disambiguate between a comma between template arguments and a comma
+/// between function parameters.
+/// The comma was already parsed.
+///
+/// BracketDepth is the depth in angle bracket we have.
+///
+Parser::TPResult Parser::TryParseParameterDeclarationWithDefaultArgument() {
+  // An attribute-specifier-seq here is a sign of a function declarator.
+  if (isCXX11AttributeSpecifier(/*Disambiguate*/false,
+                                /*OuterMightBeMessageSend*/true))
+    return TPResult::True();
+
+  ParsedAttributes attrs(AttrFactory);
+  MaybeParseMicrosoftAttributes(attrs);
+
+  TPResult TPR = TryParseDeclarationSpecifier();
+  if (TPR == TPResult::Error() || TPR == TPResult::False())
+    return TPR;
+
+  // Now try to find the '=' sign of the next parameter, or the '>' of the end
+  // of the template parameter list.
+  tok::TokenKind TokArray[] = {tok::r_paren, tok::equal, tok::less,
+                               tok::comma, tok::greater, tok::greatergreater };
+
+  int AngleBracketDepth = 0;
+
+  while (SkipUntil(TokArray, /*StopAtSemi*/ true, /*DontConsume*/ true)) {
+
+    switch(Tok.getKind()) {
+    case tok::equal:
+      // The start of the default argument: it was an argument list.
+      return TPResult::True();
+
+    case tok::r_paren:
+      // r_parent without another default argument, it cannot be an argument list
+      return TPResult::False();
+
+    case tok::less:
+      AngleBracketDepth++;
+      break;
+
+    case tok::greater:
+      AngleBracketDepth--;
+      break;
+
+    case tok::greatergreater:
+      if(getLangOpts().CPlusPlus11)
+        AngleBracketDepth -= 2;
+      break;
+
+    case tok::comma:
+      // another comma can only appears in template parameter
+      if (!AngleBracketDepth)
+        return TPResult::False();
+
+    default:
+        assert(!"unexpected token");
+    }
+
+    // There cannot be '<' if not a template argument.
+    if (AngleBracketDepth < 0)
+     return TPResult::False();
+
+    ConsumeToken();
+  }
+
+
+  return TPResult::Error();
+}
+
 /// TryParseFunctionDeclarator - We parsed a '(' and we want to try to continue
 /// parsing as a function declarator.
 /// If TryParseFunctionDeclarator fully parsed the function declarator, it will
diff --git a/test/Parser/cxx-default-args.cpp b/test/Parser/cxx-default-args.cpp
index 7fe8474..19b50cb 100644
--- a/test/Parser/cxx-default-args.cpp
+++ b/test/Parser/cxx-default-args.cpp
@@ -14,3 +14,20 @@ typedef struct Inst {
 struct X {
   void f(int x = 1:); // expected-error {{unexpected end of default argument expression}}
 };
+
+// PR13657
+struct T {
+  template <typename A, typename B> struct T1 { enum {V};};
+  template <int A, int B> struct T2 { enum {V}; };
+  template <int, int> static int func(int);
+
+
+  void f1(T1<int, int> = T1<int, int>());
+  void f2(T1<int, double> = T1<int, double>(), T2<0, 5> = T2<0, 5>());
+  void f3(int a = T2<0, (T1<int, int>::V > 10) ? 5 : 6>::V, bool b = 4<5 );
+  void f4(bool a = 1 < 0, bool b = 2 > 0 );
+  void f5(bool a = 1 > T2<0, 0>::V, bool b = T1<int,int>::V < 3, int c = 0);
+  void f6(bool a = T2<0,3>::V < 4, bool b = 4 > T2<0,3>::V);
+  void f7(bool a = T1<int, bool>::V < 3);
+  void f8(int = func<0,1<2>(0), int = 1<0, T1<int,int>(int) = 0);
+};
\ No newline at end of file
diff --git a/test/Parser/cxx0x-member-initializers.cpp b/test/Parser/cxx0x-member-initializers.cpp
index a324f97..43e99b1 100644
--- a/test/Parser/cxx0x-member-initializers.cpp
+++ b/test/Parser/cxx0x-member-initializers.cpp
@@ -27,3 +27,13 @@ struct V1 {
   int a, b;
   V1() : a(), b{} {}
 };
+
+template <typename, typename> struct T1 { enum {V};};
+template <int, int> struct T2 { enum {V};};
+struct A {
+  T1<int, int> a1 = T1<int, int>(), *a2 = new T1<int,int>;
+  T2<0,0> b1 = T2<0,0>(), b2 = T2<0,0>(), b3;
+  bool c1 = 1 < 2, c2 = 2 < 1, c3 = false;
+  bool d1 = T1<int, T1<int, int>>::V < 3, d2;
+  T1<int, int()> e = T1<int, int()>();
+};
-- 
1.7.12.1



_______________________________________________
cfe-commits mailing list
cfe-commits@cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic