[PR,C++/59930] template friend classes & default args

Message ID f80dffe0-eedc-225c-d7a3-b690bd5c2077@acm.org
State New
Headers show
Series
  • [PR,C++/59930] template friend classes & default args
Related show

Commit Message

Nathan Sidwell Dec. 14, 2017, 7:31 p.m.
PR 59930 concerns some problems with templated friend classes (of 
templates).  In rying to clean up our handling, I discovered we were 
accepting default args of such things.  This is ill formed

[temp.param]/12 'A default template-argument shall not be specified in a 
friend class template declaration.'

This patch addresses that problem by extending check_default_tmpl_args 
to deal with such friends.

We'll still have to deal with:
template <typename T> class X {
    template <typename U, T> friend class Y;
};

which is probably just as involved.  But at least this removes one 
source of confusion.

nathan
-- 
Nathan Sidwell

Comments

Nathan Sidwell Dec. 15, 2017, 3:02 p.m. | #1
On 12/14/2017 02:31 PM, Nathan Sidwell wrote:
> PR 59930 concerns some problems with templated friend classes (of 

> templates).  In rying to clean up our handling, I discovered we were 

> accepting default args of such things.  This is ill formed

> 

> [temp.param]/12 'A default template-argument shall not be specified in a 

> friend class template declaration.'

> 

> This patch addresses that problem by extending check_default_tmpl_args 

> to deal with such friends.


I realized we don't have to check default args here in the friend 
FUNCTION_DECL case, because we do that later, when we know whether it's 
a definition, introducing decl or redeclaration.  This makes it a little 
clearer as to why we don't tell check_default_tmpl_args it's a friend 
function.

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

	PR c++/59930
	* decl.c (xref_tag_1): Correct comments about template friends and
	default args.
	* friend.c (make_friend_class): Move comments concerning
	self-friendliness to code dealing with such.
	* pt.c (check_default_tmpl_args): Deal with template friend
	classes too.
	(push_template_decl_real): Check default args for non-function
	template friends.

	PR c++/59930
	* g++.dg/cpp0x/temp_default4.C: Adjust diagnostic.
	* g++.old-deja/g++.pt/friend23.C: Likewise.
	* g++.old-deja/g++.pt/friend24.C: Delete.

