Skip to content

Clarify main code paths in exhaustiveness checking #78430

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Oct 29, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 41 additions & 33 deletions compiler/rustc_mir_build/src/thir/pattern/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,9 @@ enum Constructor<'tcx> {
Opaque,
/// Fake extra constructor for enums that aren't allowed to be matched exhaustively.
NonExhaustive,
/// Fake constructor for those types for which we can't list constructors explicitely, like
/// `f64` and `&str`.
Unlistable,
/// Wildcard pattern.
Wildcard,
}
Expand Down Expand Up @@ -949,6 +952,9 @@ impl<'tcx> Constructor<'tcx> {
}
// This constructor is never covered by anything else
NonExhaustive => vec![NonExhaustive],
// This constructor is only covered by `Single`s
Unlistable if other_ctors.iter().any(|c| *c == Single) => vec![],
Unlistable => vec![Unlistable],
Opaque => bug!("found unexpected opaque ctor in all_ctors"),
Wildcard => bug!("found unexpected wildcard ctor in all_ctors"),
}
Expand Down Expand Up @@ -1068,6 +1074,11 @@ impl<'tcx> Constructor<'tcx> {
(Opaque, _) | (_, Opaque) => false,
// Only a wildcard pattern can match the special extra constructor.
(NonExhaustive, _) => false,
// If we encounter a `Single` here, this means there was only one constructor for this
// type after all.
(Unlistable, Single) => true,
// Otherwise, only a wildcard pattern can match the special extra constructor.
(Unlistable, _) => false,

_ => bug!("trying to compare incompatible constructors {:?} and {:?}", self, other),
}
Expand Down Expand Up @@ -1146,7 +1157,7 @@ impl<'tcx> Constructor<'tcx> {
&Str(value) => PatKind::Constant { value },
&FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }),
IntRange(range) => return range.to_pat(pcx.cx.tcx),
NonExhaustive => PatKind::Wild,
NonExhaustive | Unlistable => PatKind::Wild,
Opaque => bug!("we should not try to apply an opaque constructor"),
Wildcard => bug!(
"trying to apply a wildcard constructor; this should have been done in `apply_constructors`"
Expand Down Expand Up @@ -1286,7 +1297,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
}
}
}
_ => Fields::empty(),
_ => bug!("Unexpected type for `Single` constructor: {:?}", ty),
},
Slice(slice) => match *ty.kind() {
ty::Slice(ty) | ty::Array(ty, _) => {
Expand All @@ -1295,9 +1306,8 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
}
_ => bug!("bad slice pattern {:?} {:?}", constructor, ty),
},
Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque | Wildcard => {
Fields::empty()
}
Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque | Unlistable
| Wildcard => Fields::empty(),
};
debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret);
ret
Expand Down Expand Up @@ -1616,9 +1626,9 @@ fn all_constructors<'p, 'tcx>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Vec<Constructor<'tc
.unwrap(),
)
};
match *pcx.ty.kind() {
match pcx.ty.kind() {
ty::Bool => vec![make_range(0, 1)],
ty::Array(ref sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => {
ty::Array(sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => {
let len = len.eval_usize(cx.tcx, cx.param_env);
if len != 0 && cx.is_uninhabited(sub_ty) {
vec![]
Expand All @@ -1627,26 +1637,11 @@ fn all_constructors<'p, 'tcx>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Vec<Constructor<'tc
}
}
// Treat arrays of a constant but unknown length like slices.
ty::Array(ref sub_ty, _) | ty::Slice(ref sub_ty) => {
ty::Array(sub_ty, _) | ty::Slice(sub_ty) => {
let kind = if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) };
vec![Slice(Slice { array_len: None, kind })]
}
ty::Adt(def, substs) if def.is_enum() => {
let ctors: Vec<_> = if cx.tcx.features().exhaustive_patterns {
// If `exhaustive_patterns` is enabled, we exclude variants known to be
// uninhabited.
def.variants
.iter()
.filter(|v| {
!v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env)
.contains(cx.tcx, cx.module)
})
.map(|v| Variant(v.def_id))
.collect()
} else {
def.variants.iter().map(|v| Variant(v.def_id)).collect()
};

// If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an
// additional "unknown" constructor.
// There is no point in enumerating all possible variants, because the user can't
Expand All @@ -1672,7 +1667,22 @@ fn all_constructors<'p, 'tcx>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Vec<Constructor<'tc
let is_secretly_empty =
def.variants.is_empty() && !cx.tcx.features().exhaustive_patterns;

if is_secretly_empty || is_declared_nonexhaustive { vec![NonExhaustive] } else { ctors }
if is_secretly_empty || is_declared_nonexhaustive {
vec![NonExhaustive]
} else if cx.tcx.features().exhaustive_patterns {
// If `exhaustive_patterns` is enabled, we exclude variants known to be
// uninhabited.
def.variants
.iter()
.filter(|v| {
!v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env)
.contains(cx.tcx, cx.module)
})
.map(|v| Variant(v.def_id))
.collect()
} else {
def.variants.iter().map(|v| Variant(v.def_id)).collect()
}
}
ty::Char => {
vec![
Expand All @@ -1690,24 +1700,22 @@ fn all_constructors<'p, 'tcx>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Vec<Constructor<'tc
// `#[non_exhaustive]` enums by returning a special unmatcheable constructor.
vec![NonExhaustive]
}
ty::Int(ity) => {
&ty::Int(ity) => {
let bits = Integer::from_attr(&cx.tcx, SignedInt(ity)).size().bits() as u128;
let min = 1u128 << (bits - 1);
let max = min - 1;
vec![make_range(min, max)]
}
ty::Uint(uty) => {
&ty::Uint(uty) => {
let size = Integer::from_attr(&cx.tcx, UnsignedInt(uty)).size();
let max = truncate(u128::MAX, size);
vec![make_range(0, max)]
}
_ => {
if cx.is_uninhabited(pcx.ty) {
vec![]
} else {
vec![Single]
}
}
_ if cx.is_uninhabited(pcx.ty) => vec![],
ty::Adt(..) | ty::Tuple(..) => vec![Single],
ty::Ref(_, t, _) if !t.is_str() => vec![Single],
// This type is one for which we don't know how to list constructors, like &str of f64.
_ => vec![Unlistable],
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/issues/issue-30240.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
fn main() {
match "world" { //~ ERROR non-exhaustive patterns: `&_`
match "world" { //~ ERROR non-exhaustive patterns: `_`
"hello" => {}
}

match "world" { //~ ERROR non-exhaustive patterns: `&_`
match "world" { //~ ERROR non-exhaustive patterns: `_`
ref _x if false => {}
"hello" => {}
}
Expand Down
8 changes: 4 additions & 4 deletions src/test/ui/issues/issue-30240.stderr
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
error[E0004]: non-exhaustive patterns: `&_` not covered
error[E0004]: non-exhaustive patterns: `_` not covered
--> $DIR/issue-30240.rs:2:11
|
LL | match "world" {
| ^^^^^^^ pattern `&_` not covered
| ^^^^^^^ pattern `_` not covered
|
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
= note: the matched value is of type `&str`

error[E0004]: non-exhaustive patterns: `&_` not covered
error[E0004]: non-exhaustive patterns: `_` not covered
--> $DIR/issue-30240.rs:6:11
|
LL | match "world" {
| ^^^^^^^ pattern `&_` not covered
| ^^^^^^^ pattern `_` not covered
|
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
= note: the matched value is of type `&str`
Expand Down