c++: partial ordering and dep alias tmpl specs [PR90679]

During partial ordering, we want to look through dependent alias
template specializations within template arguments and otherwise
treat them as opaque in other contexts (see e.g. r7-7116-g0c942f3edab108
and r11-7011-g6e0a231a4aa240).  To that end template_args_equal was
given a partial_order flag that controls this behavior.  This flag
does the right thing when a dependent alias template specialization
appears as template argument of the partial specialization, e.g. in

  template<class T, class...> using first_t = T;
  template<class T> struct traits;
  template<class T> struct traits<first_t<T, T&>> { }; // #1
  template<class T> struct traits<first_t<const T, T&>> { }; // #2

we correctly consider #2 to be more specialized than #1.  But if the
alias specialization appears as a nested template argument of another
class template specialization, e.g. in

  template<class T> struct traits<A<first_t<T, T&>>> { }; // #1
  template<class T> struct traits<A<first_t<const T, T&>>> { }; // #2

then we incorrectly consider #1 and #2 to be unordered.  This is because

  1. we don't propagate the flag to recursive template_args_equal calls
  2. we don't use structural equality for class template specializations
     written in terms of dependent alias template specializations

This patch fixes the first issue by turning the partial_order flag into
a global.  This patch fixes the second issue by making us propagate
structural equality appropriately when building a class template
specialization.  In passing this patch also improves hashing of
specializations that use structural equality.

	PR c++/90679

gcc/cp/ChangeLog:

	* cp-tree.h (comp_template_args): Remove partial_order parameter.
	(template_args_equal): Likewise.
	* pt.cc (comparing_for_partial_ordering): New global flag.
	(iterative_hash_template_arg) <case tcc_type>: Hash the template
	and arguments for specializations that use structural equality.
	(template_args_equal): Remove partial order parameter and
	use comparing_for_partial_ordering instead.
	(comp_template_args): Likewise.
	(comp_template_args_porder): Set comparing_for_partial_ordering
	instead.  Make static.
	(any_template_arguments_need_structural_equality_p): Return true
	for an argument that's a dependent alias template specialization
	or a class template specialization that itself needs structural
	equality.
	* tree.cc (cp_tree_equal) <case TREE_VEC>: Adjust call to
	comp_template_args.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp0x/alias-decl-75a.C: New test.
	* g++.dg/cpp0x/alias-decl-75b.C: New test.
This commit is contained in:
Patrick Palka 2023-12-19 11:40:15 -05:00
parent 6d27ee7fcc
commit 0a37463758
5 changed files with 88 additions and 10 deletions

View file

@ -7507,8 +7507,8 @@ extern int template_class_depth (tree);
extern int is_specialization_of (tree, tree);
extern bool is_specialization_of_friend (tree, tree);
extern bool comp_template_args (tree, tree, tree * = NULL,
tree * = NULL, bool = false);
extern int template_args_equal (tree, tree, bool = false);
tree * = NULL);
extern int template_args_equal (tree, tree);
extern tree maybe_process_partial_specialization (tree);
extern tree most_specialized_instantiation (tree);
extern tree most_specialized_partial_spec (tree, tsubst_flags_t, bool = false);

View file

