diff mbox series

c++: DECL_FRIEND_P cleanup

Message ID 4e110c55-e902-744d-7924-9f898394142b@acm.org
State New
Headers show
Series c++: DECL_FRIEND_P cleanup | expand

Commit Message

Nathan Sidwell Oct. 14, 2020, 5:03 p.m. UTC
DECL_FRIEND_P's meaning has changed over time.  It now (almost) means
the the friend function decl has not been met via an explicit decl.
This completes that transition, renaming it to DECL_UNIQUE_FRIEND_P,
so one doesn't think it is the sole indicator of friendliness (plenty
of friends do not have the flag set).  This allows reduction in the
complexity of managing the field -- all in duplicate_decls now.

         gcc/cp/
         * cp-tree.h (struct lang_decl_fn): Adjust context comment.
         (DECL_FRIEND_P): Replace with ...
         (DECL_UNIQUE_FRIEND_P): ... this.  Only for FUNCTION_DECLs.
         (DECL_FRIEND_CONTEXT): Adjust.
         * class.c (add_implicitly_declared_members): Detect friendly
         spaceship from context.
         * constraint.cc (remove_constraints): Use a checking assert.
         (maybe_substitute_reqs_for): Use DECL_UNIQUE_FRIEND_P.
         * decl.c (check_no_redeclaration_friend_default_args):
         DECL_UNIQUE_FRIEND_P is signficant, not hiddenness.
         (duplicate_decls): Adjust DECL_UNIQUE_FRIEND_P clearing.
         (redeclaration_error_message): Use DECL_UNIQUE_FRIEND_P.
         (start_preparsed_function): Correct in-class friend processing.
         Refactor some initializers.
         (grokmethod): Directly check friend decl-spec.
         * decl2.c (grokfield): Check DECL_UNIQUE_FRIEND_P.
         * friend.c (do_friend): Set DECL_UNIQUE_FRIEND_P first, remove
         extraneous conditions.  Don't re set it afterwards.
         * name-lookup.c (lookup_elaborated_type_1): Simplify revealing
         code.
         (do_pushtag): Likewise.
         * pt.c (optimize_specialization_lookup_p): Check
         DECL_UNIQUE_FRIEND_P.
         (push_template_decl): Likewise.  Drop unneeded friend setting.
         (type_dependent_expression_p): Check DECL_UNIQUE_FRIEND_P.
         libcc1/
         * libcp1plugin.cc (plugin_add_friend): Set DECL_UNIQUE_FRIEND_P.

pushing to trunk

nathan
diff mbox series

Patch

