Hi! I've punted up on proxy iterators because I didn't know how to construct a test for those.
The following patch adds support for thosse. Besides converting those to valuet if needed and for classes making sure they live in a TARGET_EXPR where they can be subsequently destructed, the patch adds CLEANUP_POINT_EXPRs around the deref and inc expressions such that the TARGET_EXPRs in those expressions are immediately destructed after returning what we need. cmp doesn't need that because cmp = condition_conversion (cmp); already adds that. Without the CLEANUP_POINT_EXPRs stuff is destructed (and marked as if out of lifetime) only in some CLEANUP_POINT_EXPR surrounding the metafn call, which is certainly too late for stuff that is reentered and left again in every iteration. I wrongly thought I'd need to duplicate the first part of CLEANUP_POINT_EXPR handling in reflect.cc (which is complicated because struct constexpr_ctx is only defined in constexpr.cc), then cxx_eval_constant_expression e.g. deref and then duplicate the second part of CLEANUP_POINT_EXPR handling, but it seems that the CLEANUP_POINT_EXPR evaluation just returns what was returned from the recursive call on its operand, which is I think exactly what we need. This change broke 2 testcases, reflect_constant_array4.C and define_static_array4.C, but IMHO correctly so, apparently what I had there was creating std::pair<const int &, const int &> with references to something that should have been immediately destructed in each iteration. So, the patch fixes those tests to use std::pair<int, int> instead. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2026-03-11 Jakub Jelinek <[email protected]> PR c++/124425 * reflect.cc (get_range_elts): Handle proxy iterators. Call fold_build_cleanup_point_expr on both deref and inc. * g++.dg/reflect/define_static_array4.C: Enable commented out static_asserts and fix up the first one. (as_pair): Make sure to return std::pair <int, int>. * g++.dg/reflect/reflect_constant_array4.C: Enable commented out static_asserts and fix up the first one. (as_pair): Make sure to return std::pair <int, int>. * g++.dg/reflect/reflect_constant_array7.C: New test. * g++.dg/reflect/reflect_constant_array8.C: New test. --- gcc/cp/reflect.cc.jj 2026-03-10 14:26:48.798118431 +0100 +++ gcc/cp/reflect.cc 2026-03-10 18:59:10.006485933 +0100 @@ -872,15 +872,26 @@ get_range_elts (location_t loc, const co *non_constant_p = true; return NULL_TREE; } - // TODO: For REFLECT_CONSTANT_* handle proxy iterators. if (TYPE_MAIN_VARIANT (TREE_TYPE (deref)) != valuet) { - if (!cxx_constexpr_quiet_p (ctx)) - error_at (loc, "unexpected type %qT of iterator dereference", - TREE_TYPE (deref)); - *non_constant_p = true; - return NULL_TREE; + deref = perform_implicit_conversion (valuet, deref, tf_warning_or_error); + if (error_operand_p (deref)) + { + *non_constant_p = true; + return NULL_TREE; + } + if (CLASS_TYPE_P (valuet)) + { + deref = force_target_expr (valuet, deref, tf_warning_or_error); + if (error_operand_p (deref)) + { + *non_constant_p = true; + return NULL_TREE; + } + } } + deref = fold_build_cleanup_point_expr (TREE_TYPE (deref), deref); + inc = fold_build_cleanup_point_expr (TREE_TYPE (inc), inc); retvec.truncate (0); /* while (begin != end) { push (*begin); ++begin; } */ do --- gcc/testsuite/g++.dg/reflect/define_static_array4.C.jj 2026-01-15 16:33:47.006097976 +0100 +++ gcc/testsuite/g++.dg/reflect/define_static_array4.C 2026-03-10 19:36:17.024158934 +0100 @@ -8,10 +8,9 @@ constexpr int x[]{1,2,3,4,5}; constexpr int y[]{11,12,13,14,15}; constexpr auto as_pair = []<typename T1, typename T2>(const std::tuple<T1, T2>& t) static -{ return std::pair<T1, T2>(t); }; +{ return std::make_pair (std::get <0> (t), std::get <1> (t)); }; constexpr std::span spn = std::define_static_array(std::views::zip (x, y) | std::views::transform (as_pair)); -// FIXME these should pass -// static_assert (^^decltype(spn) == ^^std::span<const std::pair<int, int>>); -// static_assert (spn[2].first == 3); -// static_assert (spn[2].second == 13); +static_assert (^^decltype(spn) == ^^const std::span<const std::pair<int, int>>); +static_assert (spn[2].first == 3); +static_assert (spn[2].second == 13); --- gcc/testsuite/g++.dg/reflect/reflect_constant_array4.C.jj 2026-01-15 16:33:47.014097840 +0100 +++ gcc/testsuite/g++.dg/reflect/reflect_constant_array4.C 2026-03-10 19:29:01.127463561 +0100 @@ -9,12 +9,10 @@ using namespace std::meta; constexpr int x[]{1,2,3,4,5}; constexpr int y[]{11,12,13,14,15}; -constexpr auto as_pair = []<typename T1, typename T2>(const std::tuple<T1, T2>& t) static -{ return std::pair<T1, T2>(t); }; +constexpr auto as_pair = []<typename T1, typename T2>(const std::tuple<T1, T2>& t) +{ const auto &[x, y] = t; return std::make_pair(x, y); }; constexpr info r = reflect_constant_array(std::views::zip (x, y) | std::views::transform (as_pair)); -// FIXME this should be pass -// static_assert (type_of (^^r) == ^^const std::pair<int, int>[5]); -// static_assert ([: r :][2].first == 3); -// static_assert ([: r :][2].second == 13); - +static_assert (type_of (r) == ^^const std::pair<int, int>[5]); +static_assert ([: r :][2].first == 3); +static_assert ([: r :][2].second == 13); --- gcc/testsuite/g++.dg/reflect/reflect_constant_array7.C.jj 2026-03-10 18:15:46.853307036 +0100 +++ gcc/testsuite/g++.dg/reflect/reflect_constant_array7.C 2026-03-10 18:17:28.926588446 +0100 @@ -0,0 +1,51 @@ +// PR c++/124425 +// { dg-do compile { target c++26 } } +// { dg-additional-options "-freflection" } + +#include <meta> + +struct A { }; + +template <typename T> +struct B +{ + using value_type = T; + using difference_type = int; + struct C + { + T i; + constexpr operator T () const { return i; } + }; + constexpr B &operator++ () { ++i; return *this; } + constexpr B operator++ (int) { return B { i++ }; } + constexpr C operator * () const { return C { i }; } + constexpr bool operator== (A) const { return i == 10; } + constexpr B () : p (new char (42)) {} + constexpr B (const B &x) : i (x.i), p (new char (42)) {} + constexpr ~B () { delete p; } + int i = 0; + char *p; +}; + +template <typename T> +struct D +{ + constexpr B <T> begin () const { return {}; }; + constexpr A end () const { return {}; }; +}; + +struct E +{ + constexpr E (int x) : a (x), b (x + 1), c (x + 2) {} + constexpr E (const E &x) : a (x.a), b (x.b), c (x.c) {} + constexpr ~E () {} + int a, b, c; +}; + +constexpr auto &d = [: std::meta::reflect_constant_array (D <int> {}) :]; +constexpr auto &e = [: std::meta::reflect_constant_array (D <E> {}) :]; +consteval { + for (int i = 0; i < 10; ++i) + if (d[i] != i || e[i].a != i || e[i].b != i + 1 || e[i].c != i + 2) + throw 1; +} --- gcc/testsuite/g++.dg/reflect/reflect_constant_array8.C.jj 2026-03-10 18:24:10.325830145 +0100 +++ gcc/testsuite/g++.dg/reflect/reflect_constant_array8.C 2026-03-10 18:25:06.001892737 +0100 @@ -0,0 +1,8 @@ +// PR c++/124425 +// { dg-do compile { target c++26 } } +// { dg-additional-options "-freflection" } + +#include <meta> + +constexpr auto &s = [: std::meta::reflect_constant_array (std::vector { true, false, true }) :]; +static_assert (s[0] && !s[1] && s[2]); Jakub
