c++: constrained nested partial specialization [PR92103]
authorPatrick Palka <ppalka@redhat.com>
Wed, 3 Jun 2020 20:40:28 +0000 (16:40 -0400)
committerPatrick Palka <ppalka@redhat.com>
Wed, 3 Jun 2020 20:40:28 +0000 (16:40 -0400)
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 each 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 this 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).

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 tsubsted one.

gcc/testsuite/ChangeLog:

PR c++/92103
* g++.dg/cpp2a/concepts-partial-spec7.C: New test.

gcc/cp/pt.c
gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec7.C [new file with mode: 0644]

index 38a771b8e6fb5347fab972e51521c21bd0340eec..f5d1442a4004d9c00839dadd298a5a4650139350 100644 (file)
@@ -24488,21 +24488,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)
@@ -24511,9 +24512,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 (file)
index 0000000..5b3afce
--- /dev/null
@@ -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" }