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

Reply via email to