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 c07a48f1261..cdf6a3eeaf3 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -11213,6 +11213,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 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 00000000000..4481b5c4af4 --- /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 +};