Hi! The following testcases ICE with RAW_DATA_CSTs (so the first one since introduction of #embed C++ optimizations and the latter since optimization of large sequences of comma separated literals). I've missed the fact that implicit_conversion can embed the exact expression passed to it into stuff pointed out by conversion * (e.g. for user conversions in sub->cand->args). So, it isn't enough in convert_like_internal to pass the right INTEGER_CST for each element of the RAW_DATA_CST because the whole RAW_DATA_CST might be in sub->cand->args etc. Either I'd need to chase for wherever the RAW_DATA_CST is found and update those for each element processed, or, as implemented in the following patch, build_list_conv detects the easy optimizable case where convert_like_internal can be kept as the whole RAW_DATA_CST with changed type and possibly narrowing diagnostics, and otherwise instead of having a single subconversion it has RAW_DATA_CST separate subconversions. Instead of trying to reallocate the subconvs array when we detect that case, the patch instead uses an artificial ck_list inside of the u.list array to hold the individual subconversions. Seems the only places where u.list is used are build_list_conv and convert_like_internal.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2025-01-28 Jakub Jelinek <ja...@redhat.com> PR c++/118671 * call.cc (build_list_conv): For RAW_DATA_CST, call implicit_conversion with INTEGER_CST representing first byte instead of the whole RAW_DATA_CST. If it is an optimizable trivial conversion, just save that to subconvs, otherwise allocate an artificial ck_list for all the RAW_DATA_CST bytes and create subsubconv for each of them. (convert_like_internal): For ck_list with RAW_DATA_CST, instead of doing all the checks for optimizable conversion just check kind and assert everything else, otherwise use subsubconversions instead of the subconversion for each element. * g++.dg/cpp/embed-25.C: New test. * g++.dg/cpp0x/pr118671.C: New test. --- gcc/cp/call.cc.jj 2025-01-22 09:22:53.000000000 +0100 +++ gcc/cp/call.cc 2025-01-27 21:36:31.159889633 +0100 @@ -868,6 +868,67 @@ build_list_conv (tree type, tree ctor, i FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (ctor), i, val) { + if (TREE_CODE (val) == RAW_DATA_CST) + { + tree elt + = build_int_cst (TREE_TYPE (val), RAW_DATA_UCHAR_ELT (val, 0)); + conversion *sub + = implicit_conversion (elttype, TREE_TYPE (val), elt, + false, flags, complain); + conversion *next; + if (sub == NULL) + return NULL; + /* For conversion to initializer_list<unsigned char> or + initializer_list<char> or initializer_list<signed char> + we can optimize and keep RAW_DATA_CST with adjusted + type if we report narrowing errors if needed. + Use just one subconversion for that case. */ + if (sub->kind == ck_std + && sub->type + && (TREE_CODE (sub->type) == INTEGER_TYPE + || is_byte_access_type (sub->type)) + && TYPE_PRECISION (sub->type) == CHAR_BIT + && (next = next_conversion (sub)) + && next->kind == ck_identity) + { + subconvs[i] = sub; + continue; + } + /* Otherwise. build separate subconv for each RAW_DATA_CST + byte. Wrap those into an artificial ck_list which convert_like + will then handle. */ + conversion **subsubconvs = alloc_conversions (RAW_DATA_LENGTH (val)); + unsigned int j; + subsubconvs[0] = sub; + for (j = 1; j < (unsigned) RAW_DATA_LENGTH (val); ++j) + { + elt = build_int_cst (TREE_TYPE (val), + RAW_DATA_UCHAR_ELT (val, j)); + sub = implicit_conversion (elttype, TREE_TYPE (val), elt, + false, flags, complain); + if (sub == NULL) + return NULL; + subsubconvs[j] = sub; + } + + t = alloc_conversion (ck_list); + t->type = type; + t->u.list = subsubconvs; + t->rank = cr_exact; + for (j = 0; j < (unsigned) RAW_DATA_LENGTH (val); ++j) + { + sub = subsubconvs[i]; + if (sub->rank > t->rank) + t->rank = sub->rank; + if (sub->user_conv_p) + t->user_conv_p = true; + if (sub->bad_p) + t->bad_p = true; + } + subconvs[i] = t; + continue; + } + conversion *sub = implicit_conversion (elttype, TREE_TYPE (val), val, false, flags, complain); @@ -8841,22 +8902,22 @@ convert_like_internal (conversion *convs { if (TREE_CODE (val) == RAW_DATA_CST) { - tree elt_type; - conversion *next; /* For conversion to initializer_list<unsigned char> or initializer_list<char> or initializer_list<signed char> we can optimize and keep RAW_DATA_CST with adjusted type if we report narrowing errors if needed, for others this converts each element separately. */ - if (convs->u.list[ix]->kind == ck_std - && (elt_type = convs->u.list[ix]->type) - && (TREE_CODE (elt_type) == INTEGER_TYPE - || is_byte_access_type (elt_type)) - && TYPE_PRECISION (elt_type) == CHAR_BIT - && (next = next_conversion (convs->u.list[ix])) - && next->kind == ck_identity) + if (convs->u.list[ix]->kind == ck_std) { - if (!TYPE_UNSIGNED (elt_type) + tree et = convs->u.list[ix]->type; + conversion *next = next_conversion (convs->u.list[ix]); + gcc_assert (et + && (TREE_CODE (et) == INTEGER_TYPE + || is_byte_access_type (et)) + && TYPE_PRECISION (et) == CHAR_BIT + && next + && next->kind == ck_identity); + if (!TYPE_UNSIGNED (et) /* For RAW_DATA_CST, TREE_TYPE (val) can be either integer_type_node (when it has been created by the lexer from CPP_EMBED) or @@ -8882,7 +8943,7 @@ convert_like_internal (conversion *convs "narrowing conversion of " "%qd from %qH to %qI", RAW_DATA_UCHAR_ELT (val, i), - TREE_TYPE (val), elt_type); + TREE_TYPE (val), et); if (errorcount != savederrorcount) return error_mark_node; } @@ -8890,19 +8951,21 @@ convert_like_internal (conversion *convs return error_mark_node; } tree sub = copy_node (val); - TREE_TYPE (sub) = elt_type; + TREE_TYPE (sub) = et; CONSTRUCTOR_APPEND_ELT (CONSTRUCTOR_ELTS (new_ctor), NULL_TREE, sub); } else { + conversion *conv = convs->u.list[ix]; + gcc_assert (conv->kind == ck_list); for (int i = 0; i < RAW_DATA_LENGTH (val); ++i) { tree elt = build_int_cst (TREE_TYPE (val), RAW_DATA_UCHAR_ELT (val, i)); tree sub - = convert_like (convs->u.list[ix], elt, + = convert_like (conv->u.list[i], elt, fn, argnum, false, false, /*nested_p=*/true, complain); if (sub == error_mark_node) --- gcc/testsuite/g++.dg/cpp/embed-25.C.jj 2025-01-27 21:51:00.190990295 +0100 +++ gcc/testsuite/g++.dg/cpp/embed-25.C 2025-01-27 21:50:45.954183524 +0100 @@ -0,0 +1,56 @@ +// PR c++/118671 +// { dg-do run { target c++11 } } +// { dg-options "-O2" } + +namespace std { +template <typename T> +struct initializer_list { +private: + T *_M_array; + decltype (sizeof 0) _M_len; +public: + constexpr decltype (sizeof 0) + size () const noexcept { return _M_len; } + constexpr const T * + begin () const noexcept { return _M_array; } + constexpr const T * + end () const noexcept { return begin () + size (); } +}; +} + +struct A {} a; + +struct B { + constexpr B (int x) : B (a, x) {} + template <typename... T> + constexpr B (A, T... x) : b(x...) {} + int b; +}; + +struct C { + C (std::initializer_list<B> x) + { + unsigned char buf[] = { +#embed __FILE__ + }; + if (x.size () != sizeof (buf)) + __builtin_abort (); + unsigned int i = 0; + for (auto a = x.begin (); a < x.end (); ++a, ++i) + if (a->b != buf[i]) + __builtin_abort (); + c = true; + } + bool c; +}; + +C c { +#embed __FILE__ +}; + +int +main () +{ + if (!c.c) + __builtin_abort (); +} --- gcc/testsuite/g++.dg/cpp0x/pr118671.C.jj 2025-01-27 21:47:01.154242793 +0100 +++ gcc/testsuite/g++.dg/cpp0x/pr118671.C 2025-01-27 21:46:43.379486760 +0100 @@ -0,0 +1,61 @@ +// PR c++/118671 +// { dg-do run { target c++11 } } +// { dg-options "-O2" } + +namespace std { +template <typename T> +struct initializer_list { +private: + T *_M_array; + decltype (sizeof 0) _M_len; +public: + constexpr decltype (sizeof 0) + size () const noexcept { return _M_len; } + constexpr const T * + begin () const noexcept { return _M_array; } + constexpr const T * + end () const noexcept { return begin () + size (); } +}; +} + +struct A {} a; + +struct B { + constexpr B (int x) : B (a, x) {} + template <typename... T> + constexpr B (A, T... x) : b(x...) {} + int b; +}; + +struct C { + C (std::initializer_list<B> x) + { + if (x.size () != 130) + __builtin_abort (); + unsigned int i = 1; + for (auto a = x.begin (); a < x.end (); ++a) + if (a->b != i) + __builtin_abort (); + else + i = (i & 15) + 1; + c = true; + } + bool c; +}; + +C c { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2 }; + +int +main () +{ + if (!c.c) + __builtin_abort (); +} Jakub