Skip to content

Commit

Permalink
Reveal opaque types in exhaustiveness checking
Browse files Browse the repository at this point in the history
  • Loading branch information
Nadrieril committed Oct 29, 2023
1 parent 22e5ad0 commit 2ea5001
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 94 deletions.
4 changes: 4 additions & 0 deletions compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Expand Up @@ -28,12 +28,14 @@ use rustc_span::hygiene::DesugaringKind;
use rustc_span::Span;

pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> {
let typeck_results = tcx.typeck(def_id);
let (thir, expr) = tcx.thir_body(def_id)?;
let thir = thir.borrow();
let pattern_arena = TypedArena::default();
let mut visitor = MatchVisitor {
tcx,
thir: &*thir,
typeck_results,
param_env: tcx.param_env(def_id),
lint_level: tcx.hir().local_def_id_to_hir_id(def_id),
let_source: LetSource::None,
Expand Down Expand Up @@ -77,6 +79,7 @@ enum LetSource {
struct MatchVisitor<'a, 'p, 'tcx> {
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
typeck_results: &'tcx ty::TypeckResults<'tcx>,
thir: &'a Thir<'tcx>,
lint_level: HirId,
let_source: LetSource,
Expand Down Expand Up @@ -221,6 +224,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
fn new_cx(&self, hir_id: HirId, refutable: bool) -> MatchCheckCtxt<'p, 'tcx> {
MatchCheckCtxt {
tcx: self.tcx,
typeck_results: self.typeck_results,
param_env: self.param_env,
module: self.tcx.parent_module(hir_id).to_def_id(),
pattern_arena: &self.pattern_arena,
Expand Down
26 changes: 17 additions & 9 deletions compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
Expand Up @@ -328,6 +328,7 @@ use std::fmt;

pub(crate) struct MatchCheckCtxt<'p, 'tcx> {
pub(crate) tcx: TyCtxt<'tcx>,
pub(crate) typeck_results: &'tcx ty::TypeckResults<'tcx>,
/// The module in which the match occurs. This is necessary for
/// checking inhabited-ness of types because whether a type is (visibly)
/// inhabited can depend on whether it was defined in the current module or
Expand Down Expand Up @@ -358,6 +359,21 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
_ => false,
}
}

/// Type inference occasionally gives us opaque types in places where corresponding patterns
/// have more specific types. To avoid inconsistencies, we use the corresponding concrete type
/// if possible.
fn reveal_opaque_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
if let ty::Alias(ty::Opaque, alias_ty) = ty.kind() {
if let Some(local_def_id) = alias_ty.def_id.as_local() {
let key = ty::OpaqueTypeKey { def_id: local_def_id, args: alias_ty.args };
if let Some(hidden_ty) = self.typeck_results.concrete_opaque_types.get(&key) {
return hidden_ty.ty;
}
}
}
ty
}
}

#[derive(Copy, Clone)]
Expand Down Expand Up @@ -817,15 +833,7 @@ fn is_useful<'p, 'tcx>(
}
}
} else {
let mut ty = v.head().ty();

// Opaque types can't get destructured/split, but the patterns can
// actually hint at hidden types, so we use the patterns' types instead.
if let ty::Alias(ty::Opaque, ..) = ty.kind() {
if let Some(row) = rows.first() {
ty = row.head().ty();
}
}
let ty = cx.reveal_opaque_ty(v.head().ty());
debug!("v.head: {:?}, v.span: {:?}", v.head(), v.head().span());
let pcx = &PatCtxt { cx, ty, span: v.head().span(), is_top_level };

Expand Down
13 changes: 5 additions & 8 deletions tests/ui/pattern/usefulness/impl-trait.rs
Expand Up @@ -14,35 +14,36 @@ enum Void {}
fn return_never_rpit(x: Void) -> impl Copy {
if false {
match return_never_rpit(x) {}
//~^ERROR non-empty
}
x
}
fn friend_of_return_never_rpit(x: Void) {
match return_never_rpit(x) {}
//~^ERROR non-empty
}

type T = impl Copy;
//~^ERROR unconstrained
fn return_never_tait(x: Void) -> T {
if false {
match return_never_tait(x) {}
//~^ERROR non-empty
}
x
}
fn friend_of_return_never_tait(x: Void) {
match return_never_tait(x) {}
//~^ERROR non-empty
}

fn option_never(x: Void) -> Option<impl Copy> {
if true {
match option_never(x) {
None => {}
Some(_) => {}
Some(_) => {} //~ERROR unreachable
}
match option_never(x) {
None => {}
// FIXME: Unreachable not detected because `is_uninhabited` did not look into the
// opaque type.
_ => {}
}
}
Expand All @@ -51,10 +52,8 @@ fn option_never(x: Void) -> Option<impl Copy> {

fn inner_never(x: Void) {
type T = impl Copy;
//~^ERROR unconstrained
let y: T = x;
match y {}
//~^ERROR non-empty
}

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

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

type V = impl Copy;
Expand Down
108 changes: 31 additions & 77 deletions tests/ui/pattern/usefulness/impl-trait.stderr
@@ -1,103 +1,57 @@
error[E0004]: non-exhaustive patterns: type `impl Copy` is non-empty
--> $DIR/impl-trait.rs:16:15
|
LL | match return_never_rpit(x) {}
| ^^^^^^^^^^^^^^^^^^^^
|
= note: the matched value is of type `impl Copy`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
|
LL ~ match return_never_rpit(x) {
LL + _ => todo!(),
LL + }
|

error[E0004]: non-exhaustive patterns: type `T` is non-empty
--> $DIR/impl-trait.rs:29:15
|
LL | match return_never_tait(x) {}
| ^^^^^^^^^^^^^^^^^^^^
|
= note: the matched value is of type `T`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
|
LL ~ match return_never_tait(x) {
LL + _ => todo!(),
LL + }
|

error: unconstrained opaque type
--> $DIR/impl-trait.rs:25:10
|
LL | type T = impl Copy;
| ^^^^^^^^^
|
= note: `T` must be used in combination with a concrete type within the same module

error[E0004]: non-exhaustive patterns: type `inner_never::T` is non-empty
--> $DIR/impl-trait.rs:56:11
|
LL | match y {}
| ^
|
= note: the matched value is of type `inner_never::T`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
error: unreachable pattern
--> $DIR/impl-trait.rs:41:13
|
LL ~ match y {
LL + _ => todo!(),
LL + }
LL | Some(_) => {}
| ^^^^^^^
|

error: unconstrained opaque type
--> $DIR/impl-trait.rs:53:14
|
LL | type T = impl Copy;
| ^^^^^^^^^
note: the lint level is defined here
--> $DIR/impl-trait.rs:5:9
|
= note: `T` must be used in combination with a concrete type within the same item
LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^

error: unreachable pattern
--> $DIR/impl-trait.rs:66:9
--> $DIR/impl-trait.rs:65:9
|
LL | _ => {}
| - matches any value
LL | Some((a, b)) => {}
| ^^^^^^^^^^^^ unreachable pattern

error: unreachable pattern
--> $DIR/impl-trait.rs:79:9
|
note: the lint level is defined here
--> $DIR/impl-trait.rs:5:9
|
LL | #![deny(unreachable_patterns)]
LL | Some((mut x, mut y)) => {
| ^^^^^^^^^^^^^^^^^^^^

error[E0004]: non-exhaustive patterns: type `U` is non-empty
--> $DIR/impl-trait.rs:73:21
error[E0004]: non-exhaustive patterns: type `impl Copy` is non-empty
--> $DIR/impl-trait.rs:21:11
|
LL | if true { match u {} } else { x }
| ^
LL | match return_never_rpit(x) {}
| ^^^^^^^^^^^^^^^^^^^^
|
= note: the matched value is of type `U`
= note: the matched value is of type `impl Copy`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
|
LL ~ if true { match u {
LL ~ match return_never_rpit(x) {
LL + _ => todo!(),
LL ~ } } else { x }
LL + }
|

error: unconstrained opaque type
--> $DIR/impl-trait.rs:70:10
error[E0004]: non-exhaustive patterns: type `T` is non-empty
--> $DIR/impl-trait.rs:33:11
|
LL | type U = impl Copy;
| ^^^^^^^^^
LL | match return_never_tait(x) {}
| ^^^^^^^^^^^^^^^^^^^^
|
= note: `U` must be used in combination with a concrete type within the same module

error: unreachable pattern
--> $DIR/impl-trait.rs:82:9
= note: the matched value is of type `T`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
|
LL ~ match return_never_tait(x) {
LL + _ => todo!(),
LL + }
|
LL | Some((mut x, mut y)) => {
| ^^^^^^^^^^^^^^^^^^^^

error: aborting due to 9 previous errors
error: aborting due to 5 previous errors

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

0 comments on commit 2ea5001

Please sign in to comment.