]> granicus.if.org Git - clang/commitdiff
Recover better from a missing 'typename' in a function template definition.
authorRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 16 May 2012 23:40:17 +0000 (23:40 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 16 May 2012 23:40:17 +0000 (23:40 +0000)
Disambiguate past such a potential problem, and use the absence of 'typename'
to break ties in favor of a parenthesized thingy being an initializer, if
nothing else in the declaration disambiguates it as declaring a function.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@156963 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Parse/Parser.h
lib/Parse/ParseTentative.cpp
test/CXX/except/except.spec/p5-pointers.cpp
test/SemaCXX/unknown-type-name.cpp

index bd5592cd3383b5e3249ef3ac088989d4e142e0ef..814b45afc7473566c639bcb3880059331e57a504 100644 (file)
@@ -1753,7 +1753,8 @@ private:
   /// BracedCastResult.
   /// Doesn't consume tokens.
   TPResult
-  isCXXDeclarationSpecifier(TPResult BracedCastResult = TPResult::False());
+  isCXXDeclarationSpecifier(TPResult BracedCastResult = TPResult::False(),
+                            bool *HasMissingTypename = 0);
 
   // "Tentative parsing" functions, used for disambiguation. If a parsing error
   // is encountered they will return TPResult::Error().
@@ -1762,13 +1763,13 @@ private:
   // that more tentative parsing is necessary for disambiguation.
   // They all consume tokens, so backtracking should be used after calling them.
 
-  TPResult TryParseDeclarationSpecifier();
+  TPResult TryParseDeclarationSpecifier(bool *HasMissingTypename = 0);
   TPResult TryParseSimpleDeclaration(bool AllowForRangeDecl);
   TPResult TryParseTypeofSpecifier();
   TPResult TryParseProtocolQualifiers();
   TPResult TryParseInitDeclaratorList();
   TPResult TryParseDeclarator(bool mayBeAbstract, bool mayHaveIdentifier=true);
-  TPResult TryParseParameterDeclarationClause();
+  TPResult TryParseParameterDeclarationClause(bool *InvalidAsDeclaration = 0);
   TPResult TryParseFunctionDeclarator();
   TPResult TryParseBracketDeclarator();
 
index e9685d25fe0894413b3c32003e89752b0e896898..c2e539ca61fadb60c06826f8b8ca89df65fd2d42 100644 (file)
@@ -827,6 +827,10 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) {
 /// be either a decl-specifier or a function-style cast, and TPResult::Error()
 /// if a parsing error was found and reported.
 ///
+/// If HasMissingTypename is provided, a name with a dependent scope specifier
+/// will be treated as ambiguous if the 'typename' keyword is missing. If this
+/// happens, *HasMissingTypename will be set to 'true'.
+///
 ///         decl-specifier:
 ///           storage-class-specifier
 ///           type-specifier
@@ -918,7 +922,8 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) {
 /// [GNU]     restrict
 ///
 Parser::TPResult
-Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult) {
+Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
+                                  bool *HasMissingTypename) {
   switch (Tok.getKind()) {
   case tok::identifier:   // foo::bar
     // Check for need to substitute AltiVec __vector keyword
@@ -936,7 +941,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult) {
       return (!getLangOpts().ObjC1 && Next.is(tok::identifier)) ?
           TPResult::True() : TPResult::False();
     }
-    return isCXXDeclarationSpecifier(BracedCastResult);
+    return isCXXDeclarationSpecifier(BracedCastResult, HasMissingTypename);
 
   case tok::coloncolon: {    // ::foo::bar
     const Token &Next = NextToken();
@@ -950,7 +955,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult) {
     // recurse to handle whatever we get.
     if (TryAnnotateTypeOrScopeToken())
       return TPResult::Error();
-    return isCXXDeclarationSpecifier(BracedCastResult);
+    return isCXXDeclarationSpecifier(BracedCastResult, HasMissingTypename);
 
     // decl-specifier:
     //   storage-class-specifier
@@ -1052,12 +1057,20 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult) {
           bool isIdentifier = Tok.is(tok::identifier);
           TPResult TPR = TPResult::False();
           if (!isIdentifier)
-            TPR = isCXXDeclarationSpecifier(BracedCastResult);
+            TPR = isCXXDeclarationSpecifier(BracedCastResult,
+                                            HasMissingTypename);
           PA.Revert();
 
           if (isIdentifier ||
               TPR == TPResult::True() || TPR == TPResult::Error())
             return TPResult::Error();
+
+          if (HasMissingTypename) {
+            // We can't tell whether this is a missing 'typename' or a valid
+            // expression.
+            *HasMissingTypename = true;
+            return TPResult::Ambiguous();
+          }
         }
       }
       return TPResult::False();
