diff mbox series

c++: Hiddenness is a property of the symbol table

Message ID a1f01091-578f-2fe3-2021-3d63d8c780f2@acm.org
State New
Headers show
Series c++: Hiddenness is a property of the symbol table | expand

Commit Message

Nathan Sidwell Sept. 29, 2020, 7:23 p.m. UTC
This patch moves the handling of decl-hiddenness entirely into the
name lookup machinery, where it belongs.  We need a few new flags,
because pressing the existing OVL_HIDDEN_P into play for non-function
decls doesn't work well.  For a local binding we only need one marker,
as there cannot be both a hidden implicit typedef and a hidden
function.  That's not true for namespace-scope, where they could both
be hidden.

The name-lookup machinery maintains the existing decl_hidden and co
flags, and asserts have been sprinkled around to make sure they are
consistent.  The next series of patches will remove those old markers.
(we'll need to keep one, as there are some special restrictions on
redeclaring friend functions with in-class definitions or default args.)

         gcc/cp/
         * cp-tree.h (ovl_insert): Change final parm to hidden-or-using
         indicator.
         * name-lookup.h (HIDDEN_TYPE_BINDING_P): New.
         (struct cxx_binding): Add type_is_hidden flag.
         * tree.c (ovl_insert): Change using_p parm to using_or_hidden,
         adjust.
         (ovl_skip_hidden): Assert we never see a naked hidden decl.
         * decl.c (xref_tag_1): Delete unhiding friend from here (moved to
         lookup_elaborated_type_1).
         * name-lookup.c (STAT_TYPE_HIDDEN_P, STAT_DECL_HIDDEN_P): New.
         (name_lookup::search_namespace_only): Check new hidden markers.
         (cxx_binding_make): Clear HIDDEN_TYPE_BINDING_P.
         (update_binding): Update new hidden markers.
         (lookup_name_1): Check HIDDEN_TYPE_BINDING_P and simplify friend
         ignoring.
         (lookup_elaborated_type_1): Use new hidden markers.  Reveal the
         decl here.

pushed to trunk

nathan
diff mbox series

Patch

diff --git i/gcc/cp/cp-tree.h w/gcc/cp/cp-tree.h
index b7f5b6b399f..a25934e3263 100644
--- i/gcc/cp/cp-tree.h
+++ w/gcc/cp/cp-tree.h
@@ -7371,7 +7371,7 @@  inline tree ovl_first				(tree) ATTRIBUTE_PURE;
 extern tree ovl_make				(tree fn,
 						 tree next = NULL_TREE);
 extern tree ovl_insert				(tree fn, tree maybe_ovl,
-						 bool using_p = false);
+						 int using_or_hidden = 0);
 extern tree ovl_skip_hidden			(tree) ATTRIBUTE_PURE;
 extern void lookup_mark				(tree lookup, bool val);
 extern tree lookup_add				(tree fns, tree lookup);
diff --git i/gcc/cp/decl.c w/gcc/cp/decl.c
index c00b996294e..617b96e02e4 100644
--- i/gcc/cp/decl.c
+++ w/gcc/cp/decl.c
@@ -15089,22 +15089,9 @@  xref_tag_1 (enum tag_types tag_code, tree name,
 	  return error_mark_node;
 	}
 
-      if (how != TAG_how::HIDDEN_FRIEND && TYPE_HIDDEN_P (t))
-	{
-	  /* This is no longer an invisible friend.  Make it
-	     visible.  */
-	  tree decl = TYPE_NAME (t);
-
-	  DECL_ANTICIPATED (decl) = false;
-	  DECL_FRIEND_P (decl) = false;
-
-	  if (TYPE_TEMPLATE_INFO (t))
-	    {
-	      tree tmpl = TYPE_TI_TEMPLATE (t);
-	      DECL_ANTICIPATED (tmpl) = false;
-	      DECL_FRIEND_P (tmpl) = false;
-	    }
-	}
+      gcc_checking_assert (how == TAG_how::HIDDEN_FRIEND
+			   || !(DECL_LANG_SPECIFIC (TYPE_NAME (t))
+				&& DECL_ANTICIPATED (TYPE_NAME (t))));
     }
 
   return t;
