Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
std::meta::template_of (r) gives us a reflection of the template of the
specialization represented by r.  Consider

  template<typename T>
  struct TS {
    template<typename U>
    void fn (T) { }
  };

then template_of (^^TS<int>::fn<int>) gives us ^^TS<int>::fn.

template_of is implemented in terms of DECL_TI_TEMPLATE which gives us
the immediate parent, not the most general template, so for

  void TS<T>::fn(T) [with U = int; T = int]

we get

  template<class U> void TS<T>::fn(T) [with T = int]

The DECL_TI_TEMPLATE is set in instantiate_template:

  /* The DECL_TI_TEMPLATE should always be the immediate parent
     template, not the most general template.  */
  DECL_TI_TEMPLATE (fndecl) = tmpl;

and fn_type_unification passes tmpl which is

  template<class U> void TS<T>::fn(T) [with T = int].

So far so good.  But for alias templates, instantiate_template is
called from instantiate_alias_template and both calls to i_a_t
get the most general template as tmpl.  Thus, for

  template<typename T>
  struct TS {
    template<typename TT>
    using U = int;
  };

TI_TEMPLATE is set to:

  template<class T> template<class TT> using TS<T>::U = int

so eval_template_of gives the wrong result; it should be ^^TS<int>::U.

This seems like an undocumented quirk.  We can deal with this by
doing a substitution into the most general template in eval_template_of
and documenting the behavior, or fix it more generally, which is
riskier.  This patch takes the more general approach.

The any_dependent_template_arguments_p guard is there due to pr71655.C.

        PR c++/124245

gcc/cp/ChangeLog:

        * pt.cc (instantiate_template): For an alias template,
        substitute the most general template so that we get the
        immediate parent template for DECL_TI_TEMPLATE.

gcc/testsuite/ChangeLog:

        * g++.dg/reflect/template_of2.C: Uncomment test.  Add more
        nested testing.
---
 gcc/cp/pt.cc                                | 20 +++++++++++++-----
 gcc/testsuite/g++.dg/reflect/template_of2.C | 23 +++++++++++++++++----
 2 files changed, 34 insertions(+), 9 deletions(-)

diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 2a703330173..42b562149c5 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -24060,11 +24060,21 @@ instantiate_template (tree tmpl, tree orig_args, 
tsubst_flags_t complain)
   if (fndecl == error_mark_node)
     return error_mark_node;
 
-  /* Substituting the type might have recursively instantiated this
-     same alias (c++/117530).  */
-  if (DECL_ALIAS_TEMPLATE_P (gen_tmpl)
-      && (spec = retrieve_specialization (gen_tmpl, targ_ptr, hash)))
-    return spec;
+  if (DECL_ALIAS_TEMPLATE_P (gen_tmpl))
+    {
+      /* Substituting the type might have recursively instantiated this
+        same alias (c++/117530).  */
+      if ((spec = retrieve_specialization (gen_tmpl, targ_ptr, hash)))
+       return spec;
+      /* instantiate_alias_template gets the most general template,
+        but for DECL_TI_TEMPLATE we want the immediate parent template.  */
+      if (tmpl == gen_tmpl && TMPL_ARGS_DEPTH (targ_ptr) > 1)
+       {
+         tree outer = strip_innermost_template_args (targ_ptr, 1);
+         if (!any_dependent_template_arguments_p (outer))
+           tmpl = tsubst (gen_tmpl, outer, tf_none, NULL_TREE);
+       }
+    }
 
   /* The DECL_TI_TEMPLATE should always be the immediate parent
      template, not the most general template.  */
diff --git a/gcc/testsuite/g++.dg/reflect/template_of2.C 
b/gcc/testsuite/g++.dg/reflect/template_of2.C
index 73aa0bcb019..51d9a565185 100644
--- a/gcc/testsuite/g++.dg/reflect/template_of2.C
+++ b/gcc/testsuite/g++.dg/reflect/template_of2.C
@@ -23,10 +23,25 @@ struct TS {
   using U = int;
 };
 
+template<typename T>
+struct TS2 {
+  template<typename U>
+  struct Inner {
+    template<typename V>
+    using A = int;
+  };
+  struct N {
+    template<typename V>
+    using A = int;
+  };
+  template<typename U> struct M { };
+};
+
 static_assert (template_of (^^S::fn<int>) == ^^S::fn);
 static_assert (template_of (^^TS<int>::fn<int>) == ^^TS<int>::fn);
 static_assert (template_of (^^S::U<int>) == ^^S::U);
-// FIXME
-// template<class T> template<class U> using TS<T>::W = int
-//                   template<class U> using TS<int>::W = int
-//static_assert (template_of (^^TS<int>::U<int>) == ^^TS<int>::U);
+static_assert (template_of (^^TS<int>::U<int>) == ^^TS<int>::U);
+static_assert (template_of (^^TS2<int>::Inner<long>::A<char>)
+              == ^^TS2<int>::Inner<long>::A);
+static_assert (template_of (^^TS2<int>::N::A<char>) == ^^TS2<int>::N::A);
+static_assert (template_of (^^TS2<int>::M<char>) == ^^TS2<int>::M);

base-commit: 946f1fefc6d44afad2d3ff1e6a5fe74387bb3aea
-- 
2.56.0

Reply via email to