The current situation is something of a mess.
- `IdxSetBuf` derefs to `IdxSet`.
- `IdxSetBuf` implements `Clone`, and therefore has a provided `clone_from`
method, which does allocation and so is expensive.
- `IdxSet` has a `clone_from` method that is non-allocating and therefore
cheap, but this method is not from the `Clone` trait.
As a result, if you have an `IdxSetBuf` called `b`, if you call
`b.clone_from(b2)` you'll get the expensive `IdxSetBuf` method, but if you call
`(*b).clone_from(b2)` you'll get the cheap `IdxSetBuf` method.
`liveness_of_locals()` does the former, presumably unintentionally, and
therefore does lots of unnecessary allocations.
Having a `clone_from` method that isn't from the `Clone` trait is a bad idea in
general, so this patch renames it as `overwrite`. This avoids the unnecessary
allocations in `liveness_of_locals()`, speeding up most NLL benchmarks, the
best by 1.5%. It also means that calls of the form `(*b).clone_from(b2)` can be
rewritten as `b.overwrite(b2)`.
Partial future-proofing for Box<T, A>
In some ways, this is similar to @eddyb's PR #47043 that went stale, but doesn't cover everything. Notably, this still leaves Box internalized as a pointer in places, so practically speaking, only ZSTs can be practically added to the Box type with the changes here (the compiler ICEs otherwise).
The Box type is not changed here, that's left for the future because I want to test that further first, but this puts things in place in a way that hopefully will make things easier.
As of now, Box only contains a Unique pointer, so this is the sole
argument to box_free. Consequently, we remove the code supporting
the previous box_free signature. We however keep the old definition
for bootstrapping purpose.
box_free currently takes a pointer. With the prospect of the Box type
definition changing in the future to include an allocator, box_free will
also need to be aware of this. In order to prepare for that future, we
allow box_free to take a form where its argument are the fields of the
Box.
e.g. if Box is defined as `Box(A, B, C)`, then box_free signature
becomes `box_free(a: A, b: B, c: C)`.
We however still allow the current form (taking a pointer), so that the
same compiler can handle both forms, which helps with bootstrap.
Currently, MIR just passes the raw Box to box_free(), which happens to
work because practically, it's the same thing. But that might not be
true in the future, with Box<T, A: Alloc>.
The MIR inline pass actually fixes up the argument while inlining
box_free, but this is not enabled by default and doesn't necessarily
happen (the inline threshold needs to be passed).
This change effectively moves what the MIR inline pass does to the
elaborate_drops pass, so that box_free() is passed the raw pointer
instead of the Box.