diff mbox series

c++: constrained nested partial specialization [PR92103]

Message ID 20200602194346.701957-1-ppalka@redhat.com
State New
Headers show
Series c++: constrained nested partial specialization [PR92103] | expand

Commit Message

Patrick Palka June 2, 2020, 7:43 p.m. UTC
When determining the most specialized partial specialization of a
primary template that is nested inside a class template, we first
tsubst the outer template arguments into the TEMPLATE_DECL of each
partial specialization, and then check for satisfaction of the new
TEMPLATE_DECL's constraints.

But tsubst_template_decl does not currently guarantee that constraints
from the original DECL_TEMPLATE_RESULT get reattached to the new
DECL_TEMPLATE_RESULT.  In the testcase below, this leads to the
constraints_satisfied_p check in most_specialized_partial_spec to
trivially return true for each of the partial specializations.

I'm not sure if such a guarantee would be desirable, but in any case we
can just check constraints_satisfied_p on the original TEMPLATE_DECL
instead of on the tsubsted TEMPLATE_DECL here, which is what this patch
does (alongside some reorganizing).

Passes 'make check-c++' and also tested by building the testsuites of
cmcstl2 and range-v3.  Does this look OK to commit to mainline and to
the 10 branch after a full bootstrap and regtest?

gcc/cp/ChangeLog:

	PR c++/92103
	* pt.c (most_specialized_partial_spec): Reorganize the loop over
	DECL_TEMPLATE_SPECIALIZATIONS.  Check constraints_satisfied_p on
	the original template declaration, not on the substituted one.

gcc/testsuite/ChangeLog:

	PR c++/92103
	* g++.dg/cpp2a/concepts-partial-spec7.C: New test.
---
 gcc/cp/pt.c                                   | 19 ++++++++--------
 .../g++.dg/cpp2a/concepts-partial-spec7.C     | 22 +++++++++++++++++++
 2 files changed, 32 insertions(+), 9 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec7.C

Comments

Jason Merrill June 3, 2020, 7:14 p.m. UTC | #1
On 6/2/20 3:43 PM, Patrick Palka wrote:
> When determining the most specialized partial specialization of a
> primary template that is nested inside a class template, we first
> tsubst the outer template arguments into the TEMPLATE_DECL of each
> partial specialization, and then check for satisfaction of the new
> TEMPLATE_DECL's constraints.
> 
> But tsubst_template_decl does not currently guarantee that constraints
> from the original DECL_TEMPLATE_RESULT get reattached to the new
> DECL_TEMPLATE_RESULT.  In the testcase below, this leads to the
> constraints_satisfied_p check in most_specialized_partial_spec to
> trivially return true for each of the partial specializations.
> 
> I'm not sure if such a guarantee would be desirable, but in any case we
> can just check constraints_satisfied_p on the original TEMPLATE_DECL
> instead of on the tsubsted TEMPLATE_DECL here, which is what this patch
> does (alongside some reorganizing).
> 
> Passes 'make check-c++' and also tested by building the testsuites of
> cmcstl2 and range-v3.  Does this look OK to commit to mainline and to
> the 10 branch after a full bootstrap and regtest?

OK, thanks.

