Message ID | 20200609181841.1736938-1-ppalka@redhat.com |
---|---|
State | New |
Headers | show |
Series | c++: constrained class template friend [PR93467] | expand |
On 6/9/20 2:18 PM, Patrick Palka wrote: > This fixes two issues in our handling of constrained class template > friend declarations. > > The first issue is that we fail to set the constraints on the injected > class template declaration during tsubst_friend_class. > > The second issue is that the template parameter levels within the parsed > constraints of a class template friend declaration are shifted if the > enclosing class is a template, and this shift leads to spurious > constraint mismatch errors in associate_classtype_constraints if the > friend declaration refers to an already declared class template. > > Passes 'make check-c++', and also verified by building the testsuite of > cmcstl2. Does this look OK to commit to master and to the 10.2 branch > after a full bootstrap and regtest? OK. > gcc/cp/ChangeLog: > > PR c++/93467 > * constraint.cc (associate_classtype_constraints): If there is a > discrepancy between the current template depth and the template > depth of the original declaration, then adjust the template > parameter depth within the current constraints appropriately. > * pt.c (tsubst_friend_class): Substitute into and set the > constraints on injected declaration. > > gcc/testsuite/ChangeLog: > > PR c++/93467 > * g++.dg/cpp2a/concepts-friend6.C: New test. > * g++.dg/cpp2a/concepts-friend7.C: New test. > --- > gcc/cp/constraint.cc | 13 +++++++++++++ > gcc/cp/pt.c | 10 ++++++++++ > gcc/testsuite/g++.dg/cpp2a/concepts-friend6.C | 19 +++++++++++++++++++ > gcc/testsuite/g++.dg/cpp2a/concepts-friend7.C | 19 +++++++++++++++++++ > 4 files changed, 61 insertions(+) > create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-friend6.C > create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-friend7.C > > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc > index 92ff283013e..d0da2300ba9 100644 > --- a/gcc/cp/constraint.cc > +++ b/gcc/cp/constraint.cc > @@ -1075,6 +1075,19 @@ associate_classtype_constraints (tree type) > original declaration. */ > if (tree orig_ci = get_constraints (decl)) > { > + if (int extra_levels = (TMPL_PARMS_DEPTH (current_template_parms) > + - TMPL_ARGS_DEPTH (TYPE_TI_ARGS (type)))) > + { > + /* If there is a discrepancy between the current template depth > + and the template depth of the original declaration, then we > + must be redeclaring a class template as part of a friend > + declaration within another class template. Before matching > + constraints, we need to reduce the template parameter level > + within the current constraints via substitution. */ > + tree outer_gtargs = template_parms_to_args (current_template_parms); > + TREE_VEC_LENGTH (outer_gtargs) = extra_levels; > + ci = tsubst_constraint_info (ci, outer_gtargs, tf_none, NULL_TREE); > + } > if (!equivalent_constraints (ci, orig_ci)) > { > error ("%qT does not match original declaration", type); > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c > index 142392224c6..7fcb3e9f1c5 100644 > --- a/gcc/cp/pt.c > +++ b/gcc/cp/pt.c > @@ -11211,6 +11211,16 @@ tsubst_friend_class (tree friend_tmpl, tree args) > DECL_ANTICIPATED (tmpl) > = DECL_ANTICIPATED (DECL_TEMPLATE_RESULT (tmpl)) = true; > > + /* Substitute into and set the constraints on the new declaration. */ > + if (tree ci = get_constraints (friend_tmpl)) > + { > + ++processing_template_decl; > + ci = tsubst_constraint_info (ci, args, tf_warning_or_error, > + DECL_FRIEND_CONTEXT (friend_tmpl)); > + --processing_template_decl; > + set_constraints (tmpl, ci); > + } > + > /* Inject this template into the enclosing namspace scope. */ > tmpl = pushdecl_namespace_level (tmpl, true); > } > diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend6.C b/gcc/testsuite/g++.dg/cpp2a/concepts-friend6.C > new file mode 100644 > index 00000000000..11e8313f0ac > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend6.C > @@ -0,0 +1,19 @@ > +// PR c++/93467 > +// { dg-do compile { target c++20 } } > + > +template<bool B> requires B > + class C; > + > +template<typename> > +class S1 > +{ > + template<bool B> requires B > + friend class ::C; > +}; > + > +template<typename> > +class S2 > +{ > + template<bool B> requires (!B) > + friend class ::C; // { dg-error "does not match original declaration" } > +}; > diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend7.C b/gcc/testsuite/g++.dg/cpp2a/concepts-friend7.C > new file mode 100644 > index 00000000000..1c6893584b1 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend7.C > @@ -0,0 +1,19 @@ > +// PR c++/93467 > +// { dg-do compile { target c++20 } } > + > +template<typename T> concept True = true; > + > +template<typename U> > +struct S1 { > + template<True T> friend struct S2; // friend declaration for S2 > +}; > + > +S1<int> s; // instantiate S1 > + > +template<True T> struct S2; // another declaration for S2 > + > +template<typename U> > +struct S3 { > + template<True T> friend struct ::S2; // a third declaration for S2 > +}; > + >
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 92ff283013e..d0da2300ba9 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -1075,6 +1075,19 @@ associate_classtype_constraints (tree type) original declaration. */ if (tree orig_ci = get_constraints (decl)) { + if (int extra_levels = (TMPL_PARMS_DEPTH (current_template_parms) + - TMPL_ARGS_DEPTH (TYPE_TI_ARGS (type)))) + { + /* If there is a discrepancy between the current template depth + and the template depth of the original declaration, then we + must be redeclaring a class template as part of a friend + declaration within another class template. Before matching + constraints, we need to reduce the template parameter level + within the current constraints via substitution. */ + tree outer_gtargs = template_parms_to_args (current_template_parms); + TREE_VEC_LENGTH (outer_gtargs) = extra_levels; + ci = tsubst_constraint_info (ci, outer_gtargs, tf_none, NULL_TREE); + } if (!equivalent_constraints (ci, orig_ci)) { error ("%qT does not match original declaration", type); diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 142392224c6..7fcb3e9f1c5 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -11211,6 +11211,16 @@ tsubst_friend_class (tree friend_tmpl, tree args) DECL_ANTICIPATED (tmpl) = DECL_ANTICIPATED (DECL_TEMPLATE_RESULT (tmpl)) = true; + /* Substitute into and set the constraints on the new declaration. */ + if (tree ci = get_constraints (friend_tmpl)) + { + ++processing_template_decl; + ci = tsubst_constraint_info (ci, args, tf_warning_or_error, + DECL_FRIEND_CONTEXT (friend_tmpl)); + --processing_template_decl; + set_constraints (tmpl, ci); + } + /* Inject this template into the enclosing namspace scope. */ tmpl = pushdecl_namespace_level (tmpl, true); } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend6.C b/gcc/testsuite/g++.dg/cpp2a/concepts-friend6.C new file mode 100644 index 00000000000..11e8313f0ac --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend6.C @@ -0,0 +1,19 @@ +// PR c++/93467 +// { dg-do compile { target c++20 } } + +template<bool B> requires B + class C; + +template<typename> +class S1 +{ + template<bool B> requires B + friend class ::C; +}; + +template<typename> +class S2 +{ + template<bool B> requires (!B) + friend class ::C; // { dg-error "does not match original declaration" } +}; diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend7.C b/gcc/testsuite/g++.dg/cpp2a/concepts-friend7.C new file mode 100644 index 00000000000..1c6893584b1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend7.C @@ -0,0 +1,19 @@ +// PR c++/93467 +// { dg-do compile { target c++20 } } + +template<typename T> concept True = true; + +template<typename U> +struct S1 { + template<True T> friend struct S2; // friend declaration for S2 +}; + +S1<int> s; // instantiate S1 + +template<True T> struct S2; // another declaration for S2 + +template<typename U> +struct S3 { + template<True T> friend struct ::S2; // a third declaration for S2 +}; +