Skip to content

Commit

Permalink
c++: don't substitute TEMPLATE_PARM_CONSTRAINTS [PR100374]
Browse files Browse the repository at this point in the history
This patch makes us avoid substituting into the TEMPLATE_PARM_CONSTRAINTS
of each template parameter except as necessary for declaration matching,
like we already do for the other constituent constraints of a declaration.

This patch also improves the CA104 implementation of explicit
specialization matching of a constrained function template inside a
class template, by considering the function's combined constraints
instead of just its trailing constraints.  This allows us to correctly
handle the first three explicit specializations in concepts-spec2.C
below, but because we compare the constraints as a whole, it means we
incorrectly accept the fourth explicit specialization which writes #3's
constraints in a different way.  For complete correctness here,
determine_specialization should use tsubst_each_template_parm_constraints
and template_parameter_heads_equivalent_p.

	PR c++/100374

gcc/cp/ChangeLog:

	* pt.cc (determine_specialization): Compare overall constraints
	not just the trailing constraints.
	(tsubst_each_template_parm_constraints): Define.
	(tsubst_friend_function): Use it.
	(tsubst_friend_class): Use it.
	(tsubst_template_parm): Don't substitute TEMPLATE_PARM_CONSTRAINTS.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/concepts-spec2.C: New test.
	* g++.dg/cpp2a/concepts-template-parm11.C: New test.
  • Loading branch information
Patrick Palka committed Jun 3, 2022
1 parent df4f95d commit 43c013d
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 7 deletions.
42 changes: 35 additions & 7 deletions gcc/cp/pt.cc
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ static int unify_pack_expansion (tree, tree, tree,
tree, unification_kind_t, bool, bool);
static tree copy_template_args (tree);
static tree tsubst_template_parms (tree, tree, tsubst_flags_t);
static void tsubst_each_template_parm_constraints (tree, tree, tsubst_flags_t);
tree most_specialized_partial_spec (tree, tsubst_flags_t);
static tree tsubst_aggr_type (tree, tree, tsubst_flags_t, tree, int);
static tree tsubst_arg_types (tree, tree, tree, tsubst_flags_t, tree);
Expand Down Expand Up @@ -2323,8 +2324,8 @@ determine_specialization (tree template_id,
if (!compparms (fn_arg_types, decl_arg_types))
continue;

tree freq = get_trailing_function_requirements (fn);
tree dreq = get_trailing_function_requirements (decl);
tree freq = get_constraints (fn);
tree dreq = get_constraints (decl);
if (!freq != !dreq)
continue;
if (freq)
Expand All @@ -2333,7 +2334,7 @@ determine_specialization (tree template_id,
constraint-expression. */
tree fargs = DECL_TI_ARGS (fn);
tsubst_flags_t complain = tf_none;
freq = tsubst_constraint (freq, fargs, complain, fn);
freq = tsubst_constraint_info (freq, fargs, complain, fn);
if (!cp_tree_equal (freq, dreq))
continue;
}
Expand Down Expand Up @@ -11240,7 +11241,13 @@ tsubst_friend_function (tree decl, tree args)
tree parms = DECL_TEMPLATE_PARMS (new_friend);
tree treqs = TEMPLATE_PARMS_CONSTRAINTS (parms);
treqs = maybe_substitute_reqs_for (treqs, new_friend);
TEMPLATE_PARMS_CONSTRAINTS (parms) = treqs;
if (treqs != TEMPLATE_PARMS_CONSTRAINTS (parms))
{
TEMPLATE_PARMS_CONSTRAINTS (parms) = treqs;
/* As well as each TEMPLATE_PARM_CONSTRAINTS. */
tsubst_each_template_parm_constraints (parms, args,
tf_warning_or_error);
}
}

