On 3/11/26 3:18 AM, Jakub Jelinek wrote:
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.

And similarly finish_for_expr and finish_expr_stmt that aren't used here. Which is fine, not worth refactoring.

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);

Let's use void_type_node for inc.  OK with that change.

    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


Reply via email to