[PATCH] c++, libstdc++: Implement LWG4483 - Multidimensional arrays are not supported by meta::reflect_constant_array and related functions
Jakub Jelinek
jakub@redhat.com
Fri Apr 10 15:15:38 GMT 2026
Hi!
The following patch attempts to implement LWG4483. As written in the
approved resolution, some checks are done on strip_array_types (valuet)
rather than on valuet and one is skipped. Tomasz on IRC reasoned why
input_range should otherwise already ensure we see similar type, so just
for the possibility of fuzzed <meta> the patch adds some verification.
And as the array cases are contiguous, instead of actually recursing (for
which we'd need to have lvalue of the *it rather than prvalue) it just
walks the CONSTRUCTORs for the arrays and handles the elts in there
recursively.
The <meta> changes do exactly what the LWG4483 change says to do.
Tested on x86_64-linux, ok for trunk?
2026-04-10 Jakub Jelinek <jakub@redhat.com>
* reflect.cc (adjust_array_elt): New function.
(get_range_elts): Implement LWG4483 - Multidimensional arrays are not
supported by meta::reflect_constant_array and related functions.
Handle ARRAY_TYPE valuet. Don't unshare_expr in the class valuet case,
get_template_param_object will unshare.
* g++.dg/reflect/reflect_constant_array9.C: New test.
* g++.dg/reflect/reflect_constant_array10.C: New test.
* g++.dg/reflect/reflect_constant_array11.C: New test.
* g++.dg/reflect/define_static_array6.C: New test.
* g++.dg/reflect/define_static_object2.C: Uncomment older tests and
fix them, add tests for unions.
* include/std/meta (define_static_object): Adjust for LWG4483 changes
- handle unions and arrays differently.
--- gcc/cp/reflect.cc.jj 2026-04-10 08:45:20.716811803 +0200
+++ gcc/cp/reflect.cc 2026-04-10 15:32:30.747654790 +0200
@@ -394,6 +394,47 @@ replace_parm_r (tree *tp, int *walk_subt
static tree throw_exception (location_t, const constexpr_ctx *, const char *,
tree, bool *, tree *);
+/* Helper function for get_range_elts, handle adjustment of ARRAY_TYPE elts
+ of a retvec. */
+
+static tree
+adjust_array_elt (location_t loc, const constexpr_ctx *ctx, tree valuet,
+ tree expr, tree fun, bool *non_constant_p, tree *jump_target)
+{
+ if (TREE_CODE (valuet) == ARRAY_TYPE)
+ {
+ if (TREE_CODE (expr) != CONSTRUCTOR
+ || TREE_CODE (TREE_TYPE (expr)) != ARRAY_TYPE)
+ return throw_exception (loc, ctx, "reflect_constant_array failed",
+ fun, non_constant_p, jump_target);
+ unsigned int i;
+ tree val;
+ FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (expr), i, val)
+ {
+ CONSTRUCTOR_ELT (expr, i)->value
+ = adjust_array_elt (loc, ctx, TREE_TYPE (valuet), val, fun,
+ non_constant_p, jump_target);
+ if (*jump_target || *non_constant_p)
+ return NULL_TREE;
+ }
+ return expr;
+ }
+ else if (INTEGRAL_TYPE_P (valuet))
+ {
+ if (TREE_CODE (expr) == INTEGER_CST)
+ return expr;
+ return throw_exception (loc, ctx, "array element not a constant integer",
+ fun, non_constant_p, jump_target);
+ }
+ expr = convert_reflect_constant_arg (valuet, expr);
+ if (expr == error_mark_node)
+ return throw_exception (loc, ctx, "reflect_constant failed",
+ fun, non_constant_p, jump_target);
+ if (VAR_P (expr))
+ expr = DECL_INITIAL (expr);
+ return expr;
+}
+
/* Kinds for get_range_elts. */
enum get_range_elts_kind {
@@ -513,23 +554,24 @@ get_range_elts (location_t loc, const co
}
if (kind == REFLECT_CONSTANT_ARRAY)
{
- if (!structural_type_p (valuet))
+ tree valuete = strip_array_types (valuet);
+ if (!structural_type_p (valuete))
{
if (!cxx_constexpr_quiet_p (ctx))
{
auto_diagnostic_group d;
error_at (loc, "%<reflect_constant_array%> argument with "
"%qT which is not a structural type", inst);
- structural_type_p (valuet, true);
+ structural_type_p (valuete, true);
}
*non_constant_p = true;
return NULL_TREE;
}
TREE_VEC_ELT (args, 0)
- = build_stub_type (valuet,
- cp_type_quals (valuet) | TYPE_QUAL_CONST,
+ = build_stub_type (valuete,
+ cp_type_quals (valuete) | TYPE_QUAL_CONST,
false);
- if (!is_xible (INIT_EXPR, valuet, args))
+ if (!is_xible (INIT_EXPR, valuete, args))
{
if (!cxx_constexpr_quiet_p (ctx))
error_at (loc, "%<reflect_constant_array%> argument with %qT "
@@ -551,7 +593,23 @@ get_range_elts (location_t loc, const co
}
tree referencet = TYPE_MAIN_VARIANT (instr);
TREE_VEC_ELT (args, 0) = referencet;
- if (!is_xible (INIT_EXPR, valuet, args))
+ if (valuete != valuet)
+ {
+ tree rt = referencet;
+ if (TYPE_REF_P (rt))
+ rt = TREE_TYPE (rt);
+ if (!same_type_ignoring_top_level_qualifiers_p (valuet, rt))
+ {
+ if (!cxx_constexpr_quiet_p (ctx))
+ error_at (loc, "%<reflect_constant_array%> argument with "
+ "%qT which is not compatible with %qT "
+ "%<std::ranges::range_reference_t%>",
+ inst, referencet);
+ *non_constant_p = true;
+ return NULL_TREE;
+ }
+ }
+ else if (!is_xible (INIT_EXPR, valuet, args))
{
if (!cxx_constexpr_quiet_p (ctx))
error_at (loc, "%<reflect_constant_array%> argument with %qT "
@@ -595,12 +653,22 @@ get_range_elts (location_t loc, const co
else
{
gcc_assert (kind == REFLECT_CONSTANT_ARRAY);
+ if (TREE_CODE (valuet) == ARRAY_TYPE)
+ {
+ retvec[i]
+ = adjust_array_elt (loc, ctx, valuet,
+ unshare_expr (retvec[i]), fun,
+ non_constant_p, jump_target);
+ if (*jump_target || *non_constant_p)
+ return NULL_TREE;
+ continue;
+ }
tree expr = convert_reflect_constant_arg (valuet, retvec[i]);
if (expr == error_mark_node)
return throw_exception (loc, ctx, "reflect_constant failed",
fun, non_constant_p, jump_target);
if (VAR_P (expr))
- expr = unshare_expr (DECL_INITIAL (expr));
+ expr = DECL_INITIAL (expr);
retvec[i] = expr;
}
}
--- gcc/testsuite/g++.dg/reflect/reflect_constant_array9.C.jj 2026-04-10 14:58:28.892432782 +0200
+++ gcc/testsuite/g++.dg/reflect/reflect_constant_array9.C 2026-04-10 15:46:45.523823103 +0200
@@ -0,0 +1,28 @@
+// LWG4483 - Multidimensional arrays are not supported by
+// meta::reflect_constant_array and related functions.
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <meta>
+
+consteval auto
+foo ()
+{
+ int a[3][3][3];
+ for (int i = 0; i < 3; ++i)
+ for (int j = 0; j < 3; ++j)
+ for (int k = 0; k < 3; ++k)
+ a[i][j][k] = i + j + k;
+ return std::meta::reflect_constant_array (a);
+}
+
+static_assert (foo () == foo ());
+static_assert (type_of (foo ()) == ^^const int [3][3][3]);
+constexpr auto &m = [: foo () :];
+consteval {
+ for (int i = 0; i < 3; ++i)
+ for (int j = 0; j < 3; ++j)
+ for (int k = 0; k < 3; ++k)
+ if (m[i][j][k] != i + j + k)
+ throw 1;
+}
--- gcc/testsuite/g++.dg/reflect/reflect_constant_array10.C.jj 2026-04-10 16:43:43.192287885 +0200
+++ gcc/testsuite/g++.dg/reflect/reflect_constant_array10.C 2026-04-10 16:57:20.060192312 +0200
@@ -0,0 +1,53 @@
+// LWG4483 - Multidimensional arrays are not supported by
+// meta::reflect_constant_array and related functions.
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <ranges>
+#include <meta>
+
+consteval auto
+foo ()
+{
+ int a[3][3][3];
+ for (int i = 0; i < 3; ++i)
+ for (int j = 0; j < 3; ++j)
+ for (int k = 0; k < 3; ++k)
+ a[i][j][k] = i + 3 * j + 9 * k;
+ std::span <int[3][3]> b = a;
+ return std::meta::reflect_constant_array (b);
+}
+
+static_assert (foo () == foo ());
+static_assert (type_of (foo ()) == ^^const int [3][3][3]);
+constexpr auto &m = [: foo () :];
+consteval {
+ for (int i = 0; i < 3; ++i)
+ for (int j = 0; j < 3; ++j)
+ for (int k = 0; k < 3; ++k)
+ if (m[i][j][k] != i + 3 * j + 9 * k)
+ throw 1;
+}
+
+consteval auto
+bar ()
+{
+ int a[3][3][3];
+ for (int i = 0; i < 3; ++i)
+ for (int j = 0; j < 3; ++j)
+ for (int k = 0; k < 3; ++k)
+ a[i][j][k] = i + 3 * j + 9 * k;
+ std::span <int[3][3]> b = a;
+ return std::meta::reflect_constant_array (b | std::views::reverse);
+}
+
+static_assert (bar () == bar ());
+static_assert (type_of (bar ()) == ^^const int [3][3][3]);
+constexpr auto &n = [: bar () :];
+consteval {
+ for (int i = 0; i < 3; ++i)
+ for (int j = 0; j < 3; ++j)
+ for (int k = 0; k < 3; ++k)
+ if (n[i][j][k] != (2 - i) + 3 * j + 9 * k)
+ throw 1;
+}
--- gcc/testsuite/g++.dg/reflect/reflect_constant_array11.C.jj 2026-04-10 17:00:44.179663685 +0200
+++ gcc/testsuite/g++.dg/reflect/reflect_constant_array11.C 2026-04-10 17:05:50.683365136 +0200
@@ -0,0 +1,18 @@
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+// Test std::meta::reflect_constant_string.
+
+#include <meta>
+#include <ranges>
+#include <span>
+
+using namespace std::meta;
+
+struct A { int a, b; mutable int c; };
+constexpr A aa[2] = { { 1, 2, 3 }, { 4, 5, 6 } };
+constexpr auto a = reflect_constant_array (aa);
+// { dg-error "'reflect_constant_array' argument with 'std::ranges::range_value_t<const A \\\[2\\\]>' \\\{aka 'A'\\\} which is not a structural type" "" { target *-*-* } .-1 }
+struct B { constexpr B (int x, int y) : a (x), b (y) {} constexpr ~B () {} B (const B &) = delete; int a, b; };
+constexpr B b[2][2] = { { { 1, 2 }, { 2, 3 } }, { { 3, 4 }, { 4, 5 } } };
+constexpr auto c = reflect_constant_array (b);
+// { dg-error "'reflect_constant_array' argument with 'std::ranges::range_value_t<const B \\\[2\\\]\\\[2\\\]>' \\\{aka 'B \\\[2\\\]'\\\} which is not copy constructible" "" { target *-*-* } .-1 }
--- gcc/testsuite/g++.dg/reflect/define_static_array6.C.jj 2026-04-10 15:44:52.229788936 +0200
+++ gcc/testsuite/g++.dg/reflect/define_static_array6.C 2026-04-10 15:52:38.099857297 +0200
@@ -0,0 +1,27 @@
+// LWG4483 - Multidimensional arrays are not supported by
+// meta::reflect_constant_array and related functions.
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <meta>
+
+consteval auto
+foo ()
+{
+ int a[3][3][3];
+ for (int i = 0; i < 3; ++i)
+ for (int j = 0; j < 3; ++j)
+ for (int k = 0; k < 3; ++k)
+ a[i][j][k] = i + j + k;
+ return std::define_static_array (a);
+}
+
+static_assert (foo ().size () == 3);
+constexpr auto *m = foo ().data ();
+consteval {
+ for (int i = 0; i < 3; ++i)
+ for (int j = 0; j < 3; ++j)
+ for (int k = 0; k < 3; ++k)
+ if (m[i][j][k] != i + j + k)
+ throw 1;
+}
--- gcc/testsuite/g++.dg/reflect/define_static_object2.C.jj 2026-03-27 10:17:16.120298331 +0100
+++ gcc/testsuite/g++.dg/reflect/define_static_object2.C 2026-04-10 16:09:20.261858550 +0200
@@ -1,3 +1,5 @@
+// LWG4483 - Multidimensional arrays are not supported by
+// meta::reflect_constant_array and related functions.
// { dg-do compile { target c++26 } }
// { dg-additional-options "-freflection" }
// Test std::define_static_object.
@@ -5,14 +7,19 @@
#include <meta>
constexpr int arr[]{1, 2, 3};
-// LWG4483 use extract(reflect_constant_array())
-// constexpr const int(*ptr)[3] = std::define_static_object(arr);
-// static_assert( *ptr == std::define_static_array(arr).data() );
-// static_assert( ptr = &std::meta::constant_of(arr) );
+constexpr const int (*ptr)[3] = std::define_static_object (arr);
+static_assert (*ptr == std::define_static_array (arr).data ());
+static_assert (ptr == &[: std::meta::constant_of (^^arr) :]);
constexpr int marr[3][3]{1, 2, 3};
-// LWG4483 array are not structural so this fail
-// constexpr const int(*mptr)[3][3] = std::define_static_object(marr);
-// static_assert( *mptr == std::define_static_array(marr).data() );
-// static_assert( mptr = &std::meta::constant_of(marr) );
+constexpr const int (*mptr)[3][3] = std::define_static_object (marr);
+static_assert (*mptr == std::define_static_array (marr).data ());
+static_assert (mptr == &[: std::meta::constant_of (^^marr) :]);
+union U { int a; long long b; };
+constexpr U u = { .a = 42 };
+constexpr const U *up = std::define_static_object (u);
+static_assert (up->a == 42);
+constexpr U v = { .b = 43LL };
+constexpr const U *vp = std::define_static_object (v);
+static_assert (vp->b == 43LL);
--- libstdc++-v3/include/std/meta.jj 2026-04-08 07:49:13.477647992 +0200
+++ libstdc++-v3/include/std/meta 2026-04-10 15:57:25.423013150 +0200
@@ -682,11 +682,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
define_static_object(_Tp&& __t)
{
using _Up = remove_cvref_t<_Tp>;
- if constexpr (meta::is_class_type(^^_Up))
+ if constexpr (meta::is_class_type(^^_Up) || meta::is_union_type(^^_Up))
{
auto __cst = meta::reflect_constant(std::forward<_Tp>(__t));
return std::addressof(meta::extract<const _Up&>(__cst));
}
+ else if constexpr (meta::is_array_type(^^_Up))
+ {
+ auto __cst = meta::reflect_constant_array(std::forward<_Tp>(__t));
+ return std::addressof(meta::extract<const _Up&>(__cst));
+ }
else
return std::define_static_array(span(std::addressof(__t), 1)).data();
}
Jakub
More information about the Libstdc++
mailing list