[PR,c++/59930] template friend injection

Message ID d31285be-0d67-ab8f-8bf0-147b864a9b56@acm.org
State New
Headers show
Series
  • [PR,c++/59930] template friend injection
Related show

Commit Message

Nathan Sidwell Dec. 18, 2017, 2:56 p.m.
this patch fixes the handling of template friend classes of template 
classes.  Our current handling was somewhat messed up, possibly because 
of now-clarified ambiguities in earlier versions of the std.  In 
particular we'd search too many namespaces looking for an existing 
declaration, and if not found inject into the global namespace as a 
non-hidden name.

This patch:
a) removes some now-unneeded handling for self friendliness
b) looks only in the innermost containing namespace for an unqualified 
friend [*]
c) injects into that namespace as a hidden name.

[*] fixing this may permit removal of some checks in other friend cases 
too.  I have not investigated.

applying to trunk

nathan
-- 
Nathan Sidwell

Patch

2017-12-18  Nathan Sidwell  <nathan@acm.org>

	PR c++/59930
	* name-lookup.c (name_lookup::search_unqualified): Don't search
	parent namespace when looking for hidden things.
	* pt.c (tsubst_friend_class): Always push to friend scope, drop
	unneeded self-friend check. Inject new hidden friend into correct
	scope.

	PR c++/59930
	* g++.dg/parse/pr81247-c.C: Adjust.
	* g++.dg/template/pr59930-[123].C: New.

Index: cp/name-lookup.c
===================================================================
--- cp/name-lookup.c	(revision 255777)
+++ cp/name-lookup.c	(working copy)
@@ -711,6 +711,15 @@  name_lookup::search_unqualified (tree sc
     done:;
       if (scope == global_namespace)
 	break;
+
+      /* If looking for hidden names, we only look in the innermost
+	 namespace scope.  [namespace.memdef]/3 If a friend
+	 declaration in a non-local class first declares a class,
+	 function, class template or function template the friend is a
+	 member of the innermost enclosing namespace.  See also
+	 [basic.lookup.unqual]/7 */
+      if (flags & LOOKUP_HIDDEN)
+	break;
     }
 
   vec_safe_truncate (queue, length);
Index: cp/pt.c
===================================================================
--- cp/pt.c	(revision 255777)
+++ cp/pt.c	(working copy)
@@ -10005,57 +10005,23 @@  tsubst_friend_function (tree decl, tree
 static tree
 tsubst_friend_class (tree friend_tmpl, tree args)
 {
-  tree friend_type;
   tree tmpl;
-  tree context;
 
   if (DECL_TEMPLATE_TEMPLATE_PARM_P (friend_tmpl))
     {
-      tree t = tsubst (TREE_TYPE (friend_tmpl), args, tf_none, NULL_TREE);
-      return TREE_TYPE (t);
+      tmpl = tsubst (TREE_TYPE (friend_tmpl), args, tf_none, NULL_TREE);
+      return TREE_TYPE (tmpl);
     }
 
-  context = CP_DECL_CONTEXT (friend_tmpl);
+  tree context = CP_DECL_CONTEXT (friend_tmpl);
+  if (TREE_CODE (context) == NAMESPACE_DECL)
+    push_nested_namespace (context);
+  else
+    push_nested_class (context);
 
-  if (context != global_namespace)
-    {
-      if (TREE_CODE (context) == NAMESPACE_DECL)
-	push_nested_namespace (context);
-      else
-	push_nested_class (tsubst (context, args, tf_none, NULL_TREE));
-    }
-
-  /* Look for a class template declaration.  We look for hidden names
-     because two friend declarations of the same template are the
-     same.  For example, in:
-
-       struct A { 
-         template <typename> friend class F;
-       };
-       template <typename> struct B { 
-         template <typename> friend class F;
-       };
-
-     both F templates are the same.  */
-  tmpl = lookup_name_real (DECL_NAME (friend_tmpl), 0, 0,
-			   /*block_p=*/true, 0, LOOKUP_HIDDEN);
-
-  /* But, if we don't find one, it might be because we're in a
-     situation like this:
-
-       template <class T>
-       struct S {
-	 template <class U>
-	 friend struct S;
-       };
-
-     Here, in the scope of (say) S<int>, `S' is bound to a TYPE_DECL
-     for `S<int>', not the TEMPLATE_DECL.  */
-  if (!tmpl || !DECL_CLASS_TEMPLATE_P (tmpl))
-    {
-      tmpl = lookup_name_prefer_type (DECL_NAME (friend_tmpl), 1);
-      tmpl = maybe_get_template_decl_from_type_decl (tmpl);
-    }
+  tmpl = lookup_name_real (DECL_NAME (friend_tmpl), /*prefer_type=*/false,
+			   /*non_class=*/false, /*block_p=*/false,
+			   /*namespaces_only=*/false, LOOKUP_HIDDEN);
 
   if (tmpl && DECL_CLASS_TEMPLATE_P (tmpl))
     {
@@ -10068,53 +10034,50 @@  tsubst_friend_class (tree friend_tmpl, t
       if (TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (friend_tmpl))
 	  > TMPL_ARGS_DEPTH (args))
 	{
-	  tree parms;
-          location_t saved_input_location;
-	  parms = tsubst_template_parms (DECL_TEMPLATE_PARMS (friend_tmpl),
-					 args, tf_warning_or_error);
-
-          saved_input_location = input_location;
+	  tree parms = tsubst_template_parms (DECL_TEMPLATE_PARMS (friend_tmpl),
+					      args, tf_warning_or_error);
+          location_t saved_input_location = input_location;
           input_location = DECL_SOURCE_LOCATION (friend_tmpl);
           tree cons = get_constraints (tmpl);
           redeclare_class_template (TREE_TYPE (tmpl), parms, cons);
           input_location = saved_input_location;
-          
 	}
-
-      friend_type = TREE_TYPE (tmpl);
     }
   else
     {
       /* The friend template has not already been declared.  In this
 	 case, the instantiation of the template class will cause the
-	 injection of this template into the global scope.  */
+	 injection of this template into the namespace scope.  */
       tmpl = tsubst (friend_tmpl, args, tf_warning_or_error, NULL_TREE);
-      if (tmpl == error_mark_node)
-	return error_mark_node;
 
-      /* The new TMPL is not an instantiation of anything, so we
-	 forget its origins.  We don't reset CLASSTYPE_TI_TEMPLATE for
-	 the new type because that is supposed to be the corresponding
-	 template decl, i.e., TMPL.  */
-      DECL_USE_TEMPLATE (tmpl) = 0;
-      DECL_TEMPLATE_INFO (tmpl) = NULL_TREE;
-      CLASSTYPE_USE_TEMPLATE (TREE_TYPE (tmpl)) = 0;
-      CLASSTYPE_TI_ARGS (TREE_TYPE (tmpl))
-	= INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (TREE_TYPE (tmpl)));
+      if (tmpl != error_mark_node)
+	{
+	  /* The new TMPL is not an instantiation of anything, so we
+	     forget its origins.  We don't reset CLASSTYPE_TI_TEMPLATE
+	     for the new type because that is supposed to be the
+	     corresponding template decl, i.e., TMPL.  */
+	  DECL_USE_TEMPLATE (tmpl) = 0;
+	  DECL_TEMPLATE_INFO (tmpl) = NULL_TREE;
+	  CLASSTYPE_USE_TEMPLATE (TREE_TYPE (tmpl)) = 0;
+	  CLASSTYPE_TI_ARGS (TREE_TYPE (tmpl))
+	    = INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (TREE_TYPE (tmpl)));
+
+	  /* It is hidden.  */
+	  retrofit_lang_decl (DECL_TEMPLATE_RESULT (tmpl));
+	  DECL_ANTICIPATED (tmpl)
+	    = DECL_ANTICIPATED (DECL_TEMPLATE_RESULT (tmpl)) = true;
 
-      /* Inject this template into the global scope.  */
-      friend_type = TREE_TYPE (pushdecl_top_level (tmpl, true));
+	  /* Inject this template into the enclosing namspace scope.  */
+	  tmpl = pushdecl_namespace_level (tmpl, true);
+	}
     }
 
