[cfe-commits] r71596 - in /cfe/trunk: include/clang/Basic/DiagnosticParseKinds.td include/clang/Parse/Parser.h lib/Parse/ParseDecl.cpp lib/Parse/ParseTemplate.cpp lib/Parse/Parser.cpp test/Parser/cxx-template-decl.cpp test/SemaTemplate/temp.cpp

Douglas Gregor dgregor at apple.com
Tue May 12 14:31:52 PDT 2009


Author: dgregor
Date: Tue May 12 16:31:51 2009
New Revision: 71596

URL: http://llvm.org/viewvc/llvm-project?rev=71596&view=rev
Log:
Refactor the parsing of declarations so that template declarations can
parse just a single declaration and provide a reasonable diagnostic
when the "only one declarator per template declaration" rule is
violated. This eliminates some ugly, ugly hackery where we used to
require thatn the layout of a DeclGroup of a single element be the
same as the layout of a single declaration.


Added:
    cfe/trunk/test/SemaTemplate/temp.cpp
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
    cfe/trunk/include/clang/Parse/Parser.h
    cfe/trunk/lib/Parse/ParseDecl.cpp
    cfe/trunk/lib/Parse/ParseTemplate.cpp
    cfe/trunk/lib/Parse/Parser.cpp
    cfe/trunk/test/Parser/cxx-template-decl.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td?rev=71596&r1=71595&r2=71596&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td Tue May 12 16:31:51 2009
@@ -240,6 +240,9 @@
 def warn_cxx0x_right_shift_in_template_arg : Warning<
   "use of right-shift operator ('>>') in template argument will require "
   "parentheses in C++0x">;
+def err_multiple_template_declarators : Error<
+    "%select{a template declaration|an explicit template instantiation}0 can "
+    "only %select{declare|instante}0 a single entity">;
 
 def err_expected_qualified_after_typename : Error<
   "expected a qualified name after 'typename'">;

Modified: cfe/trunk/include/clang/Parse/Parser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=71596&r1=71595&r2=71596&view=diff

==============================================================================
--- cfe/trunk/include/clang/Parse/Parser.h (original)
+++ cfe/trunk/include/clang/Parse/Parser.h Tue May 12 16:31:51 2009
@@ -550,6 +550,8 @@
   //===--------------------------------------------------------------------===//
   // C99 6.9: External Definitions.
   DeclGroupPtrTy ParseExternalDeclaration();
+  bool isDeclarationAfterDeclarator();
+  bool isStartOfFunctionDefinition();
   DeclGroupPtrTy ParseDeclarationOrFunctionDefinition(
             TemplateParameterLists *TemplateParams = 0,
             AccessSpecifier AS = AS_none);
@@ -816,6 +818,7 @@
   DeclGroupPtrTy ParseSimpleDeclaration(unsigned Context,
                                         SourceLocation &DeclEnd,
                                         bool RequireSemi = true);
+  DeclPtrTy ParseDeclarationAfterDeclarator(Declarator &D);
   DeclGroupPtrTy ParseInitDeclaratorListAfterFirstDeclarator(Declarator &D);
   DeclPtrTy ParseFunctionStatementBody(DeclPtrTy Decl);
   DeclPtrTy ParseFunctionTryBlock(DeclPtrTy Decl);
@@ -1052,6 +1055,12 @@
   DeclPtrTy ParseTemplateDeclarationOrSpecialization(unsigned Context,
                                                      SourceLocation &DeclEnd,
                                                    AccessSpecifier AS=AS_none);
+  DeclPtrTy ParseSingleDeclarationAfterTemplate(
+                                       unsigned Context,
+                                       TemplateParameterLists *TemplateParams,
+                                       SourceLocation TemplateLoc,
+                                       SourceLocation &DeclEnd,
+                                       AccessSpecifier AS=AS_none);
   bool ParseTemplateParameters(unsigned Depth, 
                                TemplateParameterList &TemplateParams,
                                SourceLocation &LAngleLoc, 
@@ -1086,6 +1095,7 @@
                                  TemplateArgIsTypeList &TemplateArgIsType,
                                  TemplateArgLocationList &TemplateArgLocations);
   void *ParseTemplateArgument(bool &ArgIsType);
+  DeclPtrTy ParseExplicitInstantiation(SourceLocation &DeclEnd);
 
   //===--------------------------------------------------------------------===//
   // GNU G++: Type Traits [Type-Traits.html in the GCC manual]

