Hi!
For the N evaluation for iterating expansion stmts where the standard says
to evaluate:
[] consteval {
std::ptrdiff_t result = 0;
for (auto i = begin; i != end; ++i) ++result;
return result; // distance from begin to end
}()
right now (subject to further changes in CWG3140) I wanted to save compile
time/memory and effort to actually construct the lambda and it is evaluated
just using TARGET_EXPRs. On the following testcase it makes a difference,
when the lambda is consteval, the expressions inside of it are evaluated
in immediate context and so the testcase should be accepted, but we
currently reject it when i has consteval-only type and expansion stmt
doesn't appear in an immediate or immediate-escalating function.
The following patch fixes this by forcing in_immediate_context () to be true
around the evaluation.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
2026-02-04 Jakub Jelinek <[email protected]>
PR c++/123611
* pt.cc (finish_expansion_stmt): Temporarily enable
in_immediate_context () for the iterating expansion stmt N
computation.
* g++.dg/reflect/expansion-stmt1.C: New test.
--- gcc/cp/pt.cc.jj 2026-01-28 09:54:50.551498818 +0100
+++ gcc/cp/pt.cc 2026-02-03 11:55:43.683467274 +0100
@@ -33191,6 +33191,10 @@ finish_expansion_stmt (tree expansion_st
begin = cp_build_range_for_decls (loc, expansion_init, &end, true);
if (!error_operand_p (begin) && !error_operand_p (end))
{
+ /* In the standard this is all evaluated inside of a consteval
+ lambda. So, force in_immediate_context () around this. */
+ in_consteval_if_p_temp_override icip;
+ in_consteval_if_p = true;
tree i
= build_target_expr_with_type (begin,
cv_unqualified (TREE_TYPE (begin)),
--- gcc/testsuite/g++.dg/reflect/expansion-stmt1.C.jj 2026-02-03
12:02:57.718039276 +0100
+++ gcc/testsuite/g++.dg/reflect/expansion-stmt1.C 2026-02-03
12:02:01.958993875 +0100
@@ -0,0 +1,49 @@
+// PR c++/123611
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+template <typename T>
+struct S {
+ T s[2];
+ constexpr const T *begin () const { return &s[0]; }
+ constexpr const T *end () const { return &s[2]; }
+};
+
+unsigned
+foo ()
+{
+ unsigned ret = 0;
+ static constexpr S <decltype (^^int)> s = { ^^int, ^^bool };
+ {
+ static constexpr decltype (auto) r = (s);
+ static constexpr auto b = r.begin ();
+ static constexpr auto e = r.end ();
+ using ptrdiff_t = decltype (&s - &s);
+ constexpr auto N = [] consteval {
+ ptrdiff_t res = 0;
+ for (auto i = b; i != e; ++i) ++res;
+ return res;
+ };
+ {
+ static constexpr auto i = b + decltype (b - b) { ptrdiff_t (0) };
+ constexpr auto x = *i;
+ ret += sizeof (typename [: x :]);
+ }
+ {
+ static constexpr auto i = b + decltype (b - b) { ptrdiff_t (1) };
+ constexpr auto x = *i;
+ ret += sizeof (typename [: x :]);
+ }
+ }
+ return ret;
+}
+
+unsigned
+bar ()
+{
+ unsigned ret = 0;
+ static constexpr S <decltype (^^int)> s = { ^^int, ^^bool };
+ template for (constexpr auto x : s)
+ ret += sizeof (typename [: x :]);
+ return ret;
+}
Jakub