@@ -1221,21 +1234,24 @@ Parser::TPResult Parser::TryParseProtocolQualifiers() {
   return TPResult::Error();
 }
 
-Parser::TPResult Parser::TryParseDeclarationSpecifier() {
-  TPResult TPR = isCXXDeclarationSpecifier();
+Parser::TPResult
+Parser::TryParseDeclarationSpecifier(bool *HasMissingTypename) {
+  TPResult TPR = isCXXDeclarationSpecifier(TPResult::False(),
+                                           HasMissingTypename);
   if (TPR != TPResult::Ambiguous())
     return TPR;
 
   if (Tok.is(tok::kw_typeof))
     TryParseTypeofSpecifier();
   else {
+    if (Tok.is(tok::annot_cxxscope))
+      ConsumeToken();
     ConsumeToken();
     
     if (getLangOpts().ObjC1 && Tok.is(tok::less))
       TryParseProtocolQualifiers();
   }
 
-  assert(Tok.is(tok::l_paren) && "Expected '('!");
   return TPResult::Ambiguous();
 }
 
@@ -1263,9 +1279,28 @@ bool Parser::isCXXFunctionDeclarator(bool warnIfAmbiguous) {
   TentativeParsingAction PA(*this);
 
   ConsumeParen();
-  TPResult TPR = TryParseParameterDeclarationClause();
-  if (TPR == TPResult::Ambiguous() && Tok.isNot(tok::r_paren))
-    TPR = TPResult::False();
+  bool InvalidAsDeclaration = false;
+  TPResult TPR = TryParseParameterDeclarationClause(&InvalidAsDeclaration);
+  if (TPR == TPResult::Ambiguous()) {
+    if (Tok.isNot(tok::r_paren))
+      TPR = TPResult::False();
+    else {
+      const Token &Next = NextToken();
+      if (Next.is(tok::amp) || Next.is(tok::ampamp) ||
+          Next.is(tok::kw_const) || Next.is(tok::kw_volatile) ||
+          Next.is(tok::kw_throw) || Next.is(tok::kw_noexcept) ||
+          Next.is(tok::l_square) || isCXX0XVirtSpecifier(Next) ||
+          Next.is(tok::l_brace) || Next.is(tok::kw_try) ||
+          Next.is(tok::equal))
+        // The next token cannot appear after a constructor-style initializer,
+        // and can appear next in a function definition. This must be a function
+        // declarator.
+        TPR = TPResult::True();
+      else if (InvalidAsDeclaration)
+        // Use the absence of 'typename' as a tie-breaker.
+        TPR = TPResult::False();
+    }
+  }
 
   SourceLocation TPLoc = Tok.getLocation();
   PA.Revert();
@@ -1303,7 +1338,8 @@ bool Parser::isCXXFunctionDeclarator(bool warnIfAmbiguous) {
 ///   attribute-specifier-seq[opt] decl-specifier-seq abstract-declarator[opt]
 ///     attributes[opt] '=' assignment-expression
 ///
-Parser::TPResult Parser::TryParseParameterDeclarationClause() {
+Parser::TPResult
+Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration) {
 
   if (Tok.is(tok::r_paren))
     return TPResult::True();
@@ -1336,7 +1372,7 @@ Parser::TPResult Parser::TryParseParameterDeclarationClause() {
     // decl-specifier-seq
     // A parameter-declaration's initializer must be preceded by an '=', so
     // decl-specifier-seq '{' is not a parameter in C++11.
-    TPResult TPR = TryParseDeclarationSpecifier();
+    TPResult TPR = TryParseDeclarationSpecifier(InvalidAsDeclaration);
     if (TPR != TPResult::Ambiguous())
       return TPR;
 
index dd3c0600ce92ac4b3847127223f6b77e0df7ea1c..f855520aa24d0827832d5b88a5daf4c6a5487291 100644 (file)
@@ -65,9 +65,9 @@ void fnptrs()
   void (*(*t7)())() throw(B1) = &s8;       // valid
   void (*(*t8)())() throw(A) = &s8;        // expected-error {{return types differ}}
   void (*(*t9)())() throw(D) = &s8;        // expected-error {{return types differ}}
-  void (*t10)(void (*)() throw(B1)) = &s9; // valid   expected-warning{{disambiguated}}
-  void (*t11)(void (*)() throw(A)) = &s9;  // expected-error {{argument types differ}} expected-warning{{disambiguated}}
-  void (*t12)(void (*)() throw(D)) = &s9;  // expected-error {{argument types differ}} expected-warning{{disambiguated}}
+  void (*t10)(void (*)() throw(B1)) = &s9; // valid
+  void (*t11)(void (*)() throw(A)) = &s9;  // expected-error {{argument types differ}}
+  void (*t12)(void (*)() throw(D)) = &s9;  // expected-error {{argument types differ}}
 }
 
 // Member function stuff
index 7912fbec3f891ef612d9610d790f4e44a16bd5ab..893e0cc5dc860eb0fed857c7c52cd053dc502dcf 100644 (file)
@@ -22,6 +22,11 @@ struct A {
   type f();
 
   type g();
+
+  static int n;
+  static type m;
+  static int h(T::type, int); // expected-error{{missing 'typename'}}
+  static int h(T::type x, char); // expected-error{{missing 'typename'}}
 };
 
 template<typename T>
@@ -30,12 +35,50 @@ A<T>::type g(T t) { return t; } // expected-error{{missing 'typename'}}
 template<typename T>
 A<T>::type A<T>::f() { return type(); } // expected-error{{missing 'typename'}}
 
+template<typename T>
+void f(T::type) { } // expected-error{{missing 'typename'}}
+
+template<typename T>
+void g(T::type x) { } // expected-error{{missing 'typename'}}
+
+template<typename T>
+void f(T::type, int) { } // expected-error{{missing 'typename'}}
+
+template<typename T>
+void f(T::type x, char) { } // expected-error{{missing 'typename'}}
+
 template<typename T>
 void f(int, T::type) { } // expected-error{{missing 'typename'}}
 
+template<typename T>
+void f(char, T::type x) { } // expected-error{{missing 'typename'}}
+
 template<typename T>
 void f(int, T::type, int) { } // expected-error{{missing 'typename'}}
 
+template<typename T>
+void f(int, T::type x, char) { } // expected-error{{missing 'typename'}}
+
+template<typename T> int A<T>::n(T::value); // ok
+template<typename T>
+A<T>::type // expected-error{{missing 'typename'}}
+A<T>::m(T::value, 0); // ok
+
+template<typename T> int A<T>::h(T::type, int) {} // expected-error{{missing 'typename'}}
+template<typename T> int A<T>::h(T::type x, char) {} // expected-error{{missing 'typename'}}
+
+template<typename T> int h(T::type, int); // expected-error{{missing 'typename'}}
+template<typename T> int h(T::type x, char); // expected-error{{missing 'typename'}}
+
+template<typename T> int junk1(T::junk); // expected-error{{declared as a template}}
+template<typename T> int junk2(T::junk) throw(); // expected-error{{missing 'typename'}}
+template<typename T> int junk3(T::junk) = delete; // expected-error{{missing 'typename'}} expected-warning{{C++11}}
+template<typename T> int junk4(T::junk j); // expected-error{{missing 'typename'}}
+
+// FIXME: We can tell this was intended to be a function because it does not
+//        have a dependent nested name specifier.
+template<typename T> int i(T::type, int()); // expected-error{{variable 'i' declared as a template}}
+
 // FIXME: We know which type specifier should have been specified here. Provide
 //        a fix-it to add 'typename A<T>::type'
 template<typename T>