https://gcc.gnu.org/g:b95955b8854ae1e859cc7c87700922924e9a5e5f

commit r16-7899-gb95955b8854ae1e859cc7c87700922924e9a5e5f
Author: Marek Polacek <[email protected]>
Date:   Mon Mar 2 17:12:56 2026 -0500

    c++: reusing typedefs in template for [PR124229]
    
    This is a crash on code like:
    
      template for (constexpr auto val : define_static_array (enumerators_of 
(^^E)))
        {
          constexpr auto a = annotations_of(val)[0];
          using U = [:type_of(a):];
          constexpr auto m1 = extract<U>(a);
        }
    
    because the template arg to extract wasn't substituted to "info".
    Once I dug deeper I realized this problem isn't tied to Reflection:
    we also crash here:
    
      template for (constexpr auto val : { 42 })
        {
          using U = decltype(val);
          foo<U>();
        }
    
    because we emit code for foo() that still has a DECLTYPE_TYPE in it.
    
    The problem is in tsubst and reusing typedefs.  Normally, for code like
    
      template<typename T> void foo () {
          using U = T;
          U u;
      }
    
    we do the DECL_FUNCTION_SCOPE_P -> retrieve_local_specialization call.
    This call only happens in function templates (that are not explicit
    specializations), but the "template for" above are both in non-template
    functions.  So we end up returning the original tree:
    
            /* The typedef is from a non-template context.  */
            return t;
    
    It seems clear that this is the wrong thing to do, and that the
    DECL_FUNCTION_SCOPE_P code should happen in this scenario as well.
    [temp.decls.general] tells me that "For the purpose of name lookup and
    instantiation, the compound-statement of an expansion-statement is
    considered a template definition." so I'm guessing that we want to
    check for an expansion-statement as well.  As decl_dependent_p says,
    in_expansion_stmt is false when instantiating, so I'm looking for
    sk_template_for.
    
            PR c++/124229
    
    gcc/cp/ChangeLog:
    
            * pt.cc (in_expansion_stmt_p): New.
            (tsubst): When reusing typedefs, do retrieve_local_specialization 
also
            when in_expansion_stmt_p is true.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/cpp26/expansion-stmt32.C: New test.
            * g++.dg/reflect/expansion-stmt2.C: New test.
    
    Reviewed-by: Jason Merrill <[email protected]>

Diff:
---
 gcc/cp/pt.cc                                   | 26 +++++++++++++++--
 gcc/testsuite/g++.dg/cpp26/expansion-stmt32.C  | 14 +++++++++
 gcc/testsuite/g++.dg/reflect/expansion-stmt2.C | 40 ++++++++++++++++++++++++++
 3 files changed, 77 insertions(+), 3 deletions(-)

diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 33a9ab3ef6da..ddf492b34354 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -16832,6 +16832,23 @@ tsubst_splice_expr (tree t, tree args, tsubst_flags_t 
complain, tree in_decl)
   return op;
 }
 
+/* Return true iff we're in an expansion statement.  */
+
+static bool
+in_expansion_stmt_p ()
+{
+  if (in_expansion_stmt)
+    return true;
+
+  /* In instantiations in_expansion_stmt is false.  */
+  for (cp_binding_level *b = current_binding_level;
+       b && b->kind != sk_function_parms;
+       b = b->level_chain)
+    if (b->kind == sk_template_for)
+      return true;
+  return false;
+}
+
 /* Take the tree structure T and replace template parameters used
    therein with the argument vector ARGS.  IN_DECL is an associated
    decl for diagnostics.  If an error occurs, returns ERROR_MARK_NODE.
@@ -16912,9 +16929,12 @@ tsubst (tree t, tree args, tsubst_flags_t complain, 
tree in_decl)
          tree gen_args = tsubst (DECL_TI_ARGS (decl), args, complain, in_decl);
          r = retrieve_specialization (tmpl, gen_args, 0);
        }
-      else if (DECL_FUNCTION_SCOPE_P (decl)
-              && DECL_TEMPLATE_INFO (DECL_CONTEXT (decl))
-              && uses_template_parms (DECL_TI_ARGS (DECL_CONTEXT (decl))))
+      else if ((DECL_FUNCTION_SCOPE_P (decl)
+               && DECL_TEMPLATE_INFO (DECL_CONTEXT (decl))
+               && uses_template_parms (DECL_TI_ARGS (DECL_CONTEXT (decl))))
+              /* The { } of an expansion-statement is considered a template
+                 definition.  */
+              || in_expansion_stmt_p ())
        r = retrieve_local_specialization (decl);
       else
        /* The typedef is from a non-template context.  */
diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt32.C 
b/gcc/testsuite/g++.dg/cpp26/expansion-stmt32.C
new file mode 100644
index 000000000000..772834701c19
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt32.C
@@ -0,0 +1,14 @@
+// PR c++/124229
+// { dg-do compile { target c++26 } }
+
+template<typename T>
+void foo () { }
+
+int main()
+{
+  template for (constexpr auto val : { 42 })
+    {
+      using U = decltype(val);
+      foo<U>();
+    }
+}
diff --git a/gcc/testsuite/g++.dg/reflect/expansion-stmt2.C 
b/gcc/testsuite/g++.dg/reflect/expansion-stmt2.C
new file mode 100644
index 000000000000..ac3d75d804e2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/expansion-stmt2.C
@@ -0,0 +1,40 @@
+// PR c++/124229
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <meta>
+
+enum class E
+{
+  X [[=1]]
+};
+
+template<typename T>
+void foo ()
+{
+}
+
+void
+f ()
+{
+  template for (constexpr auto val : define_static_array (enumerators_of 
(^^E)))
+    {
+      constexpr auto a = annotations_of(val)[0];
+      using U = [:type_of(a):];
+      constexpr auto m1 = extract<U>(a);
+      constexpr auto m2 = extract<typename [:type_of(a):]>(a);
+    }
+
+  template for (constexpr auto val : define_static_array (enumerators_of 
(^^E)))
+    {
+      constexpr auto a = annotations_of(val)[0];
+      using U = [:type_of(a):];
+      foo<U>();
+    }
+
+  [](auto a) {
+      using V = [:type_of(^^a):];;
+      constexpr auto rt = std::meta::reflect_constant (1);
+      constexpr auto m1 = std::meta::extract<V>(rt);
+  }(1);
+}

Reply via email to