]> granicus.if.org Git - clang/commitdiff
Implement P1094R2 (nested inline namespaces)
authorErich Keane <erich.keane@intel.com>
Mon, 12 Nov 2018 17:19:48 +0000 (17:19 +0000)
committerErich Keane <erich.keane@intel.com>
Mon, 12 Nov 2018 17:19:48 +0000 (17:19 +0000)
As approved for the Working Paper in San Diego, support annotating
inline namespaces with 'inline'.

Change-Id: I51a654e11ffb475bf27cccb2458768151619e384

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

include/clang/Basic/DiagnosticParseKinds.td
include/clang/Parse/Parser.h
lib/Parse/ParseDeclCXX.cpp
test/Parser/cxx2a-inline-nested-namespace-definition.cpp [new file with mode: 0644]
www/cxx_status.html

index f47d88a82a43eed636437503a7cba018fc0b0c8e..d1d601ce24a198b20b51e51ff53d24122a70f5a4 100644 (file)
@@ -222,6 +222,11 @@ def ext_nested_namespace_definition : ExtWarn<
 def warn_cxx14_compat_nested_namespace_definition : Warning<
   "nested namespace definition is incompatible with C++ standards before C++17">,
   InGroup<CXXPre17Compat>, DefaultIgnore;
+def ext_inline_nested_namespace_definition : ExtWarn<
+  "inline nested namespace definition is a C++2a extension">, InGroup<CXX2a>;
+def warn_cxx17_compat_inline_nested_namespace_definition : Warning<
+  "inline nested namespace definition is incompatible with C++ standards before"
+  " C++2a">, InGroup<CXXPre2aCompat>, DefaultIgnore;
 def err_inline_nested_namespace_definition : Error<
   "nested namespace definition cannot be 'inline'">;
 def err_expected_semi_after_attribute_list : Error<
index e2a40b24c2014f9adcf0297ff7ce899bd7cdedbc..c643093f6e018e5a7ac198d48904ef834b1ed9ca 100644 (file)
@@ -2659,9 +2659,16 @@ private:
   DeclGroupPtrTy ParseNamespace(DeclaratorContext Context,
                                 SourceLocation &DeclEnd,
                                 SourceLocation InlineLoc = SourceLocation());
