Give temporaries in if let guards correct scopes
Temporaries in if-let guards have scopes that escape the match arm, this causes problems because the drops might be for temporaries that are not storage live. This PR changes the scope of temporaries in if-let guards to be limited to the arm:
```rust
_ if let Some(s) = std::convert::identity(&Some(String::new())) => {}
// Temporary for Some(String::new()) is dropped here ^
```
We also now deduplicate temporaries between copies of the guard created for or-patterns:
```rust
// Only create a single Some(String::new()) temporary variable
_ | _ if let Some(s) = std::convert::identity(&Some(String::new())) => {}
```
This changes MIR building to pass around `ExprId`s rather than `Expr`s so that we have a way to index different expressions.
cc #51114Closes#116079
`IntoDiagnostic` defaults to `ErrorGuaranteed`, because errors are the
most common diagnostic level. It makes sense to do likewise for the
closely-related (and much more widely used) `DiagnosticBuilder` type,
letting us write `DiagnosticBuilder<'a, ErrorGuaranteed>` as just
`DiagnosticBuilder<'a>`. This cuts over 200 lines of code due to many
multi-line things becoming single line things.
Exhaustiveness: reveal opaque types properly
Previously, exhaustiveness had no clear policy around opaque types. In this PR I propose the following policy: within the body of an item that defines the hidden type of some opaque type, exhaustiveness checking on a value of that opaque type is performed using the concrete hidden type inferred in this body.
I'm not sure how consistent this is with other operations allowed on opaque types; I believe this will require FCP.
From what I can tell, this doesn't change anything for non-empty types.
The observable changes are:
- when the real type is uninhabited, matches within the defining scopes can now rely on that for exhaustiveness, e.g.:
```rust
#[derive(Copy, Clone)]
enum Void {}
fn return_never_rpit(x: Void) -> impl Copy {
if false {
match return_never_rpit(x) {}
}
x
}
```
- this properly fixes ICEs like https://github.com/rust-lang/rust/issues/117100 that occurred because a same match could have some patterns where the type is revealed and some where it is not.
Bonus subtle point: if `x` is opaque, a match like `match x { ("", "") => {} ... }` will constrain its type ([playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=901d715330eac40339b4016ac566d6c3)). This is not the case for `match x {}`: this will not constain the type, and will only compile if something else constrains the type to be empty.
Fixes https://github.com/rust-lang/rust/issues/117100
r? `@oli-obk`
Edited for precision of the wording
[Included](https://github.com/rust-lang/rust/pull/116821#issuecomment-1813171764) in the FCP on this PR is this rule:
> Within the body of an item that defines the hidden type of some opaque type, exhaustiveness checking on a value of that opaque type is performed using the concrete hidden type inferred in this body.
- Make temporaries in if-let guards be the same variable in MIR when
the guard is duplicated due to or-patterns.
- Change the "destruction scope" for match arms to be the arm scope rather
than the arm body scope.
- Add tests.
match lowering: Remove the `make_target_blocks` hack
This hack was introduced 4 years ago in [`a1d0266` (#60730)](a1d0266878) to improve LLVM optimization time, specifically noticed in the `encoding` benchmark. Measurements today indicate it is no longer needed.
r? `@matthewjasper`
Make exhaustiveness usable outside of rustc
With this PR, `rustc_pattern_analysis` compiles on stable (with the `stable` feature)! `rust-analyzer` will be able to use it to provide match-related diagnostics and refactors.
Two questions:
- Should I name the feature `nightly` instead of `rustc` for consistency with other crates? `rustc` makes more sense imo.
- `typed-arena` is an optional dependency but tidy made me add it to the allow-list anyway. Can I avoid that somehow?
r? `@compiler-errors`
And make all hand-written `IntoDiagnostic` impls generic, by using
`DiagnosticBuilder::new(dcx, level, ...)` instead of e.g.
`dcx.struct_err(...)`.
This means the `create_*` functions are the source of the error level.
This change will let us remove `struct_diagnostic`.
Note: `#[rustc_lint_diagnostics]` is added to `DiagnosticBuilder::new`,
it's necessary to pass diagnostics tests now that it's used in
`into_diagnostic` functions.
Annotate some bugs
Gives a semi-helpful message to some `bug!()`/`unreachable!()`/`panic!()`. This also works around some other bugs/panics/etc that weren't needed, and also makes some of them into `span_bug!`s so they also have a useful span.
Note to reviewer: best to disable whitespace when comparing for some cases where indentation changed.
cc #118955
rustc_mir_build: Enforce `rustc::potential_query_instability` lint
Stop allowing `rustc::potential_query_instability` on all of `rustc_mir_build` and instead allow it on a case-by-case basis if it is safe to do so. In this crate there was only one instance of the lint, and it was safe to allow.
Part of https://github.com/rust-lang/rust/issues/84447 which is E-help-wanted.
Stop allowing `rustc::potential_query_instability` on all of
`rustc_mir_build` and instead allow it on a case-by-case basis if it is
safe to do so. In this crate there was no instance of the lint
remaining.
Renamings:
- find -> opt_hir_node
- get -> hir_node
- find_by_def_id -> opt_hir_node_by_def_id
- get_by_def_id -> hir_node_by_def_id
Fix rebase changes using removed methods
Use `tcx.hir_node_by_def_id()` whenever possible in compiler
Fix clippy errors
Fix compiler
Apply suggestions from code review
Co-authored-by: Vadim Petrochenkov <vadim.petrochenkov@gmail.com>
Add FIXME for `tcx.hir()` returned type about its removal
Simplify with with `tcx.hir_node_by_def_id`
Extract exhaustiveness into its own crate
It now makes sense to extract exhaustiveness into its own crate! This was much-requested by rust-analyzer (they currently maintain by hand a copy of the algorithm), and I hope this can serve other projects e.g. clippy.
This is the churny PR: it exclusively moves code around. It's not yet useable outside of rustc but I wanted the churny parts to be out of the way.
r? `@compiler-errors`
remove redundant imports
detects redundant imports that can be eliminated.
for #117772 :
In order to facilitate review and modification, split the checking code and removing redundant imports code into two PR.
r? `@petrochenkov`
Don't print host effect param in pretty `path_generic_args`
Make `own_args_no_defaults` pass back the `GenericParamDef`, so that we can pass both the args *and* param definitions into `path_generic_args`. That allows us to use the `GenericParamDef` to filter out effect params.
This allows us to filter out the host param regardless of whether it's `sym::host` or `true`/`false`.
This also renames a couple of `const_effect_param` -> `host_effect_param`, and restores `~const` pretty printing to `TraitPredPrintModifiersAndPath`.
cc #118785
r? `@fee1-dead` cc `@oli-obk`
detects redundant imports that can be eliminated.
for #117772 :
In order to facilitate review and modification, split the checking code and
removing redundant imports code into two PR.
Don't warn an empty pattern unreachable if we're not sure the data is valid
Exhaustiveness checking used to be naive about the possibility of a place containing invalid data. This could cause it to emit an "unreachable pattern" lint on an arm that was in fact reachable, as in https://github.com/rust-lang/rust/issues/117119.
This PR fixes that. We now track whether a place that is matched on may hold invalid data. This also forced me to be extra precise about how exhaustiveness manages empty types.
Note that this now errs in the opposite direction: the following arm is truly unreachable (because the binding causes a read of the value) but not linted as such. I'd rather not recommend writing a `match ... {}` that has the implicit side-effect of loading the value. [Never patterns](https://github.com/rust-lang/rust/issues/118155) will solve this cleanly.
```rust
match union.value {
_x => unreachable!(),
}
```
I recommend reviewing commit by commit. I went all-in on the test suite because this went through a lot of iterations and I kept everything. The bit I'm least confident in is `is_known_valid_scrutinee` in `check_match.rs`.
Fixes https://github.com/rust-lang/rust/issues/117119.
- `ConstructorSet` knows about both empty and nonempty constructors;
- If an empty constructor is present in the column, we output it in
`split().present`.
When MIR is built for an if-not expression, the `!` part of the condition
doesn't correspond to any MIR statement, so coverage instrumentation normally
can't see it.
We can fix that by deliberately injecting a dummy statement whose sole purpose
is to associate that span with its enclosing block.
There are cases where coverage instrumentation wants to show a span for some
syntax element, but there is no MIR node that naturally carries that span, so
the instrumentor can't see it.
MIR building can now use this new kind of coverage statement to deliberately
include those spans in MIR, attached to a dummy statement that has no other
effect.
Remove the `precise_pointer_size_matching` feature gate
`usize` and `isize` are special for pattern matching because their range might depend on the platform. To make code portable across platforms, the following is never considered exhaustive:
```rust
let x: usize = ...;
match x {
0..=18446744073709551615 => {}
}
```
Because of how rust handles constants, this also unfortunately counts `0..=usize::MAX` as non-exhaustive. The [`precise_pointer_size_matching`](https://github.com/rust-lang/rust/issues/56354) feature gate was introduced both for this convenience and for the possibility that the lang team could decide to allow the above.
Since then, [half-open range patterns](https://github.com/rust-lang/rust/issues/67264) have been implemented, and since #116692 they correctly support `usize`/`isize`:
```rust
match 0usize { // exhaustive!
0..5 => {}
5.. => {}
}
```
I believe this subsumes all the use cases of the feature gate. Moreover no attempt has been made to stabilize it in the 5 years of its existence. I therefore propose we retire this feature gate.
Closes https://github.com/rust-lang/rust/issues/56354