From: Patrick Palka Date: Thu, 11 Jun 2020 20:33:41 +0000 (-0400) Subject: c++: constrained class template friend [PR93467] X-Git-Tag: releases/gcc-10.2.0~226 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=652ec7e8c046b239c42dedd295acf1815ed2b93a;p=thirdparty%2Fgcc.git c++: constrained class template friend [PR93467] 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. 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 the injected declaration. gcc/testsuite/ChangeLog: PR c++/93467 * g++.dg/cpp2a/concepts-friend6.C: New test. * g++.dg/cpp2a/concepts-friend7.C: New test. (cherry picked from commit 11c7261128ad3ee136508703b20e45cbe04f8dd0) --- diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 65c047a2f626..c9be7a5ee9b1 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 732900e47969..5505a85dcc77 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -11208,6 +11208,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 000000000000..11e8313f0ac0 --- /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 requires B + class C; + +template +class S1 +{ + template requires B + friend class ::C; +}; + +template +class S2 +{ + template 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 000000000000..4481b5c4af48 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend7.C @@ -0,0 +1,18 @@ +// PR c++/93467 +// { dg-do compile { target c++20 } } + +template concept True = true; + +template +struct S1 { + template friend struct S2; // friend declaration for S2 +}; + +S1 s; // instantiate S1 + +template struct S2; // another declaration for S2 + +template +struct S3 { + template friend struct ::S2; // a third declaration for S2 +};