On Mon, Mar 01, 2021 at 09:29:19PM -0500, Jason Merrill via Gcc-patches wrote: > On 3/1/21 7:59 PM, Marek Polacek wrote: > > On Mon, Mar 01, 2021 at 03:08:58PM -0500, Jason Merrill wrote: > > > On 3/1/21 2:54 PM, Marek Polacek wrote: > > > > On Thu, Feb 25, 2021 at 10:45:29PM -0500, Jason Merrill via Gcc-patches > > > > wrote: > > > > > On 2/25/21 5:41 PM, Marek Polacek wrote: > > > > > > On Thu, Feb 25, 2021 at 10:59:49AM -0500, Jason Merrill wrote: > > > > > > > On 2/12/21 6:12 PM, Marek Polacek wrote: > > > > > > > > We represent deduction guides with FUNCTION_DECLs, but they are > > > > > > > > built > > > > > > > > without DECL_CONTEXT > > > > > > > > > > > > > > Hmm, that seems wrong: "A deduction-guide shall be declared in the > > > > > > > same scope as the corresponding class template and, for a member > > > > > > > class > > > > > > > template, with the same access." But it probably isn't necessary > > > > > > > to change > > > > > > > this: > > > > > > > > > > > > > > > leading to an ICE in type_dependent_expression_p > > > > > > > > on the assert that the type of a function template with no > > > > > > > > dependent > > > > > > > > (innermost!) template arguments must be non-dependent. > > > > > > > > Consider the > > > > > > > > attached class-deduction79.C: we create a deduction guide: > > > > > > > > > > > > > > > > template<class T> G(T)-> E<Z>::G<T> > > > > > > > > > > > > > > > > we deduce T and create a partial instantiation: > > > > > > > > > > > > > > > > G(T) -> E<Z>::G<T> [with T = int] > > > > > > > > > > > > > > > > And then do_class_deduction wants to create a CALL_EXPR from > > > > > > > > the above > > > > > > > > using build_new_function_call -> build_over_call which calls > > > > > > > > mark_used > > > > > > > > -> maybe_instantiate_noexcept -> type_dependent_expression_p. > > > > > > > > > > > > > > > > There, the innermost template arguments are non-dependent > > > > > > > > (<int>), but > > > > > > > > the fntype is dependent -- the return type is a TYPENAME_TYPE, > > > > > > > > and > > > > > > > > since we have no DECL_CONTEXT, this check holds: > > > > > > > > > > > > > > > > /* Otherwise, if the function decl isn't from a dependent > > > > > > > > scope, it can't be > > > > > > > > type-dependent. Checking this is important for > > > > > > > > functions with auto return > > > > > > > > type, which looks like a dependent type. */ > > > > > > > > if (TREE_CODE (expression) == FUNCTION_DECL > > > > > > > > && !(DECL_CLASS_SCOPE_P (expression) > > > > > > > > && dependent_type_p (DECL_CONTEXT (expression))) > > > > > > > > > > > > > > > > whereupon we ICE. > > > > > > > > > > > > > > > > Experiments with setting DECL_CONTEXT didn't pan out. > > > > > > > > > > > > > > In c8 of the PR it looks like you were using the class itself as > > > > > > > DECL_CONTEXT; the quote above says that the right context is the > > > > > > > enclosing > > > > > > > scope of the class. > > > > > > > > > > > > Sadly, using CP_TYPE_CONTEXT (type) would result in a crash in > > > > > > retrieve_specialization: > > > > > > > > > > > > /* There should be as many levels of arguments as there are > > > > > > levels of parameters. */ > > > > > > gcc_assert (TMPL_ARGS_DEPTH (args) > > > > > > == (TREE_CODE (tmpl) == TEMPLATE_DECL > > > > > > ? TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (tmpl)) > > > > > > : template_class_depth (DECL_CONTEXT (tmpl)))); > > > > > > > > > > Yeah, probably simpler not to bother. > > > > > > > > > > > > > So perhaps we > > > > > > > > just want to skip the assert for deduction guides, because they > > > > > > > > are > > > > > > > > a little special. Better ideas solicited. > > > > > > > > > > > > > > In c3 you mention that one of the variants broke with r269093; > > > > > > > this is > > > > > > > because my change to check CLASSTYPE_TEMPLATE_INSTANTIATION is > > > > > > > false for the > > > > > > > template pattern itself (E<Z>). > > > > > > > > > > > > And the original test started with my r11-1713 because using > > > > > > TREE_TYPE > > > > > > directly instead of decltype (which is a non-deduced context) means > > > > > > we > > > > > > can deduced from the argument. > > > > > > > But I think probably the right answer is to defer this deduction > > > > > > > until the > > > > > > > enclosing scope is non-dependent, i.e. (untested) > > > > > > > > > > > > Thanks. That mostly works, except the new > > > > > > class-deduction-aggr[89].C > > > > > > tests. Consider 8: > > > > > > > > > > > > namespace N { > > > > > > template <typename, typename> struct S { > > > > > > template <typename T, typename U> S(T, U); > > > > > > }; > > > > > > } // namespace N > > > > > > template <int> struct E { > > > > > > template <typename T> struct G { T t; }; > > > > > > void fn() { G{N::S<char, int>{'a', 1}}; } > > > > > > }; > > > > > > > > > > > > void > > > > > > g () > > > > > > { > > > > > > E<1> e; > > > > > > e.fn (); > > > > > > } > > > > > > > > > > > > With your patch, when in do_class_deduction when > > > > > > processing_template_decl, > > > > > > we just return. When we call do_class_deduction again when p_t_d > > > > > > is 0, > > > > > > maybe_aggr_guide returns early here: > > > > > > > > > > > > if (!CP_AGGREGATE_TYPE_P (type)) > > > > > > return NULL_TREE > > > > > > > > > > > > because G is not complete (and rightly so, we didn't instantiate > > > > > > it). So > > > > > > we aren't able to deduce the template parameters. I'm not sure if > > > > > > I should > > > > > > pursue this direction further. :( > > > > > > > > > > I think so; we just need to test CP_AGGREGATE_TYPE_P on the original > > > > > template pattern instead of the instantiation E<1>::G. > > > > > > > > I'm sorry, I've got stuck again. > > > > > > > > Yes, using the original template pattern helps us get past the > > > > CP_AGGREGATE_TYPE_P check. > > > > > > > > However, using TREE_TYPE (DECL_TI_TEMPLATE (tmpl)) as the type of the > > > > deduction guide > > > > means the guide will be "template<class T> G(T)-> E<<anonymous> > > > > >::G<T>" which > > > > results in > > > > > > > > class-deduction-aggr8.C:11:15: error: invalid use of dependent type > > > > 'typename E<<anonymous> >::G<N::S<char, int> >' > > > > > > > > which makes sense I guess: when we defer building up the guide until > > > > we've instantiated E<1>, finding the dependent type E<> is not expected. > > > > > > Yeah, I was only thinking to use the pattern for the aggregate check. > > > > Ack. Though I think I also have to use the pattern here: > > > > init = reshape_init (type, init, complain); > > > > otherwise reshape_init returns a TARGET_EXPR and we immediately > > crash in collect_ctor_idx_types because that only expects a CONSTRUCTOR. > > And what we need to get is the type T -- of the constructor's index. > > > > But creating the guide with "struct E<1>::G<T>" as its type seems > > > > wrong; I'm not even sure if a guide like > > > > > > > > template<class T> G(T)-> E<1>::G<T> > > > > > > > > makes sense. > > > > > > It looks fine to me. > > > > > > > In any case the deduction fails (when we call > > > > build_new_function_call in do_class_deduction), because we've got > > > > a mismatch: the template parameter T has level 1, but the template > > > > function parameter has level 2. > > > > > > Sure, because E<1>::G<T> has been partially instantiated, so the T has > > > been > > > reduced from level 2 to 1. > > > > Right. > > > You'll need to do a similar partial instantiation for building the > > > deduction > > > guide, either as part of the deduction guide rewriting or on the > > > constructor > > > before rewriting. > > > > So I've tried. I can't actually tsubst the constructor itself, because it > > at this point contains an AGGR_INIT_EXPR, which tsubst* can't handle. But > > what I could do is > > > > parms = tsubst (parms, DECL_TI_ARGS (tmpl), complain, init); > > > > just after the call to collect_ctor_idx_types. After all, this is what > > we care about and create the function template parameters from. So now > > T's level is reduced to 1, and the guide we create is > > > > template<class T> G(T)-> E<1>::G<T> > > > > This guide is then chosen in do_class_deduction -> build_new_function_call, > > but we crash in fn_type_unification -> instantiate_template (after we've > > deduced T to N::S<char, int>) in retrieve_specialization: > > > > gcc_assert (TMPL_ARGS_DEPTH (args) > > == (TREE_CODE (tmpl) == TEMPLATE_DECL > > ? TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (tmpl)) > > : template_class_depth (DECL_CONTEXT (tmpl)))); > > > > args is <1, S> (depth 2), tmpl is our deduction guide, and > > DECL_TEMPLATE_PARMS (tmpl) is [1 T] (depth 1). > > I'd think args should only be S (depth 1).
Ah! I think I finally got a working patch. Thanks for all the help! Added class-deduction-aggr10.C to test the deduction in more nested classes. Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? Additionally, I've built cmcstl2 and Boost 1.75. -- >8 -- We represent deduction guides with FUNCTION_DECLs, but they are built without DECL_CONTEXT, leading to an ICE in type_dependent_expression_p on the assert that the type of a function template with no dependent (innermost!) template arguments must be non-dependent. Consider the attached class-deduction79.C: we create a deduction guide: template<class T> G(T)-> E<Z>::G<T> we deduce T and create a partial instantiation: G(T) -> E<Z>::G<T> [with T = int] And then do_class_deduction wants to create a CALL_EXPR from the above using build_new_function_call -> build_over_call which calls mark_used -> maybe_instantiate_noexcept -> type_dependent_expression_p. There, the innermost template arguments are non-dependent (<int>), but the fntype is dependent -- the return type is a TYPENAME_TYPE, and since we have no DECL_CONTEXT, this check holds: /* Otherwise, if the function decl isn't from a dependent scope, it can't be type-dependent. Checking this is important for functions with auto return type, which looks like a dependent type. */ if (TREE_CODE (expression) == FUNCTION_DECL && !(DECL_CLASS_SCOPE_P (expression) && dependent_type_p (DECL_CONTEXT (expression))) whereupon we ICE. This patch fixes it by deferring the class deduction until the enclosing scope is non-dependent. build_deduction_guide and maybe_aggr_guide needed a little tweaking to make the deduction work in a member template. Co-Authored-By: Jason Merrill <ja...@redhat.com> gcc/cp/ChangeLog: PR c++/97034 PR c++/99009 * pt.c (build_deduction_guide): Use INNERMOST_TEMPLATE_ARGS. (maybe_aggr_guide): Use the original template type where needed. In a class member template, partially instantiate the result of collect_ctor_idx_types. (do_class_deduction): Defer the deduction until the enclosing scope is non-dependent. gcc/testsuite/ChangeLog: PR c++/97034 PR c++/99009 * g++.dg/cpp1z/class-deduction81.C: New test. * g++.dg/cpp1z/class-deduction82.C: New test. * g++.dg/cpp2a/class-deduction-aggr8.C: New test. * g++.dg/cpp2a/class-deduction-aggr9.C: New test. * g++.dg/cpp2a/class-deduction-aggr10.C: New test. --- gcc/cp/pt.c | 36 +++++++++++++++++-- .../g++.dg/cpp1z/class-deduction81.C | 20 +++++++++++ .../g++.dg/cpp1z/class-deduction82.C | 12 +++++++ .../g++.dg/cpp2a/class-deduction-aggr10.C | 21 +++++++++++ .../g++.dg/cpp2a/class-deduction-aggr8.C | 19 ++++++++++ .../g++.dg/cpp2a/class-deduction-aggr9.C | 18 ++++++++++ 6 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction81.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction82.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr10.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr8.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr9.C diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 8d65a6e5bd2..a4686e0affb 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -28643,7 +28643,7 @@ build_deduction_guide (tree type, tree ctor, tree outer_args, tsubst_flags_t com tree ctmpl = CLASSTYPE_TI_TEMPLATE (type); tparms = DECL_TEMPLATE_PARMS (ctmpl); - targs = CLASSTYPE_TI_ARGS (type); + targs = INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (type)); ci = NULL_TREE; fargs = NULL_TREE; loc = DECL_SOURCE_LOCATION (ctmpl); @@ -28866,8 +28866,22 @@ maybe_aggr_guide (tree tmpl, tree init, vec<tree,va_gc> *args) if (init == NULL_TREE) return NULL_TREE; + /* We might be creating a guide for a class member template, e.g., + + template<typename U> struct A { + template<typename T> struct B { T t; }; + }; + + At this point, A will have been instantiated. Below, we need to + use both A<U>::B<T> (TEMPLATE_TYPE) and A<int>::B<T> (TYPE) types. */ + const bool member_template_p + = (DECL_TEMPLATE_INFO (tmpl) + && DECL_MEMBER_TEMPLATE_P (DECL_TI_TEMPLATE (tmpl))); tree type = TREE_TYPE (tmpl); - if (!CP_AGGREGATE_TYPE_P (type)) + tree template_type = (member_template_p + ? TREE_TYPE (DECL_TI_TEMPLATE (tmpl)) + : type); + if (!CP_AGGREGATE_TYPE_P (template_type)) return NULL_TREE; /* No aggregate candidate for copy-initialization. */ @@ -28884,10 +28898,21 @@ maybe_aggr_guide (tree tmpl, tree init, vec<tree,va_gc> *args) tree parms = NULL_TREE; if (BRACE_ENCLOSED_INITIALIZER_P (init)) { - init = reshape_init (type, init, complain); + init = reshape_init (template_type, init, complain); if (init == error_mark_node) return NULL_TREE; parms = collect_ctor_idx_types (init, parms); + /* If we're creating a deduction guide for a member class template, + we've used the original template pattern type for the reshape_init + above; this is done because we want PARMS to be a template parameter + type, something that can be deduced when used as a function template + parameter. At this point the outer class template has already been + partially instantiated (we deferred the deduction until the enclosing + scope is non-dependent). Therefore we have to partially instantiate + PARMS, so that its template level is properly reduced and we don't get + mismatches when deducing types using the guide with PARMS. */ + if (member_template_p) + parms = tsubst (parms, DECL_TI_ARGS (tmpl), complain, init); } else if (TREE_CODE (init) == TREE_LIST) { @@ -29225,6 +29250,11 @@ do_class_deduction (tree ptype, tree tmpl, tree init, if (DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl)) return ptype; + /* Wait until the enclosing scope is non-dependent. */ + if (DECL_CLASS_SCOPE_P (tmpl) + && dependent_type_p (DECL_CONTEXT (tmpl))) + return ptype; + /* Initializing one placeholder from another. */ if (init && TREE_CODE (init) == TEMPLATE_PARM_INDEX && is_auto (TREE_TYPE (init)) diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction81.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction81.C new file mode 100644 index 00000000000..86a68248157 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction81.C @@ -0,0 +1,20 @@ +// PR c++/97034 +// { dg-do compile { target c++17 } } + +template <typename Z> +struct E { + template <typename T> + struct G { + T t; + G(T) { } + }; + + void fn() { G{1}; } +}; + +void +g () +{ + E<int> e; + e.fn (); +} diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction82.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction82.C new file mode 100644 index 00000000000..238024c508f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction82.C @@ -0,0 +1,12 @@ +// PR c++/99009 +// { dg-do compile { target c++17 } } + +template<typename> struct B { + B(int = A()) {} + template <typename ...> struct A; +}; + +template<typename T> struct X { + template <T...> struct Y; + X() { Y y; }; +}; diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr10.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr10.C new file mode 100644 index 00000000000..be922bbfb73 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr10.C @@ -0,0 +1,21 @@ +// PR c++/97034 +// { dg-do compile { target c++20 } } + +namespace N { +template <typename, typename> struct S { + template <typename T, typename U> S(T, U); +}; +} // namespace N +template <int I> struct E { + template<typename U> struct M { + template <typename T> struct G { T t; }; + void fn() { G{N::S<char, int>{'a', 1}}; } + }; +}; + +void +g () +{ + E<1>::M<int> m; + m.fn (); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr8.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr8.C new file mode 100644 index 00000000000..399061100ae --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr8.C @@ -0,0 +1,19 @@ +// PR c++/97034 +// { dg-do compile { target c++20 } } + +namespace N { +template <typename, typename> struct S { + template <typename T, typename U> S(T, U); +}; +} // namespace N +template <int> struct E { + template <typename T> struct G { T t; }; + void fn() { G{N::S<char, int>{'a', 1}}; } +}; + +void +g () +{ + E<1> e; + e.fn (); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr9.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr9.C new file mode 100644 index 00000000000..245a04cd5f9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr9.C @@ -0,0 +1,18 @@ +// PR c++/97034 +// { dg-do compile { target c++20 } } + +template<typename> +struct E { + template <typename T> + struct G { + T t; + }; + + void fn() { G{1}; } +}; + +void +g () { + E<int> e; + e.fn (); +} base-commit: 41fbacdd10305654b1d10f887fa3f4677f9b8f34 -- 2.29.2