Hi!

The following testcases show two bugs in build_vec_init, one
introduced with either since my r15-5958 (when using #embed) or
my r15-6339 (when not using it but large init transformed into
RAW_DATA_CST), problem that the FOR_EACH_CONSTRUCTOR_ELT loop
in build_vec_init doesn't handle RAW_DATA_CST, and another since
Marek's r15-7810 which has added limited RANGE_EXPR support to that loop,
but only changed the num_initialized_elts value computation and
has not actually also added a runtime loop over the range to initialize
multiple elements to the same value.  The lack of RAW_DATA_CST handling
causes ICEs (during expansion or later on), while the lack of proper
RANGE_EXPR handling causes wrong-code.

The following patch attempts to fix both.

RAW_DATA_CST has 2 separate variants of handling it, one is when
the types match (digested is true) and it is char/signed char/unsigned
char/std::byte array, in that case (especially if it is huge initializer,
but RAW_DATA_CST already implies 62+ elements) it emits a setting of
MEM_REF with ARRAY_TYPE for the RAW_DATA_LENGTH bytes to a CONSTRUCTOR
with the RAW_DATA_CST in it which gimplifier handles (but of course for
try_const const_vec it uses the RAW_DATA_CST directly).
The second variant is for other types or non-digested one, in that case
RAW_DATA_CST is peeled appart into individual INTEGER_CSTs.

As for RANGE_EXPR, for try_const const_vec it uses the RANGE_EXPR field
as before, but for the runtime code it puts the
one_init/base increment/iterator decrement stmts into a loop which iterates
range_expr_nelts times.

The reason for the first hunk is to optimize the CONSTRUCTOR from what
the preprocessor emits, i.e.
CPP_NUMBER CPP_COMMA CPP_EMBED CPP_COMMA CPP_NUMBER
turned into
INTEGER_CST RAW_DATA_CST INTEGER_CST
into just RAW_DATA_CST covering also the first and last number.
This is something I'm worried about though, because braced_lists_to_strings
can also turn the init into a STRING_CST instead of CONSTRUCTOR, and
while STRING_CST handling is there (and is certainly more efficient than
the previous runtime of storing one constant at a time), it doesn't handle
the try_const case.
So I'm wondering if it wouldn't be safer to use:
  if (init
      && TREE_CODE (init) == CONSTRUCTOR
      && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE
      && INTEGRAL_TYPE_P (type)
      && same_type_p (type, TREE_TYPE (TREE_TYPE (init))))
    {
      tree new_init = braced_lists_to_strings (TREE_TYPE (init), init);
      if (!try_const || TREE_CODE (new_init) == CONSTRUCTOR)
        init = new_init;
    }
so do it only for integral types (braced_lists_to_strings wants to handle
also elements with ARRAY_TYPEs) and ignore braced_lists_to_strings result
for try_const if STRING_CST is returned.  Or try to handle even
STRING_CST try_const somehow (perhaps by remembering orig init and using
that in const_vec and only use STRING_CST at runtime?  Thoughts on that?

Anyway, bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
Or somehow tweak the first hunk as mentioned above?

2026-03-17  Jakub Jelinek  <[email protected]>

        PR c++/124531
        * init.cc (build_vec_init): Call braced_lists_to_strings for
        array CONSTRUCTORs.  Handle RAW_DATA_CST and handle RANGE_EXPR
        correctly.

        * g++.dg/cpp/embed-29.C: New test.
        * g++.dg/cpp0x/pr124531.C: New test.

--- gcc/cp/init.cc.jj   2026-03-12 08:48:04.000000000 +0100
+++ gcc/cp/init.cc      2026-03-16 16:47:56.886440361 +0100
@@ -4862,6 +4862,12 @@ build_vec_init (tree base, tree maxindex
      some are non-constant.  */
   bool do_static_init = (DECL_P (obase) && TREE_STATIC (obase));
 
+  if (init
+      && TREE_CODE (init) == CONSTRUCTOR
+      && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE
+      && same_type_p (type, TREE_TYPE (TREE_TYPE (init))))
+    init = braced_lists_to_strings (TREE_TYPE (init), init);
+
   bool empty_list = false;
   if (init && BRACE_ENCLOSED_INITIALIZER_P (init)
       && CONSTRUCTOR_NELTS (init) == 0)
@@ -4875,6 +4881,7 @@ build_vec_init (tree base, tree maxindex
       /* Do non-default initialization of non-trivial arrays resulting from
         brace-enclosed initializers.  */
       unsigned HOST_WIDE_INT idx;
+      unsigned int raw_idx = -1;
       tree field, elt;
       /* If the constructor already has the array type, it's been through
         digest_init, so we shouldn't try to do anything more.  */
@@ -4892,13 +4899,84 @@ build_vec_init (tree base, tree maxindex
 
       FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), idx, field, elt)
        {
-         tree baseref = build1 (INDIRECT_REF, type, base);
          tree one_init;
 
-         if (field && TREE_CODE (field) == RANGE_EXPR)
-           num_initialized_elts += range_expr_nelts (field);
-         else
-           num_initialized_elts++;
+         if (TREE_CODE (elt) == RAW_DATA_CST)
+           {
+             if (digested
+                 && (TREE_CODE (type) == INTEGER_TYPE
+                     || is_byte_access_type (type))
+                 && TYPE_PRECISION (type) == CHAR_BIT)
+               {
+                 /* If possible, handle RAW_DATA_CST as ARRAY_TYPE
+                    copy from ctor to MEM_REF.  */
+                 tree atype
+                   = build_array_of_n_type (type, RAW_DATA_LENGTH (elt));
+                 tree alias_set
+                   = build_int_cst (build_pointer_type (type), 0);
+                 tree lhs = build2 (MEM_REF, atype, base, alias_set);
+                 tree ctor
+                   = build_constructor_single (atype, bitsize_zero_node,
+                                               copy_node (elt));
+                 one_init = build2 (MODIFY_EXPR, void_type_node, lhs, ctor);
+
+                 if (try_const)
+                   {
+                     if (!field)
+                       field = size_int (num_initialized_elts);
+                     CONSTRUCTOR_APPEND_ELT (const_vec, field, elt);
+                     if (do_static_init)
+                       one_init = NULL_TREE;
+                   }
+
+                 if (one_init)
+                   finish_expr_stmt (one_init);
+
+                 /* Adjust the counter and pointer.  */
+                 tree length = build_int_cst (ptrdiff_type_node,
+                                              RAW_DATA_LENGTH (elt));
+                 one_init = cp_build_binary_op (loc, MINUS_EXPR, iterator,
+                                                length, complain);
+                 if (one_init == error_mark_node)
+                   errors = true;
+                 else
+                   {
+                     one_init = build2 (MODIFY_EXPR, void_type_node, iterator,
+                                        one_init);
+                     finish_expr_stmt (one_init);
+                   }
+
+                 one_init = cp_build_binary_op (loc, PLUS_EXPR, base, length,
+                                                complain);
+                 if (one_init == error_mark_node)
+                   errors = true;
+                 else
+                   {
+                     one_init = build2 (MODIFY_EXPR, void_type_node, base,
+                                        one_init);
+                     finish_expr_stmt (one_init);
+                   }
+
+                 num_initialized_elts += RAW_DATA_LENGTH (elt);
+                 continue;
+               }
+             else
+               {
+                 /* Otherwise peel it off into separate constants.  */
+                 tree orig_elt = elt;
+                 elt = build_int_cst (TREE_TYPE (elt),
+                                      RAW_DATA_UCHAR_ELT (elt, ++raw_idx));
+                 if (raw_idx && field)
+                   field = size_binop (PLUS_EXPR, field,
+                                       bitsize_int (raw_idx));
+                 if (raw_idx + 1 == (unsigned) RAW_DATA_LENGTH (orig_elt))
+                   raw_idx = -1;
+                 else
+                   --idx;
+               }
+           }
+
+         tree baseref = build1 (INDIRECT_REF, type, base);
 
          /* We need to see sub-array TARGET_EXPR before cp_fold_r so we can
             handle cleanup flags properly.  */
@@ -4919,7 +4997,7 @@ build_vec_init (tree base, tree maxindex
          if (try_const)
            {
              if (!field)
-               field = size_int (idx);
+               field = size_int (num_initialized_elts);
              tree e = maybe_constant_init (one_init);
              if (reduced_constant_expression_p (e))
                {
@@ -4942,6 +5020,22 @@ build_vec_init (tree base, tree maxindex
                }
            }
 
+         tree end = NULL_TREE, body = NULL_TREE;
+         if (field && TREE_CODE (field) == RANGE_EXPR)
+           {
+             tree sub
+               = cp_build_binary_op (loc, MINUS_EXPR, iterator,
+                                     build_int_cst (TREE_TYPE (iterator),
+                                                    range_expr_nelts (field)),
+                                    complain);
+             if (sub == error_mark_node)
+               errors = true;
+             end = get_internal_target_expr (sub);
+             add_stmt (end);
+
+             body = push_stmt_list ();
+           }
+
          if (one_init)
            {
              /* Only create one std::allocator temporary.  */
@@ -4962,6 +5056,20 @@ build_vec_init (tree base, tree maxindex
            errors = true;
          else
            finish_expr_stmt (one_init);
+
+         if (field && TREE_CODE (field) == RANGE_EXPR)
+           {
+             tree exit = build1 (EXIT_EXPR, void_type_node,
+                                 build2 (EQ_EXPR, boolean_type_node,
+                                         iterator, TARGET_EXPR_SLOT (end)));
+             add_stmt (exit);
+             body = pop_stmt_list (body);
+             tree loop = build1 (LOOP_EXPR, void_type_node, body);
+             add_stmt (loop);
+             num_initialized_elts += range_expr_nelts (field);
+           }
+         else
+           num_initialized_elts++;
        }
 
       /* Any elements without explicit initializers get T{}.  */
--- gcc/testsuite/g++.dg/cpp/embed-29.C.jj      2026-03-16 17:00:53.907354295 
+0100
+++ gcc/testsuite/g++.dg/cpp/embed-29.C 2026-03-16 17:03:09.946051798 +0100
@@ -0,0 +1,24 @@
+// PR c++/124531
+// { dg-do run { target c++11 } }
+// { dg-options "-O2 --embed-dir=${srcdir}/c-c++-common/cpp/embed-dir" }
+
+unsigned char *
+foo ()
+{
+  return new unsigned char [] {
+#embed <magna-carta.txt> limit (231)
+    };
+}
+
+int
+main ()
+{
+  unsigned char *p = foo ();
+  static unsigned char q[] = {
+#embed <magna-carta.txt> limit (231)
+  };
+  for (int i = 0; i < 231; ++i)
+    if (p[i] != q[i])
+      __builtin_abort ();
+  delete [] p;
+}
--- gcc/testsuite/g++.dg/cpp0x/pr124531.C.jj    2026-03-16 16:55:29.189828203 
+0100
+++ gcc/testsuite/g++.dg/cpp0x/pr124531.C       2026-03-16 17:05:14.962935847 
+0100
@@ -0,0 +1,56 @@
+// PR c++/124531
+// { dg-do run { target c++11 } }
+// { dg-options "-O2" }
+
+unsigned char *
+foo ()
+{
+  return new unsigned char [] {
+    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+    16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+    32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+    48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+    64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+    80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+    96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+    112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 
127,
+    128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 
143,
+    144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 
159,
+    160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 
175,
+    176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 
191,
+    192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 
207,
+    208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 
223,
+    224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 
239,
+    240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 
255 };
+}
+
+struct R
+{
+  int a, b;
+  constexpr R () : a (0), b (1) { }
+  constexpr R (int x, int y) : a (x), b (y) {}
+};
+
+R *
+bar (int *p)
+{
+  return new R[64] { R (++*p, 43) };
+}
+
+int
+main ()
+{
+  unsigned char *p = foo ();
+  for (int i = 0; i < 256; ++i)
+    if (p[i] != i)
+      __builtin_abort ();
+  delete [] p;
+  int x = 41;
+  R *q = bar (&x);
+  if (x != 42 || q[0].a != 42 || q[0].b != 43)
+    __builtin_abort ();
+  for (int i = 1; i < 64; ++i)
+    if (q[i].a != 0 || q[i].b != 1)
+      __builtin_abort ();
+  delete [] q;
+}

        Jakub

Reply via email to