Commit 652ec7e8 by Patrick Palka

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)
parent 3af22062
......@@ -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);
......
......@@ -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);
}
......
// 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" }
};
// 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
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment