Skip to content

Commit eeb12a4

Browse files
committed
Typecheck never patterns
1 parent 6e98731 commit eeb12a4

File tree

6 files changed

+110
-7
lines changed

6 files changed

+110
-7
lines changed

Diff for: compiler/rustc_hir_typeck/src/pat.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
178178

179179
let ty = match pat.kind {
180180
PatKind::Wild => expected,
181-
// FIXME(never_patterns): check the type is uninhabited. If that is not possible within
182-
// typeck, do that in a later phase.
181+
// We allow any type here; we ensure that the type is uninhabited during match checking.
183182
PatKind::Never => expected,
184183
PatKind::Lit(lt) => self.check_pat_lit(pat.span, lt, expected, ti),
185184
PatKind::Range(lhs, rhs, _) => self.check_pat_range(pat.span, lhs, rhs, expected, ti),

Diff for: compiler/rustc_mir_build/messages.ftl

+5
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,11 @@ mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsa
234234
235235
mir_build_non_const_path = runtime values cannot be referenced in patterns
236236
237+
mir_build_non_empty_never_pattern =
238+
mismatched types
239+
.label = a never pattern must be used on an uninhabited type
240+
.note = the matched value is of type `{$ty}`
241+
237242
mir_build_non_exhaustive_match_all_arms_guarded =
238243
match arms with guards don't count towards exhaustivity
239244

Diff for: compiler/rustc_mir_build/src/errors.rs

+10
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,16 @@ pub struct FloatPattern;
788788
#[diag(mir_build_pointer_pattern)]
789789
pub struct PointerPattern;
790790

791+
#[derive(Diagnostic)]
792+
#[diag(mir_build_non_empty_never_pattern)]
793+
#[note]
794+
pub struct NonEmptyNeverPattern<'tcx> {
795+
#[primary_span]
796+
#[label]
797+
pub span: Span,
798+
pub ty: Ty<'tcx>,
799+
}
800+
791801
#[derive(LintDiagnostic)]
792802
#[diag(mir_build_indirect_structural_match)]
793803
#[note(mir_build_type_not_structural_tip)]

Diff for: compiler/rustc_mir_build/src/thir/pattern/check_match.rs

+16
Original file line numberDiff line numberDiff line change
@@ -276,10 +276,13 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
276276
} else {
277277
// Check the pattern for some things unrelated to exhaustiveness.
278278
let refutable = if cx.refutable { Refutable } else { Irrefutable };
279+
let mut err = Ok(());
279280
pat.walk_always(|pat| {
280281
check_borrow_conflicts_in_at_patterns(self, pat);
281282
check_for_bindings_named_same_as_variants(self, pat, refutable);
283+
err = err.and(check_never_pattern(cx, pat));
282284
});
285+
err?;
283286
Ok(cx.pattern_arena.alloc(cx.lower_pat(pat)))
284287
}
285288
}
@@ -811,6 +814,19 @@ fn check_for_bindings_named_same_as_variants(
811814
}
812815
}
813816