> gcc/cp/ChangeLog:
> 
> 	PR c++/92103
> 	* pt.c (most_specialized_partial_spec): Reorganize the loop over
> 	DECL_TEMPLATE_SPECIALIZATIONS.  Check constraints_satisfied_p on
> 	the original template declaration, not on the substituted one.
> 
> gcc/testsuite/ChangeLog:
> 
> 	PR c++/92103
> 	* g++.dg/cpp2a/concepts-partial-spec7.C: New test.
> ---
>   gcc/cp/pt.c                                   | 19 ++++++++--------
>   .../g++.dg/cpp2a/concepts-partial-spec7.C     | 22 +++++++++++++++++++
>   2 files changed, 32 insertions(+), 9 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec7.C
> 
> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> index 907ca879c73..df92f5584cf 100644
> --- a/gcc/cp/pt.c
> +++ b/gcc/cp/pt.c
> @@ -24482,21 +24482,22 @@ most_specialized_partial_spec (tree target, tsubst_flags_t complain)
>   
>     for (t = DECL_TEMPLATE_SPECIALIZATIONS (main_tmpl); t; t = TREE_CHAIN (t))
>       {
> -      tree spec_args;
> -      tree spec_tmpl = TREE_VALUE (t);
> +      const tree ospec_tmpl = TREE_VALUE (t);
>   
> +      tree spec_tmpl;
>         if (outer_args)
>   	{
>   	  /* Substitute in the template args from the enclosing class.  */
>   	  ++processing_template_decl;
> -	  spec_tmpl = tsubst (spec_tmpl, outer_args, tf_none, NULL_TREE);
> +	  spec_tmpl = tsubst (ospec_tmpl, outer_args, tf_none, NULL_TREE);
>   	  --processing_template_decl;
> +	  if (spec_tmpl == error_mark_node)
> +	    return error_mark_node;
>   	}
> +      else
> +	spec_tmpl = ospec_tmpl;
>   
> -      if (spec_tmpl == error_mark_node)
> -	return error_mark_node;
> -
> -      spec_args = get_partial_spec_bindings (tmpl, spec_tmpl, args);
> +      tree spec_args = get_partial_spec_bindings (tmpl, spec_tmpl, args);
>         if (spec_args)
>   	{
>   	  if (outer_args)
> @@ -24505,9 +24506,9 @@ most_specialized_partial_spec (tree target, tsubst_flags_t complain)
>             /* Keep the candidate only if the constraints are satisfied,
>                or if we're not compiling with concepts.  */
>             if (!flag_concepts
> -              || constraints_satisfied_p (spec_tmpl, spec_args))
> +	      || constraints_satisfied_p (ospec_tmpl, spec_args))
>               {
> -              list = tree_cons (spec_args, TREE_VALUE (t), list);
> +	      list = tree_cons (spec_args, ospec_tmpl, list);
>                 TREE_TYPE (list) = TREE_TYPE (t);
>               }
>   	}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec7.C b/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec7.C
> new file mode 100644
> index 00000000000..5b3afce3bc7
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec7.C
> @@ -0,0 +1,22 @@
> +// PR c++/92103
> +// { dg-do compile { target c++20 } }
> +
> +template<int M>
> +struct traits
> +{
> +  template<int N>
> +    struct equal_to
> +    { static constexpr bool value = false; };
> +
> +  template<int N> requires (M == N)
> +    struct equal_to<N>
> +    { static constexpr bool value = true; };
> +
> +  template<int N> requires (M < 0) || (N < 0)
> +    struct equal_to<N>
> +    { };
> +};
> +
> +static_assert(traits<0>::equal_to<0>::value);
> +static_assert(!traits<0>::equal_to<1>::value);
> +static_assert(traits<-1>::equal_to<0>::value); // { dg-error "not a member" }
>
diff mbox series

Patch

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 907ca879c73..df92f5584cf 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -24482,21 +24482,22 @@  most_specialized_partial_spec (tree target, tsubst_flags_t complain)
 
   for (t = DECL_TEMPLATE_SPECIALIZATIONS (main_tmpl); t; t = TREE_CHAIN (t))
     {
-      tree spec_args;
-      tree spec_tmpl = TREE_VALUE (t);
+      const tree ospec_tmpl = TREE_VALUE (t);
 
+      tree spec_tmpl;
       if (outer_args)
 	{
 	  /* Substitute in the template args from the enclosing class.  */
 	  ++processing_template_decl;
-	  spec_tmpl = tsubst (spec_tmpl, outer_args, tf_none, NULL_TREE);
+	  spec_tmpl = tsubst (ospec_tmpl, outer_args, tf_none, NULL_TREE);
 	  --processing_template_decl;
+	  if (spec_tmpl == error_mark_node)
+	    return error_mark_node;
 	}
+      else
+	spec_tmpl = ospec_tmpl;
 
-      if (spec_tmpl == error_mark_node)
-	return error_mark_node;
-
-      spec_args = get_partial_spec_bindings (tmpl, spec_tmpl, args);
+      tree spec_args = get_partial_spec_bindings (tmpl, spec_tmpl, args);
       if (spec_args)
 	{
 	  if (outer_args)
@@ -24505,9 +24506,9 @@  most_specialized_partial_spec (tree target, tsubst_flags_t complain)
           /* Keep the candidate only if the constraints are satisfied,
              or if we're not compiling with concepts.  */
           if (!flag_concepts
-              || constraints_satisfied_p (spec_tmpl, spec_args))
+	      || constraints_satisfied_p (ospec_tmpl, spec_args))
             {
-              list = tree_cons (spec_args, TREE_VALUE (t), list);
+	      list = tree_cons (spec_args, ospec_tmpl, list);
               TREE_TYPE (list) = TREE_TYPE (t);
             }
 	}
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec7.C b/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec7.C
new file mode 100644
index 00000000000..5b3afce3bc7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec7.C
@@ -0,0 +1,22 @@ 
+// PR c++/92103
+// { dg-do compile { target c++20 } }
+
+template<int M>
+struct traits
+{
+  template<int N>
+    struct equal_to
+    { static constexpr bool value = false; };
+
+  template<int N> requires (M == N)
+    struct equal_to<N>
+    { static constexpr bool value = true; };
+
+  template<int N> requires (M < 0) || (N < 0)
+    struct equal_to<N>
+    { };
+};
+
+static_assert(traits<0>::equal_to<0>::value);
+static_assert(!traits<0>::equal_to<1>::value);
+static_assert(traits<-1>::equal_to<0>::value); // { dg-error "not a member" }