-  void ParseInnerNamespace(std::vector<SourceLocation> &IdentLoc,
-                           std::vector<IdentifierInfo *> &Ident,
-                           std::vector<SourceLocation> &NamespaceLoc,
+
+  struct InnerNamespaceInfo {
+    SourceLocation NamespaceLoc;
+    SourceLocation InlineLoc;
+    SourceLocation IdentLoc;
+    IdentifierInfo *Ident;
+  };
+  using InnerNamespaceInfoList = llvm::SmallVector<InnerNamespaceInfo, 4>;
+
+  void ParseInnerNamespace(const InnerNamespaceInfoList &InnerNSs,
                            unsigned int index, SourceLocation &InlineLoc,
                            ParsedAttributes &attrs,
                            BalancedDelimiterTracker &Tracker);
index c814775c77e6aabeada7458f86e511ff2685951f..85c972f4107912da7e117b12b64a5cc795992eb3 100644 (file)
@@ -33,24 +33,23 @@ using namespace clang;
 /// may either be a top level namespace or a block-level namespace alias. If
 /// there was an inline keyword, it has already been parsed.
 ///
-///       namespace-definition: [C++ 7.3: basic.namespace]
+///       namespace-definition: [C++: namespace.def]
 ///         named-namespace-definition
 ///         unnamed-namespace-definition
+///         nested-namespace-definition
+///
+///       named-namespace-definition:
+///         'inline'[opt] 'namespace' attributes[opt] identifier '{' namespace-body '}'
 ///
 ///       unnamed-namespace-definition:
 ///         'inline'[opt] 'namespace' attributes[opt] '{' namespace-body '}'
 ///
-///       named-namespace-definition:
-///         original-namespace-definition
-///         extension-namespace-definition
+///       nested-namespace-definition:
+///         'namespace' enclosing-namespace-specifier '::' 'inline'[opt] identifier '{' namespace-body '}'
 ///
-///       original-namespace-definition:
-///         'inline'[opt] 'namespace' identifier attributes[opt]
-///             '{' namespace-body '}'
-///
-///       extension-namespace-definition:
-///         'inline'[opt] 'namespace' original-namespace-name
-///             '{' namespace-body '}'
+///       enclosing-namespace-specifier:
+///         identifier
+///         enclosing-namespace-specifier '::' 'inline'[opt] identifier
 ///
 ///       namespace-alias-definition:  [C++ 7.3.2: namespace.alias]
 ///         'namespace' identifier '=' qualified-namespace-specifier ';'
@@ -70,9 +69,8 @@ Parser::DeclGroupPtrTy Parser::ParseNamespace(DeclaratorContext Context,
 
   SourceLocation IdentLoc;
   IdentifierInfo *Ident = nullptr;
-  std::vector<SourceLocation> ExtraIdentLoc;
-  std::vector<IdentifierInfo*> ExtraIdent;
-  std::vector<SourceLocation> ExtraNamespaceLoc;
+  InnerNamespaceInfoList ExtraNSs;
+  SourceLocation FirstNestedInlineLoc;
 
   ParsedAttributesWithRange attrs(AttrFactory);
   SourceLocation attrLoc;
@@ -88,15 +86,29 @@ Parser::DeclGroupPtrTy Parser::ParseNamespace(DeclaratorContext Context,
   if (Tok.is(tok::identifier)) {
     Ident = Tok.getIdentifierInfo();
     IdentLoc = ConsumeToken();  // eat the identifier.
-    while (Tok.is(tok::coloncolon) && NextToken().is(tok::identifier)) {
-      ExtraNamespaceLoc.push_back(ConsumeToken());
-      ExtraIdent.push_back(Tok.getIdentifierInfo());
-      ExtraIdentLoc.push_back(ConsumeToken());
+    while (Tok.is(tok::coloncolon) &&
+           (NextToken().is(tok::identifier) ||
+            (NextToken().is(tok::kw_inline) &&
+             GetLookAheadToken(2).is(tok::identifier)))) {
+
+      InnerNamespaceInfo Info;
+      Info.NamespaceLoc = ConsumeToken();
+
+      if (Tok.is(tok::kw_inline)) {
+        Info.InlineLoc = ConsumeToken();
+        if (FirstNestedInlineLoc.isInvalid())
+          FirstNestedInlineLoc = Info.InlineLoc;
+      }
+
+      Info.Ident = Tok.getIdentifierInfo();
+      Info.IdentLoc = ConsumeToken();
+
+      ExtraNSs.push_back(Info);
     }
   }
 
   // A nested namespace definition cannot have attributes.
-  if (!ExtraNamespaceLoc.empty() && attrLoc.isValid())
+  if (!ExtraNSs.empty() && attrLoc.isValid())
     Diag(attrLoc, diag::err_unexpected_nested_namespace_attribute);
 
   // Read label attributes, if present.
@@ -138,13 +150,21 @@ Parser::DeclGroupPtrTy Parser::ParseNamespace(DeclaratorContext Context,
     return nullptr;
   }
 
-  if (ExtraIdent.empty()) {
+  if (ExtraNSs.empty()) {
     // Normal namespace definition, not a nested-namespace-definition.
   } else if (InlineLoc.isValid()) {
     Diag(InlineLoc, diag::err_inline_nested_namespace_definition);
+  } else if (getLangOpts().CPlusPlus2a) {
+    Diag(ExtraNSs[0].NamespaceLoc,
+         diag::warn_cxx14_compat_nested_namespace_definition);
+    if (FirstNestedInlineLoc.isValid())
+      Diag(FirstNestedInlineLoc,
+           diag::warn_cxx17_compat_inline_nested_namespace_definition);
   } else if (getLangOpts().CPlusPlus17) {
-    Diag(ExtraNamespaceLoc[0],
+    Diag(ExtraNSs[0].NamespaceLoc,
          diag::warn_cxx14_compat_nested_namespace_definition);
+    if (FirstNestedInlineLoc.isValid())
+      Diag(FirstNestedInlineLoc, diag::ext_inline_nested_namespace_definition);
   } else {
     TentativeParsingAction TPA(*this);
     SkipUntil(tok::r_brace, StopBeforeMatch);
@@ -152,26 +172,31 @@ Parser::DeclGroupPtrTy Parser::ParseNamespace(DeclaratorContext Context,
     TPA.Revert();
 
     if (!rBraceToken.is(tok::r_brace)) {
-      Diag(ExtraNamespaceLoc[0], diag::ext_nested_namespace_definition)
-          << SourceRange(ExtraNamespaceLoc.front(), ExtraIdentLoc.back());
+      Diag(ExtraNSs[0].NamespaceLoc, diag::ext_nested_namespace_definition)
+          << SourceRange(ExtraNSs.front().NamespaceLoc,
+                         ExtraNSs.back().IdentLoc);
     } else {
       std::string NamespaceFix;
-      for (std::vector<IdentifierInfo*>::iterator I = ExtraIdent.begin(),
-           E = ExtraIdent.end(); I != E; ++I) {
+      for (const auto &ExtraNS : ExtraNSs) {
         NamespaceFix += " { namespace ";
-        NamespaceFix += (*I)->getName();
+        NamespaceFix += ExtraNS.Ident->getName();
       }
 
       std::string RBraces;
-      for (unsigned i = 0, e = ExtraIdent.size(); i != e; ++i)
+      for (unsigned i = 0, e = ExtraNSs.size(); i != e; ++i)
         RBraces +=  "} ";
 
-      Diag(ExtraNamespaceLoc[0], diag::ext_nested_namespace_definition)
-          << FixItHint::CreateReplacement(SourceRange(ExtraNamespaceLoc.front(),
-                                                      ExtraIdentLoc.back()),
-                                          NamespaceFix)
+      Diag(ExtraNSs[0].NamespaceLoc, diag::ext_nested_namespace_definition)
+          << FixItHint::CreateReplacement(
+                 SourceRange(ExtraNSs.front().NamespaceLoc,
+                             ExtraNSs.back().IdentLoc),
+                 NamespaceFix)
           << FixItHint::CreateInsertion(rBraceToken.getLocation(), RBraces);
     }
+
+    // Warn about nested inline namespaces.
+    if (FirstNestedInlineLoc.isValid())
+      Diag(FirstNestedInlineLoc, diag::ext_inline_nested_namespace_definition);
   }
 
   // If we're still good, complain about inline namespaces in non-C++0x now.
@@ -192,8 +217,7 @@ Parser::DeclGroupPtrTy Parser::ParseNamespace(DeclaratorContext Context,
 
   // Parse the contents of the namespace.  This includes parsing recovery on
   // any improperly nested namespaces.
-  ParseInnerNamespace(ExtraIdentLoc, ExtraIdent, ExtraNamespaceLoc, 0,
-                      InlineLoc, attrs, T);
+  ParseInnerNamespace(ExtraNSs, 0, InlineLoc, attrs, T);
 
   // Leave the namespace scope.
   NamespaceScope.Exit();
@@ -206,13 +230,11 @@ Parser::DeclGroupPtrTy Parser::ParseNamespace(DeclaratorContext Context,
 }
 
 /// ParseInnerNamespace - Parse the contents of a namespace.
-void Parser::ParseInnerNamespace(std::vector<SourceLocation> &IdentLoc,
-                                 std::vector<IdentifierInfo *> &Ident,
-                                 std::vector<SourceLocation> &NamespaceLoc,
+void Parser::ParseInnerNamespace(const InnerNamespaceInfoList &InnerNSs,
                                  unsigned int index, SourceLocation &InlineLoc,
                                  ParsedAttributes &attrs,
                                  BalancedDelimiterTracker &Tracker) {
-  if (index == Ident.size()) {
+  if (index == InnerNSs.size()) {
     while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) &&
            Tok.isNot(tok::eof)) {
       ParsedAttributesWithRange attrs(AttrFactory);
@@ -233,13 +255,13 @@ void Parser::ParseInnerNamespace(std::vector<SourceLocation> &IdentLoc,
   ParseScope NamespaceScope(this, Scope::DeclScope);
   UsingDirectiveDecl *ImplicitUsingDirectiveDecl = nullptr;
   Decl *NamespcDecl = Actions.ActOnStartNamespaceDef(
-      getCurScope(), SourceLocation(), NamespaceLoc[index], IdentLoc[index],
-      Ident[index], Tracker.getOpenLocation(), attrs,
-      ImplicitUsingDirectiveDecl);
+      getCurScope(), InnerNSs[index].InlineLoc, InnerNSs[index].NamespaceLoc,
+      InnerNSs[index].IdentLoc, InnerNSs[index].Ident,
+      Tracker.getOpenLocation(), attrs, ImplicitUsingDirectiveDecl);
   assert(!ImplicitUsingDirectiveDecl &&
          "nested namespace definition cannot define anonymous namespace");
 
-  ParseInnerNamespace(IdentLoc, Ident, NamespaceLoc, ++index, InlineLoc,
+  ParseInnerNamespace(InnerNSs, ++index, InlineLoc,
                       attrs, Tracker);
 
   NamespaceScope.Exit();
diff --git a/test/Parser/cxx2a-inline-nested-namespace-definition.cpp b/test/Parser/cxx2a-inline-nested-namespace-definition.cpp
new file mode 100644 (file)
index 0000000..8e603d2
--- /dev/null
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++14
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++17
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++2a -Wc++17-compat
+
+namespace inline foo1::foo2::foo3 { // expected-error {{expected identifier or '{'}} expected-error {{use of undeclared identifier 'foo1'}}
+}
+
+inline namespace foo4::foo5::foo6 { // expected-error {{nested namespace definition cannot be 'inline'}}}
+}
+
+#if __cplusplus <= 201402L
+// expected-warning@+7 {{nested namespace definition is a C++17 extension; define each namespace separately}}
+// expected-warning@+6 {{inline nested namespace definition is a C++2a extension}}
+#elif __cplusplus <= 201703L
+// expected-warning@+4 {{inline nested namespace definition is a C++2a extension}}
+#else
+// expected-warning@+2 {{inline nested namespace definition is incompatible with C++ standards before C++2a}}
+#endif
+namespace valid1::valid2::inline valid3::inline valid4::valid5{}
+// expected-note@-1 2 {{previous definition is here}}
+
+#if __cplusplus <= 201402L
+// expected-warning@+3 {{nested namespace definition is a C++17 extension; define each namespace separately}}
+#endif
+//expected-warning@+1 2 {{inline namespace reopened as a non-inline namespace}}
+namespace valid1::valid2::valid3::valid4::valid5{}
+
+#if __cplusplus <= 201402L
+// expected-warning@+7 {{nested namespace definition is a C++17 extension; define each namespace separately}}
+// expected-warning@+6 {{inline nested namespace definition is a C++2a extension}}
+#elif __cplusplus <= 201703L
+// expected-warning@+4 {{inline nested namespace definition is a C++2a extension}}
+#else
+// expected-warning@+2 {{inline nested namespace definition is incompatible with C++ standards before C++2a}}
+#endif
+namespace valid1::valid2::inline valid3::inline valid4::valid5{}
+// expected-note@-1 2 {{previous definition is here}}
+
+namespace valid1 {
+  namespace valid2 {
+//expected-warning@+1 {{inline namespace reopened as a non-inline namespace}}
+    namespace valid3 {
+//expected-warning@+1 {{inline namespace reopened as a non-inline namespace}}
+      namespace valid4 {
+        namespace valid5 {
+        }
+      }
+    }
+  }
+}
+
index f7db88f649cc3db531ac715753e5516338f69f4c..ee0c6e8542be34d6d9b44b8c6488c54df6949837 100755 (executable)
@@ -1011,7 +1011,7 @@ as the draft C++2a standard evolves.
     <tr>
       <td>Nested inline namespaces</td>
       <td><a href="http://wg21.link/p1094r2">P1094R2</a></td>
-      <td class="none" align="center">No</td>
+      <td class="svn" align="center">SVN</td>
     </tr>
 </table>
 </details>