Modified: cfe/trunk/lib/Parse/ParseDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDecl.cpp?rev=71596&r1=71595&r2=71596&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseDecl.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDecl.cpp Tue May 12 16:31:51 2009
@@ -233,8 +233,14 @@
                                                 SourceLocation &DeclEnd) {
   DeclPtrTy SingleDecl;
   switch (Tok.getKind()) {
-  case tok::kw_export:
   case tok::kw_template:
+    if (NextToken().isNot(tok::less)) {
+      SingleDecl = ParseExplicitInstantiation(DeclEnd);
+      break;
+    }
+    // Fall through for template declarations and specializations
+
+  case tok::kw_export:
     SingleDecl = ParseTemplateDeclarationOrSpecialization(Context, DeclEnd);
     break;
   case tok::kw_namespace:
@@ -302,15 +308,11 @@
   return DG;
 }
 
-
-/// ParseInitDeclaratorListAfterFirstDeclarator - Parse 'declaration' after
-/// parsing 'declaration-specifiers declarator'.  This method is split out this
-/// way to handle the ambiguity between top-level function-definitions and
-/// declarations.
+/// \brief Parse 'declaration' after parsing 'declaration-specifiers
+/// declarator'. This method parses the remainder of the declaration
+/// (including any attributes or initializer, among other things) and
+/// finalizes the declaration.
 ///
-///       init-declarator-list: [C99 6.7]
-///         init-declarator
-///         init-declarator-list ',' init-declarator
 ///       init-declarator: [C99 6.7]
 ///         declarator
 ///         declarator '=' initializer
@@ -327,6 +329,81 @@
 /// According to the standard grammar, =default and =delete are function
 /// definitions, but that definitely doesn't fit with the parser here.
 ///
+Parser::DeclPtrTy Parser::ParseDeclarationAfterDeclarator(Declarator &D) {
+  // If a simple-asm-expr is present, parse it.
+  if (Tok.is(tok::kw_asm)) {
+    SourceLocation Loc;
+    OwningExprResult AsmLabel(ParseSimpleAsm(&Loc));
+    if (AsmLabel.isInvalid()) {
+      SkipUntil(tok::semi, true, true);
+      return DeclPtrTy();
+    }
+    
+    D.setAsmLabel(AsmLabel.release());
+    D.SetRangeEnd(Loc);
+  }
+  
+  // If attributes are present, parse them.
+  if (Tok.is(tok::kw___attribute)) {
+    SourceLocation Loc;
+    AttributeList *AttrList = ParseAttributes(&Loc);
+    D.AddAttributes(AttrList, Loc);
+  }
+  
+  // Inform the current actions module that we just parsed this declarator.
+  DeclPtrTy ThisDecl = Actions.ActOnDeclarator(CurScope, D);
+  
+  // Parse declarator '=' initializer.
+  if (Tok.is(tok::equal)) {
+    ConsumeToken();
+    if (getLang().CPlusPlus0x && Tok.is(tok::kw_delete)) {
+      SourceLocation DelLoc = ConsumeToken();
+      Actions.SetDeclDeleted(ThisDecl, DelLoc);
+    } else {
+      OwningExprResult Init(ParseInitializer());
+      if (Init.isInvalid()) {
+        SkipUntil(tok::semi, true, true);
+        return DeclPtrTy();
+      }
+      Actions.AddInitializerToDecl(ThisDecl, move(Init));
+    }
+  } else if (Tok.is(tok::l_paren)) {
+    // Parse C++ direct initializer: '(' expression-list ')'
+    SourceLocation LParenLoc = ConsumeParen();
+    ExprVector Exprs(Actions);
+    CommaLocsTy CommaLocs;
+
+    if (ParseExpressionList(Exprs, CommaLocs)) {
+      SkipUntil(tok::r_paren);
+    } else {
+      // Match the ')'.
+      SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc);
+
+      assert(!Exprs.empty() && Exprs.size()-1 == CommaLocs.size() &&
+             "Unexpected number of commas!");
+      Actions.AddCXXDirectInitializerToDecl(ThisDecl, LParenLoc,
+                                            move_arg(Exprs),
+                                            &CommaLocs[0], RParenLoc);
+    }
+  } else {
+    Actions.ActOnUninitializedDecl(ThisDecl);
+  }
+
+  return ThisDecl;
+}
+
+/// ParseInitDeclaratorListAfterFirstDeclarator - Parse 'declaration' after
+/// parsing 'declaration-specifiers declarator'.  This method is split out this
+/// way to handle the ambiguity between top-level function-definitions and
+/// declarations.
+///
+///       init-declarator-list: [C99 6.7]
+///         init-declarator
+///         init-declarator-list ',' init-declarator
+///
+/// According to the standard grammar, =default and =delete are function
+/// definitions, but that definitely doesn't fit with the parser here.
+///
 Parser::DeclGroupPtrTy Parser::
 ParseInitDeclaratorListAfterFirstDeclarator(Declarator &D) {
   // Declarators may be grouped together ("int X, *Y, Z();"). Remember the decls
@@ -336,65 +413,9 @@
   // At this point, we know that it is not a function definition.  Parse the
   // rest of the init-declarator-list.
   while (1) {
-    // If a simple-asm-expr is present, parse it.
-    if (Tok.is(tok::kw_asm)) {
-      SourceLocation Loc;
-      OwningExprResult AsmLabel(ParseSimpleAsm(&Loc));
-      if (AsmLabel.isInvalid()) {
-        SkipUntil(tok::semi, true, true);
-        return DeclGroupPtrTy();
-      }
-
-      D.setAsmLabel(AsmLabel.release());
-      D.SetRangeEnd(Loc);
-    }
-    
-    // If attributes are present, parse them.
-    if (Tok.is(tok::kw___attribute)) {
-      SourceLocation Loc;
-      AttributeList *AttrList = ParseAttributes(&Loc);
-      D.AddAttributes(AttrList, Loc);
-    }
-
-    // Inform the current actions module that we just parsed this declarator.
-    DeclPtrTy ThisDecl = Actions.ActOnDeclarator(CurScope, D);
-    DeclsInGroup.push_back(ThisDecl);
-
-    // Parse declarator '=' initializer.
-    if (Tok.is(tok::equal)) {
-      ConsumeToken();
-      if (getLang().CPlusPlus0x && Tok.is(tok::kw_delete)) {
-        SourceLocation DelLoc = ConsumeToken();
-        Actions.SetDeclDeleted(ThisDecl, DelLoc);
-      } else {
-        OwningExprResult Init(ParseInitializer());
-        if (Init.isInvalid()) {
-          SkipUntil(tok::semi, true, true);
-          return DeclGroupPtrTy();
-        }
-        Actions.AddInitializerToDecl(ThisDecl, move(Init));
-      }
-    } else if (Tok.is(tok::l_paren)) {
-      // Parse C++ direct initializer: '(' expression-list ')'
-      SourceLocation LParenLoc = ConsumeParen();
-      ExprVector Exprs(Actions);
-      CommaLocsTy CommaLocs;
-
-      if (ParseExpressionList(Exprs, CommaLocs)) {
-        SkipUntil(tok::r_paren);
-      } else {
-        // Match the ')'.
-        SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc);
-
-        assert(!Exprs.empty() && Exprs.size()-1 == CommaLocs.size() &&
-               "Unexpected number of commas!");
-        Actions.AddCXXDirectInitializerToDecl(ThisDecl, LParenLoc,
-                                              move_arg(Exprs),
-                                              &CommaLocs[0], RParenLoc);
-      }
-    } else {
-      Actions.ActOnUninitializedDecl(ThisDecl);
-    }
+    DeclPtrTy ThisDecl = ParseDeclarationAfterDeclarator(D);
+    if (ThisDecl.get())
+      DeclsInGroup.push_back(ThisDecl);
     
     // If we don't have a comma, it is either the end of the list (a ';') or an
     // error, bail out.

Modified: cfe/trunk/lib/Parse/ParseTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseTemplate.cpp?rev=71596&r1=71595&r2=71596&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseTemplate.cpp (original)
+++ cfe/trunk/lib/Parse/ParseTemplate.cpp Tue May 12 16:31:51 2009
@@ -95,15 +95,102 @@
   } while (Tok.is(tok::kw_export) || Tok.is(tok::kw_template));
 
   // Parse the actual template declaration.