@ -1646,6 +1646,12 @@ register_specialization (tree spec, tree tmpl, tree args, bool is_friend,
int comparing_specializations;
int comparing_dependent_aliases;
/* Whether we are comparing template arguments during partial ordering
(and therefore want the comparison to look through dependent alias
template specializations). */
static int comparing_for_partial_ordering;
/* Returns true iff two spec_entry nodes are equivalent. */
bool
@ -1894,6 +1900,11 @@ iterative_hash_template_arg (tree arg, hashval_t val)
default:
if (tree canonical = TYPE_CANONICAL (arg))
val = iterative_hash_object (TYPE_HASH (canonical), val);
else if (tree ti = TYPE_TEMPLATE_INFO (arg))
{
val = iterative_hash_template_arg (TI_TEMPLATE (ti), val);
val = iterative_hash_template_arg (TI_ARGS (ti), val);
}
break;
}
@ -9322,7 +9333,7 @@ class_nttp_const_wrapper_p (tree t)
/* Returns 1 if template args OT and NT are equivalent. */
int
template_args_equal (tree ot, tree nt, bool partial_order /* = false */)
template_args_equal (tree ot, tree nt)
{
if (nt == ot)
return 1;
@ -9345,7 +9356,7 @@ template_args_equal (tree ot, tree nt, bool partial_order /* = false */)
During partial ordering, however, we need to treat them normally so we can
order uses of the same alias with different cv-qualification (79960). */
auto cso = make_temp_override (comparing_dependent_aliases);
if (!partial_order)
if (!comparing_for_partial_ordering)
++comparing_dependent_aliases;
if (TREE_CODE (nt) == TREE_VEC || TREE_CODE (ot) == TREE_VEC)
@ -9393,8 +9404,7 @@ template_args_equal (tree ot, tree nt, bool partial_order /* = false */)
bool
comp_template_args (tree oldargs, tree newargs,
tree *oldarg_ptr /* = NULL */, tree *newarg_ptr /* = NULL */,
bool partial_order /* = false */)
tree *oldarg_ptr /* = NULL */, tree *newarg_ptr /* = NULL */)
{
if (oldargs == newargs)
return true;
@ -9410,7 +9420,7 @@ comp_template_args (tree oldargs, tree newargs,
tree nt = TREE_VEC_ELT (newargs, i);
tree ot = TREE_VEC_ELT (oldargs, i);
if (! template_args_equal (ot, nt, partial_order))
if (! template_args_equal (ot, nt))
{
if (oldarg_ptr != NULL)
*oldarg_ptr = ot;
@ -9422,10 +9432,13 @@ comp_template_args (tree oldargs, tree newargs,
return true;
}
inline bool
static bool
comp_template_args_porder (tree oargs, tree nargs)
{
return comp_template_args (oargs, nargs, NULL, NULL, true);
++comparing_for_partial_ordering;
bool equal = comp_template_args (oargs, nargs);
--comparing_for_partial_ordering;
return equal;
}
/* Implement a freelist interface for objects of type T.
@ -28727,6 +28740,19 @@ any_template_arguments_need_structural_equality_p (tree args)
mutated after the fact by duplicate_decls), so just require
structural equality in this case (PR52830). */
return true;
else if (TYPE_P (arg)
&& TYPE_STRUCTURAL_EQUALITY_P (arg)
&& dependent_alias_template_spec_p (arg, nt_transparent))
/* Require structural equality for specializations written
in terms of a dependent alias template specialization. */
return true;
else if (CLASS_TYPE_P (arg)
&& TYPE_TEMPLATE_INFO (arg)
&& TYPE_STRUCTURAL_EQUALITY_P (arg))
/* Require structural equality for specializations written
in terms of a class template specialization that itself
needs structural equality. */
return true;
}
}
}

View file

@ -4131,7 +4131,7 @@ cp_tree_equal (tree t1, tree t2)
case TREE_VEC:
/* These are template args. Really we should be getting the
caller to do this as it knows it to be true. */
if (!comp_template_args (t1, t2, NULL, NULL, false))
if (!comp_template_args (t1, t2))
return false;
return true;

View file

@ -0,0 +1,26 @@
// PR c++/90679
// A version of alias-decl-75.C where the specializations of the
// complex alias template first_t are dependent.
// { dg-do compile { target c++11 } }
template<class T, class...>
using first_t = T;
template<class T>
struct A;
template<class T>
struct traits;
template<class T>
struct traits<A<first_t<T, T&>>> {
static constexpr int value = 1;
};
template<class T>
struct traits<A<first_t<const T, T&>>> {
static constexpr int value = 2;
};
static_assert(traits<A<int>>::value == 1, "");
static_assert(traits<A<const int>>::value == 2, ""); // { dg-bogus "ambiguous" }

View file

@ -0,0 +1,26 @@
// PR c++/90679
// A version of alias-decl-75a.C where the alias template specialization
// appears as a more deeply nested template argument.
// { dg-do compile { target c++11 } }
template<class T, class...>
using first_t = T;
template<class T>
struct A;
template<class T>
struct traits;
template<class T>
struct traits<A<A<first_t<T, T&>>>> {
static constexpr int value = 1;
};
template<class T>
struct traits<A<A<first_t<const T, T&>>>> {
static constexpr int value = 2;
};
static_assert(traits<A<A<int>>>::value == 1, "");
static_assert(traits<A<A<const int>>>::value == 2, ""); // { dg-bogus "ambiguous" }