diff --git i/gcc/cp/class.c w/gcc/cp/class.c
index 01780fe8291..26f996b7f4b 100644
--- i/gcc/cp/class.c
+++ w/gcc/cp/class.c
@@ -3283,7 +3283,8 @@  add_implicitly_declared_members (tree t, tree* access_decls,
       {
 	tree eq = implicitly_declare_fn (sfk_comparison, t, false, space,
 					 NULL_TREE);
-	if (DECL_FRIEND_P (space))
+	bool is_friend = DECL_CONTEXT (space) != t;
+	if (is_friend)
 	  do_friend (NULL_TREE, DECL_NAME (eq), eq,
 		     NULL_TREE, NO_SPECIAL, true);
 	else
@@ -3292,7 +3293,7 @@  add_implicitly_declared_members (tree t, tree* access_decls,
 	    DECL_CHAIN (eq) = TYPE_FIELDS (t);
 	    TYPE_FIELDS (t) = eq;
 	  }
-	maybe_add_class_template_decl_list (t, eq, DECL_FRIEND_P (space));
+	maybe_add_class_template_decl_list (t, eq, is_friend);
       }
 
   while (*access_decls)
diff --git i/gcc/cp/constraint.cc w/gcc/cp/constraint.cc
index 050b55ce092..f4f5174eff3 100644
--- i/gcc/cp/constraint.cc
+++ w/gcc/cp/constraint.cc
@@ -1201,7 +1201,7 @@  set_constraints (tree t, tree ci)
 void
 remove_constraints (tree t)
 {
-  gcc_assert (DECL_P (t));
+  gcc_checking_assert (DECL_P (t));
   if (TREE_CODE (t) == TEMPLATE_DECL)
     t = DECL_TEMPLATE_RESULT (t);
 
@@ -1217,11 +1217,16 @@  maybe_substitute_reqs_for (tree reqs, const_tree decl_)
 {
   if (reqs == NULL_TREE)
     return NULL_TREE;
+
   tree decl = CONST_CAST_TREE (decl_);
   tree result = STRIP_TEMPLATE (decl);
-  if (DECL_FRIEND_P (result))
+
+  if (DECL_UNIQUE_FRIEND_P (result))
     {
-      tree tmpl = decl == result ? DECL_TI_TEMPLATE (result) : decl;
+      tree tmpl = decl;
+      if (TREE_CODE (decl) != TEMPLATE_DECL)
+	tmpl = DECL_TI_TEMPLATE (result);
+
       tree gargs = generic_targs_for (tmpl);
       processing_template_decl_sentinel s;
       if (uses_template_parms (gargs))
diff --git i/gcc/cp/cp-tree.h w/gcc/cp/cp-tree.h
index 467256117ec..5c06ac3789e 100644
--- i/gcc/cp/cp-tree.h
+++ w/gcc/cp/cp-tree.h
@@ -2736,12 +2736,14 @@  struct GTY(()) lang_decl_fn {
      thunked to function decl.  */
   tree befriending_classes;
 
-  /* For a non-virtual FUNCTION_DECL, this is
-     DECL_FRIEND_CONTEXT.  For a virtual FUNCTION_DECL for which
+  /* For a virtual FUNCTION_DECL for which
      DECL_THIS_THUNK_P does not hold, this is DECL_THUNKS. Both
      this pointer and result pointer adjusting thunks are
      chained here.  This pointer thunks to return pointer thunks
-     will be chained on the return pointer thunk.  */
+     will be chained on the return pointer thunk.
+     For a DECL_CONSTUCTOR_P FUNCTION_DECL, this is the base from
+     whence we inherit.  Otherwise, it is the class in which a
+     (namespace-scope) friend is defined (if any).   */
   tree context;
 
   union lang_decl_u5
@@ -3088,10 +3090,14 @@  struct GTY(()) lang_decl {
   (DECL_LANG_SPECIFIC (VAR_OR_FUNCTION_DECL_CHECK (DECL)) \
    ->u.base.odr_used)
 
-/* Nonzero for DECL means that this decl is just a friend declaration,
-   and should not be added to the list of members for this class.  */
-#define DECL_FRIEND_P(NODE) \
-  (DECL_LANG_SPECIFIC (TYPE_FUNCTION_OR_TEMPLATE_DECL_CHECK (NODE)) \
+/* Nonzero for FUNCTION_DECL means that this is a friend that is
+   either not pushed into a namespace/looked up in a class (because it
+   is a dependent type, in an uninstantiated template), or it has
+   /only/ been subject to hidden friend injection from one or more
+   befriending classes.  Once another decl matches, the flag is
+   cleared.  There are requirements on its default parms.  */
+#define DECL_UNIQUE_FRIEND_P(NODE) \
+  (DECL_LANG_SPECIFIC (FUNCTION_DECL_CHECK (NODE)) \
    ->u.base.friend_or_tls)
 
 /* Nonzero if the thread-local variable was declared with __thread as
@@ -3290,8 +3296,8 @@  struct GTY(()) lang_decl {
 
    the DECL_FRIEND_CONTEXT for `f' will be `S'.  */
 #define DECL_FRIEND_CONTEXT(NODE)				\
-  ((DECL_DECLARES_FUNCTION_P (NODE)				\
-    && DECL_FRIEND_P (NODE) && !DECL_FUNCTION_MEMBER_P (NODE))	\
+  ((DECL_DECLARES_FUNCTION_P (NODE) && !DECL_VIRTUAL_P (NODE)	\
+    && !DECL_CONSTRUCTOR_P (NODE))				\
    ? LANG_DECL_FN_CHECK (NODE)->context				\
    : NULL_TREE)
 
diff --git i/gcc/cp/decl.c w/gcc/cp/decl.c
index 0fe74b2e851..2f1a2f0c554 100644
--- i/gcc/cp/decl.c
+++ w/gcc/cp/decl.c
@@ -1340,18 +1340,17 @@  check_redeclaration_no_default_args (tree decl)
    the function or function template in the translation unit."  */
 
 static void
-check_no_redeclaration_friend_default_args (tree olddecl, tree newdecl,
-					    bool olddecl_hidden_p)
+check_no_redeclaration_friend_default_args (tree olddecl, tree newdecl)
 {
-  if (!olddecl_hidden_p && !DECL_FRIEND_P (newdecl))
+  if (!DECL_UNIQUE_FRIEND_P (olddecl) && !DECL_UNIQUE_FRIEND_P (newdecl))
     return;
 
   for (tree t1 = FUNCTION_FIRST_USER_PARMTYPE (olddecl),
 	 t2 = FUNCTION_FIRST_USER_PARMTYPE (newdecl);
        t1 && t1 != void_list_node;
        t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2))
-    if ((olddecl_hidden_p && TREE_PURPOSE (t1))
-	|| (DECL_FRIEND_P (newdecl) && TREE_PURPOSE (t2)))
+    if ((DECL_UNIQUE_FRIEND_P (olddecl) && TREE_PURPOSE (t1))
+	|| (DECL_UNIQUE_FRIEND_P (newdecl) && TREE_PURPOSE (t2)))
       {
 	auto_diagnostic_group d;
 	if (permerror (DECL_SOURCE_LOCATION (newdecl),
@@ -1444,8 +1443,7 @@  tree
 duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 {
   unsigned olddecl_uid = DECL_UID (olddecl);
-  int olddecl_friend = 0, types_match = 0;
-  int olddecl_hidden_friend = 0;
+  int types_match = 0;
   int new_defines_function = 0;
   tree new_template_info;
   location_t olddecl_loc = DECL_SOURCE_LOCATION (olddecl);
@@ -1987,8 +1985,7 @@  duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 		 argument expression, that declaration... shall be the only
 		 declaration of the function or function template in the
 		 translation unit."  */
-	      check_no_redeclaration_friend_default_args
-		(olddecl, newdecl, was_hidden);
+	      check_no_redeclaration_friend_default_args (olddecl, newdecl);
 	    }
 	}
     }
@@ -2135,12 +2132,6 @@  duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
   else
     DECL_ATTRIBUTES (olddecl) = DECL_ATTRIBUTES (newdecl);
 
-  if (DECL_DECLARES_FUNCTION_P (olddecl))
-    {
-      olddecl_friend = DECL_FRIEND_P (STRIP_TEMPLATE (olddecl));
-      olddecl_hidden_friend = olddecl_friend && was_hidden;
-    }
-
   if (TREE_CODE (newdecl) == TEMPLATE_DECL)
     {
       tree old_result = DECL_TEMPLATE_RESULT (olddecl);
@@ -2167,8 +2158,10 @@  duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 		 declaration of the function or function template in the
 		 translation unit."  */
 	      check_no_redeclaration_friend_default_args
-		(old_result, new_result, olddecl_hidden_friend);
+		(old_result, new_result);
 	    }
+	  if (!DECL_UNIQUE_FRIEND_P (old_result))
+	    DECL_UNIQUE_FRIEND_P (new_result) = false;
 
 	  check_default_args (newdecl);
 
@@ -2366,6 +2359,9 @@  duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 	      && !DECL_FUNCTION_SPECIFIC_OPTIMIZATION (newdecl))
 	    DECL_FUNCTION_SPECIFIC_OPTIMIZATION (newdecl)
 	      = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (olddecl);
+
+	  if (!DECL_UNIQUE_FRIEND_P (olddecl))
+	    DECL_UNIQUE_FRIEND_P (newdecl) = false;
 	}
       else
 	{
@@ -2885,8 +2881,6 @@  duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
     }
 
   DECL_UID (olddecl) = olddecl_uid;
-  if (olddecl_friend)
-    DECL_FRIEND_P (olddecl) = true;
 
   /* NEWDECL contains the merged attribute lists.
      Update OLDDECL to be the same.  */
@@ -3062,7 +3056,7 @@  redeclaration_error_message (tree newdecl, tree olddecl)
            definition and shall be the only declaration of the
            function template in the translation unit.  */
       if ((cxx_dialect != cxx98)
-          && TREE_CODE (ot) == FUNCTION_DECL && DECL_FRIEND_P (ot)
+          && TREE_CODE (ot) == FUNCTION_DECL && DECL_UNIQUE_FRIEND_P (ot)
 	  && !check_default_tmpl_args (nt, DECL_TEMPLATE_PARMS (newdecl),
                                        /*is_primary=*/true,
 				       /*is_partial=*/false,
@@ -3073,7 +3067,8 @@  redeclaration_error_message (tree newdecl, tree olddecl)
       return NULL;
     }
   else if (VAR_P (newdecl)
-	   && CP_DECL_THREAD_LOCAL_P (newdecl) != CP_DECL_THREAD_LOCAL_P (olddecl)
+	   && (CP_DECL_THREAD_LOCAL_P (newdecl)
+	       != CP_DECL_THREAD_LOCAL_P (olddecl))
 	   && (! DECL_LANG_SPECIFIC (olddecl)
 	       || ! CP_DECL_THREADPRIVATE_P (olddecl)
 	       || CP_DECL_THREAD_LOCAL_P (newdecl)))
@@ -16110,36 +16105,21 @@  bool
 start_preparsed_function (tree decl1, tree attrs, int flags)
 {
   tree ctype = NULL_TREE;
-  tree fntype;
-  tree restype;
-  int doing_friend = 0;
-  cp_binding_level *bl;
-  tree current_function_parms;
-  struct c_fileinfo *finfo
-    = get_fileinfo (LOCATION_FILE (DECL_SOURCE_LOCATION (decl1)));
-  bool honor_interface;
+  bool doing_friend = false;
 
   /* Sanity check.  */
   gcc_assert (VOID_TYPE_P (TREE_VALUE (void_list_node)));
   gcc_assert (TREE_CHAIN (void_list_node) == NULL_TREE);
 
-  fntype = TREE_TYPE (decl1);
+  tree fntype = TREE_TYPE (decl1);
   if (TREE_CODE (fntype) == METHOD_TYPE)
     ctype = TYPE_METHOD_BASETYPE (fntype);
-
-  /* ISO C++ 11.4/5.  A friend function defined in a class is in
-     the (lexical) scope of the class in which it is defined.  */
-  if (!ctype && DECL_FRIEND_P (decl1))
+  else
     {
       ctype = DECL_FRIEND_CONTEXT (decl1);
 
-      /* CTYPE could be null here if we're dealing with a template;
-	 for example, `inline friend float foo()' inside a template
-	 will have no CTYPE set.  */
-      if (ctype && TREE_CODE (ctype) != RECORD_TYPE)
-	ctype = NULL_TREE;
-      else
-	doing_friend = 1;
+      if (ctype)
+	doing_friend = true;
     }
 
   if (DECL_DECLARED_INLINE_P (decl1)
@@ -16206,7 +16186,7 @@  start_preparsed_function (tree decl1, tree attrs, int flags)
      by push_nested_class.)  */
   if (processing_template_decl)
     {
-      tree newdecl1 = push_template_decl (decl1, DECL_FRIEND_P (decl1));
+      tree newdecl1 = push_template_decl (decl1, doing_friend);
       if (newdecl1 == error_mark_node)
 	{
 	  if (ctype || DECL_STATIC_FUNCTION_P (decl1))
@@ -16222,7 +16202,7 @@  start_preparsed_function (tree decl1, tree attrs, int flags)
   check_function_type (decl1, DECL_ARGUMENTS (decl1));
 
   /* Build the return declaration for the function.  */
-  restype = TREE_TYPE (fntype);
+  tree restype = TREE_TYPE (fntype);
 
   if (DECL_RESULT (decl1) == NULL_TREE)
     {
@@ -16312,7 +16292,7 @@  start_preparsed_function (tree decl1, tree attrs, int flags)
 
   /* Save the parm names or decls from this function's declarator
      where store_parm_decls will find them.  */
-  current_function_parms = DECL_ARGUMENTS (decl1);
+  tree current_function_parms = DECL_ARGUMENTS (decl1);
 
   /* Let the user know we're compiling this function.  */
   announce_function (decl1);
@@ -16329,7 +16309,7 @@  start_preparsed_function (tree decl1, tree attrs, int flags)
      even when processing a template; this is how we get
      CFUN set up, and our per-function variables initialized.
      FIXME factor out the non-RTL stuff.  */
-  bl = current_binding_level;
+  cp_binding_level *bl = current_binding_level;
   allocate_struct_function (decl1, processing_template_decl);
 
   /* Initialize the language data structures.  Whenever we start
@@ -16384,14 +16364,16 @@  start_preparsed_function (tree decl1, tree attrs, int flags)
 	}
     }
 
-  honor_interface = (!DECL_TEMPLATE_INSTANTIATION (decl1)
-		     /* Implicitly-defined methods (like the
-			destructor for a class in which no destructor
-			is explicitly declared) must not be defined
-			until their definition is needed.  So, we
-			ignore interface specifications for
-			compiler-generated functions.  */
-		     && !DECL_ARTIFICIAL (decl1));
+  bool honor_interface = (!DECL_TEMPLATE_INSTANTIATION (decl1)
+			  /* Implicitly-defined methods (like the
+			     destructor for a class in which no destructor
+			     is explicitly declared) must not be defined
+			     until their definition is needed.  So, we
+			     ignore interface specifications for
+			     compiler-generated functions.  */
+			  && !DECL_ARTIFICIAL (decl1));
+  struct c_fileinfo *finfo
+    = get_fileinfo (LOCATION_FILE (DECL_SOURCE_LOCATION (decl1)));
 
   if (processing_template_decl)
     /* Don't mess with interface flags.  */;
@@ -17311,18 +17293,17 @@  grokmethod (cp_decl_specifier_seq *declspecs,
   /* We process method specializations in finish_struct_1.  */
   if (processing_template_decl && !DECL_TEMPLATE_SPECIALIZATION (fndecl))
     {
-      fndecl = push_template_decl (fndecl, DECL_FRIEND_P (fndecl));
+      /* Avoid calling decl_spec_seq... until we have to.  */
+      bool friendp = decl_spec_seq_has_spec_p (declspecs, ds_friend);
+      fndecl = push_template_decl (fndecl, friendp);
       if (fndecl == error_mark_node)
 	return fndecl;
     }
 
-  if (! DECL_FRIEND_P (fndecl))
+  if (DECL_CHAIN (fndecl) && !decl_spec_seq_has_spec_p (declspecs, ds_friend))
     {
-      if (DECL_CHAIN (fndecl))
-	{
-	  fndecl = copy_node (fndecl);
-	  TREE_CHAIN (fndecl) = NULL_TREE;
-	}
+      fndecl = copy_node (fndecl);
+      TREE_CHAIN (fndecl) = NULL_TREE;
     }
 
   cp_finish_decl (fndecl, NULL_TREE, false, NULL_TREE, 0);
diff --git i/gcc/cp/decl2.c w/gcc/cp/decl2.c
index db3035dfba5..2f0d6370146 100644
--- i/gcc/cp/decl2.c
+++ w/gcc/cp/decl2.c
@@ -1021,7 +1021,7 @@  grokfield (const cp_declarator *declarator,
 		      asmspec_tree, flags);
 
       /* Pass friends back this way.  */
-      if (DECL_FRIEND_P (value))
+      if (DECL_UNIQUE_FRIEND_P (value))
 	return void_type_node;
 
       DECL_IN_AGGR_P (value) = 1;
diff --git i/gcc/cp/friend.c w/gcc/cp/friend.c
index 6a783a9d88a..56fa96073d3 100644
--- i/gcc/cp/friend.c
+++ w/gcc/cp/friend.c
@@ -481,8 +481,8 @@  do_friend (tree ctype, tree declarator, tree decl,
   gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
   gcc_assert (!ctype || MAYBE_CLASS_TYPE_P (ctype));
 
-  /* Every decl that gets here is a friend of something.  */
-  DECL_FRIEND_P (decl) = 1;
+  /* Friend functions are unique, until proved otherwise.  */
+  DECL_UNIQUE_FRIEND_P (decl) = 1;
 
   if (DECL_OVERRIDE_P (decl) || DECL_FINAL_P (decl))
     error ("friend declaration %qD may not have virt-specifiers",
@@ -581,17 +581,11 @@  do_friend (tree ctype, tree declarator, tree decl,
 	error ("member %qD declared as friend before type %qT defined",
 		  decl, ctype);
     }
-  /* A global friend.
-     @@ or possibly a friend from a base class ?!?  */
-  else if (TREE_CODE (decl) == FUNCTION_DECL)
+  else
     {
+      /* Namespace-scope friend function.  */
       int is_friend_template = PROCESSING_REAL_TEMPLATE_DECL_P ();
 
-      /* Friends must all go through the overload machinery,
-	 even though they may not technically be overloaded.
-
-	 Note that because classes all wind up being top-level
-	 in their scope, their friend wind up in top-level scope as well.  */
       if (funcdef_flag)
 	SET_DECL_FRIEND_CONTEXT (decl, current_class_type);
 
@@ -653,7 +647,6 @@  do_friend (tree ctype, tree declarator, tree decl,
       add_friend (current_class_type,
 		  is_friend_template ? DECL_TI_TEMPLATE (decl) : decl,
 		  /*complain=*/true);
-      DECL_FRIEND_P (decl) = 1;
     }
 
   return decl;
diff --git i/gcc/cp/name-lookup.c w/gcc/cp/name-lookup.c
index 5dcaab4d1df..e951fb7885b 100644
--- i/gcc/cp/name-lookup.c
+++ w/gcc/cp/name-lookup.c
@@ -6719,8 +6719,6 @@  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)
@@ -6728,9 +6726,11 @@  lookup_elaborated_type_1 (tree name, TAG_how how)
 		    || 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);
+		  /* It is no longer a hidden binding.  */
+		  HIDDEN_TYPE_BINDING_P (iter) = false;
+
+		return type;
 	      }
 	  }
 	else
@@ -6739,32 +6739,12 @@  lookup_elaborated_type_1 (tree name, TAG_how how)
 		&& (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_FRIEND_P (found) = false;
+		if (how != TAG_how::HIDDEN_FRIEND && !iter->type)
+		  /* It is no longer a hidden binding.  */
+		  HIDDEN_TYPE_BINDING_P (iter) = false;
 
-		gcc_checking_assert (TREE_CODE (found) != TEMPLATE_DECL);
-
-		if (tree ti = TYPE_TEMPLATE_INFO (TREE_TYPE (found)))
-		  {
-		    tree tmpl = TI_TEMPLATE (ti);
-		    DECL_FRIEND_P (tmpl) = false;
-		  }
+		return iter->value;
 	      }
-
-	    return found;
 	  }
       }
 
@@ -6781,61 +6761,31 @@  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))
 	{
-	  found = type;
 	  if (how != TAG_how::HIDDEN_FRIEND)
-	    {
-	      reveal = STAT_TYPE_HIDDEN_P (*slot);
-	      STAT_TYPE_HIDDEN_P (*slot) = false;
-	    }
+	    /* No longer hidden.  */
+	    STAT_TYPE_HIDDEN_P (*slot) = false;
+
+	  return type;
 	}
       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))
+	      if (how != TAG_how::HIDDEN_FRIEND && STAT_HACK_P (*slot)
+		  && STAT_DECL_HIDDEN_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 (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_FRIEND_P (found) = false;
-
-	      if (TREE_CODE (found) == TEMPLATE_DECL)
-		{
-		  tree res = DECL_TEMPLATE_RESULT (found);
-		  if (DECL_LANG_SPECIFIC (res))
-		    DECL_FRIEND_P (res) = false;
-		}
-	      else if (tree ti = TYPE_TEMPLATE_INFO (TREE_TYPE (found)))
-		{
-		  tree tmpl = TI_TEMPLATE (ti);
-		  DECL_FRIEND_P (tmpl) = false;
-		}
+	      return decl;
 	    }
-
-	  return found;
 	}
     }
 
@@ -7017,18 +6967,8 @@  do_pushtag (tree name, tree type, TAG_how how)
 
       tdef = create_implicit_typedef (name, type);
       DECL_CONTEXT (tdef) = FROB_CONTEXT (context);
-      bool is_friend = how == TAG_how::HIDDEN_FRIEND;
-      if (is_friend)
-	{
-	  // FIXME: can go away
-	  /* This is a friend.  Make this TYPE_DECL node hidden from
-	     ordinary name lookup.  Its corresponding TEMPLATE_DECL
-	     will be marked in push_template_decl.  */
-	  retrofit_lang_decl (tdef);
-	  DECL_FRIEND_P (tdef) = 1;
-	}
-
-      decl = maybe_process_template_type_declaration (type, is_friend, b);
+      decl = maybe_process_template_type_declaration
+	(type, how == TAG_how::HIDDEN_FRIEND, b);
       if (decl == error_mark_node)
 	return decl;
 
diff --git i/gcc/cp/pt.c w/gcc/cp/pt.c
index 555dc47b464..503c0e722fa 100644
--- i/gcc/cp/pt.c
+++ w/gcc/cp/pt.c
@@ -1181,7 +1181,7 @@  optimize_specialization_lookup_p (tree tmpl)
 	     not have template information.  The optimized lookup relies
 	     on having ARGS be the template arguments for both the class
 	     and the function template.  */
-	  && !DECL_FRIEND_P (DECL_TEMPLATE_RESULT (tmpl)));
+	  && !DECL_UNIQUE_FRIEND_P (DECL_TEMPLATE_RESULT (tmpl)));
 }
 
 /* Make sure ARGS doesn't use any inappropriate typedefs; we should have
@@ -5701,7 +5701,7 @@  push_template_decl (tree decl, bool is_friend)
   /* No surprising friend functions.  */
   gcc_checking_assert (is_friend
 		       || !(TREE_CODE (decl) == FUNCTION_DECL
-			    && DECL_FRIEND_P (decl)));
+			    && DECL_UNIQUE_FRIEND_P (decl)));
 
   if (is_friend)
     /* For a friend, we want the context of the friend, not
@@ -6022,10 +6022,6 @@  push_template_decl (tree decl, bool is_friend)
       if (!ctx
 	  && !(is_friend && template_class_depth (current_class_type) > 0))
 	{
-	  /* Hide template friend classes that haven't been declared yet.  */
-	  if (is_friend && TREE_CODE (decl) == TYPE_DECL)
-	    DECL_FRIEND_P (tmpl) = 1;
-
 	  tmpl = pushdecl_namespace_level (tmpl, /*hiding=*/is_friend);
 	  if (tmpl == error_mark_node)
 	    return error_mark_node;