+  return ParseSingleDeclarationAfterTemplate(Context, &ParamLists,
+                                             SourceLocation(),
+                                             DeclEnd, AS);
+}
+
+/// \brief Parse a single declaration that declares a template,
+/// template specialization, or explicit instantiation of a template.
+///
+/// \param TemplateParams if non-NULL, the template parameter lists
+/// that preceded this declaration. In this case, the declaration is a
+/// template declaration, out-of-line definition of a template, or an
+/// explicit template specialization. When NULL, the declaration is an
+/// explicit template instantiation.
+///
+/// \param TemplateLoc when TemplateParams is NULL, the location of
+/// the 'template' keyword that indicates that we have an explicit
+/// template instantiation.
+///
+/// \param DeclEnd will receive the source location of the last token
+/// within this declaration.
+///
+/// \param AS the access specifier associated with this
+/// declaration. Will be AS_none for namespace-scope declarations.
+///
+/// \returns the new declaration.
+Parser::DeclPtrTy 
+Parser::ParseSingleDeclarationAfterTemplate(
+                                       unsigned Context,
+                                       TemplateParameterLists *TemplateParams,
+                                       SourceLocation TemplateLoc,
+                                       SourceLocation &DeclEnd,
+                                       AccessSpecifier AS) {
+  // Parse the declaration specifiers.
+  DeclSpec DS;
+  // FIXME: Pass TemplateLoc through for explicit template instantiations
+  ParseDeclarationSpecifiers(DS, TemplateParams, AS);
 
-  // FIXME: This accepts template<typename x> int y;
-  // FIXME: Converting DeclGroupPtr to DeclPtr like this is an insanely gruesome
-  // hack, will bring up on cfe-dev.
-  DeclGroupPtrTy DG = ParseDeclarationOrFunctionDefinition(&ParamLists, AS);
-  // FIXME: Should be ';' location not the token after it.  Resolve with above
-  // fixmes.
-  DeclEnd = Tok.getLocation();
-  return DeclPtrTy::make(DG.get());
+  if (Tok.is(tok::semi)) {
+    DeclEnd = ConsumeToken();
+    return Actions.ParsedFreeStandingDeclSpec(CurScope, DS);
+  }
+
+  // Parse the declarator.
+  Declarator DeclaratorInfo(DS, (Declarator::TheContext)Context);
+  ParseDeclarator(DeclaratorInfo);
+  // Error parsing the declarator?
+  if (!DeclaratorInfo.hasName()) {
+    // If so, skip until the semi-colon or a }.
+    SkipUntil(tok::r_brace, true, true);
+    if (Tok.is(tok::semi))
+      ConsumeToken();
+    return DeclPtrTy();
+  }
+  
+  // If we have a declaration or declarator list, handle it.
+  if (isDeclarationAfterDeclarator()) {
+    // Parse this declaration.
+    DeclPtrTy ThisDecl = ParseDeclarationAfterDeclarator(DeclaratorInfo);
+
+    if (Tok.is(tok::comma)) {
+      Diag(Tok, diag::err_multiple_template_declarators)
+        << (TemplateParams == 0);
+      SkipUntil(tok::semi, true, false);
+      return ThisDecl;
+    }
+
+    // Eat the semi colon after the declaration.
+    ExpectAndConsume(tok::semi, diag::err_expected_semi_declation);
+    return ThisDecl;
+  }
+
+  if (DeclaratorInfo.isFunctionDeclarator() &&
+      isStartOfFunctionDefinition()) {
+    if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef) {
+      Diag(Tok, diag::err_function_declared_typedef);
+
+      if (Tok.is(tok::l_brace)) {
+        // This recovery skips the entire function body. It would be nice
+        // to simply call ParseFunctionDefinition() below, however Sema
+        // assumes the declarator represents a function, not a typedef.
+        ConsumeBrace();
+        SkipUntil(tok::r_brace, true);
+      } else {
+        SkipUntil(tok::semi);
+      }
+      return DeclPtrTy();
+    }
+    return ParseFunctionDefinition(DeclaratorInfo);
+  }
+
+  if (DeclaratorInfo.isFunctionDeclarator())
+    Diag(Tok, diag::err_expected_fn_body);
+  else
+    Diag(Tok, diag::err_invalid_token_after_toplevel_declarator);
+  SkipUntil(tok::semi);
+  return DeclPtrTy();
 }
 
 /// ParseTemplateParameters - Parses a template-parameter-list enclosed in
