[gcc r16-8027] c++: Support proxy iterators in reflection get_range_elts [PR124425]

Jakub Jelinek jakub@gcc.gnu.org
Thu Mar 12 07:49:31 GMT 2026


https://gcc.gnu.org/g:2ba14bb40f2b32d6f4d88e3e6d9c16c22faef605

commit r16-8027-g2ba14bb40f2b32d6f4d88e3e6d9c16c22faef605
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Thu Mar 12 08:48:47 2026 +0100

    c++: Support proxy iterators in reflection get_range_elts [PR124425]
    
    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.
    
    2026-03-12  Jakub Jelinek  <jakub@redhat.com>
    
            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.

Diff:
---
 gcc/cp/reflect.cc                                  | 23 +++++++---
 .../g++.dg/reflect/define_static_array4.C          |  9 ++--
 .../g++.dg/reflect/reflect_constant_array4.C       | 12 +++--
 .../g++.dg/reflect/reflect_constant_array7.C       | 51 ++++++++++++++++++++++
 .../g++.dg/reflect/reflect_constant_array8.C       |  8 ++++
 5 files changed, 85 insertions(+), 18 deletions(-)

diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
index 52c667f779f4..87c0d9f1c065 100644
--- a/gcc/cp/reflect.cc
+++ b/gcc/cp/reflect.cc
@@ -872,15 +872,26 @@ get_range_elts (location_t loc, const constexpr_ctx *ctx, tree call, int n,
       *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 (void_type_node, inc);
   retvec.truncate (0);
   /* while (begin != end) { push (*begin); ++begin; }  */
   do
diff --git a/gcc/testsuite/g++.dg/reflect/define_static_array4.C b/gcc/testsuite/g++.dg/reflect/define_static_array4.C
index 1c70e73815cb..2e0eaaf215f2 100644
--- a/gcc/testsuite/g++.dg/reflect/define_static_array4.C
+++ b/gcc/testsuite/g++.dg/reflect/define_static_array4.C
@@ -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);
diff --git a/gcc/testsuite/g++.dg/reflect/reflect_constant_array4.C b/gcc/testsuite/g++.dg/reflect/reflect_constant_array4.C
index faf2c4a988e8..702e7d4dabbe 100644
--- a/gcc/testsuite/g++.dg/reflect/reflect_constant_array4.C
+++ b/gcc/testsuite/g++.dg/reflect/reflect_constant_array4.C
@@ -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);
diff --git a/gcc/testsuite/g++.dg/reflect/reflect_constant_array7.C b/gcc/testsuite/g++.dg/reflect/reflect_constant_array7.C
new file mode 100644
index 000000000000..c9ce9b55e698
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/reflect_constant_array7.C
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/reflect/reflect_constant_array8.C b/gcc/testsuite/g++.dg/reflect/reflect_constant_array8.C
new file mode 100644
index 000000000000..0e57130bd384
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/reflect_constant_array8.C
@@ -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]);


More information about the Gcc-cvs mailing list