compiler: Don't mark `SingleUseConsts` MIR pass as "required for soundness"
I don't think this MIR pass is required for soundness. The reasons are:
* Something like it was not enabled by default before PR rust-lang/rust#107404 which was the precursor to `SingleUseConsts` (see rust-lang/rust#125910 for the switch).
* By following the advice from https://github.com/rust-lang/rust/pull/128657#discussion_r1705114015 we can conclude it is not required for soundness since it has only ever run on MIR opt level > 0.
* Its [`MirPass::can_be_overridden()`](0ee7d96253/compiler/rustc_mir_transform/src/pass_manager.rs (L98-L102)) is unchanged and thus returns `true`, indicating that it is not a required MIR pass.
* PR CI pass in rust-lang/rust#151426 which stops enabling it by default in non-optimized builds.
As shown in the updated test `tests/mir-opt/optimize_none.rs`, `#[optimize(none)]` functions become even less optimized, as expected and desired.
Unblocks https://github.com/rust-lang/rust/pull/151426.
Because:
* Something like it did not exist before PR 107404
* That it is not run our mir-opt-level 0 indicates that it is not
required for soundness
* Its `MirPass::can_be_overridden()` is unchanged and thus returns true,
indicating that it is not a required MIR pass.
* No test fails in PR 151426 that stops enabling by default in non-optimized builds
As can be seen from the updated test `tests/mir-opt/optimize_none.rs`,
this means that `#[optimize(none)]` functions become even less
optimized. As expected and as desired.
replace box_new with lower-level intrinsics
The `box_new` intrinsic is super special: during THIR construction it turns into an `ExprKind::Box` (formerly known as the `box` keyword), which then during MIR building turns into a special instruction sequence that invokes the exchange_malloc lang item (which has a name from a different time) and a special MIR statement to represent a shallowly-initialized `Box` (which raises [interesting opsem questions](https://github.com/rust-lang/rust/issues/97270)).
This PR is the n-th attempt to get rid of `box_new`. That's non-trivial because it usually causes a perf regression: replacing `box_new` by naive unsafe code will incur extra copies in debug builds, making the resulting binaries a lot slower, and will generate a lot more MIR, making compilation measurably slower. Furthermore, `vec!` is a macro, so the exact code it expands to is highly relevant for borrow checking, type inference, and temporary scopes.
To avoid those problems, this PR does its best to make the MIR almost exactly the same as what it was before. `box_new` is used in two places, `Box::new` and `vec!`:
- For `Box::new` that is fairly easy: the `move_by_value` intrinsic is basically all we need. However, to avoid the extra copy that would usually be generated for the argument of a function call, we need to special-case this intrinsic during MIR building. That's what the first commit does.
- `vec!` is a lot more tricky. As a macro, its details leak to stable code, so almost every variant I tried broke either type inference or the lifetimes of temporaries in some ui test or ended up accepting unsound code due to the borrow checker not enforcing all the constraints I hoped it would enforce. I ended up with a variant that involves a new intrinsic, `fn write_box_via_move<T>(b: Box<MaybeUninit<T>>, x: T) -> Box<MaybeUninit<T>>`, that writes a value into a `Box<MaybeUninit<T>>` and returns that box again. In exchange we can get rid of somewhat similar code in the lowering for `ExprKind::Box`, and the `exchange_malloc` lang item. (We can also get rid of `Rvalue::ShallowInitBox`; I didn't include that in this PR -- I think @cjgillot has a commit for this somewhere [around here](https://github.com/rust-lang/rust/pull/147862/commits).)
See [here](https://github.com/rust-lang/rust/pull/148190#issuecomment-3457454814) for the latest perf numbers. Most of the regressions are in deep-vector which consists entirely of an invocation of `vec!`, so any change to that macro affects this benchmark disproportionally.
This is my first time even looking at MIR building code, so I am very low confidence in that part of the patch, in particular when it comes to scopes and drops and things like that.
I also had do nerf some clippy tests because clippy gets confused by the new expansion of `vec!` so it makes fewer suggestions when `vec!` is involved.
### `vec!` FAQ
- Why does `write_box_via_move` return the `Box` again? Because we need to expand `vec!` to a bunch of method invocations without any blocks or let-statements, or else the temporary scopes (and type inference) don't work out.
- Why is `box_assume_init_into_vec_unsafe` (unsoundly!) a safe function? Because we can't use an unsafe block in `vec!` as that would necessarily also include the `$x` (due to it all being one big method invocation) and therefore interpret the user's code as being inside `unsafe`, which would be bad (and 10 years later, we still don't have safe blocks for macros like this).
- Why does `write_box_via_move` use `Box` as input/output type, and not, say, raw pointers? Because that is the only way to get the correct behavior when `$x` panics or has control effects: we need the `Box` to be dropped in that case. (As a nice side-effect this also makes the intrinsic safe, which is imported as explained in the previous bullet.)
- Can't we make it safe by having `write_box_via_move` return `Box<T>`? Yes we could, but there's no easy way for the intrinsic to convert its `Box<MaybeUninit<T>>` to a `Box<T>`. Transmuting would be unsound as the borrow checker would no longer properly enforce that lifetimes involved in a `vec!` invocation behave correctly.
- Is this macro truly cursed? Yes, yes it is.
Consider captures to be used by closures that unwind
Assignments to a captured variable within a diverging closure should not be considered unused if the divergence is caught.
This patch considers such assignments/captures to be used by diverging closures irrespective of whether the divergence is caught, but better a false negative unused lint than a false positive one (the latter having caused a stable-to-stable regression).
Fixesrust-lang/rust#152079
r? compiler
GVN: Only propagate borrows from SSA locals
Fixes https://github.com/rust-lang/rust/issues/141313. This is a more principled fix than https://github.com/rust-lang/rust/pull/147886.
Using a reference that is not a borrowing of an SSA local at a new location may be UB.
The PR has two major changes.
The first one, when introducing a new dereference at a new location, is that the reference must point to an SSA local or be an immutable argument. `dereference_address` has handled SSA locals.
The second one, if we cannot guard to the reference point to an SSA local in `visit_assign`, we have to rewrite the value to opaque. This avoids unifying the following dereferences that also are references:
```rust
let b: &T = *a;
// ... `a` is allowed to be modified. `c` and `b` have different borrowing lifetime.
// Unifying them will extend the lifetime of `b`.
let c: &T = *a;
```
See also https://github.com/rust-lang/rust/issues/130853.
This still allows unifying non-reference dereferences:
```rust
let a: &T = ...;
let b: T = *a;
// ... a is NOT allowed to be modified.
let c: T = *a;
```
r? @cjgillot
Assignments to a captured variable within a diverging closure should not
be considered unused if the divergence is caught.
This patch considers such assignments/captures to be used by diverging
closures irrespective of whether the divergence is caught, but better a
false negative unused lint than a false positive one (the latter having
caused a stable-to-stable regression).
GVN: Elide more intermediate transmutes
We already skipped intermediate steps like `u32` or `i32` that support any (initialized) value.
This extends that to also allow skipping intermediate steps whose values are a superset of either the source or destination type. Most importantly, that means that `usize` → `NonZeroUsize` → `ptr::Alignment` and `ptr::Alignment` → `NonZeroUsize` → `usize` can skip the middle because `NonZeroUsize` is a superset of `Alignment`.
Then `Alignment::as_usize` is updated to take advantage of that and let us remove some more locals in a few places.
r? cjgillot
Fix suppression of `unused_assignment` in binding of `unused_variable`
Unused assignments to an unused variable should trigger only the `unused_variables` lint and not also the `unused_assignments` lint. This was previously implemented by checking whether the span of the assignee was within the span of the binding pattern, however that failed to capture situations was imported from elsewhere (eg from the input tokenstream of a proc-macro that generates the binding pattern).
By comparing the span of the assignee to those of the variable introductions instead, a reported stable-to-stable regression is resolved.
This fix also impacted some other preexisting tests, which had (undesirably) been triggering both the `unused_variables` and `unused_assignments` lints on the same initializing assignment; those tests have therefore now been updated to expect only the former lint.
Fixesrust-lang/rust#151514
r? cjgillot (as author of reworked liveness testing in rust-lang/rust#142390)
Unused assignments to an unused variable should trigger only the
`unused_variables` lint and not also the `unused_assignments` lint.
This was previously implemented by checking whether the span of the
assignee was within the span of the binding pattern, however that failed
to capture situations was imported from elsewhere (eg from the input
tokenstream of a proc-macro that generates the binding pattern).
By comparing the span of the assignee to those of the variable
introductions instead, a reported stable-to-stable regression is
resolved.
This fix also impacted some other preexisting tests, which had
(undesirably) been triggering both the `unused_variables` and
`unused_assignments` lints on the same initializing assignment; those
tests have therefore now been updated to expect only the former lint.
compiler: upgrade to hashbrown 0.16.1
See also rust-lang/rust#135634, rust-lang/rust#149159, and rust-lang/hashbrown#662.
This includes an in-tree upgrade of `indexmap` as well, which uses the
new `HashTable` buckets API internally, hopefully impacting performance
for the better.
And finally, we can remove `#[rustc_unsafe_specialization_marker]` on `Copy`!
cc @joboet
r? @Amanieu
New MIR Pass: SsaRangePropagation
As an alternative to https://github.com/rust-lang/rust/pull/150192.
Introduces a new pass that propagates the known ranges of SSA locals.
We can know the ranges of SSA locals at some locations for the following code:
```rust
fn foo(a: u32) {
let b = a < 9;
if b {
let c = b; // c is true since b is whitin the range [1, 2)
let d = a < 8; // d is true since b whitin the range [0, 9)
}
}
```
This PR only implements a trivial range: we know one value on switch, assert, and assume.
Only use SSA locals in SimplifyComparisonIntegral
Fixes https://github.com/rust-lang/rust/issues/150904.
The place may be modified from the comparison statement to the switchInt terminator.
Best reviewed commit by commit.
Remove `Deref`/`DerefMut` impl for `Providers`.
It's described as a "backwards compatibility hack to keep the diff small". Removing it requires only a modest amount of churn, and the resulting code is clearer without the invisible derefs.
r? @oli-obk