My r10-7007 patch tweaked tsubst not to reduce the template level of
template parameters when tf_partial.  That caused infinite looping in
is_specialization_of: we ended up with a class template specialization
whose TREE_TYPE (CLASSTYPE_TI_TEMPLATE (t)) == t, so the second for
loop in is_specialization_of never finished.

There's a lot going on in this test, but essentially: the template fn
here has two template parameters, we call it with one explicitly
provided, the other one has to be deduced.  So we'll find ourselves
in fn_type_unification which uses tf_partial when tsubsting the
*explicit* template arguments into the function type.  That leads to
tsubstituting the return type, C<T>.  C is a member template; its
most general template is

  template<class U> template<class V> struct B<U>::C

we figure out (tsubst_template_args) that the template argument list
is <int, int>.  They come from different levels, one comes from B<int>,
the other one from fn<int>.

So now we lookup_template_class to see if we have C<int, int>.  We
do the
  /* This is a full instantiation of a member template.  Find
     the partial instantiation of which this is an instance.  */
  TREE_VEC_LENGTH (arglist)--;
  // arglist is now <int>, not <int, int>
  found = tsubst (gen_tmpl, arglist, complain, NULL_TREE);
  TREE_VEC_LENGTH (arglist)++;

magic which is looking for the partial instantiation, in this case,
that would be template<class V> struct B<int>::C.  Note we're still
in a tf_partial context!  So the tsubst_template_args in the tsubst
(which tries to substitute <int> into <U, V>) returns <int, V>, but
V's template level hasn't been reduced!  After tsubst_template_args,
tsubst_template_decl looks to see if we already have this specialization:

  // t = template_decl C
  // full_args = <int, V>
  spec = retrieve_specialization (t, full_args, hash);

but doesn't find the one we created a while ago, when processing
B<int> b; in the test, because V's levels don't match.  Whereupon
tsubst_template_decl creates a new TEMPLATE_DECL, one that leads to
the infinite looping problem.

I think let's clear tf_partial when looking for an existing partial
instantiation.

It also occurred to me that I should be able to trigger a similar
problem with 'auto', since r10-7007 removed an is_auto check.  And lo,
I constructed deduce10.C which exhibits the same issue with pre-r10-7007
compilers.  This patch fixes that problem as well.  I'm ecstatic.

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/10?  Also
built cmcstl2.

gcc/cp/ChangeLog:

        PR c++/95888
        * pt.c (lookup_template_class_1): Clear tf_partial when looking for
        the partial instantiation.

gcc/testsuite/ChangeLog:

        PR c++/95888
        * g++.dg/template/deduce10.C: New test.
        * g++.dg/template/deduce9.C: New test.
---
 gcc/cp/pt.c                              | 10 +++++++++-
 gcc/testsuite/g++.dg/template/deduce10.C | 23 +++++++++++++++++++++++
 gcc/testsuite/g++.dg/template/deduce9.C  | 23 +++++++++++++++++++++++
 3 files changed, 55 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/g++.dg/template/deduce10.C
 create mode 100644 gcc/testsuite/g++.dg/template/deduce9.C

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index f73deb3aee3..5b8a08a30e0 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -10137,7 +10137,15 @@ lookup_template_class_1 (tree d1, tree arglist, tree 
in_decl, tree context,
          /* Temporarily reduce by one the number of levels in the ARGLIST
             so as to avoid comparing the last set of arguments.  */
          TREE_VEC_LENGTH (arglist)--;
-         found = tsubst (gen_tmpl, arglist, complain, NULL_TREE);
+         /* Clear tf_partial, which can be set because we might be at the
+            beginning of template argument deduction when any explicitly
+            specified template arguments are substituted into the function
+            type.  If we don't clear tf_partial, we won't find the partial
+            instantiation that might have been created outside tf_partial
+            context, because the levels of template parameters wouldn't
+            match, because in a tf_partial context, tsubst doesn't reduce
+            TEMPLATE_PARM_LEVEL.  */
+         found = tsubst (gen_tmpl, arglist, complain & ~tf_partial, NULL_TREE);
          TREE_VEC_LENGTH (arglist)++;
          /* FOUND is either a proper class type, or an alias
             template specialization.  In the later case, it's a
diff --git a/gcc/testsuite/g++.dg/template/deduce10.C 
b/gcc/testsuite/g++.dg/template/deduce10.C
new file mode 100644
index 00000000000..165ff195728
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/deduce10.C
@@ -0,0 +1,23 @@
+// PR c++/95888
+// { dg-do compile { target c++17 } }
+
+template <typename T> class A {
+  A(int, int);
+  template <typename> friend class A;
+  friend T;
+};
+
+template<typename U> struct B {
+  template<auto V> struct C {
+    A<B> begin() { return {1, 0}; }
+  };
+  template<auto Z, int *P = nullptr>
+  C<Z> fn();
+};
+
+int
+main ()
+{
+  B<int> b;
+  b.fn<1>().begin();
+}
diff --git a/gcc/testsuite/g++.dg/template/deduce9.C 
b/gcc/testsuite/g++.dg/template/deduce9.C
new file mode 100644
index 00000000000..5f55a84ed0a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/deduce9.C
@@ -0,0 +1,23 @@
+// PR c++/95888
+// { dg-do compile { target c++11 } }
+
+template <typename T> class A {
+  A(int, int);
+  template <typename> friend class A;
+  friend T;
+};
+
+template<typename U> struct B {
+  template<typename V> struct C {
+    A<B> begin() { return {1, 0}; }
+  };
+  template<typename Z, int *P = nullptr>
+  C<Z> fn();
+};
+
+int
+main ()
+{
+  B<int> b;
+  b.fn<int>().begin();
+}

base-commit: 20f28986a8d3cad3c848d1e7da48f4bea7637298
-- 
2.29.2

Reply via email to