Skip to content

Commit 39105be

Browse files
committed
Reveal opaque types in exhaustiveness checking
1 parent 364bbff commit 39105be

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

+4
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@ use rustc_span::hygiene::DesugaringKind;
2828
use rustc_span::Span;
2929

3030
pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> {
31+
let typeck_results = tcx.typeck(def_id);
3132
let (thir, expr) = tcx.thir_body(def_id)?;
3233
let thir = thir.borrow();
3334
let pattern_arena = TypedArena::default();
3435
let mut visitor = MatchVisitor {
3536
tcx,
3637
thir: &*thir,
38+
typeck_results,
3739
param_env: tcx.param_env(def_id),
3840
lint_level: tcx.hir().local_def_id_to_hir_id(def_id),
3941
let_source: LetSource::None,
@@ -77,6 +79,7 @@ enum LetSource {
7779
struct MatchVisitor<'a, 'p, 'tcx> {
7880
tcx: TyCtxt<'tcx>,
7981
param_env: ty::ParamEnv<'tcx>,
82+
typeck_results: &'tcx ty::TypeckResults<'tcx>,
8083
thir: &'a Thir<'tcx>,
8184
lint_level: HirId,
8285
let_source: LetSource,
@@ -221,6 +224,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
221224
fn new_cx(&self, hir_id: HirId, refutable: bool) -> MatchCheckCtxt<'p, 'tcx> {
222225
MatchCheckCtxt {
223226
tcx: self.tcx,
227+
typeck_results: self.typeck_results,
224228
param_env: self.param_env,
225229
module: self.tcx.parent_module(hir_id).to_def_id(),
226230
pattern_arena: &self.pattern_arena,

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

+22-25
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ use std::fmt;
328328

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

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

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

904901
fn analyze_ctors(&self, pcx: &PatCtxt<'_, 'p, 'tcx>) -> SplitConstructorSet<'tcx> {
@@ -954,7 +951,7 @@ fn collect_nonexhaustive_missing_variants<'p, 'tcx>(
954951
cx: &MatchCheckCtxt<'p, 'tcx>,
955952
column: &PatternColumn<'p, 'tcx>,
956953
) -> Vec<WitnessPat<'tcx>> {
957-
let Some(ty) = column.head_ty() else {
954+
let Some(ty) = column.head_ty(cx) else {
958955
return Vec::new();
959956
};
960957
let pcx = &PatCtxt { cx, ty, span: DUMMY_SP, is_top_level: false };
@@ -1004,7 +1001,7 @@ fn lint_overlapping_range_endpoints<'p, 'tcx>(
10041001
column: &PatternColumn<'p, 'tcx>,
10051002
lint_root: HirId,
10061003
) {
1007-
let Some(ty) = column.head_ty() else {
1004+
let Some(ty) = column.head_ty(cx) else {
10081005
return;
10091006
};
10101007
let pcx = &PatCtxt { cx, ty, span: DUMMY_SP, is_top_level: false };

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

+5-8
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;
+33-79
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)