Version 9.3 and later. 9.2 compiles OK. #include <boost/poly_collection/base_collection.hpp> struct base{}; int main() { const boost::base_collection<base> n; #define EXPOSE_BUG 1 #if EXPOSE_BUG n.segment<base>().begin(); #else n.segment<base,nullptr>().begin(); #endif } https://godbolt.org/z/_yau99
You were asked to read https://gcc.gnu.org/bugs/ when creating a new bug report. Please read it and provide what's missing.
The problem started with r10-7007-6b3302da9ef26aa11940f8c0dc92bec19e15c09b: PR c++/90505 - mismatch in template argument deduction. * pt.c (tsubst): Don't reduce the template level of template parameters when tf_partial.
I'm reducing that right now..
Reduced test-case: cat pr95888.ii template <typename PolyCollection> class local_iterator_impl { using const_segment_map_iterator = typename PolyCollection::const_segment_map_iterator; template <typename Iterator> local_iterator_impl(const_segment_map_iterator, Iterator); template <typename> friend class local_iterator_impl; friend PolyCollection; }; template <typename> class poly_collection { template <typename> using enable_if_acceptable = int *; using segment_map = int; using const_segment_map_iterator = segment_map; template <typename> friend class local_iterator_impl; template <typename> using local_iterator_impl = local_iterator_impl<poly_collection>; public: template <typename T> using const_local_iterator = local_iterator_impl<T>; template <typename> class const_segment_info { public: const_local_iterator<int> begin() { return {it, 0}; } const_segment_map_iterator it; }; template <typename T, enable_if_acceptable<T> = nullptr> const_segment_info<T> segment(); }; int main() { poly_collection<int> n; n.segment<int>().begin(); }
Simplified test: template <typename T> class A { A(int, int); template <typename> friend class A; friend T; }; template<typename U> struct B { template<typename T> struct C { A<B> begin() { return {1, 0}; } }; template<typename T, int *P = nullptr> C<T> fn(); }; int main () { B<int> b; b.fn<int>().begin(); }
I've found out that the same issue happens with auto template parameter too, and started way before my change: 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(); } this (valid) used to crash, and since r243867 we loop infinitely. I have a patch that fixes both.
Patch posted: https://gcc.gnu.org/pipermail/gcc-patches/2021-February/565086.html
The master branch has been updated by Marek Polacek <mpolacek@gcc.gnu.org>: https://gcc.gnu.org/g:88cfd531c69b3c1fe7a3c183d83cfeacc8f69402 commit r11-7197-g88cfd531c69b3c1fe7a3c183d83cfeacc8f69402 Author: Marek Polacek <polacek@redhat.com> Date: Tue Feb 9 15:17:48 2021 -0500 c++: Endless loop with targ deduction in member tmpl [PR95888] 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. Fixed by using tf_none 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. gcc/cp/ChangeLog: PR c++/95888 * pt.c (lookup_template_class_1): Pass tf_none to tsubst 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.
Fixed on trunk so far. Testing gcc10 now.
The releases/gcc-10 branch has been updated by Marek Polacek <mpolacek@gcc.gnu.org>: https://gcc.gnu.org/g:60dbb46b5b3c52d0483d676eabd4dfe32110e61b commit r10-9360-g60dbb46b5b3c52d0483d676eabd4dfe32110e61b Author: Marek Polacek <polacek@redhat.com> Date: Tue Feb 9 15:17:48 2021 -0500 c++: Endless loop with targ deduction in member tmpl [PR95888] 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. Fixed by using tf_none 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. gcc/cp/ChangeLog: PR c++/95888 * pt.c (lookup_template_class_1): Pass tf_none to tsubst 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. (cherry picked from commit 88cfd531c69b3c1fe7a3c183d83cfeacc8f69402)
Fixed.