On 2/4/26 5:21 PM, Jakub Jelinek wrote:
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?
OK.
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
