On 9/30/26 1:06 AM, Marek Polacek wrote:
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

OK.
-- >8 --
This patch fixes the missing part of 127349 when we have a namespace
splice scope in a template template argument:

   namespace N { template<class> struct X {}; }
   template<template<class> class> struct S {};
   template<auto ns> void f () {
     S<[:ns:]::template X> s;
   }
   void h () { f<^^N>(); }

which currently wrongly errors with "no class template named 'X' in 'N'".
The problem is that we don't handle the case when the context in tsubst's
UNBOUND_CLASS_TEMPLATE case substitutes into a NAMESPACE_DECL.  We
already handle it correctly in the TYPENAME_TYPE case.  In the
UNBOUND_CLASS_TEMPLATE case we only want a DECL_TYPE_TEMPLATE_P as the
result, so there is no shared routine.

This patch also fixes ICEs on the lambdas in h2/h3, where we crash
because dependent_type_p gets a NAMESPACE_DECL but it only wants types.

        PR c++/127349

gcc/cp/ChangeLog:

        * pt.cc (tsubst) <case UNBOUND_CLASS_TEMPLATE>: Handle the case
        when the context was substituted into a NAMESPACE_DECL.

gcc/testsuite/ChangeLog:

        * g++.dg/reflect/ns7.C: Remove unused declarations.
        * g++.dg/reflect/ns8.C: New test.
---
  gcc/cp/pt.cc                       | 22 +++++++++++++++
  gcc/testsuite/g++.dg/reflect/ns7.C |  3 --
  gcc/testsuite/g++.dg/reflect/ns8.C | 44 ++++++++++++++++++++++++++++++
  3 files changed, 66 insertions(+), 3 deletions(-)
  create mode 100644 gcc/testsuite/g++.dg/reflect/ns8.C

diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 5696828673d..6c8d0e2de71 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -18064,6 +18064,28 @@ tsubst (tree t, tree args, tsubst_flags_t complain, 
tree in_decl)
        if (ctx == error_mark_node)
          return error_mark_node;
+ /* We had [:X:]::template NAME where X was substituted into
+          a NAMESPACE_DECL and not a type.  */
+       if (TREE_CODE (ctx) == NAMESPACE_DECL)
+         {
+           tree decl = lookup_qualified_name (ctx, name);
+           if (decl == error_mark_node || TREE_CODE (decl) == TREE_LIST)
+             {
+               if (complain & tf_error)
+                 qualified_name_lookup_error (ctx, name, decl, input_location);
+               return error_mark_node;
+             }
+           /* As in make_unbound_class_template, we want only a class or
+              alias template.  */
+           if (!DECL_TYPE_TEMPLATE_P (decl))
+             {
+               if (complain & tf_error)
+                 error ("%qD is not a class template", decl);
+               return error_mark_node;
+             }
+           return decl;
+         }
+
        return make_unbound_class_template (ctx, name, parm_list, complain);
        }
diff --git a/gcc/testsuite/g++.dg/reflect/ns7.C b/gcc/testsuite/g++.dg/reflect/ns7.C
index a9ba862d77b..faf59f8b116 100644
--- a/gcc/testsuite/g++.dg/reflect/ns7.C
+++ b/gcc/testsuite/g++.dg/reflect/ns7.C
@@ -9,13 +9,10 @@ namespace N {
    template<class T> using A = X<T>;
    inline namespace I { template<class> struct V {}; }
    using namespace M;
-  template<class, class> struct Z {};
    int v;
    template<class T> void fn ();
  }
-template<template<class> class> struct S {};
-
  template<auto ns>
  void
  f ()
diff --git a/gcc/testsuite/g++.dg/reflect/ns8.C 
b/gcc/testsuite/g++.dg/reflect/ns8.C
new file mode 100644
index 00000000000..faca9d15a06
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/ns8.C
@@ -0,0 +1,44 @@
+// PR c++/127349
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+template<class> struct Y {};
+namespace M { template<class> struct W {}; }
+namespace N {
+  template<class> struct X {};
+  template<class T> using A = X<T>;
+  inline namespace I { template<class> struct V {}; }
+  using namespace M;
+  template<class, class> struct Z {};
+  int v;
+  template<class T> void fn ();
+}
+
+template<template<class> class> struct S {};
+
+template<auto ns>
+void
+f ()
+{
+  S<[:ns:]::template X> s1;
+  S<[:ns:]::template A> s2;
+  S<[:ns:]::template V> s3;
+  S<[:ns:]::template W> s4;
+  S<[:ns:]::template Y> s5; // { dg-error "'Y' is not a member of 'N'" }
+  S<[:ns:]::template v> s6; // { dg-error "'N::v' is not a class template" }
+  S<[:ns:]::template fn> s7; // { dg-error "not a class template" }
+  S<[:ns:]::template Z> s8; // { dg-error "mismatch" }
+}
+void h () { f<^^N>(); }
+
+template<auto ns>
+void h2 () {
+  [] <class T> () { S<[:ns:]::template X> s; }.template operator()<int> ();
+}
+void f2 () { h2<^^N>(); }
+
+template<auto ns>
+void h3 () {
+  [] <auto r> () { S<[:r:]::template X> s; }.template operator()<ns> ();
+}
+void f3 () { h3<^^N>(); }

base-commit: fa82fc957bccc0f29f2b41e66d1881ba821369ea

Reply via email to