@@ -693,3 +780,16 @@
   return Tok.isNot(tok::greater) && Tok.isNot(tok::greatergreater);
 }
 
+/// \brief Parse a C++ explicit template instantiation 
+/// (C++ [temp.explicit]).
+///
+///       explicit-instantiation:
+///         'template' declaration
+Parser::DeclPtrTy Parser::ParseExplicitInstantiation(SourceLocation &DeclEnd) {
+  assert(Tok.is(tok::kw_template) && NextToken().isNot(tok::less) &&
+	 "Token does not start an explicit instantiation.");
+  
+  SourceLocation TemplateLoc = ConsumeToken();
+  return ParseSingleDeclarationAfterTemplate(Declarator::FileContext, 0, 
+                                             TemplateLoc, DeclEnd, AS_none);
+}

Modified: cfe/trunk/lib/Parse/Parser.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/Parser.cpp?rev=71596&r1=71595&r2=71596&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/Parser.cpp (original)
+++ cfe/trunk/lib/Parse/Parser.cpp Tue May 12 16:31:51 2009
@@ -437,6 +437,29 @@
   return Actions.ConvertDeclToDeclGroup(SingleDecl);
 }
 
+/// \brief Determine whether the current token, if it occurs after a
+/// declarator, continues a declaration or declaration list.
+bool Parser::isDeclarationAfterDeclarator() {
+  return Tok.is(tok::equal) ||      // int X()=  -> not a function def
+    Tok.is(tok::comma) ||           // int X(),  -> not a function def
+    Tok.is(tok::semi)  ||           // int X();  -> not a function def
+    Tok.is(tok::kw_asm) ||          // int X() __asm__ -> not a function def
+    Tok.is(tok::kw___attribute) ||  // int X() __attr__ -> not a function def
+    (getLang().CPlusPlus &&
+     Tok.is(tok::l_paren));         // int X(0) -> not a function def [C++]
+}
+
+/// \brief Determine whether the current token, if it occurs after a
+/// declarator, indicates the start of a function definition.
+bool Parser::isStartOfFunctionDefinition() {
+  return Tok.is(tok::l_brace) ||    // int X() {}
+    (!getLang().CPlusPlus && 
+     isDeclarationSpecifier()) ||   // int X(f) int f; {}
+    (getLang().CPlusPlus &&
+     (Tok.is(tok::colon) ||         // X() : Base() {} (used for ctors)
+      Tok.is(tok::kw_try)));        // X() try { ... }
+}
+
 /// ParseDeclarationOrFunctionDefinition - Parse either a function-definition or
 /// a declaration.  We can't tell which we have until we read up to the
 /// compound-statement in function-definition. TemplateParams, if
