]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++/reflection: reflect on dependent class template [PR124926]
authorMarek Polacek <polacek@redhat.com>
Wed, 22 Apr 2026 15:36:37 +0000 (11:36 -0400)
committerMarek Polacek <polacek@redhat.com>
Thu, 23 Apr 2026 13:08:41 +0000 (09:08 -0400)
Here we issue a bogus error for

  ^^Cls<T>::template Inner

where Inner turns out to be a class type, but we created a SCOPE_REF
because we can't know in advance what it will substitute into, and

  ^^typename Cls<T>::template Inner

is invalid.  The typename can only be used in

  ^^typename Cls<T>::template Inner<int>

We're taking a reflection so both types and non-types are valid, so
I think we shouldn't give the error for ^^, and take the reflection
of the TEMPLATE_DECL.

PR c++/124926

gcc/cp/ChangeLog:

* pt.cc (tsubst_qualified_id): Rename name_lookup_p parameter to
reflecting_p.  Check !reflecting_p instead of name_lookup_p.  Do
not give the "instantiation yields a type" error when reflecting_p
is true.
(tsubst_expr) <case REFLECT_EXPR>: Adjust the call to
tsubst_qualified_id.

gcc/testsuite/ChangeLog:

* g++.dg/reflect/dep15.C: New test.

Reviewed-by: Patrick Palka <ppalka@redhat.com>
gcc/cp/pt.cc
gcc/testsuite/g++.dg/reflect/dep15.C [new file with mode: 0644]

index 5d97ca7997f4eda90aa39b65e28b28e2e15960ca..551c8bcbb682cf1e23d307925f36a9d6a22294a0 100644 (file)
@@ -18238,12 +18238,12 @@ tsubst_baselink (tree baselink, tree object_type,
    true if the qualified-id will be a postfix-expression in-and-of
    itself; false if more of the postfix-expression follows the
    QUALIFIED_ID.  ADDRESS_P is true if the qualified-id is the operand
-   of "&".  NAME_LOOKUP_P is true if we intend to perform name lookup.  */
+   of "&".  REFLECTING_P is true if this SCOPE_REF is an operand of ^^.  */
 
 static tree
 tsubst_qualified_id (tree qualified_id, tree args,
                     tsubst_flags_t complain, tree in_decl,
-                    bool done, bool address_p, bool name_lookup_p = true)
+                    bool done, bool address_p, bool reflecting_p = false)
 {
   tree expr;
   tree scope;
@@ -18322,7 +18322,9 @@ tsubst_qualified_id (tree qualified_id, tree args,
       else
        expr = lookup_qualified_name (scope, expr, LOOK_want::NORMAL, false);
       if (TREE_CODE (TREE_CODE (expr) == TEMPLATE_DECL
-                    ? DECL_TEMPLATE_RESULT (expr) : expr) == TYPE_DECL)
+                    ? DECL_TEMPLATE_RESULT (expr) : expr) == TYPE_DECL
+         /* For ^^T::X, we'll take both types and non-types.  */
+         && !reflecting_p)
        {
          if (complain & tf_error)
            {
@@ -18368,7 +18370,7 @@ tsubst_qualified_id (tree qualified_id, tree args,
                                 expr, input_location);
   /* For ^^S::mem, we do not want to create the dummy object that
      finish_non_static_data_member would give us.  */
-  else if (TYPE_P (scope) && name_lookup_p)
+  else if (TYPE_P (scope) && !reflecting_p)
     {
       expr = (adjust_result_of_qualified_name_lookup
              (expr, scope, current_nonlambda_class_type ()));
@@ -23355,7 +23357,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
        else if (TREE_CODE (h) == SCOPE_REF)
          h = tsubst_qualified_id (h, args, complain, in_decl,
                                   /*done=*/true, /*address_p=*/false,
-                                  /*name_lookup_p=*/false);
+                                  /*reflecting_p=*/true);
        else
          {
            /* [expr.reflect] The id-expression of a reflect-expression is
diff --git a/gcc/testsuite/g++.dg/reflect/dep15.C b/gcc/testsuite/g++.dg/reflect/dep15.C
new file mode 100644 (file)
index 0000000..4027395
--- /dev/null
@@ -0,0 +1,85 @@
+// PR c++/124926
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+template <typename T, typename U>
+constexpr bool is_same_v = false;
+
+template <typename T>
+constexpr bool is_same_v<T, T> = true;
+
+// Class template
+template <typename>
+struct C1 {
+  template <typename> struct Inner {};
+};
+
+template <typename T>
+constexpr auto vt1 = ^^C1<T>::template Inner;
+constexpr auto r1 = vt1<int>;
+typename [:r1:]<int> a;
+static_assert(r1 == ^^C1<int>::template Inner);
+static_assert(is_same_v<decltype(a), C1<int>::Inner<int>>);
+
+// Class template with typename
+template <typename>
+struct C6 {
+  template <typename> struct Inner {};
+};
+
+template <typename T>
+constexpr auto vt6 = ^^typename C6<T>::template Inner<T>;
+constexpr auto r6 = vt6<int>;
+typename [:r6:] d;
+static_assert(r6 == ^^C6<int>::template Inner<int>);
+static_assert(is_same_v<decltype(d), C6<int>::Inner<int>>);
+
+// Variable template
+template <typename>
+struct C2 {
+  template <typename> static constexpr int Inner = 42;
+};
+
+template <typename T>
+constexpr auto vt2 = ^^C2<T>::template Inner;
+constexpr auto r2 = vt2<int>;
+constexpr int i = template [:r2:]<int>;
+static_assert(i == 42);
+static_assert(vt2<int> == ^^C2<int>::template Inner);
+
+// Function template
+template <typename>
+struct C3 {
+  template <typename T> static constexpr int Inner (T t) { return t; };
+};
+template <typename T>
+constexpr auto vt3 = ^^C3<T>::template Inner;
+constexpr auto r3 = vt3<int>;
+static_assert(template [:r3:](42) == 42);
+static_assert(vt3<int> == ^^C3<int>::template Inner);
+
+// Alias template
+template <typename>
+struct C4 {
+  template <typename T> using Inner = C4<T>;
+};
+template <typename T>
+constexpr auto vt4 = ^^C4<T>::template Inner;
+constexpr auto r4 = vt4<int>;
+typename [:r4:]<int> b;
+static_assert(is_same_v<decltype(b), C4<int>>);
+static_assert(vt4<int> == ^^C4<int>::template Inner);
+
+// Alias template with typename
+template<typename T> struct X { };
+
+template <typename>
+struct C5 {
+  template <typename T> using Inner = X<T>;
+};
+template <typename T>
+constexpr auto vt5 = ^^typename C5<T>::template Inner<T>;
+constexpr auto r5 = vt5<int>;
+typename [:r5:] c;
+static_assert(is_same_v<decltype(c), X<int>>);
+static_assert(vt5<int> == ^^C5<int>::template Inner<int>);