Skip to content

Commit 1fab669

Browse files
committed
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.
1 parent db9a848 commit 1fab669

File tree

3 files changed

+47
-39
lines changed

3 files changed

+47
-39
lines changed

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

+41-33
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,9 @@ enum Constructor<'tcx> {
846846
Opaque,
847847
/// Fake extra constructor for enums that aren't allowed to be matched exhaustively.
848848
NonExhaustive,
849+
/// Fake constructor for those types for which we can't list constructors explicitely, like
850+
/// `f64` and `&str`.
851+
Unlistable,
849852
/// Wildcard pattern.
850853
Wildcard,
851854
}
@@ -949,6 +952,9 @@ impl<'tcx> Constructor<'tcx> {
949952
}
950953
// This constructor is never covered by anything else
951954
NonExhaustive => vec![NonExhaustive],
955+
// This constructor is only covered by `Single`s
956+
Unlistable if other_ctors.iter().any(|c| *c == Single) => vec![],
957+
Unlistable => vec![Unlistable],
952958
Opaque => bug!("found unexpected opaque ctor in all_ctors"),
953959
Wildcard => bug!("found unexpected wildcard ctor in all_ctors"),
954960
}
@@ -1068,6 +1074,11 @@ impl<'tcx> Constructor<'tcx> {
10681074
(Opaque, _) | (_, Opaque) => false,
10691075
// Only a wildcard pattern can match the special extra constructor.
10701076
(NonExhaustive, _) => false,
1077+
// If we encounter a `Single` here, this means there was only one constructor for this
1078+
// type after all.
1079+
(Unlistable, Single) => true,
1080+
// Otherwise, only a wildcard pattern can match the special extra constructor.
1081+
(Unlistable, _) => false,
10711082

10721083
_ => bug!("trying to compare incompatible constructors {:?} and {:?}", self, other),
10731084
}
@@ -1146,7 +1157,7 @@ impl<'tcx> Constructor<'tcx> {
11461157
&Str(value) => PatKind::Constant { value },
11471158
&FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }),
11481159
IntRange(range) => return range.to_pat(pcx.cx.tcx),
1149-
NonExhaustive => PatKind::Wild,
1160+
NonExhaustive | Unlistable => PatKind::Wild,
11501161
Opaque => bug!("we should not try to apply an opaque constructor"),
11511162
Wildcard => bug!(
11521163
"trying to apply a wildcard constructor; this should have been done in `apply_constructors`"
@@ -1286,7 +1297,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
12861297
}
12871298
}
12881299
}
1289-
_ => Fields::empty(),
1300+
_ => bug!("Unexpected type for `Single` constructor: {:?}", ty),
12901301
},
12911302
Slice(slice) => match *ty.kind() {
12921303
ty::Slice(ty) | ty::Array(ty, _) => {
@@ -1295,9 +1306,8 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
12951306
}
12961307
_ => bug!("bad slice pattern {:?} {:?}", constructor, ty),
12971308
},
1298-
Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque | Wildcard => {
1299-
Fields::empty()
1300-
}
1309+
Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque | Unlistable
1310+
| Wildcard => Fields::empty(),
13011311
};
13021312
debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret);
13031313
ret
@@ -1616,9 +1626,9 @@ fn all_constructors<'p, 'tcx>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Vec<Constructor<'tc
16161626
.unwrap(),
16171627
)
16181628
};
1619-
match *pcx.ty.kind() {
1629+
match pcx.ty.kind() {
16201630
ty::Bool => vec![make_range(0, 1)],
1621-
ty::Array(ref sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => {
1631+
ty::Array(sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => {
16221632
let len = len.eval_usize(cx.tcx, cx.param_env);
16231633
if len != 0 && cx.is_uninhabited(sub_ty) {
16241634
vec![]
@@ -1627,26 +1637,11 @@ fn all_constructors<'p, 'tcx>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Vec<Constructor<'tc
16271637
}
16281638
}
16291639
// Treat arrays of a constant but unknown length like slices.
1630-
ty::Array(ref sub_ty, _) | ty::Slice(ref sub_ty) => {
1640+
ty::Array(sub_ty, _) | ty::Slice(sub_ty) => {
16311641
let kind = if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) };
16321642
vec![Slice(Slice { array_len: None, kind })]
16331643
}
16341644
ty::Adt(def, substs) if def.is_enum() => {
1635-
let ctors: Vec<_> = if cx.tcx.features().exhaustive_patterns {
1636-
// If `exhaustive_patterns` is enabled, we exclude variants known to be
1637-
// uninhabited.
1638-
def.variants
1639-
.iter()
1640-
.filter(|v| {
1641-
!v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env)
1642-
.contains(cx.tcx, cx.module)
1643-
})
1644-
.map(|v| Variant(v.def_id))
1645-
.collect()
1646-
} else {
1647-
def.variants.iter().map(|v| Variant(v.def_id)).collect()
1648-
};
1649-
16501645
// If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an
16511646
// additional "unknown" constructor.
16521647
// 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
16721667
let is_secretly_empty =
16731668
def.variants.is_empty() && !cx.tcx.features().exhaustive_patterns;
16741669

1675-
if is_secretly_empty || is_declared_nonexhaustive { vec![NonExhaustive] } else { ctors }
1670+
if is_secretly_empty || is_declared_nonexhaustive {
1671+
vec![NonExhaustive]
1672+
} else if cx.tcx.features().exhaustive_patterns {
1673+
// If `exhaustive_patterns` is enabled, we exclude variants known to be
1674+
// uninhabited.
1675+
def.variants
1676+
.iter()
1677+
.filter(|v| {
1678+
!v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env)
1679+
.contains(cx.tcx, cx.module)
1680+
})
1681+
.map(|v| Variant(v.def_id))
1682+
.collect()
1683+
} else {
1684+
def.variants.iter().map(|v| Variant(v.def_id)).collect()
1685+
}
16761686
}
16771687
ty::Char => {
16781688
vec![
@@ -1690,24 +1700,22 @@ fn all_constructors<'p, 'tcx>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Vec<Constructor<'tc
16901700
// `#[non_exhaustive]` enums by returning a special unmatcheable constructor.
16911701
vec![NonExhaustive]
16921702
}
1693-
ty::Int(ity) => {
1703+
&ty::Int(ity) => {
16941704
let bits = Integer::from_attr(&cx.tcx, SignedInt(ity)).size().bits() as u128;
16951705
let min = 1u128 << (bits - 1);
16961706
let max = min - 1;
16971707
vec![make_range(min, max)]
16981708
}
1699-
ty::Uint(uty) => {
1709+
&ty::Uint(uty) => {
17001710
let size = Integer::from_attr(&cx.tcx, UnsignedInt(uty)).size();
17011711
let max = truncate(u128::MAX, size);
17021712
vec![make_range(0, max)]
17031713
}
1704-
_ => {
1705-
if cx.is_uninhabited(pcx.ty) {
1706-
vec![]
1707-
} else {
1708-
vec![Single]
1709-
}
1710-
}
1714+
_ if cx.is_uninhabited(pcx.ty) => vec![],
1715+
ty::Adt(..) | ty::Tuple(..) => vec![Single],
1716+
ty::Ref(_, t, _) if !t.is_str() => vec![Single],
1717+
// This type is one for which we don't know how to list constructors, like &str of f64.
1718+
_ => vec![Unlistable],
17111719
}
17121720
}
17131721

src/test/ui/issues/issue-30240.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
fn main() {
2-
match "world" { //~ ERROR non-exhaustive patterns: `&_`
2+
match "world" { //~ ERROR non-exhaustive patterns: `_`
33
"hello" => {}
44
}
55

6-
match "world" { //~ ERROR non-exhaustive patterns: `&_`
6+
match "world" { //~ ERROR non-exhaustive patterns: `_`
77
ref _x if false => {}
88
"hello" => {}
99
}

src/test/ui/issues/issue-30240.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
error[E0004]: non-exhaustive patterns: `&_` not covered
1+
error[E0004]: non-exhaustive patterns: `_` not covered
22
--> $DIR/issue-30240.rs:2:11
33
|
44
LL | match "world" {
5-
| ^^^^^^^ pattern `&_` not covered
5+
| ^^^^^^^ pattern `_` not covered
66
|
77
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
88
= note: the matched value is of type `&str`
99

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

0 commit comments

Comments
 (0)