Skip to content

Commit 48e6fc7

Browse files
authored
Unrolled build for rust-lang#120009
Rollup merge of rust-lang#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`
2 parents 0547c41 + ff6fa67 commit 48e6fc7

File tree

8 files changed

+223
-92
lines changed

8 files changed

+223
-92
lines changed

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 | PatKind::Err(_) => 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),

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

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

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,

tests/ui/pattern/never_patterns.rs

-73
This file was deleted.

tests/ui/pattern/never_patterns.stderr

-17
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
error: mismatched types
2+
--> $DIR/typeck.rs:25: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:29: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:33: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:38: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:45: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:52: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:57: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:63: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+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// revisions: pass fail
2+
//[pass] check-pass
3+
//[fail] check-fail
4+
#![feature(never_patterns)]
5+
#![feature(exhaustive_patterns)]
6+
#![allow(incomplete_features)]
7+
8+
#[derive(Copy, Clone)]
9+
enum Void {}
10+
11+
fn main() {}
12+
13+
// The classic use for empty types.
14+
fn safe_unwrap_result<T: Copy>(res: Result<T, Void>) {
15+
let Ok(_x) = res;
16+
let (Ok(_x) | Err(!)) = &res;
17+
let (Ok(_x) | Err(!)) = res.as_ref();
18+
}
19+
20+
// Check we only accept `!` where we want to.
21+
#[cfg(fail)]
22+
fn never_pattern_typeck_fail(void: Void) {
23+
// Don't accept on a non-empty type.
24+
match () {
25+
!,
26+
//[fail]~^ ERROR: mismatched types
27+
}
28+
match (0, false) {
29+
!,
30+
//[fail]~^ ERROR: mismatched types
31+
}
32+
match (0, false) {
33+
(_, !),
34+
//[fail]~^ ERROR: mismatched types
35+
}
36+
match Some(0) {
37+
None => {}
38+
Some(!),
39+
//[fail]~^ ERROR: mismatched types
40+
}
41+
42+
// Don't accept on an arbitrary type, even if there are no more branches.
43+
match () {
44+
() => {}
45+
!,
46+
//[fail]~^ ERROR: mismatched types
47+
}
48+
49+
// Don't accept even on an empty branch.
50+
match None::<Void> {
51+
None => {}
52+
!,
53+
//[fail]~^ ERROR: mismatched types
54+
}
55+
match (&[] as &[Void]) {
56+
[] => {}
57+
!,
58+
//[fail]~^ ERROR: mismatched types
59+
}
60+
// Let alone if the emptiness is behind a reference.
61+
match None::<&Void> {
62+
None => {}
63+
!,
64+
//[fail]~^ ERROR: mismatched types
65+
}
66+
}
67+
68+
#[cfg(pass)]
69+
fn never_pattern_typeck_pass(void: Void) {
70+
// Participate in match ergonomics.
71+
match &void {
72+
!,
73+
}
74+
match &&void {
75+
!,
76+
}
77+
match &&void {
78+
&!,
79+
}
80+
match &None::<Void> {
81+
None => {}
82+
Some(!),
83+
}
84+
match None::<&Void> {
85+
None => {}
86+
Some(!),
87+
}
88+
89+
// Accept on a directly empty type.
90+
match void {
91+
!,
92+
}
93+
match &void {
94+
&!,
95+
}
96+
match None::<Void> {
97+
None => {}
98+
Some(!),
99+
}
100+
match None::<&Void> {
101+
None => {}
102+
Some(&!),
103+
}
104+
match None::<&(u32, Void)> {
105+
None => {}
106+
Some(&(_, !)),
107+
}
108+
match (&[] as &[Void]) {
109+
[] => {}
110+
[!],
111+
}
112+
// Accept on a composite empty type.
113+
match None::<&(u32, Void)> {
114+
None => {}
115+
Some(&!),
116+
}
117+
match None::<&(u32, Void)> {
118+
None => {}
119+
Some(!),
120+
}
121+
match None::<&Result<Void, Void>> {
122+
None => {}
123+
Some(!),
124+
}
125+
}

0 commit comments

Comments
 (0)