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

-- >8 --
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.
---
 gcc/cp/pt.cc                                  | 26 ++++++++++--
 gcc/testsuite/g++.dg/cpp26/expansion-stmt32.C | 14 +++++++
 .../g++.dg/reflect/expansion-stmt2.C          | 40 +++++++++++++++++++
 3 files changed, 77 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp26/expansion-stmt32.C
 create mode 100644 gcc/testsuite/g++.dg/reflect/expansion-stmt2.C

diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 33a9ab3ef6d..ddf492b3435 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 00000000000..772834701c1
--- /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 00000000000..ac3d75d804e
--- /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);
+}

base-commit: 438a7925cd79e84b1e4630dde78b79e04799ce80
-- 
2.53.0

Reply via email to