@@ -13960,7 +13956,7 @@  tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
     if (!lambda_fntype)
       set_constraints (r, ci);
 
-  if (DECL_FRIEND_P (t) && DECL_FRIEND_CONTEXT (t))
+  if (DECL_FRIEND_CONTEXT (t))
     SET_DECL_FRIEND_CONTEXT (r,
 			     tsubst (DECL_FRIEND_CONTEXT (t),
 				     args, complain, in_decl));
@@ -27049,7 +27045,7 @@  type_dependent_expression_p (tree expression)
       && !(DECL_CLASS_SCOPE_P (expression)
 	   && dependent_type_p (DECL_CONTEXT (expression)))
       && !(DECL_LANG_SPECIFIC (expression)
-	   && DECL_FRIEND_P (expression)
+	   && DECL_UNIQUE_FRIEND_P (expression)
 	   && (!DECL_FRIEND_CONTEXT (expression)
 	       || dependent_type_p (DECL_FRIEND_CONTEXT (expression))))
       && !DECL_LOCAL_DECL_P (expression))
diff --git i/libcc1/libcp1plugin.cc w/libcc1/libcp1plugin.cc
index e7ab325d7df..d758df8b556 100644
--- i/libcc1/libcp1plugin.cc
+++ w/libcc1/libcp1plugin.cc
@@ -1649,7 +1649,7 @@  plugin_add_friend (cc1_plugin::connection * /* self */,
     make_friend_class (type, TREE_TYPE (decl), true);
   else
     {
-      DECL_FRIEND_P (decl) = true;
+      DECL_UNIQUE_FRIEND_P (decl) = true;
       add_friend (type, decl, true);
     }