Skip to content

Commit

Permalink
c++: partial ordering and dep alias tmpl specs [PR90679]
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Patrick Palka committed Dec 19, 2023
1 parent 6d27ee7 commit 0a37463
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 10 deletions.
4 changes: 2 additions & 2 deletions gcc/cp/cp-tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
40 changes: 33 additions & 7 deletions gcc/cp/pt.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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.
Expand Down Expand Up @@ -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;
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion gcc/cp/tree.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
26 changes: 26 additions & 0 deletions gcc/testsuite/g++.dg/cpp0x/alias-decl-75a.C
Original file line number Diff line number Diff line change
@@ -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" }
26 changes: 26 additions & 0 deletions gcc/testsuite/g++.dg/cpp0x/alias-decl-75b.C
Original file line number Diff line number Diff line change
@@ -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" }

0 comments on commit 0a37463

Please sign in to comment.