On Tue, Sep 15, 2026 at 01:50:35AM +0300, feedable wrote: > > On 09/09/2026 12:38, Richard Biener wrote: > > On Tue, Sep 8, 2026 at 11:15 PM feedable <[email protected]> wrote: > > > > > > On 08/09/2026 11:34, Richard Biener wrote: > > > > On Sat, Sep 5, 2026 at 7:39 PM feedable <[email protected]> wrote: > > > > > On 25/08/2026 13:48, Richard Biener wrote: > > > > > > On Tue, Aug 25, 2026 at 1:26 AM feedable via Sourceware Forge > > > > > > <[email protected]> wrote: > > > > > > > From: feedable <[email protected]> > > > > > > > > > > > > > > Currently, auto_vec defaults its copy&move operations. This is > > > > > > > bad, > > > > > > > since a user that tries to copy or move an auto_vec will > > > > > > > unknowingly > > > > > > > set himself up for trouble, since the pointers from the base and > > > > > > > members > > > > > > > will be copied verbatim, leading to double free situtaions and > > > > > > > the like. > > > > > > For auto_vec<T, N> with N != 0 we do not want these, instead they > > > > > > should > > > > > > be deleted. As seen in your implementation the complexity involved > > > > > > with > > > > > > having to deal with the stack backing for small number of elements > > > > > > makes > > > > > > this very inefficient. If copy/move is needed people should use > > > > > > auto_vec<T, 0> > > > > > > instead. > > > > > As stated in the discussion for v1, a move constructor is required > > > > > for a > > > > > value to be returned. Values of type auto_vec<T, N> with N != 0 are > > > > > returned in the codebase already (yes, this is currently UB if the > > > > > constructor is not elided). > > > > IMO those uses should be fixed. > > > I'm sorry but I still fail to understand the purpose of forbidding those > > > operations, given that an implementation of these operations is present > > > in this patch. > > > > > > It is fairly clear to me that forbidding these operations will incur > > > costs on the codebase, such as denying storage of `auto_vec`s in > > > containers (which is being used in the following patch), and denying the > > > ability to return them (which is used now in the code elsewhere). > > > > > > What is not clear to me is the benefits of such a decision. > > > > > > Regarding the efficiency of my implementation of those ops, claims about > > > that can only be made in comparison, and the alternative here is to > > > unconditionally allocate on the heap, which is worse than storing the > > > data inline and eliding the move on return. > > I'm not sure it is worse. > > > > > Until the net-benefits are clear to me, I cannot in good faith move > > > forward with this change. > > The argument is that once you expose an inefficient API you'll > > get lazy uses and inefficiencies that could have been easily avoided. > > But I might be overly pessimistic here, not knowing how often the > > copy/move construction/assignment ops for auto_vec would occur. > > > > There is also the semantic difference with vec<> where > > assignment does a shallow copy of the m_vec pointer and _not_ > > copy the data as with your non-move assignment/ctor. So can > > we at least avoid that? vec<> has vec<>::copy for this and > > truncate() + splice() for the assignment. > > > > We should possibly delete vec<>::operator=() and adjust code > > to use const vec<>& argument passing (it's one more indirection > > sadly). Adding move assignment might be useful, but I'd like > > to explicitly "see" it, like if we can force v1 = std::move (v2); or so? > > > > Richard. > I think there's a bit of a misunderstanding going on here, so allow me > to try to elaborate in a simplified way on semantics of initialization > and copy&move operations in C++.
I think its more a question of priorities. While its somewhat understandable as a historical artifact, that C++ does deep copies of possibly very large and expensive to copy objects with "=" is rather unfortunate, especially considering the kind of language C++ is. I would consider rust's design (see https://dev.to/lovegupta/rust-ownership-explained-why-some-values-move-and-others-copy-4ol2 ) for how to handle this much better, it uses = for trivial coppies and moves only, and has you write object.clone () for expensive coppies. So its consistent you can always do x = y.clone () but it also makes it clear when things are expensive. , especially considering the kind of language C++ is. I would consider rust's design (see https://dev.to/lovegupta/rust-ownership-explained-why-some-values-move-and-others-copy-4ol2 ) for how to handle this much better, it uses = for trivial coppies and moves only, and has you write object.clone () for expensive coppies. So its consistent you can always do x = y.clone () but it also makes it clear when things are expensive. , especially considering the kind of language C++ is. I would consider rust's design (see https://dev.to/lovegupta/rust-ownership-explained-why-some-values-move-and-others-copy-4ol2 ) for how to handle this much better, it uses = for trivial coppies and moves only, and has you write object.clone () for expensive coppies. So its consistent you can always do x = y.clone () but it also makes it clear when things are expensive. , especially considering the kind of language C++ is. I would consider rust's design (see https://dev.to/lovegupta/rust-ownership-explained-why-some-values-move-and-others-copy-4ol2 ) for how to handle this much better, it uses = for trivial coppies and moves only, and has you write object.clone () for expensive coppies. So its consistent you can always do x = y.clone () but it also makes it clear when things are expensive. > The default language infrastructure also automatically derives the > definitions of copy and move operations for aggregates: yes, and while its understandable as inherited legacy from C, I'm not sure its a great default choice for a language vs opting types in to either copy or move explicitly as appropriate. > Now, `auto_vec` is a hybrid of these concepts, as its storage itself may > be inline (modeled by the member case) or out-of-line (modeled by the > std::vector case). Other instances of this situation include > `std::string` and `std::function`, both of which can store the objects > they own inline or out-of-line, and their implementations of the > copy&move operations are similar to the one proposed in this patch, and > it's easy to see that it is as efficient as such an operation can > possibly be. Yeah, gcc's c++ification of datastructures is a bit more sloppy and haphazard than might be ideal, but that's how transitions usually are. imho auto_vec should have move constructors, but not copy constructors (and assignment operators), to follow the same design as rust uses per above. You can still make coppies with auto_vec<T> x = y.copy (); I'll grant its not quite as short as "=" and doesn't fit with how the standard library handles things, but making expensive things obviously so is far more important. I'll grant its not quite as short as "=" and doesn't fit with how the standard library handles things, but making expensive things obviously so is far more important. I'll grant its not quite as short as "=" and doesn't fit with how the standard library handles things, but making expensive things obviously so is far more important. I'll grant its not quite as short as "=" and doesn't fit with how the standard library handles things, but making expensive things obviously so is far more important. While its true that your definition is as efficient as it can be given the constraints, its still O(N0 in the size of the datastructure, and so something to be done with care only when needed. > As for `vec`, this type by itself is not responsible for managing its > content, it has a trivial destructor, so for all intents and purposes it > behaves like a non-owning pointer to the data, so deleting copy and move > operations there is pointless. yes, the non gc variants of vec should probably all be moved to auto_vec some day... > It is important to emphasize that these mechanics are an integral part > of the language, and not using them will only add churn to the user's > code, since operations will no longer be derived for aggregates, and > containers will have to special-case types which have special patterns > for copying. given we already have copy () on vec, if hash tables really must be coppied they should be consistent in using the same name, and yes its a little more work, but deeply nested datastructures past hash_map<tree, auto_vec<T>> are pretty unusual so I don't find the argument that copy operators be available for consistency that compelling. > Even GCC itself uses this infrastructure in e.g. `hash_table` and > `hash_map`, which already have copy-constructors, which in turn require > the contained types to be copyable (via standard language means) > as well. huh, that's unfortunate, I wonder what actually needs to copy a hash table, I'd hope nothing in a critical path, and I'd think ideally it would also be movable, but not copyable except with a .copy () method. > From my knowledge, those types only work with `vec` semi-properly, and > that is only because those are non-owning pointers and have copy&move > operations defaulted. Still, if a `hash_map<vec<T>>` is global and not > cleaned in time, its global destructor will not clean up resources that > the `vec`s reference, producing leaks. Copies also do not copy the > contents of `vec`s, so we end up with aliased maps, and if one contained > `vec` is resized, the corresponding one in the copy silently becomes > invalid, causing use-after-frees. > > The solution there is to use `auto_vec`s, since `hash_map`s expect to > retain ownership over their values, but `auto_vec`s need proper copy > and move operations defined for `hash_map`s to be able to use them. I think hash_map<T, auto_vec<U>> should mostly work today because of move operators? or perhaps only because it memcpy's and forgets to call destructors, but iirc it does try to move when resizing the table, and in any case just the move operators should be necessary for that except perhaps that case of copying hash tables if its really needed. > Very similar situation occurs with `vec<vec<T>>`, where even a manual > copy produces aliased unusable results, with a similar solution of using > `auto_vec<auto_vec<T>>`, irrespective of whether there is an inline > buffer or not. vec<vec<T>> isn't very friendly, but it is perfectly usable if you accept it is essentially a C datastructure? and that sort of thing is exactly what the auto_vec<T> without inline storage is for. The inline storage is just intended for stack usage where its cheap. While given its usage pattern its not completely unreasonable to use inline storage with recog_data, I'd still tend to suggest that inline storage was mostlyl an artifact of C and replacing it with the no inline storage version of auto_vec, especially considering it probably wastes a lot of space with most targets very rarely using 30 operands. Thanks Trev While given its usage pattern its not completely unreasonable to use inline storage with recog_data, I'd still tend to suggest that inline storage was mostlyl an artifact of C and replacing it with the no inline storage version of auto_vec, especially considering it probably wastes a lot of space with most targets very rarely using 30 operands. Thanks Trev While given its usage pattern its not completely unreasonable to use inline storage with recog_data, I'd still tend to suggest that inline storage was mostlyl an artifact of C and replacing it with the no inline storage version of auto_vec, especially considering it probably wastes a lot of space with most targets very rarely using 30 operands. Thanks Trev While given its usage pattern its not completely unreasonable to use inline storage with recog_data, I'd still tend to suggest that inline storage was mostlyl an artifact of C and replacing it with the no inline storage version of auto_vec, especially considering it probably wastes a lot of space with most targets very rarely using 30 operands. Thanks Trev > > Thanks, > feedable >
