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" }