Skip to content

Commit e398d89

Browse files
committed
Move NonExhaustive checks to the relevant match branches
1 parent 357d53c commit e398d89

File tree

1 file changed

+54
-51
lines changed

1 file changed

+54
-51
lines changed

src/librustc_mir/hair/pattern/_match.rs

+54-51
Original file line numberDiff line numberDiff line change
@@ -560,13 +560,6 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
560560
}
561561
}
562562

563-
fn is_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
564-
match ty.kind {
565-
ty::Adt(adt_def, ..) => adt_def.is_variant_list_non_exhaustive(),
566-
_ => false,
567-
}
568-
}
569-
570563
fn is_local(&self, ty: Ty<'tcx>) -> bool {
571564
match ty.kind {
572565
ty::Adt(adt_def, ..) => adt_def.did.is_local(),
@@ -1133,7 +1126,7 @@ fn all_constructors<'a, 'tcx>(
11331126
pcx: PatCtxt<'tcx>,
11341127
) -> Vec<Constructor<'tcx>> {
11351128
debug!("all_constructors({:?})", pcx.ty);
1136-
let ctors = match pcx.ty.kind {
1129+
match pcx.ty.kind {
11371130
ty::Bool => [true, false]
11381131
.iter()
11391132
.map(|&b| ConstantValue(ty::Const::from_bool(cx.tcx, b), pcx.span))
@@ -1150,17 +1143,49 @@ fn all_constructors<'a, 'tcx>(
11501143
vec![VarLenSlice(0, 0)]
11511144
}
11521145
}
1153-
ty::Adt(def, substs) if def.is_enum() => def
1154-
.variants
1155-
.iter()
1156-
.filter(|v| {
1157-
!cx.tcx.features().exhaustive_patterns
1158-
|| !v
1159-
.uninhabited_from(cx.tcx, substs, def.adt_kind())
1160-
.contains(cx.tcx, cx.module)
1161-
})
1162-
.map(|v| Variant(v.def_id))
1163-
.collect(),
1146+
ty::Adt(def, substs) if def.is_enum() => {
1147+
let ctors: Vec<_> = def
1148+
.variants
1149+
.iter()
1150+
.filter(|v| {
1151+
!cx.tcx.features().exhaustive_patterns
1152+
|| !v
1153+
.uninhabited_from(cx.tcx, substs, def.adt_kind())
1154+
.contains(cx.tcx, cx.module)
1155+
})
1156+
.map(|v| Variant(v.def_id))
1157+
.collect();
1158+
1159+
// If our scrutinee is *privately* an empty enum, we must treat it as though it had an
1160+
// "unknown" constructor (in that case, all other patterns obviously can't be variants)
1161+
// to avoid exposing its emptyness. See the `match_privately_empty` test for details.
1162+
// FIXME: currently the only way I know of something can be a privately-empty enum is
1163+
// when the exhaustive_patterns feature flag is not present, so this is only needed for
1164+
// that case.
1165+
let is_privately_empty = ctors.is_empty() && !cx.is_uninhabited(pcx.ty);
1166+
// If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an
1167+
// additionnal "unknown" constructor.
1168+
let is_declared_nonexhaustive =
1169+
def.is_variant_list_non_exhaustive() && !cx.is_local(pcx.ty);
1170+
1171+
if is_privately_empty || is_declared_nonexhaustive {
1172+
// There is no point in enumerating all possible variants, because the user can't
1173+
// actually match against them themselves. So we return only the fictitious
1174+
// constructor.
1175+
// E.g., in an example like:
1176+
// ```
1177+
// let err: io::ErrorKind = ...;
1178+
// match err {
1179+
// io::ErrorKind::NotFound => {},
1180+
// }
1181+
// ```
1182+
// we don't want to show every possible IO error, but instead have only `_` as the
1183+
// witness.
1184+
vec![NonExhaustive]
1185+
} else {
1186+
ctors
1187+
}
1188+
}
11641189
ty::Char => {
11651190
vec![
11661191
// The valid Unicode Scalar Value ranges.
@@ -1180,6 +1205,15 @@ fn all_constructors<'a, 'tcx>(
11801205
),
11811206
]
11821207
}
1208+
ty::Int(_) | ty::Uint(_)
1209+
if pcx.ty.is_ptr_sized_integral()
1210+
&& !cx.tcx.features().precise_pointer_size_matching =>
1211+
{
1212+
// `usize`/`isize` are not allowed to be matched exhaustively unless the
1213+
// `precise_pointer_size_matching` feature is enabled. So we treat those types like
1214+
// `#[non_exhaustive]` enums by returning a special unmatcheable constructor.
1215+
vec![NonExhaustive]
1216+
}
11831217
ty::Int(ity) => {
11841218
let bits = Integer::from_attr(&cx.tcx, SignedInt(ity)).size().bits() as u128;
11851219
let min = 1u128 << (bits - 1);
@@ -1198,38 +1232,7 @@ fn all_constructors<'a, 'tcx>(
11981232
vec![Single]
11991233
}
12001234
}
1201-
};
1202-
1203-
// FIXME: currently the only way I know of something can
1204-
// be a privately-empty enum is when the exhaustive_patterns
1205-
// feature flag is not present, so this is only
1206-
// needed for that case.
1207-
let is_privately_empty = ctors.is_empty() && !cx.is_uninhabited(pcx.ty);
1208-
let is_declared_nonexhaustive = cx.is_non_exhaustive_enum(pcx.ty) && !cx.is_local(pcx.ty);
1209-
let is_non_exhaustive = is_privately_empty
1210-
|| is_declared_nonexhaustive
1211-
|| (pcx.ty.is_ptr_sized_integral() && !cx.tcx.features().precise_pointer_size_matching);
1212-
if is_non_exhaustive {
1213-
// If our scrutinee is *privately* an empty enum, we must treat it as though it had an
1214-
// "unknown" constructor (in that case, all other patterns obviously can't be variants) to
1215-
// avoid exposing its emptyness. See the `match_privately_empty` test for details.
1216-
//
1217-
// If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an additionnal
1218-
// "unknown" constructor. However there is no point in enumerating all possible variants,
1219-
// because the user can't actually match against them themselves. So we return only the
1220-
// fictitious constructor.
1221-
// E.g., in an example like:
1222-
// ```
1223-
// let err: io::ErrorKind = ...;
1224-
// match err {
1225-
// io::ErrorKind::NotFound => {},
1226-
// }
1227-
// ```
1228-
// we don't want to show every possible IO error, but instead have only `_` as the witness.
1229-
return vec![NonExhaustive];
1230-
}
1231-
1232-
ctors
1235+
}
12331236
}
12341237

12351238
/// An inclusive interval, used for precise integer exhaustiveness checking.

0 commit comments

Comments
 (0)