Message ID | 20200602194346.701957-1-ppalka@redhat.com |
---|---|
State | New |
Headers | show |
Series | c++: constrained nested partial specialization [PR92103] | expand |
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 --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" }