c++: hash table ICE with variadic alias [PR105003]

For PR104008 we thought it might be enough to keep strip_typedefs from
removing this alias template specialization, but this PR demonstrates that
other parts of the compiler also need to know to consider it dependent.

So, this patch changes complex_alias_template_p to no longer consider
template parameters used when their only use appears in a pack expansion,
unless they are the parameter packs being expanded.

To do that I also needed to change it to use cp_walk_tree instead of
for_each_template_parm.  It occurs to me that find_template_parameters
should probably also use cp_walk_tree, but I'm not messing with that now.

	PR c++/105003
	PR c++/104008
	PR c++/102869

gcc/cp/ChangeLog:

	* pt.cc (complex_alias_template_r): walk_tree callback,	replacing
	uses_all_template_parms_r, complex_pack_expansion_r.
	(complex_alias_template_p): Adjust.
	* tree.cc (strip_typedefs): Revert r12-7710 change.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp0x/variadic-alias6.C: New test.
	* g++.dg/cpp0x/variadic-alias7.C: New test.
This commit is contained in:
Jason Merrill 2022-03-25 13:13:35 -04:00
parent 875342766d
commit fc50d9a252
4 changed files with 85 additions and 36 deletions

View File

@ -6460,10 +6460,7 @@ alias_template_specialization_p (const_tree t,
return NULL_TREE;
}
/* An alias template is complex from a SFINAE perspective if a template-id
using that alias can be ill-formed when the expansion is not, as with
the void_t template. We determine this by checking whether the
expansion for the alias template uses all its template parameters. */
/* Data structure for complex_alias_template_*. */
struct uses_all_template_parms_data
{
@ -6471,31 +6468,36 @@ struct uses_all_template_parms_data
bool *seen;
};
static int
uses_all_template_parms_r (tree t, void *data_)
/* walk_tree callback for complex_alias_template_p. */
static tree
complex_alias_template_r (tree *tp, int *walk_subtrees, void *data_)
{
struct uses_all_template_parms_data &data
= *(struct uses_all_template_parms_data*)data_;
tree idx = get_template_parm_index (t);
tree t = *tp;
auto &data = *(struct uses_all_template_parms_data*)data_;
if (TEMPLATE_PARM_LEVEL (idx) == data.level)
data.seen[TEMPLATE_PARM_IDX (idx)] = true;
return 0;
}
switch (TREE_CODE (t))
{
case TEMPLATE_TYPE_PARM:
case TEMPLATE_PARM_INDEX:
case TEMPLATE_TEMPLATE_PARM:
case BOUND_TEMPLATE_TEMPLATE_PARM:
{
tree idx = get_template_parm_index (t);
if (TEMPLATE_PARM_LEVEL (idx) == data.level)
data.seen[TEMPLATE_PARM_IDX (idx)] = true;
}
/* for_each_template_parm any_fn callback for complex_alias_template_p. */
default:;
}
if (!PACK_EXPANSION_P (t))
return 0;
static int
complex_pack_expansion_r (tree t, void *data_)
{
/* An alias template with a pack expansion that expands a pack from the
enclosing class needs to be considered complex, to avoid confusion with
the same pack being used as an argument to the alias's own template
parameter (91966). */
if (!PACK_EXPANSION_P (t))
return 0;
struct uses_all_template_parms_data &data
= *(struct uses_all_template_parms_data*)data_;
for (tree pack = PACK_EXPANSION_PARAMETER_PACKS (t); pack;
pack = TREE_CHAIN (pack))
{
@ -6505,11 +6507,34 @@ complex_pack_expansion_r (tree t, void *data_)
int idx, level;
template_parm_level_and_index (parm_pack, &level, &idx);
if (level < data.level)
return 1;
return t;
/* Consider the expanded packs to be used outside the expansion... */
data.seen[idx] = true;
}
/* ...but don't walk into the pattern. Consider PR104008:
template <typename T, typename... Ts>
using IsOneOf = disjunction<is_same<T, Ts>...>;
where IsOneOf seemingly uses all of its template parameters in its
expansion (and does not expand a pack from the enclosing class), so the
alias was not marked as complex. However, if it is used like
"IsOneOf<T>", the empty pack for Ts means that T no longer appears in the
expansion. So only Ts is considered used by the pack expansion. */
*walk_subtrees = false;
return 0;
}
/* An alias template is complex from a SFINAE perspective if a template-id
using that alias can be ill-formed when the expansion is not, as with
the void_t template.
Returns 1 if always complex, 0 if not complex, -1 if complex iff any of the
template arguments are empty packs. */
static bool
complex_alias_template_p (const_tree tmpl)
{
@ -6530,8 +6555,7 @@ complex_alias_template_p (const_tree tmpl)
for (int i = 0; i < len; ++i)
data.seen[i] = false;
if (for_each_template_parm (pat, uses_all_template_parms_r, &data,
NULL, true, complex_pack_expansion_r))
if (cp_walk_tree_without_duplicates (&pat, complex_alias_template_r, &data))
return true;
for (int i = 0; i < len; ++i)
if (!data.seen[i])

View File

@ -1778,18 +1778,7 @@ strip_typedefs (tree t, bool *remove_attributes, unsigned int flags)
if (TYPE_P (pat))
{
type = strip_typedefs (pat, remove_attributes, flags);
/* Empty packs can thwart our efforts here. Consider
template <typename T, typename... Ts>
using IsOneOf = disjunction<is_same<T, Ts>...>;
where IsOneOf seemingly uses all of its template parameters in
its expansion (and does not expand a pack from the enclosing
class), so the alias is not marked as complex. However, it may
be used as in "IsOneOf<Ts>", where Ts is an empty parameter pack,
and stripping it down into "disjunction<>" here would exclude the
Ts pack, resulting in an error. */
if (type != pat && uses_parameter_packs (type))
if (type != pat)
{
result = build_distinct_type_copy (t);
PACK_EXPANSION_PATTERN (result) = type;

View File

@ -0,0 +1,20 @@
// PR c++/105003
// { dg-do compile { target c++11 } }
template <class T> struct A;
template <class T, class U> struct B { };
template <class... Ts> struct C { };
// Fn is not complex, since T is used outside the pack expansion, so the two
// partial specializations are equivalent.
template <class T, class... Ts> using Fn = T(Ts...);
template <class T, class... Ts> struct A<Fn<T,Ts...>*> { };
template <class T, class... Ts> struct A<T(*)(Ts...)> { }; // { dg-error "redefinition" }
// CB is complex, since T is only used in the pack expansion, so the two
// partial specializations are functionally equivalent but not equivalent.
template <class T, class ...Ts> using CB = C<B<T,Ts>...>;
template <class T, class ...Ts> struct A<CB<T,Ts...>*> { };
template <class T, class ...Ts> struct A<C<B<T,Ts>...>*> { }; // IFNDR
A<C<B<int,int>>*> a; // { dg-error "ambiguous" }
// { dg-prune-output "incomplete" }

View File

@ -0,0 +1,16 @@
// PR c++/102869
// { dg-do compile { target c++11 } }
template<int...> struct integer_sequence;
template<int _Num>
using make_index_sequence = integer_sequence<__integer_pack(_Num)...>;
template<class...> struct Tuple;
template<int... Is> using tuple_t = Tuple<make_index_sequence<Is>...>;
template<int... Is>
void f() {
tuple_t<Is...> t;
}