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