Reimplement Usefulness::merge in terms of a binop

This commit is contained in:
Nadrieril 2020-12-21 04:51:46 +00:00
parent 5547105f6b
commit f4f20c0663

View file

@ -696,9 +696,9 @@ impl<'tcx> Usefulness<'tcx> {
} }
} }
/// When trying several branches and each returns a `Usefulness`, we need to combine the /// Combine usefulnesses from two branches. This is an associative operation and `NotUseful` is
/// results together. /// a unit.
fn merge(usefulnesses: impl Iterator<Item = Self>) -> 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
// in the second branch of the first or-pattern, but not otherwise. Therefore we don't want // in the second branch of the first or-pattern, but not otherwise. Therefore we don't want
@ -709,54 +709,39 @@ impl<'tcx> Usefulness<'tcx> {
// (false | true, false | true) => {} // (false | true, false | true) => {}
// } // }
// ``` // ```
// Here however we _do_ want to lint that the last `false` is unreachable. So we don't want // Here however we _do_ want to lint that the last `false` is unreachable. In order to
// to intersect the spans that come directly from the or-pattern, since each branch of the // handle that correctly, each branch of an or-pattern marks the other branches as
// or-pattern brings a new disjoint pattern. // unreachable (see `unsplit_or_pat`). That way, intersecting the results will correctly
// identify unreachable sub-patterns.
// ``` // ```
// match None { // match None {
// Some(false) => {} // Some(false) => {}
// None | Some(true | false) => {} // None | Some(true | false) => {}
// } // }
// ``` // ```
match (&mut *self, other) {
(Useful(s), Useful(o)) => s.intersection_mut(&o),
(UsefulWithWitness(s), UsefulWithWitness(o)) => s.extend(o),
(_, NotUseful) => {}
(NotUseful, other) => *self = other,
(UsefulWithWitness(_), Useful(_)) | (Useful(_), UsefulWithWitness(_)) => unreachable!(),
}
}
// Is `None` when no branch was useful. Will often be `Some(Spanset::new())` because the /// When trying several branches and each returns a `Usefulness`, we need to combine the
// sets are only non-empty in the presence of or-patterns. /// results together.
let mut unreachables: Option<SpanSet> = None; fn merge(usefulnesses: impl Iterator<Item = Self>) -> Self {
// Witnesses of usefulness, if any. let mut ret = NotUseful;
let mut witnesses = Vec::new();
for u in usefulnesses { for u in usefulnesses {
match u { ret.extend(u);
Useful(spans) if spans.is_empty() => { if let Useful(spans) = &ret {
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 Useful(SpanSet::new()); return ret;
}
Useful(spans) => {
if let Some(unreachables) = &mut unreachables {
if !unreachables.is_empty() {
unreachables.intersection_mut(&spans);
}
if unreachables.is_empty() {
return Useful(SpanSet::new());
}
} else {
unreachables = Some(spans);
}
}
NotUseful => {}
UsefulWithWitness(wits) => {
witnesses.extend(wits);
} }
} }
} }
ret
if !witnesses.is_empty() {
UsefulWithWitness(witnesses)
} else if let Some(unreachables) = unreachables {
Useful(unreachables)
} else {
NotUseful
}
} }
/// After calculating the usefulness for a branch of an or-pattern, call this to make this /// After calculating the usefulness for a branch of an or-pattern, call this to make this