diff mbox series

c++: constrained class template friend [PR93467]

Message ID 20200609181841.1736938-1-ppalka@redhat.com
State New
Headers show
Series c++: constrained class template friend [PR93467] | expand

Commit Message

Patrick Palka June 9, 2020, 6:18 p.m. UTC
This fixes two issues in our handling of constrained class template
friend declarations.

The first issue is that we fail to set the constraints on the injected
class template declaration during tsubst_friend_class.

The second issue is that the template parameter levels within the parsed
constraints of a class template friend declaration are shifted if the
enclosing class is a template, and this shift leads to spurious
constraint mismatch errors in associate_classtype_constraints if the
friend declaration refers to an already declared class template.

Passes 'make check-c++', and also verified by building the testsuite of
cmcstl2.  Does this look OK to commit to master and to the 10.2 branch
after a full bootstrap and regtest?

gcc/cp/ChangeLog:

	PR c++/93467
	* constraint.cc (associate_classtype_constraints): If there is a
	discrepancy between the current template depth and the template
	depth of the original declaration, then adjust the template
	parameter depth within the current constraints appropriately.
	* pt.c (tsubst_friend_class): Substitute into and set the
	constraints on injected declaration.

gcc/testsuite/ChangeLog:

	PR c++/93467
	* g++.dg/cpp2a/concepts-friend6.C: New test.
	* g++.dg/cpp2a/concepts-friend7.C: New test.
---
 gcc/cp/constraint.cc                          | 13 +++++++++++++
 gcc/cp/pt.c                                   | 10 ++++++++++
 gcc/testsuite/g++.dg/cpp2a/concepts-friend6.C | 19 +++++++++++++++++++
 gcc/testsuite/g++.dg/cpp2a/concepts-friend7.C | 19 +++++++++++++++++++
 4 files changed, 61 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-friend6.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-friend7.C

Comments

Jason Merrill June 11, 2020, 7:47 p.m. UTC | #1
On 6/9/20 2:18 PM, Patrick Palka wrote:
> This fixes two issues in our handling of constrained class template
> friend declarations.
> 
> The first issue is that we fail to set the constraints on the injected
> class template declaration during tsubst_friend_class.
> 
> The second issue is that the template parameter levels within the parsed
> constraints of a class template friend declaration are shifted if the
> enclosing class is a template, and this shift leads to spurious
> constraint mismatch errors in associate_classtype_constraints if the
> friend declaration refers to an already declared class template.
> 
> Passes 'make check-c++', and also verified by building the testsuite of
> cmcstl2.  Does this look OK to commit to master and to the 10.2 branch
> after a full bootstrap and regtest?

OK.

