Skip to content

Commit f750770

Browse files
committed
Reveal opaque types in exhaustiveness checking
1 parent 16f6894 commit f750770

File tree

4 files changed

+64
-112
lines changed

4 files changed

+64
-112
lines changed

compiler/rustc_mir_build/src/thir/pattern/check_match.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@ use rustc_span::hygiene::DesugaringKind;
2626
use rustc_span::Span;
2727

2828
pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> {
29+
let typeck_results = tcx.typeck(def_id);
2930
let (thir, expr) = tcx.thir_body(def_id)?;
3031
let thir = thir.borrow();
3132
let pattern_arena = TypedArena::default();
3233
let mut visitor = MatchVisitor {
3334
tcx,
3435
thir: &*thir,
36+
typeck_results,
3537
param_env: tcx.param_env(def_id),
3638
lint_level: tcx.hir().local_def_id_to_hir_id(def_id),
3739
let_source: LetSource::None,
@@ -76,6 +78,7 @@ enum LetSource {
7678
struct MatchVisitor<'thir, 'p, 'tcx> {
7779
tcx: TyCtxt<'tcx>,
7880
param_env: ty::ParamEnv<'tcx>,
81+
typeck_results: &'tcx ty::TypeckResults<'tcx>,
7982
thir: &'thir Thir<'tcx>,
8083
lint_level: HirId,
8184
let_source: LetSource,
@@ -292,6 +295,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
292295
};
293296
MatchCheckCtxt {
294297
tcx: self.tcx,
298+
typeck_results: self.typeck_results,
295299
param_env: self.param_env,
296300
module: self.tcx.parent_module(self.lint_level).to_def_id(),
297301
pattern_arena: &self.pattern_arena,

compiler/rustc_mir_build/src/thir/pattern/usefulness.rs

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ use std::fmt;
329329

330330
pub(crate) struct MatchCheckCtxt<'p, 'tcx> {
331331
pub(crate) tcx: TyCtxt<'tcx>,
332+
pub(crate) typeck_results: &'tcx ty::TypeckResults<'tcx>,
332333
/// The module in which the match occurs. This is necessary for
333334
/// checking inhabited-ness of types because whether a type is (visibly)
334335
/// inhabited can depend on whether it was defined in the current module or
@@ -359,6 +360,21 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
359360
_ => false,
360361
}
361362
}
363+
364+
/// Type inference occasionally gives us opaque types in places where corresponding patterns
365+
/// have more specific types. To avoid inconsistencies as well as detect opaque uninhabited
366+
/// types, we use the corresponding concrete type if possible.
367+
fn reveal_opaque_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
368+
if let ty::Alias(ty::Opaque, alias_ty) = ty.kind() {
369+
if let Some(local_def_id) = alias_ty.def_id.as_local() {
370+
let key = ty::OpaqueTypeKey { def_id: local_def_id, args: alias_ty.args };
371+
if let Some(real_ty) = self.typeck_results.concrete_opaque_types.get(&key) {
372+
return real_ty.ty;
373+
}
374+
}
375+
}
376+
ty
377+
}
362378
}
363379

364380
#[derive(Copy, Clone)]
@@ -818,15 +834,7 @@ fn is_useful<'p, 'tcx>(
818834
}
819835
}
820836
} else {
821-
let mut ty = v.head().ty();
822-
823-
// Opaque types can't get destructured/split, but the patterns can
824-
// actually hint at hidden types, so we use the patterns' types instead.
825-
if let ty::Alias(ty::Opaque, ..) = ty.kind() {
826-
if let Some(row) = rows.first() {
827-
ty = row.head().ty();
828-
}
829-
}
837+
let ty = cx.reveal_opaque_ty(v.head().ty());
830838
debug!("v.head: {:?}, v.span: {:?}", v.head(), v.head().span());
831839
let pcx = &PatCtxt { cx, ty, span: v.head().span(), is_top_level };
832840

@@ -883,23 +891,12 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
883891
fn is_empty(&self) -> bool {
884892
self.patterns.is_empty()
885893
}
886-
fn head_ty(&self) -> Option<Ty<'tcx>> {
894+
fn head_ty(&self, cx: &MatchCheckCtxt<'p, 'tcx>) -> Option<Ty<'tcx>> {
887895
if self.patterns.len() == 0 {
888896
return None;
889897
}
890-
// If the type is opaque and it is revealed anywhere in the column, we take the revealed
891-
// version. Otherwise we could encounter constructors for the revealed type and crash.
892-
let is_opaque = |ty: Ty<'tcx>| matches!(ty.kind(), ty::Alias(ty::Opaque, ..));
893-
let first_ty = self.patterns[0].ty();
894-
if is_opaque(first_ty) {
895-
for pat in &self.patterns {
896-
let ty = pat.ty();
897-
if !is_opaque(ty) {
898-
return Some(ty);
899-
}
900-
}
901-
}
902-
Some(first_ty)
898+
// Important: we reaveal the opaque type if necessary.
899+
Some(cx.reveal_opaque_ty(self.patterns[0].ty()))
903900
}
904901