diff --git i/gcc/cp/name-lookup.c w/gcc/cp/name-lookup.c
index 89f1a4c5d64..bc60d343f7e 100644
--- i/gcc/cp/name-lookup.c
+++ w/gcc/cp/name-lookup.c
@@ -55,6 +55,15 @@  static name_hint suggest_alternatives_for_1 (location_t location, tree name,
 #define MAYBE_STAT_DECL(N) (STAT_HACK_P (N) ? STAT_DECL (N) : N)
 #define MAYBE_STAT_TYPE(N) (STAT_HACK_P (N) ? STAT_TYPE (N) : NULL_TREE)
 
+/* For regular (maybe) overloaded functions, we have OVL_HIDDEN_P.
+   But we also need to indicate hiddenness on implicit type decls
+   (injected friend classes), and (coming soon) decls injected from
+   block-scope externs.  It is too awkward to press the existing
+   overload marking for that.  If we have a hidden non-function, we
+   always create a STAT_HACK, and use these two markers as needed.  */
+#define STAT_TYPE_HIDDEN_P(N) OVL_HIDDEN_P (N)
+#define STAT_DECL_HIDDEN_P(N) OVL_DEDUP_P (N)
+
 /* Create a STAT_HACK node with DECL as the value binding and TYPE as
    the type binding.  */
 
@@ -545,14 +554,18 @@  name_lookup::search_namespace_only (tree scope)
 	{
 	  type = STAT_TYPE (value);
 	  value = STAT_DECL (value);
-      
-	  if (!bool (want & LOOK_want::HIDDEN_FRIEND)
-	      && DECL_LANG_SPECIFIC (type)
-	      && DECL_ANTICIPATED (type))
-	    type = NULL_TREE;
+	  
+	  if (!bool (want & LOOK_want::HIDDEN_FRIEND))
+	    {
+	      if (STAT_TYPE_HIDDEN_P (*binding))
+		type = NULL_TREE;
+	      if (STAT_DECL_HIDDEN_P (*binding))
+		value = NULL_TREE;
+	      else
+		value = ovl_skip_hidden (value);
+	    }
 	}
-
-      if (!bool (want & LOOK_want::HIDDEN_FRIEND))
+      else if (!bool (want & LOOK_want::HIDDEN_FRIEND))
 	value = ovl_skip_hidden (value);
 
       found |= process_binding (value, type);
@@ -1975,6 +1988,7 @@  cxx_binding_make (tree value, tree type)
   /* Clear flags by default.  */
   LOCAL_BINDING_P (binding) = false;
   INHERITED_VALUE_BINDING_P (binding) = false;
+  HIDDEN_TYPE_BINDING_P (binding) = false;
 
   cxx_binding_init (binding, value, type);
 
@@ -2046,13 +2060,15 @@  pop_local_binding (tree id, tree decl)
   /* The name should be bound.  */
   gcc_assert (binding != NULL);
 
-  /* The DECL will be either the ordinary binding or the type
-     binding for this identifier.  Remove that binding.  */
+  /* The DECL will be either the ordinary binding or the type binding
+     for this identifier.  Remove that binding.  We don't have to
+     clear HIDDEN_TYPE_BINDING_P, as the whole binding will be going
+     away.  */
   if (binding->value == decl)
     binding->value = NULL_TREE;
   else
     {
-      gcc_assert (binding->type == decl);
+      gcc_checking_assert (binding->type == decl);
       binding->type = NULL_TREE;
     }
 
@@ -2367,11 +2383,22 @@  update_binding (cp_binding_level *level, cxx_binding *binding, tree *slot,
 		tree old, tree decl, bool hiding = false)
 {
   tree old_type = NULL_TREE;
+  bool hide_type = false;
+  bool hide_value = false;
 
   if (!slot)
-    old_type = binding->type;
+    {
+      old_type = binding->type;
+      hide_type = HIDDEN_TYPE_BINDING_P (binding);
+      if (!old_type)
+	hide_value = hide_type, hide_type = false;
+    }
   else if (STAT_HACK_P (*slot))
+    {
       old_type = STAT_TYPE (*slot);
+      hide_type = STAT_TYPE_HIDDEN_P (*slot);
+      hide_value = STAT_DECL_HIDDEN_P (*slot);
+    }
 
   tree to_val = decl;
   tree to_type = old_type;
@@ -2394,9 +2421,12 @@  update_binding (cp_binding_level *level, cxx_binding *binding, tree *slot,
 	{
 	  /* Put DECL into the type slot.  */
 	  gcc_checking_assert (!to_type);
+	  hide_type = hiding;
 	  to_type = decl;
 	  to_val = old;
 	}
+      else
+	hide_value = hiding;
 
       goto done;
     }
@@ -2407,7 +2437,9 @@  update_binding (cp_binding_level *level, cxx_binding *binding, tree *slot,
       gcc_checking_assert (!to_type);
 
       to_type = old;
+      hide_type = hide_value;
       old = NULL_TREE;
+      hide_value = false;
     }
 
   if (DECL_DECLARES_FUNCTION_P (decl))
@@ -2450,7 +2482,7 @@  update_binding (cp_binding_level *level, cxx_binding *binding, tree *slot,
 		 decl, to_type);
 
       local_overload = old && level->kind != sk_namespace;
-      to_val = ovl_insert (decl, old);
+      to_val = ovl_insert (decl, old, -int (hiding));
     }
   else if (old)
     {
@@ -2483,11 +2515,13 @@  update_binding (cp_binding_level *level, cxx_binding *binding, tree *slot,
 	{
 	  /* There can be two block-scope declarations of the same
 	     variable, so long as they are `extern' declarations.  */
-	  // FIXME: This is DECL_LOCAL_DECL_P type stuff.
 	  if (!DECL_EXTERNAL (old) || !DECL_EXTERNAL (decl))
 	    goto conflict;
 	  else if (tree match = duplicate_decls (decl, old))
-	    return match;
+	    {
+	      gcc_checking_assert (!hide_value && !hiding);
+	      return match;
+	    }
 	  else
 	    goto conflict;
 	}
@@ -2498,6 +2532,8 @@  update_binding (cp_binding_level *level, cxx_binding *binding, tree *slot,
 	  to_val = NULL_TREE;
 	}
     }
+  else if (hiding)
+    hide_value = true;
 
  done:
   if (to_val)
@@ -2516,16 +2552,26 @@  update_binding (cp_binding_level *level, cxx_binding *binding, tree *slot,
 	    {
 	      STAT_TYPE (*slot) = to_type;
 	      STAT_DECL (*slot) = to_val;
+	      STAT_TYPE_HIDDEN_P (*slot) = hide_type;
+	      STAT_DECL_HIDDEN_P (*slot) = hide_value;
+	    }
+	  else if (to_type || hide_value)
+	    {
+	      *slot = stat_hack (to_val, to_type);
+	      STAT_TYPE_HIDDEN_P (*slot) = hide_type;
+	      STAT_DECL_HIDDEN_P (*slot) = hide_value;
 	    }
-	  else if (to_type)
-	    *slot = stat_hack (to_val, to_type);
 	  else
-	    *slot = to_val;
+	    {
+	      gcc_checking_assert (!hide_type);
+	      *slot = to_val;
+	    }
 	}
       else
 	{
 	  binding->type = to_type;
 	  binding->value = to_val;
+	  HIDDEN_TYPE_BINDING_P (binding) = hide_type || hide_value;
 	}
     }
 
@@ -6489,86 +6535,37 @@  lookup_name_1 (tree name, LOOK_where where, LOOK_want want)
     for (cxx_binding *iter = nullptr;
 	 (iter = outer_binding (name, iter, bool (where & LOOK_where::CLASS)));)
       {
-	tree binding;
-
 	/* Skip entities we don't want.  */
 	if (!bool (where & (LOCAL_BINDING_P (iter)
 			    ? LOOK_where::BLOCK : LOOK_where::CLASS)))
 	  continue;
 
 	/* If this is the kind of thing we're looking for, we're done.  */
-	if (iter->value
-	    && (bool (want & LOOK_want::HIDDEN_LAMBDA)
-		|| !is_lambda_ignored_entity (iter->value))
-	    && qualify_lookup (iter->value, want))
-	  binding = iter->value;
-	else if (bool (want & LOOK_want::TYPE)
-		 && qualify_lookup (iter->type, want))
-	  binding = iter->type;
-	else
-	  binding = NULL_TREE;
-
-	if (binding)
+	if (iter->value)
 	  {
-	    if (TREE_CODE (binding) == TYPE_DECL && DECL_HIDDEN_P (binding))
+	    tree binding = NULL_TREE;
+
+	    if (!(!iter->type && HIDDEN_TYPE_BINDING_P (iter))
+		&& (bool (want & LOOK_want::HIDDEN_LAMBDA)
+		    || !is_lambda_ignored_entity (iter->value))
+		&& qualify_lookup (iter->value, want))
+	      binding = iter->value;
+	    else if (bool (want & LOOK_want::TYPE)
+		     && !HIDDEN_TYPE_BINDING_P (iter)
+		     && iter->type)
+	      binding = iter->type;
+
+	    if (binding)
 	      {
-		/* A non namespace-scope binding can only be hidden in the
-		   presence of a local class, due to friend declarations.
-
-		   In particular, consider:
-
-		   struct C;
-		   void f() {
-		     struct A {
-		       friend struct B;
-		       friend struct C;
-		       void g() {
-		         B* b; // error: B is hidden
-			 C* c; // OK, finds ::C
-		       } 
-		     };
-		     B *b;  // error: B is hidden
-		     C *c;  // OK, finds ::C
-		     struct B {};
-		     B *bb; // OK
-		   }
-
-		   The standard says that "B" is a local class in "f"
-		   (but not nested within "A") -- but that name lookup
-		   for "B" does not find this declaration until it is
-		   declared directly with "f".
-
-		   In particular:
-
-		   [class.friend]
-
-		   If a friend declaration appears in a local class and
-		   the name specified is an unqualified name, a prior
-		   declaration is looked up without considering scopes
-		   that are outside the innermost enclosing non-class
-		   scope. For a friend function declaration, if there is
-		   no prior declaration, the program is ill-formed. For a
-		   friend class declaration, if there is no prior
-		   declaration, the class that is specified belongs to the
-		   innermost enclosing non-class scope, but if it is
-		   subsequently referenced, its name is not found by name
-		   lookup until a matching declaration is provided in the
-		   innermost enclosing nonclass scope.
-
-		   So just keep looking for a non-hidden binding.
-		*/
-		gcc_assert (TREE_CODE (binding) == TYPE_DECL);
-		continue;
+		/* The saved lookups for an operator record 'nothing
+		   found' as error_mark_node.  We need to stop the search
+		   here, but not return the error mark node.  */
+		if (binding == error_mark_node)
+		  binding = NULL_TREE;
+
+		val = binding;
+		goto found;
 	      }
-
-	    /* The saved lookups for an operator record 'nothing
-	       found' as error_mark_node.  We need to stop the search
-	       here, but not return the error mark node.  */
-	    if (binding == error_mark_node)
-	      binding = NULL_TREE;
-
-	    val = binding;
-	    goto found;
 	  }
       }
 
@@ -6649,17 +6646,55 @@  lookup_elaborated_type_1 (tree name, TAG_how how)
 	     typedef struct C {} C;
 	   correctly.  */
 
+	tree found = NULL_TREE;
+	bool reveal = false;
 	if (tree type = iter->type)
-	  if (qualify_lookup (type, LOOK_want::TYPE)
-	      && (how != TAG_how::CURRENT_ONLY
-		  || LOCAL_BINDING_P (iter)
-		  || DECL_CONTEXT (type) == iter->scope->this_entity))
-	    return type;
-
-	if (qualify_lookup (iter->value, LOOK_want::TYPE)
-	    && (how != TAG_how::CURRENT_ONLY
-		|| !INHERITED_VALUE_BINDING_P (iter)))
-	  return iter->value;
+	  {
+	    if (qualify_lookup (type, LOOK_want::TYPE)
+		&& (how != TAG_how::CURRENT_ONLY
+		    || LOCAL_BINDING_P (iter)
+		    || DECL_CONTEXT (type) == iter->scope->this_entity))
+	      {
+		found = type;
+		if (how != TAG_how::HIDDEN_FRIEND)
+		  reveal = HIDDEN_TYPE_BINDING_P (iter);
+	      }
+	  }
+	else
+	  {
+	    if (qualify_lookup (iter->value, LOOK_want::TYPE)
+		&& (how != TAG_how::CURRENT_ONLY
+		    || !INHERITED_VALUE_BINDING_P (iter)))
+	      {
+		found = iter->value;
+		if (how != TAG_how::HIDDEN_FRIEND)
+		  reveal = !iter->type && HIDDEN_TYPE_BINDING_P (iter);
+	      }
+	  }
+
+	if (found)
+	  {
+	    if (reveal)
+	      {
+		/* It is no longer a hidden binding.  */
+		HIDDEN_TYPE_BINDING_P (iter) = false;
+
+		/* Unanticipate the decl itself.  */
+		DECL_ANTICIPATED (found) = false;
+		DECL_FRIEND_P (found) = false;
+
+		gcc_checking_assert (TREE_CODE (found) != TEMPLATE_DECL);
+
+		if (tree ti = TYPE_TEMPLATE_INFO (TREE_TYPE (found)))
+		  {
+		    tree tmpl = TI_TEMPLATE (ti);
+		    DECL_ANTICIPATED (tmpl) = false;
+		    DECL_FRIEND_P (tmpl) = false;
+		  }
+	      }
+
+	    return found;
+	  }
       }
 
   /* Now check if we can look in namespace scope.  */
