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.
|
//! we ignore all the patterns in the first column of `P` that involve other constructors.
|
||||||
//! This is where `S(c, P)` comes in:
|
//! This is where `S(c, P)` comes in:
|
||||||
//! `U(P, p) := U(S(c, P), S(c, p))`
|
//! `U(P, p) := U(S(c, P), S(c, p))`
|
||||||
//! This special case is handled in `is_useful_specialized`.
|
|
||||||
//!
|
//!
|
||||||
//! For example, if `P` is:
|
//! 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
|
/// Some constructors (namely Wildcard, IntRange and Slice) actually stand for a set of actual
|
||||||
/// constructors (integers and fixed-sized slices). When specializing for these
|
/// constructors (like variants, integers or fixed-sized slices). When specializing for these
|
||||||
/// constructors, we want to be specialising for the actual underlying constructors.
|
/// 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
|
/// 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
|
/// 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);
|
debug!("Constructor::split({:#?}, {:#?})", self, pcx.matrix);
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
|
Wildcard => Constructor::split_wildcard(pcx),
|
||||||
// Fast-track if the range is trivial. In particular, we don't do the overlapping
|
// Fast-track if the range is trivial. In particular, we don't do the overlapping
|
||||||
// ranges check.
|
// ranges check.
|
||||||
IntRange(ctor_range)
|
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
|
/// 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,
|
/// the simple cases, this is simply checking for equality. For the "grouped" constructors,
|
||||||
/// this checks for inclusion.
|
/// this checks for inclusion.
|
||||||
@ -1617,8 +1641,8 @@ impl<'tcx> Usefulness<'tcx> {
|
|||||||
match self {
|
match self {
|
||||||
UsefulWithWitness(witnesses) => {
|
UsefulWithWitness(witnesses) => {
|
||||||
let new_witnesses = if ctor.is_wildcard() {
|
let new_witnesses = if ctor.is_wildcard() {
|
||||||
let missing_ctors = MissingConstructors::new(pcx, is_top_level);
|
let missing_ctors = MissingConstructors::new(pcx);
|
||||||
let new_patterns = missing_ctors.report_patterns(pcx);
|
let new_patterns = missing_ctors.report_patterns(pcx, is_top_level);
|
||||||
witnesses
|
witnesses
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|witness| {
|
.flat_map(|witness| {
|
||||||
@ -2217,16 +2241,15 @@ impl<'tcx> std::cmp::PartialEq for IntRange<'tcx> {
|
|||||||
struct MissingConstructors<'tcx> {
|
struct MissingConstructors<'tcx> {
|
||||||
all_ctors: Vec<Constructor<'tcx>>,
|
all_ctors: Vec<Constructor<'tcx>>,
|
||||||
used_ctors: Vec<Constructor<'tcx>>,
|
used_ctors: Vec<Constructor<'tcx>>,
|
||||||
is_top_level: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> MissingConstructors<'tcx> {
|
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<'_>> =
|
let used_ctors: Vec<Constructor<'_>> =
|
||||||
pcx.matrix.head_ctors(pcx.cx).cloned().filter(|c| !c.is_wildcard()).collect();
|
pcx.matrix.head_ctors(pcx.cx).cloned().filter(|c| !c.is_wildcard()).collect();
|
||||||
let all_ctors = all_constructors(pcx);
|
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>>) {
|
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
|
/// 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.
|
/// 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.
|
// There are 2 ways we can report a witness here.
|
||||||
// Commonly, we can report all the "free"
|
// Commonly, we can report all the "free"
|
||||||
// constructors as witnesses, e.g., if we have:
|
// constructors as witnesses, e.g., if we have:
|
||||||
@ -2272,7 +2299,7 @@ impl<'tcx> MissingConstructors<'tcx> {
|
|||||||
// `used_ctors` is empty.
|
// `used_ctors` is empty.
|
||||||
// The exception is: if we are at the top-level, for example in an empty match, we
|
// 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 `_`.
|
// 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 {
|
if self.used_ctors.is_empty() && !report_when_all_missing {
|
||||||
// All constructors are unused. Report only a wildcard
|
// All constructors are unused. Report only a wildcard
|
||||||
// rather than each individual constructor.
|
// 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());
|
debug!("is_useful_expand_first_col: ty={:#?}, expanding {:#?}", pcx.ty, v.head());
|
||||||
|
|
||||||
let constructor = v.head_ctor(cx);
|
let ret = v
|
||||||
let ret = if !constructor.is_wildcard() {
|
.head_ctor(cx)
|
||||||
debug!("is_useful - expanding constructor: {:#?}", constructor);
|
.split(pcx, Some(hir_id))
|
||||||
constructor
|
.into_iter()
|
||||||
.split(pcx, Some(hir_id))
|
.map(|ctor| {
|
||||||
.into_iter()
|
// We cache the result of `Fields::wildcards` because it is used a lot.
|
||||||
.map(|c| {
|
let ctor_wild_subpatterns = Fields::wildcards(pcx, &ctor);
|
||||||
is_useful_specialized(
|
let matrix = pcx.matrix.specialize_constructor(pcx, &ctor, &ctor_wild_subpatterns);
|
||||||
pcx,
|
// Unwrap is ok: v can always be specialized with its own constructor.
|
||||||
v,
|
let v = v.specialize_constructor(pcx, &ctor, &ctor_wild_subpatterns, true).unwrap();
|
||||||
&c,
|
let usefulness =
|
||||||
witness_preference,
|
is_useful(pcx.cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false);
|
||||||
hir_id,
|
usefulness.apply_constructor(pcx, &ctor, &ctor_wild_subpatterns, is_top_level)
|
||||||
is_under_guard,
|
})
|
||||||
is_top_level,
|
.find(|result| result.is_useful())
|
||||||
)
|
.unwrap_or(NotUseful);
|
||||||
})
|
|
||||||
.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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
debug!("is_useful::returns({:#?}, {:#?}) = {:?}", matrix, v, ret);
|
debug!("is_useful::returns({:#?}, {:#?}) = {:?}", matrix, v, ret);
|
||||||
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.
|
/// Determines the constructor that the given pattern can be specialized to.
|
||||||
/// Returns `None` in case of a catch-all, which can't be specialized.
|
/// Returns `None` in case of a catch-all, which can't be specialized.
|
||||||
fn pat_constructor<'p, 'tcx>(
|
fn pat_constructor<'p, 'tcx>(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user