905902
fn analyze_ctors(&self, pcx: &PatCtxt<'_, 'p, 'tcx>) -> SplitConstructorSet<'tcx> {
@@ -955,7 +952,7 @@ fn collect_nonexhaustive_missing_variants<'p, 'tcx>(
955952
cx: &MatchCheckCtxt<'p, 'tcx>,
956953
column: &PatternColumn<'p, 'tcx>,
957954
) -> Vec<WitnessPat<'tcx>> {
958-
let Some(ty) = column.head_ty() else {
955+
let Some(ty) = column.head_ty(cx) else {
959956
return Vec::new();
960957
};
961958
let pcx = &PatCtxt { cx, ty, span: DUMMY_SP, is_top_level: false };
@@ -1005,7 +1002,7 @@ fn lint_overlapping_range_endpoints<'p, 'tcx>(
10051002
column: &PatternColumn<'p, 'tcx>,
10061003
lint_root: HirId,
10071004
) {
1008-
let Some(ty) = column.head_ty() else {
1005+
let Some(ty) = column.head_ty(cx) else {
10091006
return;
10101007
};
10111008
let pcx = &PatCtxt { cx, ty, span: DUMMY_SP, is_top_level: false };

tests/ui/pattern/usefulness/impl-trait.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,35 +14,36 @@ enum Void {}
1414
fn return_never_rpit(x: Void) -> impl Copy {
1515
if false {
1616
match return_never_rpit(x) {}
17-
//~^ERROR non-empty
1817
}
1918
x
2019
}
2120
fn friend_of_return_never_rpit(x: Void) {
2221
match return_never_rpit(x) {}
22+
//~^ERROR non-empty
2323
}
2424

2525
type T = impl Copy;
26-
//~^ERROR unconstrained
2726
fn return_never_tait(x: Void) -> T {
2827
if false {
2928
match return_never_tait(x) {}
30-
//~^ERROR non-empty
3129
}
3230
x
3331
}
3432
fn friend_of_return_never_tait(x: Void) {
3533
match return_never_tait(x) {}
34+
//~^ERROR non-empty
3635
}
3736

3837
fn option_never(x: Void) -> Option<impl Copy> {
3938
if true {
4039
match option_never(x) {
4140
None => {}
42-
Some(_) => {}
41+
Some(_) => {} //~ERROR unreachable
4342
}
4443
match option_never(x) {
4544
None => {}
45+
// FIXME: Unreachable not detected because `is_uninhabited` did not look into the
46+
// opaque type.
4647
_ => {}
4748
}
4849
}
@@ -68,10 +69,8 @@ fn option_never2(x: Void) -> impl Copy {
6869

6970
fn inner_never(x: Void) {
7071
type T = impl Copy;
71-
//~^ERROR unconstrained
7272
let y: T = x;
7373
match y {}
74-
//~^ERROR non-empty
7574
}
7675

7776
// This one caused ICE https://github.com/rust-lang/rust/issues/117100.
@@ -85,10 +84,8 @@ fn inner_tuple() {
8584
}
8685

8786
type U = impl Copy;
88-
//~^ERROR unconstrained
8987
fn unify_never(x: Void, u: U) -> U {
9088
if true { match u {} } else { x }
91-
//~^ERROR non-empty
9289
}
9390

9491
type V = impl Copy;
Lines changed: 33 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,5 @@
1-
error[E0004]: non-exhaustive patterns: type `impl Copy` is non-empty
2-
--> $DIR/impl-trait.rs:16:15
3-
|
4-
LL | match return_never_rpit(x) {}
5-
| ^^^^^^^^^^^^^^^^^^^^
6-
|
7-
= note: the matched value is of type `impl Copy`
8-
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
9-
|
10-
LL ~ match return_never_rpit(x) {
11-
LL + _ => todo!(),
12-
LL + }
13-
|
14-
15-
error[E0004]: non-exhaustive patterns: type `T` is non-empty
16-
--> $DIR/impl-trait.rs:29:15
17-
|
18-
LL | match return_never_tait(x) {}
19-
| ^^^^^^^^^^^^^^^^^^^^
20-
|
21-
= note: the matched value is of type `T`
22-
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
23-
|
24-
LL ~ match return_never_tait(x) {
25-
LL + _ => todo!(),
26-
LL + }
27-
|
28-
29-
error: unconstrained opaque type
30-
--> $DIR/impl-trait.rs:25:10
31-
|
32-
LL | type T = impl Copy;
33-
| ^^^^^^^^^
34-
|
35-
= note: `T` must be used in combination with a concrete type within the same module
36-
371
error: unreachable pattern
38-
--> $DIR/impl-trait.rs:56:13
2+
--> $DIR/impl-trait.rs:41:13
393
|
404
LL | Some(_) => {}
415
| ^^^^^^^
@@ -47,69 +11,59 @@ LL | #![deny(unreachable_patterns)]
4711
| ^^^^^^^^^^^^^^^^^^^^
4812

4913
error: unreachable pattern
50-
--> $DIR/impl-trait.rs:60:13
51-
|
52-
LL | _ => {}
53-
| ^
54-
55-
error[E0004]: non-exhaustive patterns: type `inner_never::T` is non-empty
56-
--> $DIR/impl-trait.rs:73:11
57-
|
58-
LL | match y {}
59-
| ^
60-
|
61-
= note: the matched value is of type `inner_never::T`
62-
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
63-
|
64-
LL ~ match y {
65-
LL + _ => todo!(),
66-
LL + }
14+
--> $DIR/impl-trait.rs:57:13
6715
|
16+
LL | Some(_) => {}
17+
| ^^^^^^^
6818

69-
error: unconstrained opaque type
70-
--> $DIR/impl-trait.rs:70:14
71-
|
72-
LL | type T = impl Copy;
73-
| ^^^^^^^^^
19+
error: unreachable pattern
20+
--> $DIR/impl-trait.rs:61:13
7421
|
75-
= note: `T` must be used in combination with a concrete type within the same item
22+
LL | _ => {}
23+
| ^
7624

7725
error: unreachable pattern
78-
--> $DIR/impl-trait.rs:83:9
26+
--> $DIR/impl-trait.rs:82:9
7927
|
8028
LL | _ => {}
8129
| - matches any value
8230
LL | Some((a, b)) => {}
8331
| ^^^^^^^^^^^^ unreachable pattern
8432

85-
error[E0004]: non-exhaustive patterns: type `U` is non-empty
86-
--> $DIR/impl-trait.rs:90:21
33+
error: unreachable pattern
34+
--> $DIR/impl-trait.rs:96:9
35+
|
36+
LL | Some((mut x, mut y)) => {
37+
| ^^^^^^^^^^^^^^^^^^^^
38+
39+
error[E0004]: non-exhaustive patterns: type `impl Copy` is non-empty
40+
--> $DIR/impl-trait.rs:21:11
8741
|
88-
LL | if true { match u {} } else { x }
89-
| ^
42+
LL | match return_never_rpit(x) {}
43+
| ^^^^^^^^^^^^^^^^^^^^
9044
|
91-
= note: the matched value is of type `U`
45+
= note: the matched value is of type `impl Copy`
9246
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
9347
|
94-
LL ~ if true { match u {
48+
LL ~ match return_never_rpit(x) {
9549
LL + _ => todo!(),
96-
LL ~ } } else { x }
50+
LL + }
9751
|
9852

99-
error: unconstrained opaque type
100-
--> $DIR/impl-trait.rs:87:10
53+
error[E0004]: non-exhaustive patterns: type `T` is non-empty
54+
--> $DIR/impl-trait.rs:33:11
10155
|
102-
LL | type U = impl Copy;
103-
| ^^^^^^^^^
56+
LL | match return_never_tait(x) {}
57+
| ^^^^^^^^^^^^^^^^^^^^
10458
|
105-
= note: `U` must be used in combination with a concrete type within the same module
106-
107-
error: unreachable pattern
108-
--> $DIR/impl-trait.rs:99:9
59+
= note: the matched value is of type `T`
60+
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
61+
|
62+
LL ~ match return_never_tait(x) {
63+
LL + _ => todo!(),
64+
LL + }
10965
|
110-
LL | Some((mut x, mut y)) => {
111-
| ^^^^^^^^^^^^^^^^^^^^
11266

113-
error: aborting due to 11 previous errors
67+
error: aborting due to 7 previous errors
11468

11569
For more information about this error, try `rustc --explain E0004`.

0 commit comments

Comments
 (0)