c++: Fix ICE with constrained friend (PR93400).

Here, the problem was that tsubst_friend_function was modifying the
CONSTRAINT_INFO for the friend template to have the constraints for one
instantiation, which fell down when we went to adjust it for another
instantiation.  Fixed by deferring substitution of trailing requirements
until we try to check declaration matching.

	PR c++/93400 - ICE with constrained friend.
	* constraint.cc (maybe_substitute_reqs_for): New.
	* decl.c (function_requirements_equivalent_p): Call it.
	* pt.c (tsubst_friend_function): Only substitute
	TEMPLATE_PARMS_CONSTRAINTS.
	(tsubst_template_parms): Copy constraints.
This commit is contained in:
Jason Merrill 2020-01-24 14:58:56 -05:00
parent 8b91e84813
commit 9c1179c339
8 changed files with 56 additions and 26 deletions

View file

@ -1,3 +1,12 @@
2020-01-24 Jason Merrill <jason@redhat.com>
PR c++/93400 - ICE with constrained friend.
* constraint.cc (maybe_substitute_reqs_for): New.
* decl.c (function_requirements_equivalent_p): Call it.
* pt.c (tsubst_friend_function): Only substitute
TEMPLATE_PARMS_CONSTRAINTS.
(tsubst_template_parms): Copy constraints.
2020-01-24 Jason Merrill <jason@redhat.com>
PR c++/93279 - ICE with lambda in member operator.

View file

@ -1189,6 +1189,29 @@ remove_constraints (tree t)
decl_constraints->remove (t);
}
/* If DECL is a friend, substitute into REQS to produce requirements suitable
for declaration matching. */
tree
maybe_substitute_reqs_for (tree reqs, const_tree decl_)
{
if (reqs == NULL_TREE)
return NULL_TREE;
tree decl = CONST_CAST_TREE (decl_);
tree result = STRIP_TEMPLATE (decl);
if (DECL_FRIEND_P (result))
{
tree tmpl = decl == result ? DECL_TI_TEMPLATE (result) : decl;
tree gargs = generic_targs_for (tmpl);
processing_template_decl_sentinel s;
if (uses_template_parms (gargs))
++processing_template_decl;
reqs = tsubst_constraint (reqs, gargs,
tf_warning_or_error, NULL_TREE);
}
return reqs;
}
/* Returns the template-head requires clause for the template
declaration T or NULL_TREE if none. */

View file

@ -7832,6 +7832,7 @@ extern void remove_constraints (tree);
extern tree current_template_constraints (void);
extern tree associate_classtype_constraints (tree);
extern tree build_constraints (tree, tree);
extern tree maybe_substitute_reqs_for (tree, const_tree);
extern tree get_template_head_requirements (tree);
extern tree get_trailing_function_requirements (tree);
extern tree get_shorthand_constraints (tree);

View file

@ -942,6 +942,8 @@ function_requirements_equivalent_p (tree newfn, tree oldfn)
tree reqs2 = get_trailing_function_requirements (oldfn);
if ((reqs1 != NULL_TREE) != (reqs2 != NULL_TREE))
return false;
reqs1 = maybe_substitute_reqs_for (reqs1, newfn);
reqs2 = maybe_substitute_reqs_for (reqs2, oldfn);
return cp_tree_equal (reqs1, reqs2);
}

View file

@ -10834,29 +10834,12 @@ tsubst_friend_function (tree decl, tree args)
DECL_SAVED_TREE (DECL_TEMPLATE_RESULT (new_friend))
= DECL_SAVED_TREE (DECL_TEMPLATE_RESULT (decl));
/* Attach the template requirements to the new declaration
for declaration matching. We need to rebuild the requirements
so that parameter levels match. */
if (tree ci = get_constraints (decl))
{
tree parms = DECL_TEMPLATE_PARMS (new_friend);
tree args = generic_targs_for (new_friend);
tree treqs = tsubst_constraint (CI_TEMPLATE_REQS (ci), args,
tf_warning_or_error, NULL_TREE);
tree freqs = tsubst_constraint (CI_DECLARATOR_REQS (ci), args,
tf_warning_or_error, NULL_TREE);
/* Update the constraints -- these won't really be valid for
checking, but that's not what we need them for. These ensure
that the declared function can find the friend during
declaration matching. */
tree new_ci = get_constraints (new_friend);
CI_TEMPLATE_REQS (new_ci) = treqs;
CI_DECLARATOR_REQS (new_ci) = freqs;
/* Also update the template parameter list. */
TEMPLATE_PARMS_CONSTRAINTS (parms) = treqs;
}
/* Substitute TEMPLATE_PARMS_CONSTRAINTS so that parameter levels will
match in decls_match. */
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;
}
/* The mangled name for the NEW_FRIEND is incorrect. The function
@ -13225,6 +13208,8 @@ tsubst_template_parms (tree parms, tree args, tsubst_flags_t complain)
tree_cons (size_int (TMPL_PARMS_DEPTH (parms)
- TMPL_ARGS_DEPTH (args)),
new_vec, NULL_TREE);
TEMPLATE_PARMS_CONSTRAINTS (*new_parms)
= TEMPLATE_PARMS_CONSTRAINTS (parms);
}
--processing_template_decl;

View file

@ -4,6 +4,7 @@ template <class T> concept True = true;
template <True U> struct B { int i = ++U::x; };
template <True U> void f() { ++U::x; }
template <class U> void g() requires True<U> { ++U::x; }
template <class V> class C
{
@ -11,10 +12,12 @@ template <class V> class C
template <True U> friend struct B;
template <True U> friend void f();
template <class U> friend void g() requires True<U>;
};
int main()
{
f<C<int>>();
g<C<int>>();
B<C<int>>();
}

View file

@ -0,0 +1,8 @@
// PR c++/93400
// { dg-do compile { target concepts } }
template <typename> bool a = true;
template <typename i> concept b = a<i>;
template <int> struct f { template <b c> friend auto g(c, f); };
auto d = f<1>{};
auto e = f<0>{};

View file

@ -1,14 +1,13 @@
// { dg-do run { target c++2a } }
// { dg-additional-options "-fconcepts-ts" }
template<class, class> constexpr bool is_same_v = false;
template<class T> constexpr bool is_same_v<T, T> = true;
template<class T, class U>
concept bool Same = is_same_v<T, U>;
concept Same = is_same_v<T, U>;
template<class T, class U>
concept bool Diff = requires(T& t, U& u) { u - t; };
concept Diff = requires(T& t, U& u) { u - t; };
template<class I, class S>
int distance(I, S) { return 0; }