Message ID | 20210407213350.3190828-1-ppalka@redhat.com |
---|---|
State | New |
Headers | show |
Series | c++: constrained CTAD for nested class template [PR97679] | expand |
On 4/7/21 5:33 PM, Patrick Palka wrote: > In the testcase below, we're crashing during constraint checking of the > implicitly generated deduction guides for the nested class template A::B > because we never substitute the outer template arguments (for A) into > the constraint, neither ahead of time nor as part of satisfaction. > > Ideally we'd like to avoid substituting into a constraint ahead of > time, but the "flattening" vector 'tsubst_args' is constructed under the > assumption that all outer template arguments are already substituted in, > and eliminating this assumption to yield a flattening vector that > includes outer (generic) template arguments suitable for substituting > into the constraint would be tricky. So this patch takes the > approximate approach of substituting the outer arguments into the > constraint ahead of time, so that we could subsequently substitute > 'tsubst_args' into the constraint. This seems reasonable, and related to the substitution needed for http://wg21.link/p2103#ca104 , though in this case the substitution could be avoided. > NB: I noticed that [over.match.class.deduct]/1, which describes how > guides are formed from constructors of a class template, doesn't mention > that a guide inherits the constraints of the corresponding constructor. > Though [over.match.class.deduct]/2 later suggests that guides do have > constraints: "The associated constraints [of f'] are the conjunction of > the associated constraints of [the guide] g and ..." So I'm unsure if > we're being conforming by giving guides the constraints of the > corresponding constructor. I think propagating the constraints is necessary, or deduction would do the wrong thing for constructors that try to exclude problematic arguments. > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for > trunk and 10 branch (after 10.3 is released)? OK. Please also document outer_args in the comment for the function. > gcc/cp/ChangeLog: > > PR c++/97679 > * pt.c (build_deduction_guide): Substitute outer template > arguments into the constraints. > > gcc/testsuite/ChangeLog: > > PR c++/97679 > * g++.dg/cpp2a/concepts-ctad3.C: New test. > --- > gcc/cp/pt.c | 17 +++++++++++++++-- > gcc/testsuite/g++.dg/cpp2a/concepts-ctad3.C | 16 ++++++++++++++++ > 2 files changed, 31 insertions(+), 2 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-ctad3.C > > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c > index 7917a280804..6f44199e8eb 100644 > --- a/gcc/cp/pt.c > +++ b/gcc/cp/pt.c > @@ -28719,7 +28719,15 @@ build_deduction_guide (tree type, tree ctor, tree outer_args, tsubst_flags_t com > if (fparms == error_mark_node) > ok = false; > if (ci) > - ci = tsubst_constraint_info (ci, tsubst_args, complain, ctor); > + { > + if (outer_args) > + /* FIXME: We'd like to avoid substituting outer template > + arguments into the constraint ahead of time, but the > + construction of tsubst_args assumes that outer arguments > + are already substituted in. */ > + ci = tsubst_constraint_info (ci, outer_args, complain, ctor); > + ci = tsubst_constraint_info (ci, tsubst_args, complain, ctor); > + } > > /* Parms are to have DECL_CHAIN tsubsted, which would be skipped if > cp_unevaluated_operand. */ > @@ -28735,7 +28743,12 @@ build_deduction_guide (tree type, tree ctor, tree outer_args, tsubst_flags_t com > fparms = tsubst_arg_types (fparms, targs, NULL_TREE, complain, ctor); > fargs = tsubst (fargs, targs, complain, ctor); > if (ci) > - ci = tsubst_constraint_info (ci, targs, complain, ctor); > + { > + if (outer_args) > + /* FIXME: As above. */ > + ci = tsubst_constraint_info (ci, outer_args, complain, ctor); > + ci = tsubst_constraint_info (ci, targs, complain, ctor); > + } > } > > --processing_template_decl; > diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-ctad3.C b/gcc/testsuite/g++.dg/cpp2a/concepts-ctad3.C > new file mode 100644 > index 00000000000..3546b7461a8 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-ctad3.C > @@ -0,0 +1,16 @@ > +// PR c++/97679 > +// { dg-do compile { target c++20 } } > + > +template <bool V> struct A { > + template <class T> struct B { > + B(T) requires V; > + template <class U> B(T, U) requires V || (__is_same(T, char) && __is_same(U, int)); > + }; > +}; > + > +A<true>::B x1(0); > +A<false>::B x2(0); // { dg-error "deduction|no match" } > + > +A<true>::B y1(0, '0'); > +A<false>::B y2(0, '0'); // { dg-error "deduction|no match" } > +A<false>::B y3('0', 0); >
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 7917a280804..6f44199e8eb 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -28719,7 +28719,15 @@ build_deduction_guide (tree type, tree ctor, tree outer_args, tsubst_flags_t com if (fparms == error_mark_node) ok = false; if (ci) - ci = tsubst_constraint_info (ci, tsubst_args, complain, ctor); + { + if (outer_args) + /* FIXME: We'd like to avoid substituting outer template + arguments into the constraint ahead of time, but the + construction of tsubst_args assumes that outer arguments + are already substituted in. */ + ci = tsubst_constraint_info (ci, outer_args, complain, ctor); + ci = tsubst_constraint_info (ci, tsubst_args, complain, ctor); + } /* Parms are to have DECL_CHAIN tsubsted, which would be skipped if cp_unevaluated_operand. */ @@ -28735,7 +28743,12 @@ build_deduction_guide (tree type, tree ctor, tree outer_args, tsubst_flags_t com fparms = tsubst_arg_types (fparms, targs, NULL_TREE, complain, ctor); fargs = tsubst (fargs, targs, complain, ctor); if (ci) - ci = tsubst_constraint_info (ci, targs, complain, ctor); + { + if (outer_args) + /* FIXME: As above. */ + ci = tsubst_constraint_info (ci, outer_args, complain, ctor); + ci = tsubst_constraint_info (ci, targs, complain, ctor); + } } --processing_template_decl; diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-ctad3.C b/gcc/testsuite/g++.dg/cpp2a/concepts-ctad3.C new file mode 100644 index 00000000000..3546b7461a8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-ctad3.C @@ -0,0 +1,16 @@ +// PR c++/97679 +// { dg-do compile { target c++20 } } + +template <bool V> struct A { + template <class T> struct B { + B(T) requires V; + template <class U> B(T, U) requires V || (__is_same(T, char) && __is_same(U, int)); + }; +}; + +A<true>::B x1(0); +A<false>::B x2(0); // { dg-error "deduction|no match" } + +A<true>::B y1(0, '0'); +A<false>::B y2(0, '0'); // { dg-error "deduction|no match" } +A<false>::B y3('0', 0);