1
Fork 0

Make the special "missing patterns" constructor real

This commit is contained in:
Nadrieril 2020-12-20 00:39:37 +00:00
parent 2a541cea35
commit 53e03fb7c1
2 changed files with 57 additions and 64 deletions

View file

@ -607,6 +607,9 @@ pub(super) enum Constructor<'tcx> {
/// Fake extra constructor for enums that aren't allowed to be matched exhaustively. Also used /// Fake extra constructor for enums that aren't allowed to be matched exhaustively. Also used
/// for those types for which we cannot list constructors explicitly, like `f64` and `str`. /// for those types for which we cannot list constructors explicitly, like `f64` and `str`.
NonExhaustive, NonExhaustive,
/// Stands for constructors that are not seen in the matrix, as explained in the documentation
/// for [`SplitWildcard`].
Missing,
/// Wildcard pattern. /// Wildcard pattern.
Wildcard, Wildcard,
} }
@ -758,8 +761,8 @@ impl<'tcx> Constructor<'tcx> {
match (self, other) { match (self, other) {
// Wildcards cover anything // Wildcards cover anything
(_, Wildcard) => true, (_, Wildcard) => true,
// Wildcards are only covered by wildcards // The missing ctors are not covered by anything in the matrix except wildcards.
(Wildcard, _) => false, (Missing | Wildcard, _) => false,
(Single, Single) => true, (Single, Single) => true,
(Variant(self_id), Variant(other_id)) => self_id == other_id, (Variant(self_id), Variant(other_id)) => self_id == other_id,
@ -832,7 +835,7 @@ impl<'tcx> Constructor<'tcx> {
.any(|other| slice.is_covered_by(other)), .any(|other| slice.is_covered_by(other)),
// This constructor is never covered by anything else // This constructor is never covered by anything else
NonExhaustive => false, NonExhaustive => false,
Str(..) | FloatRange(..) | Opaque | Wildcard => { Str(..) | FloatRange(..) | Opaque | Missing | Wildcard => {
span_bug!(pcx.span, "found unexpected ctor in all_ctors: {:?}", self) span_bug!(pcx.span, "found unexpected ctor in all_ctors: {:?}", self)
} }
} }
@ -1002,7 +1005,7 @@ impl<'tcx> SplitWildcard<'tcx> {
} }
/// Iterate over the constructors for this type that are not present in the matrix. /// Iterate over the constructors for this type that are not present in the matrix.
fn iter_missing<'a, 'p>( pub(super) fn iter_missing<'a, 'p>(
&'a self, &'a self,
pcx: PatCtxt<'a, 'p, 'tcx>, pcx: PatCtxt<'a, 'p, 'tcx>,
) -> impl Iterator<Item = &'a Constructor<'tcx>> + Captures<'p> { ) -> impl Iterator<Item = &'a Constructor<'tcx>> + Captures<'p> {
@ -1013,64 +1016,45 @@ impl<'tcx> SplitWildcard<'tcx> {
/// top of the file, if any constructors are missing we can ignore the present ones. /// top of the file, if any constructors are missing we can ignore the present ones.
fn into_ctors(self, pcx: PatCtxt<'_, '_, 'tcx>) -> SmallVec<[Constructor<'tcx>; 1]> { fn into_ctors(self, pcx: PatCtxt<'_, '_, 'tcx>) -> SmallVec<[Constructor<'tcx>; 1]> {
if self.any_missing(pcx) { if self.any_missing(pcx) {
// Some constructors are missing, thus we can specialize with the wildcard constructor, // Some constructors are missing, thus we can specialize with the special `Missing`
// which will stand for those constructors that are missing, and matches the same rows // constructor, which stands for those constructors that are not seen in the matrix,
// as any of them (namely the wildcard rows). // and matches the same rows as any of them (namely the wildcard rows). See the top of
return smallvec![Wildcard]; // the file for details.
// However, when all constructors are missing we can also specialize with the full
// `Wildcard` constructor. The difference will depend on what we want in diagnostics.
// If some constructors are missing, we typically want to report those constructors,
// e.g.:
// ```
// enum Direction { N, S, E, W }
// let Direction::N = ...;
// ```
// we can report 3 witnesses: `S`, `E`, and `W`.
//
// However, if the user didn't actually specify a constructor
// in this arm, e.g., in
// ```
// let x: (Direction, Direction, bool) = ...;
// let (_, _, false) = x;
// ```
// we don't want to show all 16 possible witnesses `(<direction-1>, <direction-2>,
// true)` - we are satisfied with `(_, _, true)`. So if all constructors are missing we
// prefer to report just a wildcard `_`.
//
// 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 = pcx.is_top_level && !IntRange::is_integral(pcx.ty);
let ctor = if !self.matrix_ctors.is_empty() || report_when_all_missing {
Missing
} else {
Wildcard
};
return smallvec![ctor];
} }
// All the constructors are present in the matrix, so we just go through them all. // All the constructors are present in the matrix, so we just go through them all.
self.all_ctors self.all_ctors
} }
/// 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.
pub(super) fn report_missing_patterns<'p>(
&self,
pcx: PatCtxt<'_, 'p, 'tcx>,
) -> 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:
//
// ```
// enum Direction { N, S, E, W }
// let Direction::N = ...;
// ```
//
// we can report 3 witnesses: `S`, `E`, and `W`.
//
// However, there is a case where we don't want
// to do this and instead report a single `_` witness:
// if the user didn't actually specify a constructor
// in this arm, e.g., in
//
// ```
// let x: (Direction, Direction, bool) = ...;
// let (_, _, false) = x;
// ```
//
// we don't want to show all 16 possible witnesses
// `(<direction-1>, <direction-2>, true)` - we are
// satisfied with `(_, _, true)`. In this case,
// `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 = pcx.is_top_level && !IntRange::is_integral(pcx.ty);
if self.matrix_ctors.is_empty() && !report_when_all_missing {
// All constructors are unused. Report only a wildcard
// rather than each individual constructor.
smallvec![Pat::wildcard_from_ty(pcx.ty)]
} else {
// Construct for each missing constructor a "wild" version of this
// constructor, that matches everything that can be built with
// it. For example, if `ctor` is a `Constructor::Variant` for
// `Option::Some`, we get the pattern `Some(_)`.
self.iter_missing(pcx)
.map(|missing_ctor| Fields::wildcards(pcx, &missing_ctor).apply(pcx, missing_ctor))
.collect()
}
}
} }
/// Some fields need to be explicitly hidden away in certain cases; see the comment above the /// Some fields need to be explicitly hidden away in certain cases; see the comment above the
@ -1211,9 +1195,8 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
} }
_ => bug!("bad slice pattern {:?} {:?}", constructor, ty), _ => bug!("bad slice pattern {:?} {:?}", constructor, ty),
}, },
Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque | Wildcard => { Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque | Missing
Fields::empty() | Wildcard => Fields::empty(),
}
}; };
debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret); debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret);
ret ret
@ -1297,9 +1280,10 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
&FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }), &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }),
IntRange(range) => return range.to_pat(pcx.cx.tcx, pcx.ty), IntRange(range) => return range.to_pat(pcx.cx.tcx, pcx.ty),
NonExhaustive => PatKind::Wild, NonExhaustive => PatKind::Wild,
Wildcard => return Pat::wildcard_from_ty(pcx.ty),
Opaque => bug!("we should not try to apply an opaque constructor"), Opaque => bug!("we should not try to apply an opaque constructor"),
Wildcard => bug!( Missing => bug!(
"trying to apply a wildcard constructor; this should have been done in `apply_constructors`" "trying to apply the `Missing` constructor; this should have been done in `apply_constructors`"
), ),
}; };

View file

@ -784,10 +784,19 @@ impl<'tcx> Usefulness<'tcx> {
) -> Self { ) -> Self {
match self { match self {
UsefulWithWitness(witnesses) => { UsefulWithWitness(witnesses) => {
let new_witnesses = if ctor.is_wildcard() { 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));
let new_patterns = split_wildcard.report_missing_patterns(pcx); // Construct for each missing constructor a "wild" version of this
// constructor, that matches everything that can be built with
// it. For example, if `ctor` is a `Constructor::Variant` for
// `Option::Some`, we get the pattern `Some(_)`.
let new_patterns: Vec<_> = split_wildcard
.iter_missing(pcx)
.map(|missing_ctor| {
Fields::wildcards(pcx, missing_ctor).apply(pcx, missing_ctor)
})
.collect();
witnesses witnesses
.into_iter() .into_iter()
.flat_map(|witness| { .flat_map(|witness| {