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

Jonathan Wakely jwakely@redhat.com
Tue Apr 14 09:44:52 GMT 2026


On Fri, 10 Apr 2026 at 17:45 +0200, Jakub Jelinek wrote:
>On Fri, Apr 10, 2026 at 05:29:13PM +0200, Tomasz Kaminski wrote:
>> 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.
>
>Here is an updated patch.

The library part is OK, thanks.

>Interdiff is just
>+static_assert (foo () == std::meta::reflect_constant_array (m));
>+static_assert (foo () == std::meta::reflect_constant_array (std::span <const int[3][3]> (m)));
>...
>+static_assert (bar () == std::meta::reflect_constant_array (n));
>+static_assert (bar () == std::meta::reflect_constant_array (std::span <const int[3][3]> (n)));
>in reflect_constant_array10.C test.
>
>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 17:39:16.165672372 +0200
>@@ -0,0 +1,57 @@
>+// 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 () :];
>+static_assert (foo () == std::meta::reflect_constant_array (m));
>+static_assert (foo () == std::meta::reflect_constant_array (std::span <const int[3][3]> (m)));
>+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 () :];
>+static_assert (bar () == std::meta::reflect_constant_array (n));
>+static_assert (bar () == std::meta::reflect_constant_array (std::span <const int[3][3]> (n)));
>+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