Rename `rustc::pass_by_value` lint as `rustc::disallowed_pass_by_ref`.
The name `pass_by_value` is completely wrong. The lint actually checks for the use of pass by reference for types marked with `rustc_pass_by_value`.
The hardest part of this was choosing the new name. The `disallowed_` part of the name closely matches the following clippy lints:
- `disallowed_macros`
- `disallowed_methods`
- `disallowed_names`
- `disallowed_script_idents`
- `disallowed_types`
The `pass_by_value` part of the name aligns with the following clippy lints:
- `needless_pass_by_value`
- `needless_pass_by_ref_mut`
- `trivially_copy_pass_by_ref`
- `large_types_passed_by_value` (less so)
r? @Urgau
The name `pass_by_value` is completely wrong. The lint actually checks
for the use of pass by reference for types marked with
`rustc_pass_by_value`.
The hardest part of this was choosing the new name. The `disallowed_`
part of the name closely matches the following clippy lints:
- `disallowed_macros`
- `disallowed_methods`
- `disallowed_names`
- `disallowed_script_idents`
- `disallowed_types`
The `pass_by_value` part of the name aligns with the following clippy
lints:
- `needless_pass_by_value`
- `needless_pass_by_ref_mut`
- `trivially_copy_pass_by_ref`
- `large_types_passed_by_value` (less so)
GVN: consider constants of primitive types as deterministic
*[View all comments](https://triagebot.infra.rust-lang.org/gh-comments/rust-lang/rust/pull/149366)*
GVN separates MIR constants into deterministic and non-deterministic constants. Deterministic constants are defined here as: gives the exact same value each time it is evaluated.
This was mainly useful because of `ConstValue::Slice` that generated an extra `AllocId` each time it appeared in the MIR. That variant has been removed. This is still useful for valtrees that hold references, which generate a fresh `AllocId` for each evaluation.
This PR proposes to consider all constants of primitive type to be deterministic. If a constant of primitive type passes validation, then it does not contain provenance, so we have no risk of having a reference becoming different `AllocId`s. In particular, valtrees only are leaves.
r? @ghost for perf
Remove redundant call to `check_codegen_attributes_extra` in Inliner
Inside `try_inlining`, `check_codegen_attributes` is called first. This function internally invokes `check_codegen_attributes_extra`.
However, immediately after that returns, `try_inlining` calls `check_codegen_attributes_extra` again.
First call:
https://github.com/rust-lang/rust/blob/7ec34defe9e62a1a6946d3e700b5903d8dc89ece/compiler/rustc_mir_transform/src/inline.rs#L800-L814
Second call:
https://github.com/rust-lang/rust/blob/7ec34defe9e62a1a6946d3e700b5903d8dc89ece/compiler/rustc_mir_transform/src/inline.rs#L598-L612
```rust
// Inside try_inlining:
check_codegen_attributes(inliner, callsite, callee_attrs)?; // Internally calls `check_codegen_attributes_extra`
inliner.check_codegen_attributes_extra(callee_attrs)?; // Called again here
```
In `try_inlining`, inliner is held as a shared reference (`&I`). Since `check_codegen_attributes_extra` takes `&self` and does not rely on interior mutability or external state, there does not seem to be any state change between the two calls.
Therefore, the second call appears to be redundant.
Currently, `check_codegen_attributes` is only called from `try_inlining`, and `check_codegen_attributes_extra` appears to be called only from these two locations. It seems reasonable for the `check_codegen_attributes` wrapper to handle the hook automatically.
`IntoQueryParam` is a trait that lets query callers be a bit sloppy with
the passed-in key.
- Types similar to `DefId` will be auto-converted to `DefId`. Likewise
for `LocalDefId`.
- Reference types will be auto-derefed.
The auto-conversion is genuinely useful; the auto-derefing much less so.
In practice it's only used for passing `&DefId` to queries that accept
`DefId`, which is an anti-pattern because `DefId` is marked with
`#[rustc_pass_by_value]`.
This commit removes the auto-deref impl and makes the necessary sigil
adjustments. (I generally avoid using `*` to deref manually at call
sites, preferring to deref via `&` in patterns or via `*` in match
expressions. Mostly because that way a single deref often covers
multiple call sites.)
Not linting irrefutable_let_patterns on let chains
*[View all comments](https://triagebot.infra.rust-lang.org/gh-comments/rust-lang/rust/pull/146832)*
# Description
this PR makes the lint `irrefutable_let_patterns` not check for `let chains`,
only check for single `if let`, `while let`, and `if let guard`.
# Motivation
Since `let chains` were stabilized, the following code has become common:
```rust
fn max() -> usize { 42 }
fn main() {
if let mx = max() && mx < usize::MAX { /* */ }
}
```
This code naturally expresses "please call that function and then do something if the return value satisfies a condition".
Putting the let binding outside the if would be bad as then it remains in scope after the if, which is not the intent.
Current Output:
```bash
warning: leading irrefutable pattern in let chain
--> src/main.rs:7:8
|
7 | if let mx = max() && mx < usize::MAX {
| ^^^^^^^^^^^^^^
|
= note: this pattern will always match
= help: consider moving it outside of the construct
= note: `#[warn(irrefutable_let_patterns)]` on by default
```
Another common case is progressively destructuring a struct with enum fields, or an enum with struct variants:
```rust
struct NameOfOuterStruct {
middle: NameOfMiddleEnum,
other: (),
}
enum NameOfMiddleEnum {
Inner(NameOfInnerStruct),
Other(()),
}
struct NameOfInnerStruct {
id: u32,
}
fn test(outer: NameOfOuterStruct) {
if let NameOfOuterStruct { middle, .. } = outer
&& let NameOfMiddleEnum::Inner(inner) = middle
&& let NameOfInnerStruct { id } = inner
{
/* */
}
}
```
Current Output:
```bash
warning: leading irrefutable pattern in let chain
--> src\main.rs:17:8
|
17 | if let NameOfOuterStruct { middle, .. } = outer
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this pattern will always match
= help: consider moving it outside of the construct
= note: `#[warn(irrefutable_let_patterns)]` on by default
warning: trailing irrefutable pattern in let chain
--> src\main.rs:19:12
|
19 | && let NameOfInnerStruct { id } = inner
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this pattern will always match
= help: consider moving it into the body
```
To avoid the warning, the readability would be much worse:
```rust
fn test(outer: NameOfOuterStruct) {
if let NameOfOuterStruct {
middle: NameOfMiddleEnum::Inner(NameOfInnerStruct { id }),
..
} = outer
{
/* */
}
}
```
# related issue
* rust-lang/rust#139369
# possible questions
1. Moving the irrefutable pattern at the head of the chain out of it would cause a variable that was intended to be temporary to remain in scope, so we remove it.
However, should we keep the check for moving the irrefutable pattern at the tail into the body?
2. Should we still lint `entire chain is made up of irrefutable let`?
---
This is my first time contributing non-documentation code to Rust. If there are any irregularities, please feel free to point them out.
: )
Simplify `size/align_of_val<T: Sized>` to `size/align_of<T>` instead
This is relevant to things like `Box<[u8; 1024]>` where the drop looks at the `size_of_val` (since obviously it might be DST in general) but where we don't actually need to do that since it's always that same value for the `Sized` type.
(Equivalent to rust-lang/rust#152681, but flipped in the rebase so it can land before rust-lang/rust#152641 instead of depending on it.)
Simplify the canonical enum clone branches to a copy statement
I have overhauled MatchBranchSimplification in this PR. This pass tries to unify statements one by one, which is more readable and extensible.
This PR also unifies the following pattern that is mostly generated by GVN into one basic block that contains the copy statement:
```rust
match a {
Foo::A(_) => *a,
Foo::B => Foo::B
}
```
Fixes https://github.com/rust-lang/rust/issues/128081.
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