817+
/// Check that never patterns are only used on inhabited types.
818+
fn check_never_pattern<'tcx>(
819+
cx: &MatchCheckCtxt<'_, 'tcx>,
820+
pat: &Pat<'tcx>,
821+
) -> Result<(), ErrorGuaranteed> {
822+
if let PatKind::Never = pat.kind {
823+
if !cx.is_uninhabited(pat.ty) {
824+
return Err(cx.tcx.dcx().emit_err(NonEmptyNeverPattern { span: pat.span, ty: pat.ty }));
825+
}
826+
}
827+
Ok(())
828+
}
829+
814830
fn report_irrefutable_let_patterns(
815831
tcx: TyCtxt<'_>,
816832
id: HirId,

Diff for: tests/ui/rfcs/rfc-0000-never_patterns/typeck.rs

+12-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// check-pass
21
#![feature(never_patterns)]
32
#![feature(exhaustive_patterns)]
43
#![allow(incomplete_features)]
@@ -17,40 +16,48 @@ fn safe_unwrap_result<T: Copy>(res: Result<T, Void>) {
1716

1817
// Check we only accept `!` where we want to.
1918
fn never_pattern_typeck(void: Void) {
20-
// FIXME(never_patterns): Don't accept on a non-empty type.
19+
// Don't accept on a non-empty type.
2120
match () {
2221
!,
22+
//~^ ERROR: mismatched types
2323
}
2424
match (0, false) {
2525
!,
26+
//~^ ERROR: mismatched types
2627
}
2728
match (0, false) {
2829
(_, !),
30+
//~^ ERROR: mismatched types
2931
}
3032
match Some(0) {
3133
None => {}
3234
Some(!),
35+
//~^ ERROR: mismatched types
3336
}
3437

35-
// FIXME(never_patterns): Don't accept on an arbitrary type, even if there are no more branches.
38+
// Don't accept on an arbitrary type, even if there are no more branches.
3639
match () {
3740
() => {}
3841
!,
42+
//~^ ERROR: mismatched types
3943
}
4044

41-
// FIXME(never_patterns): Don't accept even on an empty branch.
45+
// Don't accept even on an empty branch.
4246
match None::<Void> {
4347
None => {}
4448
!,
49+
//~^ ERROR: mismatched types
4550
}
4651
match (&[] as &[Void]) {
4752
[] => {}
4853
!,
54+
//~^ ERROR: mismatched types
4955
}
50-
// FIXME(never_patterns): Let alone if the emptiness is behind a reference.
56+
// Let alone if the emptiness is behind a reference.
5157
match None::<&Void> {
5258
None => {}
5359
!,
60+
//~^ ERROR: mismatched types
5461
}
5562

5663
// Participate in match ergonomics.

Diff for: tests/ui/rfcs/rfc-0000-never_patterns/typeck.stderr

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
error: mismatched types
2+
--> $DIR/typeck.rs:21:9
3+
|
4+
LL | !,
5+
| ^ a never pattern must be used on an uninhabited type
6+
|
7+
= note: the matched value is of type `()`
8+
9+
error: mismatched types
10+
--> $DIR/typeck.rs:25:9
11+
|
12+
LL | !,
13+
| ^ a never pattern must be used on an uninhabited type
14+
|
15+
= note: the matched value is of type `(i32, bool)`
16+
17+
error: mismatched types
18+
--> $DIR/typeck.rs:29:13
19+
|
20+
LL | (_, !),
21+
| ^ a never pattern must be used on an uninhabited type
22+
|
23+
= note: the matched value is of type `bool`
24+
25+
error: mismatched types
26+
--> $DIR/typeck.rs:34:14
27+
|
28+
LL | Some(!),
29+
| ^ a never pattern must be used on an uninhabited type
30+
|
31+
= note: the matched value is of type `i32`
32+
33+
error: mismatched types
34+
--> $DIR/typeck.rs:41:9
35+
|
36+
LL | !,
37+
| ^ a never pattern must be used on an uninhabited type
38+
|
39+
= note: the matched value is of type `()`
40+
41+
error: mismatched types
42+
--> $DIR/typeck.rs:48:9
43+
|
44+
LL | !,
45+
| ^ a never pattern must be used on an uninhabited type
46+
|
47+
= note: the matched value is of type `Option<Void>`
48+
49+
error: mismatched types
50+
--> $DIR/typeck.rs:53:9
51+
|
52+
LL | !,
53+
| ^ a never pattern must be used on an uninhabited type
54+
|
55+
= note: the matched value is of type `[Void]`
56+
57+
error: mismatched types
58+
--> $DIR/typeck.rs:59:9
59+
|
60+
LL | !,
61+
| ^ a never pattern must be used on an uninhabited type
62+
|
63+
= note: the matched value is of type `Option<&Void>`
64+
65+
error: aborting due to 8 previous errors
66+

0 commit comments

Comments
 (0)