/* The mangled name for the NEW_FRIEND is incorrect. The function
Expand Down Expand Up @@ -11486,6 +11493,8 @@ tsubst_friend_class (tree friend_tmpl, tree args)
{
tree parms = tsubst_template_parms (DECL_TEMPLATE_PARMS (friend_tmpl),
args, tf_warning_or_error);
tsubst_each_template_parm_constraints (parms, args,
tf_warning_or_error);
location_t saved_input_location = input_location;
input_location = DECL_SOURCE_LOCATION (friend_tmpl);
tree cons = get_constraints (tmpl);
Expand Down Expand Up @@ -11520,6 +11529,8 @@ tsubst_friend_class (tree friend_tmpl, tree args)
DECL_FRIEND_CONTEXT (friend_tmpl));
--processing_template_decl;
set_constraints (tmpl, ci);
tsubst_each_template_parm_constraints (DECL_TEMPLATE_PARMS (tmpl),
args, tf_warning_or_error);
}

/* Inject this template into the enclosing namspace scope. */
Expand Down Expand Up @@ -13632,21 +13643,38 @@ tsubst_template_parm (tree t, tree args, tsubst_flags_t complain)

default_value = TREE_PURPOSE (t);
parm_decl = TREE_VALUE (t);
tree constraint = TEMPLATE_PARM_CONSTRAINTS (t);

parm_decl = tsubst (parm_decl, args, complain, NULL_TREE);
if (TREE_CODE (parm_decl) == PARM_DECL
&& invalid_nontype_parm_type_p (TREE_TYPE (parm_decl), complain))
parm_decl = error_mark_node;
default_value = tsubst_template_arg (default_value, args,
complain, NULL_TREE);
constraint = tsubst_constraint (constraint, args, complain, NULL_TREE);

tree r = build_tree_list (default_value, parm_decl);
TEMPLATE_PARM_CONSTRAINTS (r) = constraint;
TEMPLATE_PARM_CONSTRAINTS (r) = TEMPLATE_PARM_CONSTRAINTS (t);
return r;
}

/* Substitute in-place the TEMPLATE_PARM_CONSTRAINTS of each template
parameter in PARMS for sake of declaration matching. */

static void
tsubst_each_template_parm_constraints (tree parms, tree args,
tsubst_flags_t complain)
{
++processing_template_decl;
for (; parms; parms = TREE_CHAIN (parms))
{
tree level = TREE_VALUE (parms);
for (tree parm : tree_vec_range (level))
TEMPLATE_PARM_CONSTRAINTS (parm)
= tsubst_constraint (TEMPLATE_PARM_CONSTRAINTS (parm), args,
complain, NULL_TREE);
}
--processing_template_decl;
}

/* Substitute the ARGS into the indicated aggregate (or enumeration)
type T. If T is not an aggregate or enumeration type, it is
handled as if by tsubst. IN_DECL is as for tsubst. If
Expand Down
16 changes: 16 additions & 0 deletions gcc/testsuite/g++.dg/cpp2a/concepts-spec2.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// { dg-do compile { target c++20 } }

template<class T, int> concept C = true;

template<class T> struct A {
template<C<sizeof(T)> U> void f(); // #1
template<C<0> U> void f(); // #2
template<C<-1> U> void f(); // #3
};

constexpr int n = sizeof(int);
template<> template<C<n> U> void A<int>::f() { } // matches #1
template<> template<C<0> U> void A<int>::f() { } // matches #2
template<> template<C<-2> U> void A<int>::f() { } // no match { dg-error "match" }
template<> template<class U> void A<int>::f() requires C<U, -1> { } // shouldn't match #3
// { dg-error "match" "" { xfail *-*-* } .-1 }
20 changes: 20 additions & 0 deletions gcc/testsuite/g++.dg/cpp2a/concepts-template-parm11.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// PR c++/100374
// { dg-do compile { target c++20 } }

template<class T, class U>
concept C = requires { typename T; };

template<class T>
struct A {
template<C<typename T::value_type> U>
void f();

template<C<typename T::value_type> U>
struct B;
};

int main() {
A<int> a;
a.f<void>();
using type = A<int>::B<void>;
}

0 comments on commit 43c013d

Please sign in to comment.