@@ -514,14 +537,8 @@
     return DeclGroupPtrTy();
   }
 
-  // If the declarator is the start of a function definition, handle it.
-  if (Tok.is(tok::equal) ||           // int X()=  -> not a function def
-      Tok.is(tok::comma) ||           // int X(),  -> not a function def
-      Tok.is(tok::semi)  ||           // int X();  -> not a function def
-      Tok.is(tok::kw_asm) ||          // int X() __asm__ -> not a function def
-      Tok.is(tok::kw___attribute) ||  // int X() __attr__ -> not a function def
-      (getLang().CPlusPlus &&
-       Tok.is(tok::l_paren))) {       // int X(0) -> not a function def [C++]
+  // If we have a declaration or declarator list, handle it.
+  if (isDeclarationAfterDeclarator()) {
     // Parse the init-declarator-list for a normal declaration.
     DeclGroupPtrTy DG =
       ParseInitDeclaratorListAfterFirstDeclarator(DeclaratorInfo);
@@ -530,14 +547,8 @@
     return DG;
   }
   
-  
   if (DeclaratorInfo.isFunctionDeclarator() &&
-             (Tok.is(tok::l_brace) ||             // int X() {}
-              (!getLang().CPlusPlus &&
-               isDeclarationSpecifier()) ||   // int X(f) int f; {}
-              (getLang().CPlusPlus &&
-               (Tok.is(tok::colon) ||     // X() : Base() {} (used for ctors)
-                Tok.is(tok::kw_try))))) { // X() try { ... }
+      isStartOfFunctionDefinition()) {
     if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef) {
       Diag(Tok, diag::err_function_declared_typedef);
 

Modified: cfe/trunk/test/Parser/cxx-template-decl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/cxx-template-decl.cpp?rev=71596&r1=71595&r2=71596&view=diff

==============================================================================
--- cfe/trunk/test/Parser/cxx-template-decl.cpp (original)
+++ cfe/trunk/test/Parser/cxx-template-decl.cpp Tue May 12 16:31:51 2009
@@ -2,8 +2,7 @@
 
 // Errors
 export class foo { };   // expected-error {{expected template}}
-template  x;            // expected-error {{expected '<' after 'template'}} \
-// expected-error {{C++ requires a type specifier for all declarations}}
+template  x;            // expected-error {{C++ requires a type specifier for all declarations}}
 export template x;      // expected-error {{expected '<' after 'template'}} \
                         // expected-note {{exported templates are unsupported}} \
 // expected-error {{C++ requires a type specifier for all declarations}}

Added: cfe/trunk/test/SemaTemplate/temp.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/temp.cpp?rev=71596&view=auto

==============================================================================
--- cfe/trunk/test/SemaTemplate/temp.cpp (added)
+++ cfe/trunk/test/SemaTemplate/temp.cpp Tue May 12 16:31:51 2009
@@ -0,0 +1,2 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+template<typename T> int foo(T), bar(T, T); // expected-error{{single entity}}





More information about the cfe-commits mailing list