Be honest about being able to list constructors

The test change is because we used to treat `&str` like other `&T`s, ie
as having a single constructor. That's not quite true though since we
consider `&str` constants as atomic instead of refs to `str` constants.
This commit is contained in:
Nadrieril 2020-10-20 02:31:21 +01:00
parent db9a8480c4
commit 1fab669f8d
3 changed files with 47 additions and 39 deletions

View File

@ -846,6 +846,9 @@ enum Constructor<'tcx> {
Opaque, Opaque,
/// Fake extra constructor for enums that aren't allowed to be matched exhaustively. /// Fake extra constructor for enums that aren't allowed to be matched exhaustively.
NonExhaustive, NonExhaustive,
/// Fake constructor for those types for which we can't list constructors explicitely, like
/// `f64` and `&str`.
Unlistable,
/// Wildcard pattern. /// Wildcard pattern.
Wildcard, Wildcard,
} }
@ -949,6 +952,9 @@ impl<'tcx> Constructor<'tcx> {
} }
// This constructor is never covered by anything else // This constructor is never covered by anything else
NonExhaustive => vec![NonExhaustive], 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"), Opaque => bug!("found unexpected opaque ctor in all_ctors"),
Wildcard => bug!("found unexpected wildcard ctor in all_ctors"), Wildcard => bug!("found unexpected wildcard ctor in all_ctors"),
} }
@ -1068,6 +1074,11 @@ impl<'tcx> Constructor<'tcx> {
(Opaque, _) | (_, Opaque) => false, (Opaque, _) | (_, Opaque) => false,
// Only a wildcard pattern can match the special extra constructor. // Only a wildcard pattern can match the special extra constructor.
(NonExhaustive, _) => false, (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), _ => bug!("trying to compare incompatible constructors {:?} and {:?}", self, other),
} }
@ -1146,7 +1157,7 @@ impl<'tcx> Constructor<'tcx> {
&Str(value) => PatKind::Constant { value }, &Str(value) => PatKind::Constant { value },
&FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }), &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }),
IntRange(range) => return range.to_pat(pcx.cx.tcx), 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"), Opaque => bug!("we should not try to apply an opaque constructor"),
Wildcard => bug!( Wildcard => bug!(
"trying to apply a wildcard constructor; this should have been done in `apply_constructors`" "trying to apply a wildcard constructor; this should have been done in `apply_constructors`"
@ -1286,7 +1297,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
} }
} }
} }
_ => Fields::empty(), _ => bug!("Unexpected type for `Single` constructor: {:?}", ty),
}, },
Slice(slice) => match *ty.kind() { Slice(slice) => match *ty.kind() {
ty::Slice(ty) | ty::Array(ty, _) => { ty::Slice(ty) | ty::Array(ty, _) => {
@ -1295,9 +1306,8 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
} }
_ => bug!("bad slice pattern {:?} {:?}", constructor, ty), _ => bug!("bad slice pattern {:?} {:?}", constructor, ty),
}, },
Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque | Wildcard => { Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque | Unlistable
Fields::empty() | Wildcard => Fields::empty(),
}
}; };
debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret); debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret);
ret ret
@ -1616,9 +1626,9 @@ fn all_constructors<'p, 'tcx>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Vec<Constructor<'tc
.unwrap(), .unwrap(),
) )
}; };
match *pcx.ty.kind() { match pcx.ty.kind() {
ty::Bool => vec![make_range(0, 1)], 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); let len = len.eval_usize(cx.tcx, cx.param_env);
if len != 0 && cx.is_uninhabited(sub_ty) { if len != 0 && cx.is_uninhabited(sub_ty) {
vec![] vec![]
@ -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. // 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) }; let kind = if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) };
vec![Slice(Slice { array_len: None, kind })] vec![Slice(Slice { array_len: None, kind })]
} }
ty::Adt(def, substs) if def.is_enum() => { 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 // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an
// additional "unknown" constructor. // additional "unknown" constructor.
// There is no point in enumerating all possible variants, because the user can't // There is no point in enumerating all possible variants, because the user can't
@ -1672,7 +1667,22 @@ fn all_constructors<'p, 'tcx>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Vec<Constructor<'tc
let is_secretly_empty = let is_secretly_empty =
def.variants.is_empty() && !cx.tcx.features().exhaustive_patterns; 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 => { ty::Char => {
vec![ vec![
@ -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. // `#[non_exhaustive]` enums by returning a special unmatcheable constructor.
vec![NonExhaustive] vec![NonExhaustive]
} }
ty::Int(ity) => { &ty::Int(ity) => {
let bits = Integer::from_attr(&cx.tcx, SignedInt(ity)).size().bits() as u128; let bits = Integer::from_attr(&cx.tcx, SignedInt(ity)).size().bits() as u128;
let min = 1u128 << (bits - 1); let min = 1u128 << (bits - 1);
let max = min - 1; let max = min - 1;
vec![make_range(min, max)] vec![make_range(min, max)]
} }
ty::Uint(uty) => { &ty::Uint(uty) => {
let size = Integer::from_attr(&cx.tcx, UnsignedInt(uty)).size(); let size = Integer::from_attr(&cx.tcx, UnsignedInt(uty)).size();
let max = truncate(u128::MAX, size); let max = truncate(u128::MAX, size);
vec![make_range(0, max)] vec![make_range(0, max)]
} }
_ => { _ if cx.is_uninhabited(pcx.ty) => vec![],
if cx.is_uninhabited(pcx.ty) { ty::Adt(..) | ty::Tuple(..) => vec![Single],
vec![] ty::Ref(_, t, _) if !t.is_str() => vec![Single],
} else { // This type is one for which we don't know how to list constructors, like &str of f64.
vec![Single] _ => vec![Unlistable],
}
}
} }
} }

View File

@ -1,9 +1,9 @@
fn main() { fn main() {
match "world" { //~ ERROR non-exhaustive patterns: `&_` match "world" { //~ ERROR non-exhaustive patterns: `_`
"hello" => {} "hello" => {}
} }
match "world" { //~ ERROR non-exhaustive patterns: `&_` match "world" { //~ ERROR non-exhaustive patterns: `_`
ref _x if false => {} ref _x if false => {}
"hello" => {} "hello" => {}
} }

View File

@ -1,17 +1,17 @@
error[E0004]: non-exhaustive patterns: `&_` not covered error[E0004]: non-exhaustive patterns: `_` not covered
--> $DIR/issue-30240.rs:2:11 --> $DIR/issue-30240.rs:2:11
| |
LL | match "world" { 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 = 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` = 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 --> $DIR/issue-30240.rs:6:11
| |
LL | match "world" { 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 = 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` = note: the matched value is of type `&str`