Skip to content

Commit 2ea5001

Browse files
committed
Reveal opaque types in exhaustiveness checking
1 parent 22e5ad0 commit 2ea5001

File tree

4 files changed

+57
-94
lines changed

4 files changed

+57
-94
lines changed

Diff for: 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,

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

+17-9
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, we use the corresponding concrete type
365+
/// 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(hidden_ty) = self.typeck_results.concrete_opaque_types.get(&key) {
371+
return hidden_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

Diff for: 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
}
@@ -51,10 +52,8 @@ fn option_never(x: Void) -> Option<impl Copy> {
5152

5253
fn inner_never(x: Void) {
5354
type T = impl Copy;
54-
//~^ERROR unconstrained
5555
let y: T = x;
5656
match y {}
57-
//~^ERROR non-empty
5857
}
5958

6059
// This one caused ICE https://github.com/rust-lang/rust/issues/117100.
@@ -68,10 +67,8 @@ fn inner_tuple() {
6867
}
6968

7069
type U = impl Copy;
71-
//~^ERROR unconstrained
7270
fn unify_never(x: Void, u: U) -> U {
7371
if true { match u {} } else { x }
74-
//~^ERROR non-empty
7572
}
7673

7774
type V = impl Copy;

Diff for: tests/ui/pattern/usefulness/impl-trait.stderr

+31-77
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,57 @@
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-
37-
error[E0004]: non-exhaustive patterns: type `inner_never::T` is non-empty
38-
--> $DIR/impl-trait.rs:56:11
39-
|
40-
LL | match y {}
41-
| ^
42-
|
43-
= note: the matched value is of type `inner_never::T`
44-
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
1+
error: unreachable pattern
2+
--> $DIR/impl-trait.rs:41:13
453
|
46-
LL ~ match y {
47-
LL + _ => todo!(),
48-
LL + }
4+
LL | Some(_) => {}
5+
| ^^^^^^^
496
|
50-
51-
error: unconstrained opaque type
52-
--> $DIR/impl-trait.rs:53:14
53-
|
54-
LL | type T = impl Copy;
55-
| ^^^^^^^^^
7+
note: the lint level is defined here
8+
--> $DIR/impl-trait.rs:5:9
569
|
57-
= note: `T` must be used in combination with a concrete type within the same item
10+
LL | #![deny(unreachable_patterns)]
11+
| ^^^^^^^^^^^^^^^^^^^^
5812

5913
error: unreachable pattern
60-
--> $DIR/impl-trait.rs:66:9
14+
--> $DIR/impl-trait.rs:65:9
6115
|
6216
LL | _ => {}
6317
| - matches any value
6418
LL | Some((a, b)) => {}
6519
| ^^^^^^^^^^^^ unreachable pattern
20+
21+
error: unreachable pattern
22+
--> $DIR/impl-trait.rs:79:9
6623
|
67-
note: the lint level is defined here
68-
--> $DIR/impl-trait.rs:5:9
69-
|
70-
LL | #![deny(unreachable_patterns)]
24+
LL | Some((mut x, mut y)) => {
7125
| ^^^^^^^^^^^^^^^^^^^^
7226

73-
error[E0004]: non-exhaustive patterns: type `U` is non-empty
74-
--> $DIR/impl-trait.rs:73:21
27+
error[E0004]: non-exhaustive patterns: type `impl Copy` is non-empty
28+
--> $DIR/impl-trait.rs:21:11
7529
|
76-
LL | if true { match u {} } else { x }
77-
| ^
30+
LL | match return_never_rpit(x) {}
31+
| ^^^^^^^^^^^^^^^^^^^^
7832
|
79-
= note: the matched value is of type `U`
33+
= note: the matched value is of type `impl Copy`
8034
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
8135
|
82-
LL ~ if true { match u {
36+
LL ~ match return_never_rpit(x) {
8337
LL + _ => todo!(),
84-
LL ~ } } else { x }
38+
LL + }
8539
|
8640

87-
error: unconstrained opaque type
88-
--> $DIR/impl-trait.rs:70:10
41+
error[E0004]: non-exhaustive patterns: type `T` is non-empty
42+
--> $DIR/impl-trait.rs:33:11
8943
|
90-
LL | type U = impl Copy;
91-
| ^^^^^^^^^
44+
LL | match return_never_tait(x) {}
45+
| ^^^^^^^^^^^^^^^^^^^^
9246
|
93-
= note: `U` must be used in combination with a concrete type within the same module
94-
95-
error: unreachable pattern
96-
--> $DIR/impl-trait.rs:82:9
47+
= note: the matched value is of type `T`
48+
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
49+
|
50+
LL ~ match return_never_tait(x) {
51+
LL + _ => todo!(),
52+
LL + }
9753
|
98-
LL | Some((mut x, mut y)) => {
99-
| ^^^^^^^^^^^^^^^^^^^^
10054

101-
error: aborting due to 9 previous errors
55+
error: aborting due to 5 previous errors
10256

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

0 commit comments

Comments
 (0)