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

Message ID 20220328145658.2789620-1-jason@redhat.com
State Committed
Commit fc50d9a252c89c1bac78192bd0884ff23f2bf48b
Headers
Series [pushed] c++: hash table ICE with variadic alias [PR105003] |

Commit Message

Jason Merrill March 28, 2022, 2:56 p.m. UTC
  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.

Tested x86_64-pc-linux-gnu, applying to trunk.

	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.
---
 gcc/cp/pt.cc                                 | 72 +++++++++++++-------
 gcc/cp/tree.cc                               | 13 +---
 gcc/testsuite/g++.dg/cpp0x/variadic-alias6.C | 20 ++++++
 gcc/testsuite/g++.dg/cpp0x/variadic-alias7.C | 16 +++++
 4 files changed, 85 insertions(+), 36 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/variadic-alias6.C
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/variadic-alias7.C


base-commit: 19b87a06482756739087283cd8b884cb3de693f9
  

Patch

diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 678063f6e4c..3df509bbed0 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -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])
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 492921721f2..780a8d89165 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -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;
diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic-alias6.C b/gcc/testsuite/g++.dg/cpp0x/variadic-alias6.C
new file mode 100644
index 00000000000..c095bc537d7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/variadic-alias6.C
@@ -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" }
diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic-alias7.C b/gcc/testsuite/g++.dg/cpp0x/variadic-alias7.C
new file mode 100644
index 00000000000..41a432a8e41
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/variadic-alias7.C
@@ -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;
+}