]> granicus.if.org Git - clang/commitdiff
Refactor the parsing of declarations so that template declarations can
authorDouglas Gregor <dgregor@apple.com>
Tue, 12 May 2009 21:31:51 +0000 (21:31 +0000)
committerDouglas Gregor <dgregor@apple.com>
Tue, 12 May 2009 21:31:51 +0000 (21:31 +0000)
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.

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

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 [new file with mode: 0644]

index 9481ce531710ac8735541a8e4c46e92ab3953c24..adfbb3540b48d69ed0acdbe0fc1cd00f812af612 100644 (file)
@@ -240,6 +240,9 @@ def err_two_right_angle_brackets_need_space : Error<
 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'">;
index 084996d61c199b4621abf6fddd5abfdf4f0b1d23..49554422bbeb5170dc792a695a59e5f1fb4686ba 100644 (file)
@@ -550,6 +550,8 @@ private:
   //===--------------------------------------------------------------------===//
   // C99 6.9: External Definitions.
   DeclGroupPtrTy ParseExternalDeclaration();
+  bool isDeclarationAfterDeclarator();
+  bool isStartOfFunctionDefinition();
   DeclGroupPtrTy ParseDeclarationOrFunctionDefinition(
             TemplateParameterLists *TemplateParams = 0,
             AccessSpecifier AS = AS_none);
@@ -816,6 +818,7 @@ private:
   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 @@ private:
   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 @@ private:
                                  TemplateArgIsTypeList &TemplateArgIsType,
                                  TemplateArgLocationList &TemplateArgLocations);
   void *ParseTemplateArgument(bool &ArgIsType);
+  DeclPtrTy ParseExplicitInstantiation(SourceLocation &DeclEnd);
 
   //===--------------------------------------------------------------------===//
   // GNU G++: Type Traits [Type-Traits.html in the GCC manual]
index b3c2d8c555613236e6a498c7bb9d1ede675ead04..9d7d9d400f7a03bd519cede503a492e76bd76c7d 100644 (file)
@@ -233,8 +233,14 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(unsigned Context,
                                                 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 @@ Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration(unsigned Context,
   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 @@ Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration(unsigned Context,
 /// 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 @@ ParseInitDeclaratorListAfterFirstDeclarator(Declarator &D) {
   // 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.
index d38df93ace1b736725af0bd17f58275c8e63a112..22d11779ab32113cbe52e47efc77b88f828384c0 100644 (file)
@@ -95,15 +95,102 @@ Parser::ParseTemplateDeclarationOrSpecialization(unsigned Context,
   } 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);
+
+  if (Tok.is(tok::semi)) {
+    DeclEnd = ConsumeToken();
+    return Actions.ParsedFreeStandingDeclSpec(CurScope, DS);
+  }
 
-  // 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());
+  // 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 @@ Parser::ParseTemplateArgumentList(TemplateArgList &TemplateArgs,
   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);
+}
index 94036dbd86e64ed1b340cee30ee4f0d3f93a287e..e886bd43ca999ece8156be3ace1d84c82e2572df 100644 (file)
@@ -437,6 +437,29 @@ Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration() {
   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 @@ Parser::ParseDeclarationOrFunctionDefinition(
     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 @@ Parser::ParseDeclarationOrFunctionDefinition(
     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);
 
index ec333de8b4f17a9b9360b20657ff31f6095c62f1..6a795eeabe49d69986fd98ceb8ca158935d359bc 100644 (file)
@@ -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}}
diff --git a/test/SemaTemplate/temp.cpp b/test/SemaTemplate/temp.cpp
new file mode 100644 (file)
index 0000000..bd37cc6
--- /dev/null
@@ -0,0 +1,2 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+template<typename T> int foo(T), bar(T, T); // expected-error{{single entity}}