-  if (context != global_namespace)
-    {
-      if (TREE_CODE (context) == NAMESPACE_DECL)
-	pop_nested_namespace (context);
-      else
-	pop_nested_class ();
-    }
+  if (TREE_CODE (context) == NAMESPACE_DECL)
+    pop_nested_namespace (context);
+  else
+    pop_nested_class ();
 
-  return friend_type;
+  return TREE_TYPE (tmpl);
 }
 
 /* Returns zero if TYPE cannot be completed later due to circularity.
Index: testsuite/g++.dg/parse/pr81247-c.C
===================================================================
--- testsuite/g++.dg/parse/pr81247-c.C	(revision 255777)
+++ testsuite/g++.dg/parse/pr81247-c.C	(working copy)
@@ -1,8 +1,9 @@ 
-// PR c++/81247 confused error
+// PR c++/81247 rejected well-formed
 
-namespace N { // { dg-message "previous declaration" }
+namespace N {
   template < typename T > class A
-  { // { dg-error "conflicts with a previous" }
+  {
+    // injects a hidden class N::N at instantiation time
     template < T > friend class N;
   };
 }
Index: testsuite/g++.dg/template/pr59930-1.C
===================================================================
--- testsuite/g++.dg/template/pr59930-1.C	(revision 0)
+++ testsuite/g++.dg/template/pr59930-1.C	(working copy)
@@ -0,0 +1,18 @@ 
+// PR c++/59930
+
+namespace N {
+  template<typename T> class A {
+    // The injected name is N::B, because we don;t look outside of N
+    template<typename U> friend struct B;
+  private:
+    int n; // { dg-message "declared private here" }
+  public:
+    A (int);
+  };
+}
+
+template<typename T> struct B {
+  int f(N::A<int> ai) { return ai.n; } // { dg-error "is private" }
+};
+
+int k = B<int>().f(0);
Index: testsuite/g++.dg/template/pr59930-2.C
===================================================================
--- testsuite/g++.dg/template/pr59930-2.C	(revision 0)
+++ testsuite/g++.dg/template/pr59930-2.C	(working copy)
@@ -0,0 +1,17 @@ 
+// PR c++/59930
+
+namespace N {
+  template < typename T > class A
+  {
+    // Injects N::N
+    template < T > friend class N;
+    // { dg-error "template parameter" "" { target *-*-* } .-1 }
+    // { dg-error "redeclared"  "" { target *-*-* } .-2 }
+  };
+}
+
+void f ()
+{
+  N::A < int > a1;
+  N::A <short > a2;
+}
Index: testsuite/g++.dg/template/pr59930-3.C
===================================================================
--- testsuite/g++.dg/template/pr59930-3.C	(revision 0)
+++ testsuite/g++.dg/template/pr59930-3.C	(working copy)
@@ -0,0 +1,29 @@ 
+// PR c++/59930
+
+namespace NS {
+  template<typename T> class Holder
+  {
+  private:
+    void func();
+
+    template<typename> friend class User;
+  };
+
+  template class Holder<long>;
+
+  template<typename T> class User
+  {
+  public:
+    void method() const
+    {
+      Holder<T> x;
+      x.func();
+    }
+  };
+} // namespace
+
+void Foo()
+{
+  NS::User<long> decl;
+  decl.method();
+}