> gcc/cp/ChangeLog:
> 
> 	PR c++/93467
> 	* constraint.cc (associate_classtype_constraints): If there is a
> 	discrepancy between the current template depth and the template
> 	depth of the original declaration, then adjust the template
> 	parameter depth within the current constraints appropriately.
> 	* pt.c (tsubst_friend_class): Substitute into and set the
> 	constraints on injected declaration.
> 
> gcc/testsuite/ChangeLog:
> 
> 	PR c++/93467
> 	* g++.dg/cpp2a/concepts-friend6.C: New test.
> 	* g++.dg/cpp2a/concepts-friend7.C: New test.
> ---
>   gcc/cp/constraint.cc                          | 13 +++++++++++++
>   gcc/cp/pt.c                                   | 10 ++++++++++
>   gcc/testsuite/g++.dg/cpp2a/concepts-friend6.C | 19 +++++++++++++++++++
>   gcc/testsuite/g++.dg/cpp2a/concepts-friend7.C | 19 +++++++++++++++++++
>   4 files changed, 61 insertions(+)
>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-friend6.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-friend7.C
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index 92ff283013e..d0da2300ba9 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -1075,6 +1075,19 @@ associate_classtype_constraints (tree type)
>   	 original declaration.  */
>         if (tree orig_ci = get_constraints (decl))
>           {
> +	  if (int extra_levels = (TMPL_PARMS_DEPTH (current_template_parms)
> +				  - TMPL_ARGS_DEPTH (TYPE_TI_ARGS (type))))
> +	    {
> +	      /* If there is a discrepancy between the current template depth
> +		 and the template depth of the original declaration, then we
> +		 must be redeclaring a class template as part of a friend
> +		 declaration within another class template.  Before matching
> +		 constraints, we need to reduce the template parameter level
> +		 within the current constraints via substitution.  */
> +	      tree outer_gtargs = template_parms_to_args (current_template_parms);
> +	      TREE_VEC_LENGTH (outer_gtargs) = extra_levels;
> +	      ci = tsubst_constraint_info (ci, outer_gtargs, tf_none, NULL_TREE);
> +	    }
>             if (!equivalent_constraints (ci, orig_ci))
>               {
>   	      error ("%qT does not match original declaration", type);
> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> index 142392224c6..7fcb3e9f1c5 100644
> --- a/gcc/cp/pt.c
> +++ b/gcc/cp/pt.c
> @@ -11211,6 +11211,16 @@ tsubst_friend_class (tree friend_tmpl, tree args)
>   	  DECL_ANTICIPATED (tmpl)
>   	    = DECL_ANTICIPATED (DECL_TEMPLATE_RESULT (tmpl)) = true;
>   
> +	  /* Substitute into and set the constraints on the new declaration.  */
> +	  if (tree ci = get_constraints (friend_tmpl))
> +	    {
> +	      ++processing_template_decl;
> +	      ci = tsubst_constraint_info (ci, args, tf_warning_or_error,
> +					   DECL_FRIEND_CONTEXT (friend_tmpl));
> +	      --processing_template_decl;
> +	      set_constraints (tmpl, ci);
> +	    }
> +
>   	  /* Inject this template into the enclosing namspace scope.  */
>   	  tmpl = pushdecl_namespace_level (tmpl, true);
>   	}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend6.C b/gcc/testsuite/g++.dg/cpp2a/concepts-friend6.C
> new file mode 100644
> index 00000000000..11e8313f0ac
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend6.C
> @@ -0,0 +1,19 @@
> +// PR c++/93467
> +// { dg-do compile { target c++20 } }
> +
> +template<bool B> requires B
> +  class C;
> +
> +template<typename>
> +class S1
> +{
> +  template<bool B> requires B
> +    friend class ::C;
> +};
> +
> +template<typename>
> +class S2
> +{
> +  template<bool B> requires (!B)
> +    friend class ::C; // { dg-error "does not match original declaration" }
> +};
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend7.C b/gcc/testsuite/g++.dg/cpp2a/concepts-friend7.C
> new file mode 100644
> index 00000000000..1c6893584b1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend7.C
> @@ -0,0 +1,19 @@
> +// PR c++/93467
> +// { dg-do compile { target c++20 } }
> +
> +template<typename T> concept True = true;
> +
> +template<typename U>
> +struct S1 {
> +    template<True T> friend struct S2; // friend declaration for S2
> +};
> +
> +S1<int> s; // instantiate S1
> +
> +template<True T> struct S2; // another declaration for S2
> +
> +template<typename U>
> +struct S3 {
> +    template<True T> friend struct ::S2; // a third declaration for S2
> +};
> +
>
diff mbox series

Patch

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 92ff283013e..d0da2300ba9 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -1075,6 +1075,19 @@  associate_classtype_constraints (tree type)
 	 original declaration.  */
       if (tree orig_ci = get_constraints (decl))
         {
+	  if (int extra_levels = (TMPL_PARMS_DEPTH (current_template_parms)
+				  - TMPL_ARGS_DEPTH (TYPE_TI_ARGS (type))))
+	    {
+	      /* If there is a discrepancy between the current template depth
+		 and the template depth of the original declaration, then we
+		 must be redeclaring a class template as part of a friend
+		 declaration within another class template.  Before matching
+		 constraints, we need to reduce the template parameter level
+		 within the current constraints via substitution.  */
+	      tree outer_gtargs = template_parms_to_args (current_template_parms);
+	      TREE_VEC_LENGTH (outer_gtargs) = extra_levels;
+	      ci = tsubst_constraint_info (ci, outer_gtargs, tf_none, NULL_TREE);
+	    }
           if (!equivalent_constraints (ci, orig_ci))
             {
 	      error ("%qT does not match original declaration", type);
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 142392224c6..7fcb3e9f1c5 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -11211,6 +11211,16 @@  tsubst_friend_class (tree friend_tmpl, tree args)
 	  DECL_ANTICIPATED (tmpl)
 	    = DECL_ANTICIPATED (DECL_TEMPLATE_RESULT (tmpl)) = true;
 
+	  /* Substitute into and set the constraints on the new declaration.  */
+	  if (tree ci = get_constraints (friend_tmpl))
+	    {
+	      ++processing_template_decl;
+	      ci = tsubst_constraint_info (ci, args, tf_warning_or_error,
+					   DECL_FRIEND_CONTEXT (friend_tmpl));
+	      --processing_template_decl;
+	      set_constraints (tmpl, ci);
+	    }
+
 	  /* Inject this template into the enclosing namspace scope.  */
 	  tmpl = pushdecl_namespace_level (tmpl, true);
 	}
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend6.C b/gcc/testsuite/g++.dg/cpp2a/concepts-friend6.C
new file mode 100644
index 00000000000..11e8313f0ac
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend6.C
@@ -0,0 +1,19 @@ 
+// PR c++/93467
+// { dg-do compile { target c++20 } }
+
+template<bool B> requires B
+  class C;
+
+template<typename>
+class S1
+{
+  template<bool B> requires B
+    friend class ::C;
+};
+
+template<typename>
+class S2
+{
+  template<bool B> requires (!B)
+    friend class ::C; // { dg-error "does not match original declaration" }
+};
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend7.C b/gcc/testsuite/g++.dg/cpp2a/concepts-friend7.C
new file mode 100644
index 00000000000..1c6893584b1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend7.C
@@ -0,0 +1,19 @@ 
+// PR c++/93467
+// { dg-do compile { target c++20 } }
+
+template<typename T> concept True = true;
+
+template<typename U>
+struct S1 {
+    template<True T> friend struct S2; // friend declaration for S2
+};
+
+S1<int> s; // instantiate S1
+
+template<True T> struct S2; // another declaration for S2
+
+template<typename U>
+struct S3 {
+    template<True T> friend struct ::S2; // a third declaration for S2
+};
+