mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
Rollup merge of #120009 - Nadrieril:never_patterns_tyck, r=compiler-errors
never_patterns: typecheck never patterns This checks that a `!` pattern is only used on an uninhabited type (modulo match ergonomics, i.e. `!` is allowed on `&Void`). r? `@compiler-errors`
This commit is contained in:
commit
5761c36c0a
@ -178,8 +178,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
let ty = match pat.kind {
|
||||
PatKind::Wild | PatKind::Err(_) => expected,
|
||||
// FIXME(never_patterns): check the type is uninhabited. If that is not possible within
|
||||
// typeck, do that in a later phase.
|
||||
// We allow any type here; we ensure that the type is uninhabited during match checking.
|
||||
PatKind::Never => expected,
|
||||
PatKind::Lit(lt) => self.check_pat_lit(pat.span, lt, expected, ti),
|
||||
PatKind::Range(lhs, rhs, _) => self.check_pat_range(pat.span, lhs, rhs, expected, ti),
|
||||
|
@ -234,6 +234,11 @@ mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsa
|
||||
|
||||
mir_build_non_const_path = runtime values cannot be referenced in patterns
|
||||
|
||||
mir_build_non_empty_never_pattern =
|
||||
mismatched types
|
||||
.label = a never pattern must be used on an uninhabited type
|
||||
.note = the matched value is of type `{$ty}`
|
||||
|
||||
mir_build_non_exhaustive_match_all_arms_guarded =
|
||||
match arms with guards don't count towards exhaustivity
|
||||
|
||||
|
@ -788,6 +788,16 @@ pub struct FloatPattern;
|
||||
#[diag(mir_build_pointer_pattern)]
|
||||
pub struct PointerPattern;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(mir_build_non_empty_never_pattern)]
|
||||
#[note]
|
||||
pub struct NonEmptyNeverPattern<'tcx> {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
pub ty: Ty<'tcx>,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(mir_build_indirect_structural_match)]
|
||||
#[note(mir_build_type_not_structural_tip)]
|
||||
|
@ -276,10 +276,13 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
|
||||
} else {
|
||||
// Check the pattern for some things unrelated to exhaustiveness.
|
||||
let refutable = if cx.refutable { Refutable } else { Irrefutable };
|
||||
let mut err = Ok(());
|
||||
pat.walk_always(|pat| {
|
||||
check_borrow_conflicts_in_at_patterns(self, pat);
|
||||
check_for_bindings_named_same_as_variants(self, pat, refutable);
|
||||
err = err.and(check_never_pattern(cx, pat));
|
||||
});
|
||||
err?;
|
||||
Ok(cx.pattern_arena.alloc(cx.lower_pat(pat)))
|
||||
}
|
||||
}
|
||||
@ -812,6 +815,19 @@ fn check_for_bindings_named_same_as_variants(
|
||||
}
|
||||
}
|
||||
|
||||
/// Check that never patterns are only used on inhabited types.
|
||||
fn check_never_pattern<'tcx>(
|
||||
cx: &MatchCheckCtxt<'_, 'tcx>,
|
||||
pat: &Pat<'tcx>,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
if let PatKind::Never = pat.kind {
|
||||
if !cx.is_uninhabited(pat.ty) {
|
||||
return Err(cx.tcx.dcx().emit_err(NonEmptyNeverPattern { span: pat.span, ty: pat.ty }));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn report_irrefutable_let_patterns(
|
||||
tcx: TyCtxt<'_>,
|
||||
id: HirId,
|
||||
|
@ -1,73 +0,0 @@
|
||||
#![feature(never_patterns)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
enum Void {}
|
||||
|
||||
fn main() {}
|
||||
|
||||
// The classic use for empty types.
|
||||
fn safe_unwrap_result<T>(res: Result<T, Void>) {
|
||||
let Ok(_x) = res; //~ ERROR refutable pattern in local binding
|
||||
let (Ok(_x) | Err(!)) = &res;
|
||||
let (Ok(_x) | Err(&!)) = res.as_ref();
|
||||
}
|
||||
|
||||
// Check we only accept `!` where we want to.
|
||||
fn never_pattern_location(void: Void) {
|
||||
// FIXME(never_patterns): Don't accept on a non-empty type.
|
||||
match Some(0) {
|
||||
None => {}
|
||||
Some(!),
|
||||
}
|
||||
// FIXME(never_patterns): Don't accept on an arbitrary type, even if there are no more branches.
|
||||
match () {
|
||||
() => {}
|
||||
!,
|
||||
}
|
||||
// FIXME(never_patterns): Don't accept even on an empty branch.
|
||||
match None::<Void> {
|
||||
None => {}
|
||||
!,
|
||||
}
|
||||
// FIXME(never_patterns): Let alone if the emptiness is behind a reference.
|
||||
match None::<&Void> {
|
||||
None => {}
|
||||
!,
|
||||
}
|
||||
// Participate in match ergonomics.
|
||||
match &void {
|
||||
!
|
||||
}
|
||||
match &&void {
|
||||
!
|
||||
}
|
||||
match &&void {
|
||||
&!
|
||||
}
|
||||
match &None::<Void> {
|
||||
None => {}
|
||||
Some(!)
|
||||
}
|
||||
match None::<&Void> {
|
||||
None => {}
|
||||
Some(!),
|
||||
}
|
||||
// Accept on a composite empty type.
|
||||
match None::<&(u32, Void)> {
|
||||
None => {}
|
||||
Some(&!),
|
||||
}
|
||||
// Accept on an simple empty type.
|
||||
match None::<Void> {
|
||||
None => {}
|
||||
Some(!),
|
||||
}
|
||||
match None::<&Void> {
|
||||
None => {}
|
||||
Some(&!),
|
||||
}
|
||||
match None::<&(u32, Void)> {
|
||||
None => {}
|
||||
Some(&(_, !)),
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
error[E0005]: refutable pattern in local binding
|
||||
--> $DIR/never_patterns.rs:10:9
|
||||
|
|
||||
LL | let Ok(_x) = res;
|
||||
| ^^^^^^ pattern `Err(_)` not covered
|
||||
|
|
||||
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
|
||||
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
|
||||
= note: the matched value is of type `Result<T, Void>`
|
||||
help: you might want to use `let else` to handle the variant that isn't matched
|
||||
|
|
||||
LL | let Ok(_x) = res else { todo!() };
|
||||
| ++++++++++++++++
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0005`.
|
66
tests/ui/rfcs/rfc-0000-never_patterns/typeck.fail.stderr
Normal file
66
tests/ui/rfcs/rfc-0000-never_patterns/typeck.fail.stderr
Normal file
@ -0,0 +1,66 @@
|
||||
error: mismatched types
|
||||
--> $DIR/typeck.rs:25:9
|
||||
|
|
||||
LL | !,
|
||||
| ^ a never pattern must be used on an uninhabited type
|
||||
|
|
||||
= note: the matched value is of type `()`
|
||||
|
||||
error: mismatched types
|
||||
--> $DIR/typeck.rs:29:9
|
||||
|
|
||||
LL | !,
|
||||
| ^ a never pattern must be used on an uninhabited type
|
||||
|
|
||||
= note: the matched value is of type `(i32, bool)`
|
||||
|
||||
error: mismatched types
|
||||
--> $DIR/typeck.rs:33:13
|
||||
|
|
||||
LL | (_, !),
|
||||
| ^ a never pattern must be used on an uninhabited type
|
||||
|
|
||||
= note: the matched value is of type `bool`
|
||||
|
||||
error: mismatched types
|
||||
--> $DIR/typeck.rs:38:14
|
||||
|
|
||||
LL | Some(!),
|
||||
| ^ a never pattern must be used on an uninhabited type
|
||||
|
|
||||
= note: the matched value is of type `i32`
|
||||
|
||||
error: mismatched types
|
||||
--> $DIR/typeck.rs:45:9
|
||||
|
|
||||
LL | !,
|
||||
| ^ a never pattern must be used on an uninhabited type
|
||||
|
|
||||
= note: the matched value is of type `()`
|
||||
|
||||
error: mismatched types
|
||||
--> $DIR/typeck.rs:52:9
|
||||
|
|
||||
LL | !,
|
||||
| ^ a never pattern must be used on an uninhabited type
|
||||
|
|
||||
= note: the matched value is of type `Option<Void>`
|
||||
|
||||
error: mismatched types
|
||||
--> $DIR/typeck.rs:57:9
|
||||
|
|
||||
LL | !,
|
||||
| ^ a never pattern must be used on an uninhabited type
|
||||
|
|
||||
= note: the matched value is of type `[Void]`
|
||||
|
||||
error: mismatched types
|
||||
--> $DIR/typeck.rs:63:9
|
||||
|
|
||||
LL | !,
|
||||
| ^ a never pattern must be used on an uninhabited type
|
||||
|
|
||||
= note: the matched value is of type `Option<&Void>`
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
125
tests/ui/rfcs/rfc-0000-never_patterns/typeck.rs
Normal file
125
tests/ui/rfcs/rfc-0000-never_patterns/typeck.rs
Normal file
@ -0,0 +1,125 @@
|
||||
// revisions: pass fail
|
||||
//[pass] check-pass
|
||||
//[fail] check-fail
|
||||
#![feature(never_patterns)]
|
||||
#![feature(exhaustive_patterns)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum Void {}
|
||||
|
||||
fn main() {}
|
||||
|
||||
// The classic use for empty types.
|
||||
fn safe_unwrap_result<T: Copy>(res: Result<T, Void>) {
|
||||
let Ok(_x) = res;
|
||||
let (Ok(_x) | Err(!)) = &res;
|
||||
let (Ok(_x) | Err(!)) = res.as_ref();
|
||||
}
|
||||
|
||||
// Check we only accept `!` where we want to.
|
||||
#[cfg(fail)]
|
||||
fn never_pattern_typeck_fail(void: Void) {
|
||||
// Don't accept on a non-empty type.
|
||||
match () {
|
||||
!,
|
||||
//[fail]~^ ERROR: mismatched types
|
||||
}
|
||||
match (0, false) {
|
||||
!,
|
||||
//[fail]~^ ERROR: mismatched types
|
||||
}
|
||||
match (0, false) {
|
||||
(_, !),
|
||||
//[fail]~^ ERROR: mismatched types
|
||||
}
|
||||
match Some(0) {
|
||||
None => {}
|
||||
Some(!),
|
||||
//[fail]~^ ERROR: mismatched types
|
||||
}
|
||||
|
||||
// Don't accept on an arbitrary type, even if there are no more branches.
|
||||
match () {
|
||||
() => {}
|
||||
!,
|
||||
//[fail]~^ ERROR: mismatched types
|
||||
}
|
||||
|
||||
// Don't accept even on an empty branch.
|
||||
match None::<Void> {
|
||||
None => {}
|
||||
!,
|
||||
//[fail]~^ ERROR: mismatched types
|
||||
}
|
||||
match (&[] as &[Void]) {
|
||||
[] => {}
|
||||
!,
|
||||
//[fail]~^ ERROR: mismatched types
|
||||
}
|
||||
// Let alone if the emptiness is behind a reference.
|
||||
match None::<&Void> {
|
||||
None => {}
|
||||
!,
|
||||
//[fail]~^ ERROR: mismatched types
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(pass)]
|
||||
fn never_pattern_typeck_pass(void: Void) {
|
||||
// Participate in match ergonomics.
|
||||
match &void {
|
||||
!,
|
||||
}
|
||||
match &&void {
|
||||
!,
|
||||
}
|
||||
match &&void {
|
||||
&!,
|
||||
}
|
||||
match &None::<Void> {
|
||||
None => {}
|
||||
Some(!),
|
||||
}
|
||||
match None::<&Void> {
|
||||
None => {}
|
||||
Some(!),
|
||||
}
|
||||
|
||||
// Accept on a directly empty type.
|
||||
match void {
|
||||
!,
|
||||
}
|
||||
match &void {
|
||||
&!,
|
||||
}
|
||||
match None::<Void> {
|
||||
None => {}
|
||||
Some(!),
|
||||
}
|
||||
match None::<&Void> {
|
||||
None => {}
|
||||
Some(&!),
|
||||
}
|
||||
match None::<&(u32, Void)> {
|
||||
None => {}
|
||||
Some(&(_, !)),
|
||||
}
|
||||
match (&[] as &[Void]) {
|
||||
[] => {}
|
||||
[!],
|
||||
}
|
||||
// Accept on a composite empty type.
|
||||
match None::<&(u32, Void)> {
|
||||
None => {}
|
||||
Some(&!),
|
||||
}
|
||||
match None::<&(u32, Void)> {
|
||||
None => {}
|
||||
Some(!),
|
||||
}
|
||||
match None::<&Result<Void, Void>> {
|
||||
None => {}
|
||||
Some(!),
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user