Unify the paths through is_useful
This commit is contained in:
parent
c96bd28ab3
commit
54fa70290d
@ -181,7 +181,6 @@
|
||||
//! we ignore all the patterns in the first column of `P` that involve other constructors.
|
||||
//! This is where `S(c, P)` comes in:
|
||||
//! `U(P, p) := U(S(c, P), S(c, p))`
|
||||
//! This special case is handled in `is_useful_specialized`.
|
||||
//!
|
||||
//! For example, if `P` is:
|
||||
//!
|
||||
@ -1116,8 +1115,8 @@ impl<'tcx> Constructor<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Some constructors (namely IntRange and Slice) actually stand for a set of actual
|
||||
/// constructors (integers and fixed-sized slices). When specializing for these
|
||||
/// Some constructors (namely Wildcard, IntRange and Slice) actually stand for a set of actual
|
||||
/// constructors (like variants, integers or fixed-sized slices). When specializing for these
|
||||
/// constructors, we want to be specialising for the actual underlying constructors.
|
||||
/// Naively, we would simply return the list of constructors they correspond to. We instead are
|
||||
/// more clever: if there are constructors that we know will behave the same wrt the current
|
||||
@ -1136,6 +1135,7 @@ impl<'tcx> Constructor<'tcx> {
|
||||
debug!("Constructor::split({:#?}, {:#?})", self, pcx.matrix);
|
||||
|
||||
match self {
|
||||
Wildcard => Constructor::split_wildcard(pcx),
|
||||
// Fast-track if the range is trivial. In particular, we don't do the overlapping
|
||||
// ranges check.
|
||||
IntRange(ctor_range)
|
||||
@ -1149,6 +1149,30 @@ impl<'tcx> Constructor<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// For wildcards, there are two groups of constructors: there are the constructors actually
|
||||
/// present in the matrix (`head_ctors`), and the constructors not present (`missing_ctors`).
|
||||
/// Two constructors that are not in the matrix will either both be catched (by a wildcard), or
|
||||
/// both not be catched. Therefore we can keep the missing constructors grouped together.
|
||||
fn split_wildcard<'p>(pcx: PatCtxt<'_, 'p, 'tcx>) -> SmallVec<[Self; 1]> {
|
||||
// Missing constructors are those that are not matched by any non-wildcard patterns in the
|
||||
// current column. We only fully construct them on-demand, because they're rarely used and
|
||||
// can be big.
|
||||
let missing_ctors = MissingConstructors::new(pcx);
|
||||
|
||||
if missing_ctors.is_empty() {
|
||||
// All the constructors are present in the matrix, so we just go through them all.
|
||||
// We must also split them first.
|
||||
// Since `all_ctors` never contains wildcards, this won't recurse more than once.
|
||||
let (all_ctors, _) = missing_ctors.into_inner();
|
||||
all_ctors.into_iter().flat_map(|ctor| ctor.split(pcx, None)).collect()
|
||||
} else {
|
||||
// Some constructors are missing, thus we can specialize with the wildcard constructor,
|
||||
// which will stand for those constructors that are missing, and behaves like any of
|
||||
// them.
|
||||
smallvec![Wildcard]
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether `self` is covered by `other`, ie whether `self` is a subset of `other`. For
|
||||
/// the simple cases, this is simply checking for equality. For the "grouped" constructors,
|
||||
/// this checks for inclusion.
|
||||
@ -1617,8 +1641,8 @@ impl<'tcx> Usefulness<'tcx> {
|
||||
match self {
|
||||
UsefulWithWitness(witnesses) => {
|
||||
let new_witnesses = if ctor.is_wildcard() {
|
||||
let missing_ctors = MissingConstructors::new(pcx, is_top_level);
|
||||
let new_patterns = missing_ctors.report_patterns(pcx);
|
||||
let missing_ctors = MissingConstructors::new(pcx);
|
||||
let new_patterns = missing_ctors.report_patterns(pcx, is_top_level);
|
||||
witnesses
|
||||
.into_iter()
|
||||
.flat_map(|witness| {
|
||||
@ -2217,16 +2241,15 @@ impl<'tcx> std::cmp::PartialEq for IntRange<'tcx> {
|
||||
struct MissingConstructors<'tcx> {
|
||||
all_ctors: Vec<Constructor<'tcx>>,
|
||||
used_ctors: Vec<Constructor<'tcx>>,
|
||||
is_top_level: bool,
|
||||
}
|
||||
|
||||
impl<'tcx> MissingConstructors<'tcx> {
|
||||
fn new<'p>(pcx: PatCtxt<'_, 'p, 'tcx>, is_top_level: bool) -> Self {
|
||||
fn new<'p>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Self {
|
||||
let used_ctors: Vec<Constructor<'_>> =
|
||||
pcx.matrix.head_ctors(pcx.cx).cloned().filter(|c| !c.is_wildcard()).collect();
|
||||
let all_ctors = all_constructors(pcx);
|
||||
|
||||
MissingConstructors { all_ctors, used_ctors, is_top_level }
|
||||
MissingConstructors { all_ctors, used_ctors }
|
||||
}
|
||||
|
||||
fn into_inner(self) -> (Vec<Constructor<'tcx>>, Vec<Constructor<'tcx>>) {
|
||||
@ -2244,7 +2267,11 @@ impl<'tcx> MissingConstructors<'tcx> {
|
||||
|
||||
/// List the patterns corresponding to the missing constructors. In some cases, instead of
|
||||
/// listing all constructors of a given type, we prefer to simply report a wildcard.
|
||||
fn report_patterns<'p>(&self, pcx: PatCtxt<'_, 'p, 'tcx>) -> SmallVec<[Pat<'tcx>; 1]> {
|
||||
fn report_patterns<'p>(
|
||||
&self,
|
||||
pcx: PatCtxt<'_, 'p, 'tcx>,
|
||||
is_top_level: bool,
|
||||
) -> SmallVec<[Pat<'tcx>; 1]> {
|
||||
// There are 2 ways we can report a witness here.
|
||||
// Commonly, we can report all the "free"
|
||||
// constructors as witnesses, e.g., if we have:
|
||||
@ -2272,7 +2299,7 @@ impl<'tcx> MissingConstructors<'tcx> {
|
||||
// `used_ctors` is empty.
|
||||
// The exception is: if we are at the top-level, for example in an empty match, we
|
||||
// sometimes prefer reporting the list of constructors instead of just `_`.
|
||||
let report_when_all_missing = self.is_top_level && !IntRange::is_integral(pcx.ty);
|
||||
let report_when_all_missing = is_top_level && !IntRange::is_integral(pcx.ty);
|
||||
if self.used_ctors.is_empty() && !report_when_all_missing {
|
||||
// All constructors are unused. Report only a wildcard
|
||||
// rather than each individual constructor.
|
||||
@ -2407,103 +2434,26 @@ crate fn is_useful<'p, 'tcx>(
|
||||
|
||||
debug!("is_useful_expand_first_col: ty={:#?}, expanding {:#?}", pcx.ty, v.head());
|
||||
|
||||
let constructor = v.head_ctor(cx);
|
||||
let ret = if !constructor.is_wildcard() {
|
||||
debug!("is_useful - expanding constructor: {:#?}", constructor);
|
||||
constructor
|
||||
.split(pcx, Some(hir_id))
|
||||
.into_iter()
|
||||
.map(|c| {
|
||||
is_useful_specialized(
|
||||
pcx,
|
||||
v,
|
||||
&c,
|
||||
witness_preference,
|
||||
hir_id,
|
||||
is_under_guard,
|
||||
is_top_level,
|
||||
)
|
||||
})
|
||||
.find(|result| result.is_useful())
|
||||
.unwrap_or(NotUseful)
|
||||
} else {
|
||||
debug!("is_useful - expanding wildcard");
|
||||
|
||||
// `missing_ctors` is the set of constructors from the same type as the
|
||||
// first column of `matrix` that are matched only by wildcard patterns
|
||||
// from the first column.
|
||||
//
|
||||
// Therefore, if there is some pattern that is unmatched by `matrix`,
|
||||
// it will still be unmatched if the first constructor is replaced by
|
||||
// any of the constructors in `missing_ctors`
|
||||
|
||||
// Missing constructors are those that are not matched by any non-wildcard patterns in the
|
||||
// current column. We only fully construct them on-demand, because they're rarely used and
|
||||
// can be big.
|
||||
let missing_ctors = MissingConstructors::new(pcx, is_top_level);
|
||||
|
||||
debug!("is_useful_missing_ctors.empty()={:#?}", missing_ctors.is_empty(),);
|
||||
|
||||
if missing_ctors.is_empty() {
|
||||
let (all_ctors, _) = missing_ctors.into_inner();
|
||||
all_ctors
|
||||
.into_iter()
|
||||
.flat_map(|ctor| ctor.split(pcx, None))
|
||||
.map(|c| {
|
||||
is_useful_specialized(
|
||||
pcx,
|
||||
v,
|
||||
&c,
|
||||
witness_preference,
|
||||
hir_id,
|
||||
is_under_guard,
|
||||
is_top_level,
|
||||
)
|
||||
})
|
||||
.find(|result| result.is_useful())
|
||||
.unwrap_or(NotUseful)
|
||||
} else {
|
||||
// Some constructors are missing, thus we can specialize with the wildcard constructor,
|
||||
// which will stand for those constructors that are missing, and behaves like any of
|
||||
// them.
|
||||
is_useful_specialized(
|
||||
pcx,
|
||||
v,
|
||||
constructor,
|
||||
witness_preference,
|
||||
hir_id,
|
||||
is_under_guard,
|
||||
is_top_level,
|
||||
)
|
||||
}
|
||||
};
|
||||
let ret = v
|
||||
.head_ctor(cx)
|
||||
.split(pcx, Some(hir_id))
|
||||
.into_iter()
|
||||
.map(|ctor| {
|
||||
// We cache the result of `Fields::wildcards` because it is used a lot.
|
||||
let ctor_wild_subpatterns = Fields::wildcards(pcx, &ctor);
|
||||
let matrix = pcx.matrix.specialize_constructor(pcx, &ctor, &ctor_wild_subpatterns);
|
||||
// Unwrap is ok: v can always be specialized with its own constructor.
|
||||
let v = v.specialize_constructor(pcx, &ctor, &ctor_wild_subpatterns, true).unwrap();
|
||||
let usefulness =
|
||||
is_useful(pcx.cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false);
|
||||
usefulness.apply_constructor(pcx, &ctor, &ctor_wild_subpatterns, is_top_level)
|
||||
})
|
||||
.find(|result| result.is_useful())
|
||||
.unwrap_or(NotUseful);
|
||||
debug!("is_useful::returns({:#?}, {:#?}) = {:?}", matrix, v, ret);
|
||||
ret
|
||||
}
|
||||
|
||||
/// A shorthand for the `U(S(c, P), S(c, q))` operation from the paper. I.e., `is_useful` applied
|
||||
/// to the specialised version of both the pattern matrix `P` and the new pattern `q`.
|
||||
fn is_useful_specialized<'p, 'tcx>(
|
||||
pcx: PatCtxt<'_, 'p, 'tcx>,
|
||||
v: &PatStack<'p, 'tcx>,
|
||||
ctor: &Constructor<'tcx>,
|
||||
witness_preference: WitnessPreference,
|
||||
hir_id: HirId,
|
||||
is_under_guard: bool,
|
||||
is_top_level: bool,
|
||||
) -> Usefulness<'tcx> {
|
||||
debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, pcx.ty);
|
||||
|
||||
// We cache the result of `Fields::wildcards` because it is used a lot.
|
||||
let ctor_wild_subpatterns = Fields::wildcards(pcx, ctor);
|
||||
let matrix = pcx.matrix.specialize_constructor(pcx, ctor, &ctor_wild_subpatterns);
|
||||
// Unwrap is ok: v can always be specialized with its own constructor.
|
||||
let v = v.specialize_constructor(pcx, ctor, &ctor_wild_subpatterns, true).unwrap();
|
||||
let usefulness =
|
||||
is_useful(pcx.cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false);
|
||||
usefulness.apply_constructor(pcx, ctor, &ctor_wild_subpatterns, is_top_level)
|
||||
}
|
||||
|
||||
/// Determines the constructor that the given pattern can be specialized to.
|
||||
/// Returns `None` in case of a catch-all, which can't be specialized.
|
||||
fn pat_constructor<'p, 'tcx>(
|
||||
|
Loading…
x
Reference in New Issue
Block a user