On 3/17/26 4:54 AM, Jakub Jelinek wrote:
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;
Please add a comment explaining that this handles iterating through a
RAW_DATA_CST.
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)
iterator is an internal ptrdiff_t, it would be very surprising if
subtraction resulted in 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)
...similarly, we know base is a pointer. Though I suppose that could
fail if 'type' isn't complete. But that should have been detected
before we got here; I'd rather these all be asserts.
OK with those changes.
Jason