On 4/2/26 2:57 PM, Jason Merrill wrote:
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 theone_init/base increment/iterator decrement stmts into a loop which iteratesrange_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_stringscan also turn the init into a STRING_CST instead of CONSTRUCTOR, and while STRING_CST handling is there (and is certainly more efficient thanthe previous runtime of storing one constant at a time), it doesn't handlethe 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?
Oops, forgot to respond to this. Yeah, probably safer to go with the v2, but please add a comment about this question.
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 frombrace-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 throughdigest_init, so we shouldn't try to do anything more. */ @@ -4892,13 +4899,84 @@ build_vec_init (tree base, tree maxindexFOR_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
