stop marking `deref_patterns` as an incomplete feature
This PR removes the `incomplete_feature` warning for `deref_patterns`. The reason given for this in the tracking issue (rust-lang/rust#87121) was
> Per policy, the `incomplete_feature` is supposed to stay on until the feature has an accepted RFC. We're slowly working on writing up that RFC so it'll take some more time unfortunately.
>
> I don't know of any compiler crashes it causes today. The feature should be pretty usable.
However, I could not find any evidence of such a policy. The [lint documentation](https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#incomplete-features) for `incomplete_features` also only mentions features that are more likely to cause errors.
There are also many other features without an RFC that are not considered incomplete, e.g. `macro_metavar_expr_concat`, `negative_impls` or `yeet_expr`.
The feature does not cause any known ICEs either.
The concrete motivation is to use this feature to replace `box_patterns` in the compiler and pave the way towards removing that legacy feature.
num: Separate public API from internal implementations
Currently we have a single `core::num` module that contains both thin wrapper API and higher-complexity numeric routines. Restructure this by moving implementation details to a new `imp` module.
This results in a more clean separation of what is actually user-facing compared to items that have a stability attribute because they are public for testing.
The first commit does the actual change then the second moves a portion back.
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.
Add a mir-opt test for alignment check generation [zero changes outside tests]
I wrote this as part of rust-lang/rust#152641 which it looks like I'm going to just close, so submitting the new test separately since we didn't have any mir-opt testing of this pass that I could find (at least `x test tests/mir-opt` didn't fail when I broke them) so figured it's something that should exist.
We have a constant for it already (used in `RawVec` for basically the same polymorphization) so let's use it.
Conveniently, it can even be safe that way!
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.
Pass alignments through the shim as `Alignment` (not `usize`)
We're using `Layout` on both sides, so might as well skip the transmutes back and forth to `usize`.
The mir-opt test shows that doing so allows simplifying the boxed-slice drop slightly, for example.
Make operational semantics of pattern matching independent of crate and module
The question of "when does matching an enum against a pattern of one of its variants read its discriminant" is currently an underspecified part of the language, causing weird behavior around borrowck, drop order, and UB.
Of course, in the common cases, the discriminant must be read to distinguish the variant of the enum, but currently the following exceptions are implemented:
1. If the enum has only one variant, we currently skip the discriminant read.
- This has the advantage that single-variant enums behave the same way as structs in this regard.
- However, it means that if the discriminant exists in the layout, we can't say that this discriminant being invalid is UB. This makes me particularly uneasy in its interactions with niches – consider the following example ([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=5904a6155cbdd39af4a2e7b1d32a9b1a)), where miri currently doesn't detect any UB (because the semantics don't specify any):
<details><summary>Example 1</summary>
```rust
#![allow(dead_code)]
use core::mem::{size_of, transmute};
#[repr(u8)]
enum Inner {
X(u8),
}
enum Outer {
A(Inner),
B(u8),
}
fn f(x: &Inner) {
match x {
Inner::X(v) => {
println!("{v}");
}
}
}
fn main() {
assert_eq!(size_of::<Inner>(), 2);
assert_eq!(size_of::<Outer>(), 2);
let x = Outer::B(42);
let y = &x;
f(unsafe { transmute(y) });
}
```
</details>
2. For the purpose of the above, enums with marked with `#[non_exhaustive]` are always considered to have multiple variants when observed from foreign crates, but the actual number of variants is considered in the current crate.
- This means that whether code has UB can depend on which crate it is in: https://github.com/rust-lang/rust/issues/147722
- In another case of `#[non_exhaustive]` affecting the runtime semantics, its presence or absence can change what gets captured by a closure, and by extension, the drop order: https://github.com/rust-lang/rust/issues/147722#issuecomment-3674554872
- Also at the above link, there is an example where removing `#[non_exhaustive]` can cause borrowck to suddenly start failing in another crate.
3. Moreover, we currently make a more specific check: we only read the discriminant if there is more than one *inhabited* variant in the enum.
- This means that the semantics can differ between `foo<!>`, and a copy of `foo` where `T` was manually replaced with `!`: rust-lang/rust#146803
- Moreover, due to the privacy rules for inhabitedness, it means that the semantics of code can depend on the *module* in which it is located.
- Additionally, this inhabitedness rule is even uglier due to the fact that closure capture analysis needs to happen before we can determine whether types are uninhabited, which means that whether the discriminant read happens has a different answer specifically for capture analysis.
- For the two above points, see the following example ([playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=a07d8a3ec0b31953942e96e2130476d9)):
<details><summary>Example 2</summary>
```rust
#![allow(unused)]
mod foo {
enum Never {}
struct PrivatelyUninhabited(Never);
pub enum A {
V(String, String),
Y(PrivatelyUninhabited),
}
fn works(mut x: A) {
let a = match x {
A::V(ref mut a, _) => a,
_ => unreachable!(),
};
let b = match x {
A::V(_, ref mut b) => b,
_ => unreachable!(),
};
a.len(); b.len();
}
fn fails(mut x: A) {
let mut f = || match x {
A::V(ref mut a, _) => (),
_ => unreachable!(),
};
let mut g = || match x {
A::V(_, ref mut b) => (),
_ => unreachable!(),
};
f(); g();
}
}
use foo::A;
fn fails(mut x: A) {
let a = match x {
A::V(ref mut a, _) => a,
_ => unreachable!(),
};
let b = match x {
A::V(_, ref mut b) => b,
_ => unreachable!(),
};
a.len(); b.len();
}
fn fails2(mut x: A) {
let mut f = || match x {
A::V(ref mut a, _) => (),
_ => unreachable!(),
};
let mut g = || match x {
A::V(_, ref mut b) => (),
_ => unreachable!(),
};
f(); g();
}
```
</details>
In light of the above, and following the discussion at rust-lang/rust#138961 and rust-lang/rust#147722, this PR ~~makes it so that, operationally, matching on an enum *always* reads its discriminant.~~ introduces the following changes to this behavior:
- matching on a `#[non_exhaustive]` enum will always introduce a discriminant read, regardless of whether the enum is from an external crate
- uninhabited variants now count just like normal ones, and don't get skipped in the checks
As per the discussion below, the resolution for point (1) above is that it should land as part of a separate PR, so that the subtler decision can be more carefully considered.
Note that this is a breaking change, due to the aforementioned changes in borrow checking behavior, new UB (or at least UB newly detected by miri), as well as drop order around closure captures. However, it seems to me that the combination of this PR with rust-lang/rust#138961 should have smaller real-world impact than rust-lang/rust#138961 by itself.
Fixesrust-lang/rust#142394Fixesrust-lang/rust#146590Fixesrust-lang/rust#146803 (though already marked as duplicate)
Fixes parts of rust-lang/rust#147722Fixesrust-lang/miri#4778
r? @Nadrieril @RalfJung
@rustbot label +A-closures +A-patterns +T-opsem +T-lang
We're using `Layout` on both sides, so might as well skip the transmutes back and forth to `usize`.
The mir-opt test shows that doing so allows simplifying the boxed-slice drop slightly, for example.
Currently we have a single `core::num` module that contains both thin
wrapper API and higher-complexity numeric routines. Restructure this by
moving implementation details to a new `imp` module.
This results in a more clean separation of what is actually user-facing
compared to items that have a stability attribute because they are
public for testing.
Stop having two different alignment constants
Now that there's a `<T as SizedTypeProperties>::ALIGNMENT` constant, `Alignment::of` can use that instead of an inline constant, like how `Layout::new` uses the constant from `SizedTypeProperties`.
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
Tweak `SlicePartialEq` to allow MIR-inlining the `compare_bytes` call
rust-lang/rust#150265 disabled this because it was a net perf win, but let's see if we can tweak the structure of this to allow more inlining on this side while still not MIR-inlining the loop when it's not just `memcmp` and thus hopefully preserving the perf win.
This should also allow MIR-inlining the length check, which was previously blocked, and thus might allow some obvious non-matches to optimize away as well.
Remove `skip-filecheck` and add FileCheck directives to verify that GVN
propagates the constant `false` and eliminates the match entirely.
The test now verifies:
- The debug info shows `x` as `const false` (constant propagation)
- No `switchInt` remains (match elimination)
- The function body is just `return` (dead code elimination)
150265 disabled this because it was a net perf win, but let's see if we can tweak the structure of this to allow more inlining on this side while still not MIR-inlining the loop when it's not just `memcmp`.
This should also allow MIR-inlining the length check, which was previously blocked.
Ignore `#[doc(hidden)]` items when computing trimmed paths for printing
The `trimmed_def_paths` query examines all items in the current crate, and all pub items in immediate-dependency crates (including the standard library), to see which item names are unique and can therefore be printed unambiguously as a bare name without a module path.
Currently that query has no special handling for `#[doc(hidden)]` items, which has two consequences:
- Hidden names can be considered unique, and will therefore be printed without a path, making it hard to find where that name is defined (since it normally isn't listed in documentation).
- Hidden names can conflict with visible names that would otherwise be considered unique, causing diagnostics to mysteriously become more verbose based on internal details of other crates.
This PR therefore makes the `trimmed_def_paths` query ignore external-crate items that are `#[doc(hidden)]`, along with their descendants.
As a result, hidden item names are never considered unique for trimming, and no longer interfere with visible item names being considered unique.
---
- Fixes https://github.com/rust-lang/rust/issues/148387.
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.