Index: cp/decl.c
===================================================================
--- cp/decl.c	(revision 255691)
+++ cp/decl.c	(working copy)
@@ -13538,37 +13538,28 @@ xref_tag_1 (enum tag_types tag_code, tre
 	 processing a (member) template declaration of a template
 	 class, we must be very careful; consider:
 
-	   template <class X>
-	   struct S1
+	   template <class X> struct S1
 
-	   template <class U>
-	   struct S2
-	   { template <class V>
-	   friend struct S1; };
+	   template <class U> struct S2
+	   {
+	     template <class V> friend struct S1;
+	   };
 
 	 Here, the S2::S1 declaration should not be confused with the
 	 outer declaration.  In particular, the inner version should
-	 have a template parameter of level 2, not level 1.  This
-	 would be particularly important if the member declaration
-	 were instead:
-
-	   template <class V = U> friend struct S1;
-
-	 say, when we should tsubst into `U' when instantiating
-	 S2.  On the other hand, when presented with:
-
-	   template <class T>
-	   struct S1 {
-	     template <class U>
-	     struct S2 {};
-	     template <class U>
-	     friend struct S2;
+	 have a template parameter of level 2, not level 1.
+
+	 On the other hand, when presented with:
+
+	   template <class T> struct S1
+	   {
+	     template <class U> struct S2 {};
+	     template <class U> friend struct S2;
 	   };
 
-	 we must find the inner binding eventually.  We
-	 accomplish this by making sure that the new type we
-	 create to represent this declaration has the right
-	 TYPE_CONTEXT.  */
+	 the friend must find S1::S2 eventually.  We accomplish this
+	 by making sure that the new type we create to represent this
+	 declaration has the right TYPE_CONTEXT.  */
       context = TYPE_CONTEXT (t);
       t = NULL_TREE;
     }
@@ -13622,9 +13613,10 @@ xref_tag_1 (enum tag_types tag_code, tre
 	  return error_mark_node;
 	}
 
-      /* Make injected friend class visible.  */
       if (scope != ts_within_enclosing_non_class && TYPE_HIDDEN_P (t))
 	{
+	  /* This is no longer an invisible friend.  Make it
+	     visible.  */
 	  tree decl = TYPE_NAME (t);
 
 	  DECL_ANTICIPATED (decl) = false;
Index: cp/friend.c
===================================================================
--- cp/friend.c	(revision 255691)
+++ cp/friend.c	(working copy)
@@ -283,21 +283,18 @@ make_friend_class (tree type, tree frien
     return;
 
   if (friend_depth)
-    /* If the TYPE is a template then it makes sense for it to be
-       friends with itself; this means that each instantiation is
-       friends with all other instantiations.  */
     {
+      /* [temp.friend] Friend declarations shall not declare partial
+	 specializations.  */
       if (CLASS_TYPE_P (friend_type)
 	  && CLASSTYPE_TEMPLATE_SPECIALIZATION (friend_type)
 	  && uses_template_parms (friend_type))
 	{
-	  /* [temp.friend]
-	     Friend declarations shall not declare partial
-	     specializations.  */
 	  error ("partial specialization %qT declared %<friend%>",
 		 friend_type);
 	  return;
 	}
+
       if (TYPE_TEMPLATE_INFO (friend_type)
 	  && !PRIMARY_TEMPLATE_P (TYPE_TI_TEMPLATE (friend_type)))
 	{
@@ -311,7 +308,11 @@ make_friend_class (tree type, tree frien
 	  return;
 	}
     }
-  else if (same_type_p (type, friend_type))
+
+  /* It makes sense for a template class to be friends with itself,
+     that means the instantiations can be friendly.  Other cases are
+     not so meaningful.  */
+  if (!friend_depth && same_type_p (type, friend_type))
     {
       if (complain)
 	warning (0, "class %qT is implicitly friends with itself",
Index: cp/pt.c
===================================================================
--- cp/pt.c	(revision 255691)
+++ cp/pt.c	(working copy)
@@ -4980,9 +4980,10 @@ fixed_parameter_pack_p (tree parm)
    a primary template.  IS_PARTIAL is true if DECL is a partial
    specialization.
 
-   IS_FRIEND_DECL is nonzero if DECL is a friend function template
-   declaration (but not a definition); 1 indicates a declaration, 2
-   indicates a redeclaration. When IS_FRIEND_DECL=2, no errors are
+   IS_FRIEND_DECL is nonzero if DECL is either a non-defining friend
+   function template declaration or a friend class template
+   declaration.  In the function case, 1 indicates a declaration, 2
+   indicates a redeclaration.  When IS_FRIEND_DECL=2, no errors are
    emitted for extraneous default arguments.
 
    Returns TRUE if there were no errors found, FALSE otherwise. */
@@ -5130,7 +5131,7 @@ check_default_tmpl_args (tree decl, tree
     msg = G_("default template arguments may not be used in function template "
 	     "friend re-declaration");
   else if (is_friend_decl)
-    msg = G_("default template arguments may not be used in function template "
+    msg = G_("default template arguments may not be used in template "
 	     "friend declarations");
   else if (TREE_CODE (decl) == FUNCTION_DECL && (cxx_dialect == cxx98))
     msg = G_("default template arguments may not be used in function templates "
@@ -5277,7 +5278,7 @@ push_template_decl_real (tree decl, bool
     is_friend = true;
 
   if (is_friend)
-    /* For a friend, we want the context of the friend function, not
+    /* For a friend, we want the context of the friend, not
        the type of which it is a friend.  */
     ctx = CP_DECL_CONTEXT (decl);
   else if (CP_DECL_CONTEXT (decl)
@@ -5380,9 +5381,12 @@ push_template_decl_real (tree decl, bool
     }
 
   /* Check to see that the rules regarding the use of default
-     arguments are not being violated.  */
-  check_default_tmpl_args (decl, current_template_parms,
-			   is_primary, is_partial, /*is_friend_decl=*/0);
+     arguments are not being violated.  We check args for a friend
+     functions when we know whether it's a definition, introducing
+     declaration or re-declaration.  */
+  if (!is_friend || TREE_CODE (decl) != FUNCTION_DECL)
+    check_default_tmpl_args (decl, current_template_parms,
+			     is_primary, is_partial, is_friend);
 
   /* Ensure that there are no parameter packs in the type of this
      declaration that have not been expanded.  */
Index: testsuite/g++.dg/cpp0x/temp_default4.C
===================================================================
--- testsuite/g++.dg/cpp0x/temp_default4.C	(revision 255691)
+++ testsuite/g++.dg/cpp0x/temp_default4.C	(working copy)
@@ -1,9 +1,9 @@
 // { dg-do compile { target c++11 } }
 
 class X {
-  template<typename T = int> friend void f(X) { }
+  template<typename T = int> friend void f(X) { } // OK
   template<typename T> friend void g(X); // { dg-message "previously declared here" }
-  template<typename T = int> friend void h(X); // { dg-error "function template friend" }
+  template<typename T = int> friend void h(X); // { dg-error "template friend" }
 };
 
 template<typename T = int> void g(X) // { dg-error "default template argument" }
Index: testsuite/g++.old-deja/g++.pt/friend23.C
===================================================================
--- testsuite/g++.old-deja/g++.pt/friend23.C	(revision 255691)
+++ testsuite/g++.old-deja/g++.pt/friend23.C	(working copy)
@@ -1,10 +1,9 @@
-// { dg-do assemble  }
+// PR 59930 (part)  templated class friend declarations cannot have
+// default args.
 
-template <class T = int> // { dg-message "note: original definition" }
+template <class T>
 struct S
 { 
-  template <class U = int>
-  friend class S; // { dg-error "redefinition of default argument" }
+  template <class U = int> friend class R; // { dg-error "template friend" }
+  template <class U = int> friend class S; // { dg-error "template friend" }
 };
-
-template struct S<int>;
Index: testsuite/g++.old-deja/g++.pt/friend24.C
===================================================================
--- testsuite/g++.old-deja/g++.pt/friend24.C	(revision 255691)
+++ testsuite/g++.old-deja/g++.pt/friend24.C	(working copy)
@@ -1,18 +0,0 @@
-// { dg-do assemble  }
-
-template <class T>
-struct S
-{
-  template <class U = T>
-  friend class S;
-
-  void f(T);
-};
-
-template struct S<int>;
-
-void g()
-{
-  S<> s;
-  s.f(3);
-}

Patch

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

	PR c++/59930
	* decl.c (xref_tag_1): Correct comments about template friends and
	default args.
	* friend.c (make_friend_class): Move comments concerning
	self-friendliness to code dealing with such.
	* pt.c (check_default_tmpl_args): Deal with template friend
	classes too.
	(push_template_decl_real): Adjust for template friend classes.

	PR c++/59930
	* g++.dg/cpp0x/temp_default4.C: Adjust diagnostic.
	* g++.old-deja/g++.pt/friend23.C: Likewise.
	* g++.old-deja/g++.pt/friend24.C: Delete.

Index: cp/decl.c
===================================================================
--- cp/decl.c	(revision 255634)
+++ cp/decl.c	(working copy)
@@ -13538,37 +13538,28 @@  xref_tag_1 (enum tag_types tag_code, tre
 	 processing a (member) template declaration of a template
 	 class, we must be very careful; consider:
 
-	   template <class X>
-	   struct S1
+	   template <class X> struct S1
 
-	   template <class U>
-	   struct S2
-	   { template <class V>
-	   friend struct S1; };
+	   template <class U> struct S2
+	   {
+	     template <class V> friend struct S1;
+	   };
 
 	 Here, the S2::S1 declaration should not be confused with the
 	 outer declaration.  In particular, the inner version should
-	 have a template parameter of level 2, not level 1.  This
-	 would be particularly important if the member declaration
-	 were instead:
-
-	   template <class V = U> friend struct S1;
-
-	 say, when we should tsubst into `U' when instantiating
-	 S2.  On the other hand, when presented with:
-
-	   template <class T>
-	   struct S1 {
-	     template <class U>
-	     struct S2 {};
-	     template <class U>
-	     friend struct S2;
+	 have a template parameter of level 2, not level 1.
+
+	 On the other hand, when presented with:
+
+	   template <class T> struct S1
+	   {
+	     template <class U> struct S2 {};
+	     template <class U> friend struct S2;
 	   };
 
-	 we must find the inner binding eventually.  We
-	 accomplish this by making sure that the new type we
-	 create to represent this declaration has the right
-	 TYPE_CONTEXT.  */
+	 the friend must find S1::S2 eventually.  We accomplish this
+	 by making sure that the new type we create to represent this
+	 declaration has the right TYPE_CONTEXT.  */
       context = TYPE_CONTEXT (t);
       t = NULL_TREE;
     }
@@ -13622,9 +13613,10 @@  xref_tag_1 (enum tag_types tag_code, tre
 	  return error_mark_node;
 	}
 
-      /* Make injected friend class visible.  */
       if (scope != ts_within_enclosing_non_class && TYPE_HIDDEN_P (t))
 	{
+	  /* This is no longer an invisible friend.  Make it
+	     visible.  */
 	  tree decl = TYPE_NAME (t);
 
 	  DECL_ANTICIPATED (decl) = false;
Index: cp/friend.c
===================================================================
--- cp/friend.c	(revision 255634)
+++ cp/friend.c	(working copy)
@@ -283,21 +283,18 @@  make_friend_class (tree type, tree frien
     return;
 
   if (friend_depth)
-    /* If the TYPE is a template then it makes sense for it to be
-       friends with itself; this means that each instantiation is
-       friends with all other instantiations.  */
     {
+      /* [temp.friend] Friend declarations shall not declare partial
+	 specializations.  */
       if (CLASS_TYPE_P (friend_type)
 	  && CLASSTYPE_TEMPLATE_SPECIALIZATION (friend_type)
 	  && uses_template_parms (friend_type))
 	{
-	  /* [temp.friend]
-	     Friend declarations shall not declare partial
-	     specializations.  */
 	  error ("partial specialization %qT declared %<friend%>",
 		 friend_type);
 	  return;
 	}
+
       if (TYPE_TEMPLATE_INFO (friend_type)
 	  && !PRIMARY_TEMPLATE_P (TYPE_TI_TEMPLATE (friend_type)))
 	{
@@ -311,7 +308,11 @@  make_friend_class (tree type, tree frien
 	  return;
 	}
     }
-  else if (same_type_p (type, friend_type))
+
+  /* It makes sense for a template class to be friends with itself,
+     that means the instantiations can be friendly.  Other cases are
+     not so meaningful.  */
+  if (!friend_depth && same_type_p (type, friend_type))
     {
       if (complain)
 	warning (0, "class %qT is implicitly friends with itself",
Index: cp/pt.c
===================================================================
--- cp/pt.c	(revision 255634)
+++ cp/pt.c	(working copy)
@@ -4980,9 +4980,10 @@  fixed_parameter_pack_p (tree parm)
    a primary template.  IS_PARTIAL is true if DECL is a partial
    specialization.
 
-   IS_FRIEND_DECL is nonzero if DECL is a friend function template
-   declaration (but not a definition); 1 indicates a declaration, 2
-   indicates a redeclaration. When IS_FRIEND_DECL=2, no errors are
+   IS_FRIEND_DECL is nonzero if DECL is either a non-defining friend
+   function template declaration or a friend class template
+   declaration.  In the function case, 1 indicates a declaration, 2
+   indicates a redeclaration.  When IS_FRIEND_DECL=2, no errors are
    emitted for extraneous default arguments.
 
    Returns TRUE if there were no errors found, FALSE otherwise. */
@@ -5130,7 +5131,7 @@  check_default_tmpl_args (tree decl, tree
     msg = G_("default template arguments may not be used in function template "
 	     "friend re-declaration");
   else if (is_friend_decl)
-    msg = G_("default template arguments may not be used in function template "
+    msg = G_("default template arguments may not be used in template "
 	     "friend declarations");
   else if (TREE_CODE (decl) == FUNCTION_DECL && (cxx_dialect == cxx98))
     msg = G_("default template arguments may not be used in function templates "
@@ -5277,7 +5278,7 @@  push_template_decl_real (tree decl, bool
     is_friend = true;
 
   if (is_friend)
-    /* For a friend, we want the context of the friend function, not
+    /* For a friend, we want the context of the friend, not
        the type of which it is a friend.  */
     ctx = CP_DECL_CONTEXT (decl);
   else if (CP_DECL_CONTEXT (decl)
@@ -5380,9 +5381,12 @@  push_template_decl_real (tree decl, bool
     }
 
   /* Check to see that the rules regarding the use of default
-     arguments are not being violated.  */
+     arguments are not being violated.  We recheck args for a
+     friend function when we know whether it's a definition,
+     introducing declaration or re-declaration.  */
   check_default_tmpl_args (decl, current_template_parms,
-			   is_primary, is_partial, /*is_friend_decl=*/0);
+			   is_primary, is_partial,
+			   is_friend && MAYBE_CLASS_TYPE_P (TREE_TYPE (decl)));
 
   /* Ensure that there are no parameter packs in the type of this
      declaration that have not been expanded.  */
Index: testsuite/g++.dg/cpp0x/temp_default4.C
===================================================================
--- testsuite/g++.dg/cpp0x/temp_default4.C	(revision 255634)
+++ testsuite/g++.dg/cpp0x/temp_default4.C	(working copy)
@@ -1,9 +1,9 @@ 
 // { dg-do compile { target c++11 } }
 
 class X {
-  template<typename T = int> friend void f(X) { }
+  template<typename T = int> friend void f(X) { } // OK
   template<typename T> friend void g(X); // { dg-message "previously declared here" }
-  template<typename T = int> friend void h(X); // { dg-error "function template friend" }
+  template<typename T = int> friend void h(X); // { dg-error "template friend" }
 };
 
 template<typename T = int> void g(X) // { dg-error "default template argument" }
Index: testsuite/g++.old-deja/g++.pt/friend23.C
===================================================================
--- testsuite/g++.old-deja/g++.pt/friend23.C	(revision 255634)
+++ testsuite/g++.old-deja/g++.pt/friend23.C	(working copy)
@@ -1,10 +1,9 @@ 
-// { dg-do assemble  }
+// PR 59930 (part)  templated class friend declarations cannot have
+// default args.
 
-template <class T = int> // { dg-message "note: original definition" }
+template <class T>
 struct S
 { 
-  template <class U = int>
-  friend class S; // { dg-error "redefinition of default argument" }
+  template <class U = int> friend class R; // { dg-error "template friend" }
+  template <class U = int> friend class S; // { dg-error "template friend" }
 };
-
-template struct S<int>;
Index: testsuite/g++.old-deja/g++.pt/friend24.C
===================================================================
--- testsuite/g++.old-deja/g++.pt/friend24.C	(revision 255634)
+++ testsuite/g++.old-deja/g++.pt/friend24.C	(working copy)
@@ -1,18 +0,0 @@ 
-// { dg-do assemble  }
-
-template <class T>
-struct S
-{
-  template <class U = T>
-  friend class S;
-
-  void f(T);
-};
-
-template struct S<int>;
-
-void g()
-{
-  S<> s;
-  s.f(3);
-}