1
Fork 0

Split Usefulness::NotUseful into two

This commit is contained in:
Nadrieril 2021-01-01 21:28:32 +00:00
parent f4f20c0663
commit 293af41790

View file

@ -680,24 +680,32 @@ impl SpanSet {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum Usefulness<'tcx> { enum Usefulness<'tcx> {
/// Pontentially carries a set of sub-branches that have been found to be unreachable. Used /// Potentially carries a set of sub-branches that have been found to be unreachable. Used
/// only in the presence of or-patterns, otherwise it stays empty. /// only in the presence of or-patterns, otherwise it stays empty.
Useful(SpanSet), NoWitnesses(SpanSet),
/// Carries a list of witnesses of non-exhaustiveness. /// When not carrying witnesses, indicates that the whole pattern is unreachable.
UsefulWithWitness(Vec<Witness<'tcx>>), NoWitnessesFull,
NotUseful, /// Carries a list of witnesses of non-exhaustiveness. Non-empty.
WithWitnesses(Vec<Witness<'tcx>>),
/// When carrying witnesses, indicates that the whole pattern is unreachable.
WithWitnessesEmpty,
} }
impl<'tcx> Usefulness<'tcx> { impl<'tcx> Usefulness<'tcx> {
fn new_useful(preference: WitnessPreference) -> Self { fn new_useful(preference: WitnessPreference) -> Self {
match preference { match preference {
ConstructWitness => UsefulWithWitness(vec![Witness(vec![])]), ConstructWitness => WithWitnesses(vec![Witness(vec![])]),
LeaveOutWitness => Useful(Default::default()), LeaveOutWitness => NoWitnesses(Default::default()),
}
}
fn new_not_useful(preference: WitnessPreference) -> Self {
match preference {
ConstructWitness => WithWitnessesEmpty,
LeaveOutWitness => NoWitnessesFull,
} }
} }
/// Combine usefulnesses from two branches. This is an associative operation and `NotUseful` is /// Combine usefulnesses from two branches. This is an associative operation.
/// a unit.
fn extend(&mut self, other: Self) { fn extend(&mut self, other: Self) {
// If we have detected some unreachable sub-branches, we only want to keep them when they // If we have detected some unreachable sub-branches, we only want to keep them when they
// were unreachable in _all_ branches. Eg. in the following, the last `true` is unreachable // were unreachable in _all_ branches. Eg. in the following, the last `true` is unreachable
@ -720,21 +728,29 @@ impl<'tcx> Usefulness<'tcx> {
// } // }
// ``` // ```
match (&mut *self, other) { match (&mut *self, other) {
(Useful(s), Useful(o)) => s.intersection_mut(&o), (WithWitnesses(s), WithWitnesses(o)) => s.extend(o),
(UsefulWithWitness(s), UsefulWithWitness(o)) => s.extend(o), (WithWitnessesEmpty, WithWitnesses(o)) => *self = WithWitnesses(o),
(_, NotUseful) => {} (WithWitnesses(_), WithWitnessesEmpty) => {}
(NotUseful, other) => *self = other, (WithWitnessesEmpty, WithWitnessesEmpty) => {}
(UsefulWithWitness(_), Useful(_)) | (Useful(_), UsefulWithWitness(_)) => unreachable!(),
(NoWitnesses(s), NoWitnesses(o)) => s.intersection_mut(&o),
(NoWitnessesFull, NoWitnesses(o)) => *self = NoWitnesses(o),
(NoWitnesses(_), NoWitnessesFull) => {}
(NoWitnessesFull, NoWitnessesFull) => {}
_ => {
unreachable!()
}
} }
} }
/// When trying several branches and each returns a `Usefulness`, we need to combine the /// When trying several branches and each returns a `Usefulness`, we need to combine the
/// results together. /// results together.
fn merge(usefulnesses: impl Iterator<Item = Self>) -> Self { fn merge(pref: WitnessPreference, usefulnesses: impl Iterator<Item = Self>) -> Self {
let mut ret = NotUseful; let mut ret = Self::new_not_useful(pref);
for u in usefulnesses { for u in usefulnesses {
ret.extend(u); ret.extend(u);
if let Useful(spans) = &ret { if let NoWitnesses(spans) = &ret {
if spans.is_empty() { if spans.is_empty() {
// Once we reach the empty set, more intersections won't change the result. // Once we reach the empty set, more intersections won't change the result.
return ret; return ret;
@ -748,7 +764,7 @@ impl<'tcx> Usefulness<'tcx> {
/// usefulness mergeable with those from the other branches. /// usefulness mergeable with those from the other branches.
fn unsplit_or_pat(self, this_span: Span, or_pat_spans: &[Span]) -> Self { fn unsplit_or_pat(self, this_span: Span, or_pat_spans: &[Span]) -> Self {
match self { match self {
Useful(mut spans) => { NoWitnesses(mut spans) => {
// We register the spans of the other branches of this or-pattern as being // We register the spans of the other branches of this or-pattern as being
// unreachable from this one. This ensures that intersecting together the sets of // unreachable from this one. This ensures that intersecting together the sets of
// spans returns what we want. // spans returns what we want.
@ -759,9 +775,10 @@ impl<'tcx> Usefulness<'tcx> {
spans.push_nonintersecting(span); spans.push_nonintersecting(span);
} }
} }
Useful(spans) NoWitnesses(spans)
} }
x => x, NoWitnessesFull => NoWitnessesFull,
WithWitnesses(_) | WithWitnessesEmpty => bug!(),
} }
} }
@ -776,7 +793,7 @@ impl<'tcx> Usefulness<'tcx> {
ctor_wild_subpatterns: &Fields<'p, 'tcx>, ctor_wild_subpatterns: &Fields<'p, 'tcx>,
) -> Self { ) -> Self {
match self { match self {
UsefulWithWitness(witnesses) => { WithWitnesses(witnesses) => {
let new_witnesses = if matches!(ctor, Constructor::Missing) { let new_witnesses = if matches!(ctor, Constructor::Missing) {
let mut split_wildcard = SplitWildcard::new(pcx); let mut split_wildcard = SplitWildcard::new(pcx);
split_wildcard.split(pcx, matrix.head_ctors(pcx.cx)); split_wildcard.split(pcx, matrix.head_ctors(pcx.cx));
@ -806,7 +823,7 @@ impl<'tcx> Usefulness<'tcx> {
.map(|witness| witness.apply_constructor(pcx, &ctor, ctor_wild_subpatterns)) .map(|witness| witness.apply_constructor(pcx, &ctor, ctor_wild_subpatterns))
.collect() .collect()
}; };
UsefulWithWitness(new_witnesses) WithWitnesses(new_witnesses)
} }
x => x, x => x,
} }
@ -935,8 +952,11 @@ fn is_useful<'p, 'tcx>(
// first and then, if v is non-empty, the return value is based on whether // first and then, if v is non-empty, the return value is based on whether
// the type of the tuple we're checking is inhabited or not. // the type of the tuple we're checking is inhabited or not.
if v.is_empty() { if v.is_empty() {
let ret = let ret = if rows.is_empty() {
if rows.is_empty() { Usefulness::new_useful(witness_preference) } else { NotUseful }; Usefulness::new_useful(witness_preference)
} else {
Usefulness::new_not_useful(witness_preference)
};
debug!(?ret); debug!(?ret);
return ret; return ret;
} }
@ -966,7 +986,7 @@ fn is_useful<'p, 'tcx>(
} }
usefulness.unsplit_or_pat(v_span, &subspans) usefulness.unsplit_or_pat(v_span, &subspans)
}); });
Usefulness::merge(usefulnesses) Usefulness::merge(witness_preference, usefulnesses)
} else { } else {
let v_ctor = v.head_ctor(cx); let v_ctor = v.head_ctor(cx);
if let Constructor::IntRange(ctor_range) = &v_ctor { if let Constructor::IntRange(ctor_range) = &v_ctor {
@ -994,7 +1014,7 @@ fn is_useful<'p, 'tcx>(
is_useful(cx, &spec_matrix, &v, witness_preference, hir_id, is_under_guard, false); is_useful(cx, &spec_matrix, &v, witness_preference, hir_id, is_under_guard, false);
usefulness.apply_constructor(pcx, start_matrix, &ctor, &ctor_wild_subpatterns) usefulness.apply_constructor(pcx, start_matrix, &ctor, &ctor_wild_subpatterns)
}); });
Usefulness::merge(usefulnesses) Usefulness::merge(witness_preference, usefulnesses)
}; };
debug!(?ret); debug!(?ret);
ret ret
@ -1049,9 +1069,9 @@ crate fn compute_match_usefulness<'p, 'tcx>(
matrix.push(v); matrix.push(v);
} }
let reachability = match usefulness { let reachability = match usefulness {
Useful(spans) => Reachability::Reachable(spans), NoWitnesses(spans) => Reachability::Reachable(spans),
NotUseful => Reachability::Unreachable, NoWitnessesFull => Reachability::Unreachable,
UsefulWithWitness(..) => bug!(), WithWitnesses(..) | WithWitnessesEmpty => bug!(),
}; };
(arm, reachability) (arm, reachability)
}) })
@ -1061,15 +1081,15 @@ crate fn compute_match_usefulness<'p, 'tcx>(
let v = PatStack::from_pattern(wild_pattern); let v = PatStack::from_pattern(wild_pattern);
let usefulness = is_useful(cx, &matrix, &v, ConstructWitness, scrut_hir_id, false, true); let usefulness = is_useful(cx, &matrix, &v, ConstructWitness, scrut_hir_id, false, true);
let non_exhaustiveness_witnesses = match usefulness { let non_exhaustiveness_witnesses = match usefulness {
NotUseful => vec![], // Wildcard pattern isn't useful, so the match is exhaustive. WithWitnessesEmpty => vec![], // Wildcard pattern isn't useful, so the match is exhaustive.
UsefulWithWitness(pats) => { WithWitnesses(pats) => {
if pats.is_empty() { if pats.is_empty() {
bug!("Exhaustiveness check returned no witnesses") bug!("Exhaustiveness check returned no witnesses")
} else { } else {
pats.into_iter().map(|w| w.single_pattern()).collect() pats.into_iter().map(|w| w.single_pattern()).collect()
} }
} }
Useful(_) => bug!(), NoWitnesses(_) | NoWitnessesFull => bug!(),
}; };
UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses } UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses }
} }