@@ -6675,13 +6710,63 @@  lookup_elaborated_type_1 (tree name, TAG_how how)
   if (tree *slot = find_namespace_slot (ns, name))
     {
       /* If this is the kind of thing we're looking for, we're done.  */
+      tree found = NULL_TREE;
+      bool reveal = false;
+
       if (tree type = MAYBE_STAT_TYPE (*slot))
-	if (qualify_lookup (type, LOOK_want::TYPE))
-	  return type;
+	{
+	  found = type;
+	  if (how != TAG_how::HIDDEN_FRIEND)
+	    {
+	      reveal = STAT_TYPE_HIDDEN_P (*slot);
+	      STAT_TYPE_HIDDEN_P (*slot) = false;
+	    }
+	}
+      else if (tree decl = MAYBE_STAT_DECL (*slot))
+	{
+	  if (qualify_lookup (decl, LOOK_want::TYPE))
+	    {
+	      found = decl;
+
+	      if (how != TAG_how::HIDDEN_FRIEND  && STAT_HACK_P (*slot))
+		{
+		  reveal = STAT_DECL_HIDDEN_P (*slot);
+		  if (reveal)
+		    {
+		      if (STAT_TYPE (*slot))
+			STAT_DECL_HIDDEN_P (*slot) = false;
+		      else
+			/* There is no type, just remove the stat
+			   hack.  */
+			*slot = decl;
+		    }
+		}
+	    }
+	}
+
+      if (found)
+	{
+	  if (reveal)
+	    {
+	      /* Reveal the previously hidden thing.  */
+	      DECL_ANTICIPATED (found) = false;
+	      DECL_FRIEND_P (found) = false;
+
+	      if (TREE_CODE (found) == TEMPLATE_DECL)
+		{
+		  DECL_ANTICIPATED (DECL_TEMPLATE_RESULT (found)) = false;
+		  DECL_FRIEND_P (DECL_TEMPLATE_RESULT (found)) = false;
+		}
+	      else if (tree ti = TYPE_TEMPLATE_INFO (TREE_TYPE (found)))
+		{
+		  tree tmpl = TI_TEMPLATE (ti);
+		  DECL_ANTICIPATED (tmpl) = false;
+		  DECL_FRIEND_P (tmpl) = false;
+		}
+	    }
 
-      if (tree decl = MAYBE_STAT_DECL (*slot))
-	if (qualify_lookup (decl, LOOK_want::TYPE))
-	  return decl;
+	  return found;
+	}
     }
 
   return NULL_TREE;
