Skip to content

Commit 8256379

Browse files
committed
Auto merge of #78995 - Nadrieril:clean-empty-match, r=varkor
Handle empty matches cleanly in exhaustiveness checking This removes the special-casing of empty matches that was done in `check_match`. This fixes most of #55123. Somewhat unrelatedly, I also made `_match.rs` more self-contained, because I think it's cleaner. r? `@varkor` `@rustbot` modify labels: +A-exhaustiveness-checking
2 parents 8d2d001 + 69821cf commit 8256379

File tree

9 files changed

+317
-168
lines changed

9 files changed

+317
-168
lines changed

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

+91-24
Original file line numberDiff line numberDiff line change
@@ -364,14 +364,14 @@ impl<'tcx> Pat<'tcx> {
364364
/// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]`
365365
/// works well.
366366
#[derive(Debug, Clone)]
367-
crate struct PatStack<'p, 'tcx> {
367+
struct PatStack<'p, 'tcx> {
368368
pats: SmallVec<[&'p Pat<'tcx>; 2]>,
369369
/// Cache for the constructor of the head
370370
head_ctor: OnceCell<Constructor<'tcx>>,
371371
}
372372

373373
impl<'p, 'tcx> PatStack<'p, 'tcx> {
374-
crate fn from_pattern(pat: &'p Pat<'tcx>) -> Self {
374+
fn from_pattern(pat: &'p Pat<'tcx>) -> Self {
375375
Self::from_vec(smallvec![pat])
376376
}
377377

@@ -455,17 +455,17 @@ impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> {
455455

456456
/// A 2D matrix.
457457
#[derive(Clone, PartialEq)]
458-
crate struct Matrix<'p, 'tcx> {
458+
struct Matrix<'p, 'tcx> {
459459
patterns: Vec<PatStack<'p, 'tcx>>,
460460
}
461461

462462
impl<'p, 'tcx> Matrix<'p, 'tcx> {
463-
crate fn empty() -> Self {
463+
fn empty() -> Self {
464464
Matrix { patterns: vec![] }
465465
}
466466

467467
/// Pushes a new row to the matrix. If the row starts with an or-pattern, this expands it.
468-
crate fn push(&mut self, row: PatStack<'p, 'tcx>) {
468+
fn push(&mut self, row: PatStack<'p, 'tcx>) {
469469
if let Some(rows) = row.expand_or_pat() {
470470
for row in rows {
471471
// We recursively expand the or-patterns of the new rows.
@@ -588,7 +588,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
588588
}
589589

590590
/// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
591-
crate fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
591+
fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
592592
match ty.kind() {
593593
ty::Adt(def, ..) => {
594594
def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did.is_local()
@@ -1392,13 +1392,12 @@ impl<'tcx> Usefulness<'tcx> {
13921392
pcx: PatCtxt<'_, 'p, 'tcx>,
13931393
ctor: &Constructor<'tcx>,
13941394
ctor_wild_subpatterns: &Fields<'p, 'tcx>,
1395-
is_top_level: bool,
13961395
) -> Self {
13971396
match self {
13981397
UsefulWithWitness(witnesses) => {
13991398
let new_witnesses = if ctor.is_wildcard() {
14001399
let missing_ctors = MissingConstructors::new(pcx);
1401-
let new_patterns = missing_ctors.report_patterns(pcx, is_top_level);
1400+
let new_patterns = missing_ctors.report_patterns(pcx);
14021401
witnesses
14031402
.into_iter()
14041403
.flat_map(|witness| {
@@ -1440,7 +1439,7 @@ impl<'tcx> Usefulness<'tcx> {
14401439
}
14411440

14421441
#[derive(Copy, Clone, Debug)]
1443-
crate enum WitnessPreference {
1442+
enum WitnessPreference {
14441443
ConstructWitness,
14451444
LeaveOutWitness,
14461445
}
@@ -1454,6 +1453,9 @@ struct PatCtxt<'a, 'p, 'tcx> {
14541453
ty: Ty<'tcx>,
14551454
/// Span of the current pattern under investigation.
14561455
span: Span,
1456+
/// Whether the current pattern is the whole pattern as found in a match arm, or if it's a
1457+
/// subpattern.
1458+
is_top_level: bool,
14571459
}
14581460

14591461
/// A witness of non-exhaustiveness for error reporting, represented
@@ -1493,7 +1495,8 @@ struct PatCtxt<'a, 'p, 'tcx> {
14931495
crate struct Witness<'tcx>(Vec<Pat<'tcx>>);
14941496

14951497
impl<'tcx> Witness<'tcx> {
1496-
crate fn single_pattern(self) -> Pat<'tcx> {
1498+
/// Asserts that the witness contains a single pattern, and returns it.
1499+
fn single_pattern(self) -> Pat<'tcx> {
14971500
assert_eq!(self.0.len(), 1);
14981501
self.0.into_iter().next().unwrap()
14991502
}
@@ -1585,11 +1588,12 @@ fn all_constructors<'p, 'tcx>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Vec<Constructor<'tc
15851588
let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(pcx.ty);
15861589

15871590
// If `exhaustive_patterns` is disabled and our scrutinee is an empty enum, we treat it
1588-
// as though it had an "unknown" constructor to avoid exposing its emptyness. Note that
1589-
// an empty match will still be considered exhaustive because that case is handled
1590-
// separately in `check_match`.
1591-
let is_secretly_empty =
1592-
def.variants.is_empty() && !cx.tcx.features().exhaustive_patterns;
1591+
// as though it had an "unknown" constructor to avoid exposing its emptiness. The
1592+
// exception is if the pattern is at the top level, because we want empty matches to be
1593+
// considered exhaustive.
1594+
let is_secretly_empty = def.variants.is_empty()
1595+
&& !cx.tcx.features().exhaustive_patterns
1596+
&& !pcx.is_top_level;
15931597

15941598
if is_secretly_empty || is_declared_nonexhaustive {
15951599
vec![NonExhaustive]
@@ -1635,6 +1639,13 @@ fn all_constructors<'p, 'tcx>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Vec<Constructor<'tc
16351639
let max = size.truncate(u128::MAX);
16361640
vec![make_range(0, max)]
16371641
}
1642+
// If `exhaustive_patterns` is disabled and our scrutinee is the never type, we cannot
1643+
// expose its emptiness. The exception is if the pattern is at the top level, because we
1644+
// want empty matches to be considered exhaustive.
1645+
ty::Never if !cx.tcx.features().exhaustive_patterns && !pcx.is_top_level => {
1646+
vec![NonExhaustive]
1647+
}
1648+
ty::Never => vec![],
16381649
_ if cx.is_uninhabited(pcx.ty) => vec![],
16391650
ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => vec![Single],
16401651
// This type is one for which we cannot list constructors, like `str` or `f64`.
@@ -2012,11 +2023,7 @@ impl<'tcx> MissingConstructors<'tcx> {
20122023

20132024
/// List the patterns corresponding to the missing constructors. In some cases, instead of
20142025
/// listing all constructors of a given type, we prefer to simply report a wildcard.
2015-
fn report_patterns<'p>(
2016-
&self,
2017-
pcx: PatCtxt<'_, 'p, 'tcx>,
2018-
is_top_level: bool,
2019-
) -> SmallVec<[Pat<'tcx>; 1]> {
2026+
fn report_patterns<'p>(&self, pcx: PatCtxt<'_, 'p, 'tcx>) -> SmallVec<[Pat<'tcx>; 1]> {
20202027
// There are 2 ways we can report a witness here.
20212028
// Commonly, we can report all the "free"
20222029
// constructors as witnesses, e.g., if we have:
@@ -2044,7 +2051,7 @@ impl<'tcx> MissingConstructors<'tcx> {
20442051
// `used_ctors` is empty.
20452052
// The exception is: if we are at the top-level, for example in an empty match, we
20462053
// sometimes prefer reporting the list of constructors instead of just `_`.
2047-
let report_when_all_missing = is_top_level && !IntRange::is_integral(pcx.ty);
2054+
let report_when_all_missing = pcx.is_top_level && !IntRange::is_integral(pcx.ty);
20482055
if self.used_ctors.is_empty() && !report_when_all_missing {
20492056
// All constructors are unused. Report only a wildcard
20502057
// rather than each individual constructor.
@@ -2086,7 +2093,7 @@ impl<'tcx> MissingConstructors<'tcx> {
20862093
/// `is_under_guard` is used to inform if the pattern has a guard. If it
20872094
/// has one it must not be inserted into the matrix. This shouldn't be
20882095
/// relied on for soundness.
2089-
crate fn is_useful<'p, 'tcx>(
2096+
fn is_useful<'p, 'tcx>(
20902097
cx: &MatchCheckCtxt<'p, 'tcx>,
20912098
matrix: &Matrix<'p, 'tcx>,
20922099
v: &PatStack<'p, 'tcx>,
@@ -2200,7 +2207,7 @@ crate fn is_useful<'p, 'tcx>(
22002207

22012208
// FIXME(Nadrieril): Hack to work around type normalization issues (see #72476).
22022209
let ty = matrix.heads().next().map(|r| r.ty).unwrap_or(v.head().ty);
2203-
let pcx = PatCtxt { cx, matrix, ty, span: v.head().span };
2210+
let pcx = PatCtxt { cx, matrix, ty, span: v.head().span, is_top_level };
22042211

22052212
debug!("is_useful_expand_first_col: ty={:#?}, expanding {:#?}", pcx.ty, v.head());
22062213

@@ -2215,7 +2222,7 @@ crate fn is_useful<'p, 'tcx>(
22152222
let v = v.pop_head_constructor(&ctor_wild_subpatterns);
22162223
let usefulness =
22172224
is_useful(pcx.cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false);
2218-
usefulness.apply_constructor(pcx, &ctor, &ctor_wild_subpatterns, is_top_level)
2225+
usefulness.apply_constructor(pcx, &ctor, &ctor_wild_subpatterns)
22192226
})
22202227
.find(|result| result.is_useful())
22212228
.unwrap_or(NotUseful);
@@ -2283,3 +2290,63 @@ fn pat_constructor<'p, 'tcx>(
22832290
PatKind::Or { .. } => bug!("Or-pattern should have been expanded earlier on."),
22842291
}
22852292
}
2293+
2294+
/// The arm of a match expression.
2295+
#[derive(Clone, Copy)]
2296+
crate struct MatchArm<'p, 'tcx> {
2297+
/// The pattern must have been lowered through `MatchVisitor::lower_pattern`.
2298+
crate pat: &'p super::Pat<'tcx>,
2299+
crate hir_id: HirId,
2300+
crate has_guard: bool,
2301+
}
2302+
2303+
/// The output of checking a match for exhaustiveness and arm reachability.
2304+
crate struct UsefulnessReport<'p, 'tcx> {
2305+
/// For each arm of the input, whether that arm is reachable after the arms above it.
2306+
crate arm_usefulness: Vec<(MatchArm<'p, 'tcx>, Usefulness<'tcx>)>,
2307+
/// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of
2308+
/// exhaustiveness.
2309+
crate non_exhaustiveness_witnesses: Vec<super::Pat<'tcx>>,
2310+
}
2311+
2312+
/// The entrypoint for the usefulness algorithm. Computes whether a match is exhaustive and which
2313+
/// of its arms are reachable.
2314+
///
2315+
/// Note: the input patterns must have been lowered through `MatchVisitor::lower_pattern`.
2316+
crate fn compute_match_usefulness<'p, 'tcx>(
2317+
cx: &MatchCheckCtxt<'p, 'tcx>,
2318+
arms: &[MatchArm<'p, 'tcx>],
2319+
scrut_hir_id: HirId,
2320+
scrut_ty: Ty<'tcx>,
2321+
) -> UsefulnessReport<'p, 'tcx> {
2322+
let mut matrix = Matrix::empty();
2323+
let arm_usefulness: Vec<_> = arms
2324+
.iter()
2325+
.copied()
2326+
.map(|arm| {
2327+
let v = PatStack::from_pattern(arm.pat);
2328+
let usefulness =
2329+
is_useful(cx, &matrix, &v, LeaveOutWitness, arm.hir_id, arm.has_guard, true);
2330+
if !arm.has_guard {
2331+
matrix.push(v);
2332+
}
2333+
(arm, usefulness)
2334+
})
2335+
.collect();
2336+
2337+
let wild_pattern = cx.pattern_arena.alloc(super::Pat::wildcard_from_ty(scrut_ty));
2338+
let v = PatStack::from_pattern(wild_pattern);
2339+
let usefulness = is_useful(cx, &matrix, &v, ConstructWitness, scrut_hir_id, false, true);
2340+
let non_exhaustiveness_witnesses = match usefulness {
2341+
NotUseful => vec![], // Wildcard pattern isn't useful, so the match is exhaustive.
2342+
UsefulWithWitness(pats) => {
2343+
if pats.is_empty() {
2344+
bug!("Exhaustiveness check returned no witnesses")
2345+
} else {
2346+
pats.into_iter().map(|w| w.single_pattern()).collect()
2347+
}
2348+
}
2349+
Useful(_) => bug!(),
2350+
};
2351+
UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses }
2352+
}

0 commit comments

Comments
 (0)