diff mbox

[C++] push_namespace cleanup

Message ID 5a5c09f5-1e45-8b63-bed9-7e9531519abd@acm.org
State New
Headers show

Commit Message

Nathan Sidwell May 15, 2017, 7:38 p.m. UTC
This cleanup patch from the modules branch fixes pr 79369, where we 
would accept inlining of an already existing namespace.

I changed push_namespace to return a count of the depth pushed, because 
I also have a fix for DR 2061, where we can end up pushing more than one 
namespace.  That'll be applied later.

nathan
diff mbox

Patch

2017-05-15  Nathan Sidwell  <nathan@acm.org>

	gcc/cp/
	PR c++/79369
	* cp-tree.h (DECL_NAMESPACE_INLINE_P): New.
	* name-lookup.h (push_namespace): Return int, add make_inline arg.
	* name-lookup.c (push_namespace): Deal with inline directly.
	Return pushed count.
	* parser.c (cp_parser_namespace_definition): Adjust for
	push_namespace change.

	gcc/testsuite/
	* g++.dg/cpp0x/pr65558.C: Adjust diagnostic location.
	* g++.dg/cpp0x/pr79369.C: New.

Index: cp/cp-tree.h
===================================================================
--- cp/cp-tree.h	(revision 248066)
+++ cp/cp-tree.h	(working copy)
@@ -333,6 +333,7 @@  extern GTY(()) tree cp_global_trees[CPTI
       FOLD_EXPR_MODOP_P (*_FOLD_EXPR)
       IF_STMT_CONSTEXPR_P (IF_STMT)
       TEMPLATE_TYPE_PARM_FOR_CLASS (TEMPLATE_TYPE_PARM)
+      DECL_NAMESPACE_INLINE_P (in NAMESPACE_DECL)
    1: IDENTIFIER_VIRTUAL_P (in IDENTIFIER_NODE)
       TI_PENDING_TEMPLATE_FLAG.
       TEMPLATE_PARMS_FOR_INLINE.
@@ -2916,6 +2917,10 @@  struct GTY(()) lang_decl {
 #define LOCAL_CLASS_P(NODE)				\
   (decl_function_context (TYPE_MAIN_DECL (NODE)) != NULL_TREE)
 
+/* Whether the namepace is an inline namespace.  */
+#define DECL_NAMESPACE_INLINE_P(NODE) \
+  TREE_LANG_FLAG_0 (NAMESPACE_DECL_CHECK (NODE))
+
 /* For a NAMESPACE_DECL: the list of using namespace directives
    The PURPOSE is the used namespace, the value is the namespace
    that is the common ancestor.  */
Index: cp/name-lookup.c
===================================================================
--- cp/name-lookup.c	(revision 248066)
+++ cp/name-lookup.c	(working copy)
@@ -6441,107 +6441,112 @@  pop_from_top_level (void)
   timevar_cond_stop (TV_NAME_LOOKUP, subtime);
 }
 
-/* Push into the scope of the NAME namespace.  If NAME is NULL_TREE, then we
-   select a name that is unique to this compilation unit.  Returns FALSE if
-   pushdecl fails, TRUE otherwise.  */
+/* Push into the scope of the NAME namespace.  If NAME is NULL_TREE,
+   then we enter an anonymous namespace.  If MAKE_INLINE is true, then
+   we create an inline namespace (it is up to the caller to check upon
+   redefinition). Return the number of namespaces entered.  */
 
-bool
-push_namespace (tree name)
+int
+push_namespace (tree name, bool make_inline)
 {
-  tree d = NULL_TREE;
-  bool need_new = true;
-  bool implicit_use = false;
-  bool anon = !name;
-
   bool subtime = timevar_cond_start (TV_NAME_LOOKUP);
+  int count = 0;
 
   /* We should not get here if the global_namespace is not yet constructed
      nor if NAME designates the global namespace:  The global scope is
      constructed elsewhere.  */
   gcc_assert (global_namespace != NULL && name != global_identifier);
 
-  if (anon)
-    {
-      name = anon_identifier;
-      d = get_namespace_binding (current_namespace, name);
-      if (d)
-	/* Reopening anonymous namespace.  */
-	need_new = false;
-      implicit_use = true;
-    }
-  else
+  if (!name)
+    name = anon_identifier;
+
+  /* Check whether this is an extended namespace definition.  */
+  tree ns = get_namespace_binding (current_namespace, name);
+  if (ns && TREE_CODE (ns) == NAMESPACE_DECL)
     {
-      /* Check whether this is an extended namespace definition.  */
-      d = get_namespace_binding (current_namespace, name);
-      if (d != NULL_TREE && TREE_CODE (d) == NAMESPACE_DECL)
+      if (tree dna = DECL_NAMESPACE_ALIAS (ns))
 	{
-	  tree dna = DECL_NAMESPACE_ALIAS (d);
-	  if (dna)
- 	    {
-	      /* We do some error recovery for, eg, the redeclaration
-		 of M here:
-
-		 namespace N {}
-		 namespace M = N;
-		 namespace M {}
-
-		 However, in nasty cases like:
-
-		 namespace N
-		 {
-		   namespace M = N;
-		   namespace M {}
-		 }
-
-		 we just error out below, in duplicate_decls.  */
-	      if (NAMESPACE_LEVEL (dna)->level_chain
-		  == current_binding_level)
-		{
-		  error ("namespace alias %qD not allowed here, "
-			 "assuming %qD", d, dna);
-		  d = dna;
-		  need_new = false;
-		}
+	  /* We do some error recovery for, eg, the redeclaration of M
+	     here:
+
+	     namespace N {}
+	     namespace M = N;
+	     namespace M {}
+
+	     However, in nasty cases like:
+
+	     namespace N
+	     {
+	       namespace M = N;
+	       namespace M {}
+	     }
+
+	     we just error out below, in duplicate_decls.  */
+	  if (NAMESPACE_LEVEL (dna)->level_chain == current_binding_level)
+	    {
+	      error ("namespace alias %qD not allowed here, "
+		     "assuming %qD", ns, dna);
+	      ns = dna;
 	    }
 	  else
-	    need_new = false;
+	    ns = NULL_TREE;
 	}
     }
+  else
+    ns = NULL_TREE;
 
-  if (need_new)
+  bool new_ns = false;
+  if (!ns)
     {
-      /* Make a new namespace, binding the name to it.  */
-      d = build_lang_decl (NAMESPACE_DECL, name, void_type_node);
-      DECL_CONTEXT (d) = FROB_CONTEXT (current_namespace);
-      /* The name of this namespace is not visible to other translation
-	 units if it is an anonymous namespace or member thereof.  */
-      if (anon || decl_anon_ns_mem_p (current_namespace))
-	TREE_PUBLIC (d) = 0;
+      ns = build_lang_decl (NAMESPACE_DECL, name, void_type_node);
+      DECL_CONTEXT (ns) = FROB_CONTEXT (current_namespace);
+      new_ns = true;
+
+      if (pushdecl (ns) == error_mark_node)
+	ns = NULL_TREE;
       else
-	TREE_PUBLIC (d) = 1;
-      if (pushdecl (d) == error_mark_node)
 	{
-	  timevar_cond_stop (TV_NAME_LOOKUP, subtime);
-	  return false;
+	  if (name == anon_identifier)
+	    {
+	      /* Clear DECL_NAME for the benefit of debugging back ends.  */
+	      SET_DECL_ASSEMBLER_NAME (ns, name);
+	      DECL_NAME (ns) = NULL_TREE;
+
+	      if (!make_inline)
+		do_using_directive (ns);
+	    }
+	  else if (TREE_PUBLIC (current_namespace))
+	    TREE_PUBLIC (ns) = 1;
+
+	  if (make_inline)
+	    {
+	      DECL_NAMESPACE_INLINE_P (ns) = true;
+	      /* Set up namespace association.  */
+	      DECL_NAMESPACE_ASSOCIATIONS (ns)
+		= tree_cons (current_namespace, NULL_TREE, NULL_TREE);
+	      /* Import the contents of the inline namespace.  */
+	      do_using_directive (ns);
+	    }
 	}
-      if (anon)
+    }
+
+  if (ns)
+    {
+      if (make_inline && !DECL_NAMESPACE_INLINE_P (ns))
 	{
-	  /* Clear DECL_NAME for the benefit of debugging back ends.  */
-	  SET_DECL_ASSEMBLER_NAME (d, name);
-	  DECL_NAME (d) = NULL_TREE;
+	  error ("inline namespace must be specified at initial definition");
+	  inform (DECL_SOURCE_LOCATION (ns), "%qD defined here", ns);
 	}
-      begin_scope (sk_namespace, d);
+      if (new_ns)
+	begin_scope (sk_namespace, ns);
+      else
+	resume_scope (NAMESPACE_LEVEL (ns));
+      current_namespace = ns;
+      count++;
     }
-  else
-    resume_scope (NAMESPACE_LEVEL (d));
-
-  if (implicit_use)
-    do_using_directive (d);
-  /* Enter the name space.  */
-  current_namespace = d;
 
   timevar_cond_stop (TV_NAME_LOOKUP, subtime);
-  return true;
+  return count;
 }
 
 /* Pop from the scope of the current namespace.  */
Index: cp/name-lookup.h
===================================================================
--- cp/name-lookup.h	(revision 248066)
+++ cp/name-lookup.h	(working copy)
@@ -340,7 +340,7 @@  extern tree pushdecl (tree, bool is_frie
 extern tree pushdecl_top_level (tree, bool is_friend = false);
 extern tree pushdecl_top_level_and_finish (tree, tree);
 extern tree pushtag (tree, tree, tag_scope);
-extern bool push_namespace (tree);
+extern int push_namespace (tree, bool make_inline = false);
 extern void pop_namespace (void);
 extern void push_nested_namespace (tree);
 extern void pop_nested_namespace (tree);
Index: cp/parser.c
===================================================================
--- cp/parser.c	(revision 248066)
+++ cp/parser.c	(working copy)
@@ -18172,114 +18172,88 @@  cp_parser_namespace_name (cp_parser* par
 static void
 cp_parser_namespace_definition (cp_parser* parser)
 {
-  tree identifier, attribs;
-  bool has_visibility;
-  bool is_inline;
-  cp_token* token;
+  tree identifier;
   int nested_definition_count = 0;
 
   cp_ensure_no_omp_declare_simd (parser);
   cp_ensure_no_oacc_routine (parser);
-  if (cp_lexer_next_token_is_keyword (parser->lexer, RID_INLINE))
+
+  bool is_inline = cp_lexer_next_token_is_keyword (parser->lexer, RID_INLINE);
+
+  if (is_inline)
     {
       maybe_warn_cpp0x (CPP0X_INLINE_NAMESPACES);
-      is_inline = true;
       cp_lexer_consume_token (parser->lexer);
     }
-  else
-    is_inline = false;
 
   /* Look for the `namespace' keyword.  */
-  token = cp_parser_require_keyword (parser, RID_NAMESPACE, RT_NAMESPACE);
+  cp_token* token
+    = cp_parser_require_keyword (parser, RID_NAMESPACE, RT_NAMESPACE);
 
   /* Parse any specified attributes before the identifier.  */
-  attribs = cp_parser_attributes_opt (parser);
+  tree attribs = cp_parser_attributes_opt (parser);
 
-  /* Get the name of the namespace.  We do not attempt to distinguish
-     between an original-namespace-definition and an
-     extension-namespace-definition at this point.  The semantic
-     analysis routines are responsible for that.  */
-  if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
-    identifier = cp_parser_identifier (parser);
-  else
-    identifier = NULL_TREE;
-
-  /* Parse any specified attributes after the identifier.  */
-  tree post_ident_attribs = cp_parser_attributes_opt (parser);
-  if (post_ident_attribs)
+  for (;;)
     {
-      if (attribs)
-        attribs = chainon (attribs, post_ident_attribs);
-      else
-        attribs = post_ident_attribs;
-    }
-
-  /* Start the namespace.  */
-  bool ok = push_namespace (identifier);
-
-  /* Parse any nested namespace definition. */
-  if (cp_lexer_next_token_is (parser->lexer, CPP_SCOPE))
-    {
-      if (attribs)
-        error_at (token->location, "a nested namespace definition cannot have attributes");
-      if (cxx_dialect < cxx1z)
+      identifier = NULL_TREE;
+      
+      if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
+	{
+	  identifier = cp_parser_identifier (parser);
+
+	  /* Parse any attributes specified after the identifier.  */
+	  attribs = chainon (attribs, cp_parser_attributes_opt (parser));
+	}
+
+      if (cp_lexer_next_token_is_not (parser->lexer, CPP_SCOPE))
+	break;
+  
+      if (!nested_definition_count && cxx_dialect < cxx1z)
         pedwarn (input_location, OPT_Wpedantic,
                  "nested namespace definitions only available with "
                  "-std=c++1z or -std=gnu++1z");
-      if (is_inline)
-        error_at (token->location, "a nested namespace definition cannot be inline");
-      while (cp_lexer_next_token_is (parser->lexer, CPP_SCOPE))
-        {
-          cp_lexer_consume_token (parser->lexer);
-          if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
-            identifier = cp_parser_identifier (parser);
-          else
-            {
-              cp_parser_error (parser, "nested identifier required");
-              break;
-            }
-	  if (push_namespace (identifier))
-	    ++nested_definition_count;
-        }
+
+      /* Nested namespace names can create new namespaces (unlike
+	 other qualified-ids).  */
+      if (int count = identifier ? push_namespace (identifier) : 0)
+	nested_definition_count += count;
+      else
+	cp_parser_error (parser, "nested namespace name required");
+      cp_lexer_consume_token (parser->lexer);
     }
 
-  /* Look for the `{' to validate starting the namespace.  */
-  cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE);
+  if (nested_definition_count && !identifier)
+    cp_parser_error (parser, "namespace name required");
+  
+  if (nested_definition_count && attribs)
+    error_at (token->location,
+	      "a nested namespace definition cannot have attributes");
+  if (nested_definition_count && is_inline)
+    error_at (token->location,
+	      "a nested namespace definition cannot be inline");
 
-  /* "inline namespace" is equivalent to a stub namespace definition
-     followed by a strong using directive.  */
-  if (is_inline && ok)
-    {
-      tree name_space = current_namespace;
-      /* Set up namespace association.  */
-      DECL_NAMESPACE_ASSOCIATIONS (name_space)
-	= tree_cons (CP_DECL_CONTEXT (name_space), NULL_TREE,
-		     DECL_NAMESPACE_ASSOCIATIONS (name_space));
-      /* Import the contents of the inline namespace.  */
-      pop_namespace ();
-      do_using_directive (name_space);
-      push_namespace (identifier);
-    }
+  /* Start the namespace.  */
+  nested_definition_count += push_namespace (identifier, is_inline);
 
-  has_visibility = handle_namespace_attrs (current_namespace, attribs);
+  bool has_visibility = handle_namespace_attrs (current_namespace, attribs);
 
   warning  (OPT_Wnamespaces, "namespace %qD entered", current_namespace);
 
+  /* Look for the `{' to validate starting the namespace.  */
+  cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE);
+
   /* Parse the body of the namespace.  */
   cp_parser_namespace_body (parser);
 
+  /* Look for the final `}'.  */
+  cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE);
+
   if (has_visibility)
     pop_visibility (1);
 
-  /* Finish the nested namespace definitions.  */
+  /* Pop the nested namespace definitions.  */
   while (nested_definition_count--)
     pop_namespace ();
-
-  /* Finish the namespace.  */
-  if (ok)
-    pop_namespace ();
-  /* Look for the final `}'.  */
-  cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE);
 }
 
 /* Parse a namespace-body.
Index: testsuite/g++.dg/cpp0x/pr65558.C
===================================================================
--- testsuite/g++.dg/cpp0x/pr65558.C	(revision 248066)
+++ testsuite/g++.dg/cpp0x/pr65558.C	(working copy)
@@ -1,6 +1,7 @@ 
 // PR c++/65558
 // { dg-do compile { target c++11 } }
 
-inline namespace __attribute__((__abi_tag__))
-{ // { dg-warning "ignoring .__abi_tag__. attribute on anonymous namespace" }
+inline namespace
+__attribute__((__abi_tag__)) // { dg-warning "ignoring .__abi_tag__. attribute on anonymous namespace" }
+{
 }
Index: testsuite/g++.dg/cpp0x/pr79369.C
===================================================================
--- testsuite/g++.dg/cpp0x/pr79369.C	(revision 0)
+++ testsuite/g++.dg/cpp0x/pr79369.C	(working copy)
@@ -0,0 +1,9 @@ 
+// { dg-do compile { target c++11 } }
+// PR c++/79369 accept late inline of namespace
+
+namespace X {}
+inline namespace X {} // { dg-error "must be specified" }
+
+inline namespace Y {}
+namespace Y {} // OK
+inline namespace Y {} // also Ok