Typecheck never patterns

This commit is contained in:
Nadrieril 2024-01-05 17:21:09 +01:00
parent a947c4c2c3
commit d8b72e796e
6 changed files with 110 additions and 7 deletions

View File

@ -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),

View File

@ -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

View File

@ -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)]

View File

@ -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)))
}
}
@ -811,6 +814,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,

View File

@ -1,4 +1,3 @@
// check-pass
#![feature(never_patterns)]
#![feature(exhaustive_patterns)]
#![allow(incomplete_features)]
@ -17,40 +16,48 @@ fn safe_unwrap_result<T: Copy>(res: Result<T, Void>) {
// Check we only accept `!` where we want to.
fn never_pattern_typeck(void: Void) {
// FIXME(never_patterns): Don't accept on a non-empty type.
// Don't accept on a non-empty type.
match () {
!,
//~^ ERROR: mismatched types
}
match (0, false) {
!,
//~^ ERROR: mismatched types
}
match (0, false) {
(_, !),
//~^ ERROR: mismatched types
}
match Some(0) {
None => {}
Some(!),
//~^ ERROR: mismatched types
}
// FIXME(never_patterns): Don't accept on an arbitrary type, even if there are no more branches.
// Don't accept on an arbitrary type, even if there are no more branches.
match () {
() => {}
!,
//~^ ERROR: mismatched types
}
// FIXME(never_patterns): Don't accept even on an empty branch.
// Don't accept even on an empty branch.
match None::<Void> {
None => {}
!,
//~^ ERROR: mismatched types
}
match (&[] as &[Void]) {
[] => {}
!,
//~^ ERROR: mismatched types
}
// FIXME(never_patterns): Let alone if the emptiness is behind a reference.
// Let alone if the emptiness is behind a reference.
match None::<&Void> {
None => {}
!,
//~^ ERROR: mismatched types
}
// Participate in match ergonomics.

View File

@ -0,0 +1,66 @@
error: mismatched types
--> $DIR/typeck.rs:21: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:25: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:29: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:34: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:41: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:48: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:53: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:59: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