On Fri, Jan 17, 2025 at 07:00:33PM -0500, Jason Merrill wrote: > > --- gcc/c-family/c-common.cc.jj 2025-01-02 11:47:29.803228077 +0100 > > +++ gcc/c-family/c-common.cc 2025-01-17 13:32:53.512294482 +0100 > > @@ -9016,9 +9016,26 @@ vec<tree, va_gc> * > > make_tree_vector_from_ctor (tree ctor) > > { > > vec<tree,va_gc> *ret = make_tree_vector (); > > + unsigned nelts = CONSTRUCTOR_NELTS (ctor); > > vec_safe_reserve (ret, CONSTRUCTOR_NELTS (ctor)); > > for (unsigned i = 0; i < CONSTRUCTOR_NELTS (ctor); ++i) > > - ret->quick_push (CONSTRUCTOR_ELT (ctor, i)->value); > > + if (TREE_CODE (CONSTRUCTOR_ELT (ctor, i)->value) == RAW_DATA_CST) > > + { > > + tree raw_data = CONSTRUCTOR_ELT (ctor, i)->value; > > + nelts += RAW_DATA_LENGTH (raw_data); > > + vec_safe_reserve (ret, nelts); > > I notice that the second argument to vec_safe_reserve is the number of empty > spaces to provide, rather than the new desired capacity. So this will > allocate space for an additional copy of the whole constructor.
You're right. > I guess you want to say reserve (ret, nelts - vec_safe_length (res))? It's > weird that there isn't a more convenient way to express that. I think vec_safe_reserve (ret, nelts - ret->length ()); is enough, the first vec_safe_reserve already necessarily make ret non-NULL because if it would not have reserved anything (nelts == 0), then there would be no ctor elements to walk. > Note that this behavior differs from the C++ standard library, where reserve > indeed specifies the new capacity. This seems likely to cause a lot of > confusion (and over-allocation) going forward. And indeed looking through > exising uses turns up at least two that seem to assume the standard library > semantics (as well as others that assume the actual semantics). Which ones did you find? To me cp/name-lookup,cc (name_lookup::preserve_state) unsigned length = vec_safe_length (previous->scopes); vec_safe_reserve (previous->scopes, length * 2); looks possibly unintended, as it quick_pushes at most length further elements. I've so far skimmed just vec_safe_reserve uses in {c-family,c,cp} subdirectories, there are 26 further vec_safe_reserve and around 500 .reserve or ->reserve cases. Most of the vec_safe_reserve and reserve calls are on empty vectors so it really doesn't matter which one is which. So, I wonder if at least during the conversion it wouldn't be best to have all 3 namings, reserve, reserve_space and reserve_capacity, where the first one would at runtime assert that length () is 0. > I wonder about renaming the existing reserve to reserve_space and adding a > reserve_capacity? Would it be ok to first handle these PRs (fixed patch below) and go through the renaming and checking incrementally? There was another thinko in the patch, nelts += RAW_DATA_LENGTH (raw_data); would mean again reserving too much, it needs to be nelts += RAW_DATA_LENGTH (raw_data) - 1, as we don't push the RAW_DATA_CST itself which was already accounted for, but replace it with RAW_DATA_LENGTH pushes. So far tested on GXX_TESTSUITE_STDS=98,11,14,17,20,23,26 make check-g++ RUNTESTFLAGS="dg.exp='embed* class-deduction-aggr*.C explicit*.C pr11853[24]*'" ok if it passes full bootstrap+regtest? 2025-01-18 Jakub Jelinek <ja...@redhat.com> PR c++/118528 * c-common.cc (make_tree_vector_from_ctor): Expand RAW_DATA_CST elements from the CONSTRUCTOR to individual INTEGER_CSTs. * g++.dg/cpp/embed-21.C: New test. * g++.dg/cpp2a/class-deduction-aggr16.C: New test. --- gcc/c-family/c-common.cc.jj 2025-01-02 11:47:29.803228077 +0100 +++ gcc/c-family/c-common.cc 2025-01-17 13:32:53.512294482 +0100 @@ -9016,9 +9016,26 @@ vec<tree, va_gc> * make_tree_vector_from_ctor (tree ctor) { vec<tree,va_gc> *ret = make_tree_vector (); + unsigned nelts = CONSTRUCTOR_NELTS (ctor); vec_safe_reserve (ret, CONSTRUCTOR_NELTS (ctor)); for (unsigned i = 0; i < CONSTRUCTOR_NELTS (ctor); ++i) - ret->quick_push (CONSTRUCTOR_ELT (ctor, i)->value); + if (TREE_CODE (CONSTRUCTOR_ELT (ctor, i)->value) == RAW_DATA_CST) + { + tree raw_data = CONSTRUCTOR_ELT (ctor, i)->value; + nelts += RAW_DATA_LENGTH (raw_data) - 1; + vec_safe_reserve (ret, nelts - ret->length ()); + if (TYPE_PRECISION (TREE_TYPE (raw_data)) > CHAR_BIT + || TYPE_UNSIGNED (TREE_TYPE (raw_data))) + for (unsigned j = 0; j < (unsigned) RAW_DATA_LENGTH (raw_data); ++j) + ret->quick_push (build_int_cst (TREE_TYPE (raw_data), + RAW_DATA_UCHAR_ELT (raw_data, j))); + else + for (unsigned j = 0; j < (unsigned) RAW_DATA_LENGTH (raw_data); ++j) + ret->quick_push (build_int_cst (TREE_TYPE (raw_data), + RAW_DATA_SCHAR_ELT (raw_data, j))); + } + else + ret->quick_push (CONSTRUCTOR_ELT (ctor, i)->value); return ret; } --- gcc/testsuite/g++.dg/cpp/embed-21.C.jj 2025-01-17 12:18:00.571912195 +0100 +++ gcc/testsuite/g++.dg/cpp/embed-21.C 2025-01-17 13:37:31.478489868 +0100 @@ -0,0 +1,22 @@ +// PR c++/118528 +// { dg-do compile { target c++20 } } +// { dg-options "" } + +template<class T> +struct E { T t[130][2]; }; + +E e1 { +#embed __FILE__ limit (260) +}; + +template<class T> +struct F { T t[2][130]; }; + +F f1 { +#embed __FILE__ limit (260) +}; +F f2 { { { +#embed __FILE__ limit (130) +}, { +#embed __FILE__ limit (130) +} } }; --- gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr16.C.jj 2025-01-17 12:17:23.715424611 +0100 +++ gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr16.C 2025-01-17 12:17:43.717146527 +0100 @@ -0,0 +1,17 @@ +// PR c++/118528 +// { dg-do compile { target c++20 } } + +template<class T> +struct E { T t[130][2]; }; + +#define P 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 +#define Q { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 }, { 9, 10 }, { 11, 12 }, \ + { 13, 14 }, { 15, 16 } +E e1 { P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, 1, 2, 3, 4 }; +E e2 { { Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, { 1, 2 }, { 3, 4 } } }; + +template<class T> +struct F { T t[2][130]; }; + +F f1 { P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, 1, 2, 3, 4 }; +F f2 { { { P, P, P, P, P, P, P, P, 1, 2 }, { P, P, P, P, P, P, P, P, 3, 4 } } }; Jakub