[PATCH] c++, libstdc++: Implement LWG4483 - Multidimensional arrays are not supported by meta::reflect_constant_array and related functions

Tomasz Kaminski tkaminsk@redhat.com
Fri Apr 10 15:29:13 GMT 2026


On Fri, Apr 10, 2026 at 5:15 PM Jakub Jelinek <jakub@redhat.com> wrote:

> 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?
>
Library test looks good to me. Just a small suggestion for additional test.
(It occurred to me after seeing the bar function).

>
> 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 () :];
>
Could you add a line:
     static_assert(foo() ==  std::meta::reflect_constant_array(std::span
<int[3][3]>(m)));
I.e. we check that only content matters, and not type of the range.

> +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
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20260410/daa2f281/attachment-0001.htm>


More information about the Libstdc++ mailing list