diff --git i/gcc/cp/name-lookup.h w/gcc/cp/name-lookup.h
index 7b463386725..01643fb9b34 100644
--- i/gcc/cp/name-lookup.h
+++ w/gcc/cp/name-lookup.h
@@ -58,6 +58,12 @@  struct cp_binding_level;
    currently being defined.  */
 #define INHERITED_VALUE_BINDING_P(NODE) ((NODE)->value_is_inherited)
 
+/* The IMPLICIT_TYPEDEF is hidden from ordinary name lookup (it was
+   injected via a local class's friend decl). The typdef may be in the
+   VALUE or the TYPE slot.  We do not get the situation where the
+   value and type slots are both filled and both hidden.  */
+#define HIDDEN_TYPE_BINDING_P(NODE) ((NODE)->type_is_hidden)
+
 /* Datatype that represents binding established by a declaration between
    a name and a C++ entity.  */
 struct GTY(()) cxx_binding {
@@ -72,6 +78,7 @@  struct GTY(()) cxx_binding {
 
   bool value_is_inherited : 1;
   bool is_local : 1;
+  bool type_is_hidden : 1;
 };
 
 /* Datatype used to temporarily save C++ bindings (for implicit
diff --git i/gcc/cp/tree.c w/gcc/cp/tree.c
index e8606602bd2..0b80d8ed408 100644
--- i/gcc/cp/tree.c
+++ w/gcc/cp/tree.c
@@ -2237,13 +2237,13 @@  ovl_make (tree fn, tree next)
   return result;
 }
 
-/* Add FN to the (potentially NULL) overload set OVL.  USING_P is
-   true, if FN is via a using declaration.  We also pay attention to
-   DECL_HIDDEN.  We keep the hidden decls first, but remaining ones
-   are unordered.  */
+/* Add FN to the (potentially NULL) overload set OVL.  USING_OR_HIDDEN
+   is > 0, if FN is via a using declaration.  USING_OR_HIDDEN is < 0,
+   if FN is hidden.  (A decl cannot be both using and hidden.)  We
+   keep the hidden decls first, but remaining ones are unordered.  */
 
 tree
-ovl_insert (tree fn, tree maybe_ovl, bool using_p)
+ovl_insert (tree fn, tree maybe_ovl, int using_or_hidden)
 {
   tree result = maybe_ovl;
   tree insert_after = NULL_TREE;
@@ -2257,13 +2257,15 @@  ovl_insert (tree fn, tree maybe_ovl, bool using_p)
       insert_after = maybe_ovl;
     }
 
-  bool hidden_p = DECL_HIDDEN_P (fn);
-  if (maybe_ovl || using_p || hidden_p || TREE_CODE (fn) == TEMPLATE_DECL)
+  if (maybe_ovl || using_or_hidden || TREE_CODE (fn) == TEMPLATE_DECL)
     {
       maybe_ovl = ovl_make (fn, maybe_ovl);
-      if (hidden_p)
+
+      gcc_checking_assert ((using_or_hidden < 0) == DECL_HIDDEN_P (fn));
+
+      if (using_or_hidden < 0)
 	OVL_HIDDEN_P (maybe_ovl) = true;
-      if (using_p)
+      if (using_or_hidden > 0)
 	OVL_DEDUP_P (maybe_ovl) = OVL_USING_P (maybe_ovl) = true;
     }
   else
@@ -2290,13 +2292,9 @@  ovl_skip_hidden (tree ovl)
        ovl = OVL_CHAIN (ovl))
     gcc_checking_assert (DECL_HIDDEN_P (OVL_FUNCTION (ovl)));
 
-  if (ovl && TREE_CODE (ovl) != OVERLOAD && DECL_HIDDEN_P (ovl))
-    {
-      /* Any hidden functions should have been wrapped in an
-	 overload, but injected friend classes will not.  */
-      gcc_checking_assert (!DECL_DECLARES_FUNCTION_P (ovl));
-      ovl = NULL_TREE;
-    }
+  /* We should not see a naked hidden decl.  */
+  gcc_checking_assert (!(ovl && TREE_CODE (ovl) != OVERLOAD
+			 && DECL_HIDDEN_P (ovl)));
 
   return ovl;
 }