Skip to content

Commit bcea3cb

Browse files
committed
Auto merge of rust-lang#120692 - Nadrieril:move-column-analysis-to-placeinfo, r=compiler-errors
pattern_analysis: Move constructor selection logic to `PlaceInfo` This is a small refactor PR. There was a dense bit of constructor-related logic in `compute_exhaustiveness_and_usefulness`. I'm moving it out into a `PlaceInfo` method to make it easier to follow both separately. I also have plans that will complicate it further so it's good that it's somewhat encapsulated. r? `@compiler-errors`
2 parents 0b9f6ad + 778c7e1 commit bcea3cb

File tree

1 file changed

+90
-74
lines changed

1 file changed

+90
-74
lines changed

compiler/rustc_pattern_analysis/src/usefulness.rs

+90-74
Original file line numberDiff line numberDiff line change
@@ -760,9 +760,6 @@ impl<'a, Cx: TypeCx> PlaceCtxt<'a, Cx> {
760760
fn ctor_arity(&self, ctor: &Constructor<Cx>) -> usize {
761761
self.cx.ctor_arity(ctor, self.ty)
762762
}
763-
fn ctors_for_ty(&self) -> Result<ConstructorSet<Cx>, Cx::Error> {
764-
self.cx.ctors_for_ty(self.ty)
765-
}
766763
fn wild_from_ctor(&self, ctor: Constructor<Cx>) -> WitnessPat<Cx> {
767764
WitnessPat::wild_from_ctor(self.cx, ctor, self.ty.clone())
768765
}
@@ -815,7 +812,8 @@ impl fmt::Display for ValidityConstraint {
815812
}
816813
}
817814

818-
/// Data about a place under investigation.
815+
/// Data about a place under investigation. Its methods contain a lot of the logic used to analyze
816+
/// the constructors in the matrix.
819817
struct PlaceInfo<Cx: TypeCx> {
820818
/// The type of the place.
821819
ty: Cx::Ty,
@@ -826,6 +824,8 @@ struct PlaceInfo<Cx: TypeCx> {
826824
}
827825

828826
impl<Cx: TypeCx> PlaceInfo<Cx> {
827+
/// Given a constructor for the current place, we return one `PlaceInfo` for each field of the
828+
/// constructor.
829829
fn specialize<'a>(
830830
&'a self,
831831
cx: &'a Cx,
@@ -839,6 +839,77 @@ impl<Cx: TypeCx> PlaceInfo<Cx> {
839839
is_scrutinee: false,
840840
})
841841
}
842+
843+
/// This analyzes a column of constructors corresponding to the current place. It returns a pair
844+
/// `(split_ctors, missing_ctors)`.
845+
///
846+
/// `split_ctors` is a splitted list of constructors that cover the whole type. This will be
847+
/// used to specialize the matrix.
848+
///
849+
/// `missing_ctors` is a list of the constructors not found in the column, for reporting
850+
/// purposes.
851+
fn split_column_ctors<'a>(
852+
&self,
853+
cx: &Cx,
854+
ctors: impl Iterator<Item = &'a Constructor<Cx>> + Clone,
855+
) -> Result<(SmallVec<[Constructor<Cx>; 1]>, Vec<Constructor<Cx>>), Cx::Error>
856+
where
857+
Cx: 'a,
858+
{
859+
let ctors_for_ty = cx.ctors_for_ty(&self.ty)?;
860+
861+
// We treat match scrutinees of type `!` or `EmptyEnum` differently.
862+
let is_toplevel_exception =
863+
self.is_scrutinee && matches!(ctors_for_ty, ConstructorSet::NoConstructors);
864+
// Whether empty patterns are counted as useful or not. We only warn an empty arm unreachable if
865+
// it is guaranteed unreachable by the opsem (i.e. if the place is `known_valid`).
866+
let empty_arms_are_unreachable = self.validity.is_known_valid()
867+
&& (is_toplevel_exception
868+
|| cx.is_exhaustive_patterns_feature_on()
869+
|| cx.is_min_exhaustive_patterns_feature_on());
870+
// Whether empty patterns can be omitted for exhaustiveness. We ignore place validity in the
871+
// toplevel exception and `exhaustive_patterns` cases for backwards compatibility.
872+
let can_omit_empty_arms = empty_arms_are_unreachable
873+
|| is_toplevel_exception
874+
|| cx.is_exhaustive_patterns_feature_on();
875+
876+
// Analyze the constructors present in this column.
877+
let mut split_set = ctors_for_ty.split(ctors);
878+
let all_missing = split_set.present.is_empty();
879+
880+
// Build the set of constructors we will specialize with. It must cover the whole type, so
881+
// we add `Missing` to represent the missing ones. This is explained under "Constructor
882+
// Splitting" at the top of this file.
883+
let mut split_ctors = split_set.present;
884+
if !(split_set.missing.is_empty()
885+
&& (split_set.missing_empty.is_empty() || empty_arms_are_unreachable))
886+
{
887+
split_ctors.push(Constructor::Missing);
888+
}
889+
890+
// Which empty constructors are considered missing. We ensure that
891+
// `!missing_ctors.is_empty() => split_ctors.contains(Missing)`. The converse usually holds
892+
// except when `!self.validity.is_known_valid()`.
893+
let mut missing_ctors = split_set.missing;
894+
if !can_omit_empty_arms {
895+
missing_ctors.append(&mut split_set.missing_empty);
896+
}
897+
898+
// Whether we should report "Enum::A and Enum::C are missing" or "_ is missing". At the top
899+
// level we prefer to list all constructors.
900+
let report_individual_missing_ctors = self.is_scrutinee || !all_missing;
901+
if !missing_ctors.is_empty() && !report_individual_missing_ctors {
902+
// Report `_` as missing.
903+
missing_ctors = vec![Constructor::Wildcard];
904+
} else if missing_ctors.iter().any(|c| c.is_non_exhaustive()) {
905+
// We need to report a `_` anyway, so listing other constructors would be redundant.
906+
// `NonExhaustive` is displayed as `_` just like `Wildcard`, but it will be picked
907+
// up by diagnostics to add a note about why `_` is required here.
908+
missing_ctors = vec![Constructor::NonExhaustive];
909+
}
910+
911+
Ok((split_ctors, missing_ctors))
912+
}
842913
}
843914

844915
impl<Cx: TypeCx> Clone for PlaceInfo<Cx> {
@@ -1314,40 +1385,23 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> {
13141385
pcx: &PlaceCtxt<'_, Cx>,
13151386
missing_ctors: &[Constructor<Cx>],
13161387
ctor: &Constructor<Cx>,
1317-
report_individual_missing_ctors: bool,
13181388
) {
13191389
if self.is_empty() {
13201390
return;
13211391
}
13221392
if matches!(ctor, Constructor::Missing) {
13231393
// We got the special `Missing` constructor that stands for the constructors not present
1324-
// in the match.
1325-
if missing_ctors.is_empty() {
1326-
// Nothing to report.
1327-
*self = Self::empty();
1328-
} else if !report_individual_missing_ctors {
1329-
// Report `_` as missing.
1330-
let pat = pcx.wild_from_ctor(Constructor::Wildcard);
1331-
self.push_pattern(pat);
1332-
} else if missing_ctors.iter().any(|c| c.is_non_exhaustive()) {
1333-
// We need to report a `_` anyway, so listing other constructors would be redundant.
1334-
// `NonExhaustive` is displayed as `_` just like `Wildcard`, but it will be picked
1335-
// up by diagnostics to add a note about why `_` is required here.
1336-
let pat = pcx.wild_from_ctor(Constructor::NonExhaustive);
1337-
self.push_pattern(pat);
1338-
} else {
1339-
// For each missing constructor `c`, we add a `c(_, _, _)` witness appropriately
1340-
// filled with wildcards.
1341-
let mut ret = Self::empty();
1342-
for ctor in missing_ctors {
1343-
let pat = pcx.wild_from_ctor(ctor.clone());
1344-
// Clone `self` and add `c(_, _, _)` to each of its witnesses.
1345-
let mut wit_matrix = self.clone();
1346-
wit_matrix.push_pattern(pat);
1347-
ret.extend(wit_matrix);
1348-
}
1349-
*self = ret;
1394+
// in the match. For each missing constructor `c`, we add a `c(_, _, _)` witness
1395+
// appropriately filled with wildcards.
1396+
let mut ret = Self::empty();
1397+
for ctor in missing_ctors {
1398+
let pat = pcx.wild_from_ctor(ctor.clone());
1399+
// Clone `self` and add `c(_, _, _)` to each of its witnesses.
1400+
let mut wit_matrix = self.clone();
1401+
wit_matrix.push_pattern(pat);
1402+
ret.extend(wit_matrix);
13501403
}
1404+
*self = ret;
13511405
} else {
13521406
// Any other constructor we unspecialize as expected.
13531407
for witness in self.0.iter_mut() {
@@ -1479,51 +1533,13 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
14791533
};
14801534
};
14811535

1482-
let ty = &place.ty.clone(); // Clone it out so we can mutate `matrix` later.
1483-
let pcx = &PlaceCtxt { cx: mcx.tycx, ty };
1484-
debug!("ty: {:?}", pcx.ty);
1485-
let ctors_for_ty = pcx.ctors_for_ty()?;
1486-
1487-
// We treat match scrutinees of type `!` or `EmptyEnum` differently.
1488-
let is_toplevel_exception =
1489-
place.is_scrutinee && matches!(ctors_for_ty, ConstructorSet::NoConstructors);
1490-
// Whether empty patterns are counted as useful or not. We only warn an empty arm unreachable if
1491-
// it is guaranteed unreachable by the opsem (i.e. if the place is `known_valid`).
1492-
let empty_arms_are_unreachable = place.validity.is_known_valid()
1493-
&& (is_toplevel_exception
1494-
|| mcx.tycx.is_exhaustive_patterns_feature_on()
1495-
|| mcx.tycx.is_min_exhaustive_patterns_feature_on());
1496-
// Whether empty patterns can be omitted for exhaustiveness. We ignore place validity in the
1497-
// toplevel exception and `exhaustive_patterns` cases for backwards compatibility.
1498-
let can_omit_empty_arms = empty_arms_are_unreachable
1499-
|| is_toplevel_exception
1500-
|| mcx.tycx.is_exhaustive_patterns_feature_on();
1501-
15021536
// Analyze the constructors present in this column.
1537+
debug!("ty: {:?}", place.ty);
15031538
let ctors = matrix.heads().map(|p| p.ctor());
1504-
let mut split_set = ctors_for_ty.split(ctors);
1505-
let all_missing = split_set.present.is_empty();
1506-
// Build the set of constructors we will specialize with. It must cover the whole type.
1507-
// We need to iterate over a full set of constructors, so we add `Missing` to represent the
1508-
// missing ones. This is explained under "Constructor Splitting" at the top of this file.
1509-
let mut split_ctors = split_set.present;
1510-
if !(split_set.missing.is_empty()
1511-
&& (split_set.missing_empty.is_empty() || empty_arms_are_unreachable))
1512-
{
1513-
split_ctors.push(Constructor::Missing);
1514-
}
1515-
1516-
// Whether we should report "Enum::A and Enum::C are missing" or "_ is missing". At the top
1517-
// level we prefer to list all constructors.
1518-
let report_individual_missing_ctors = place.is_scrutinee || !all_missing;
1519-
// Which constructors are considered missing. We ensure that `!missing_ctors.is_empty() =>
1520-
// split_ctors.contains(Missing)`. The converse usually holds except when
1521-
// `!place_validity.is_known_valid()`.
1522-
let mut missing_ctors = split_set.missing;
1523-
if !can_omit_empty_arms {
1524-
missing_ctors.append(&mut split_set.missing_empty);
1525-
}
1539+
let (split_ctors, missing_ctors) = place.split_column_ctors(mcx.tycx, ctors)?;
15261540

1541+
let ty = &place.ty.clone(); // Clone it out so we can mutate `matrix` later.
1542+
let pcx = &PlaceCtxt { cx: mcx.tycx, ty };
15271543
let mut ret = WitnessMatrix::empty();
15281544
for ctor in split_ctors {
15291545
// Dig into rows that match `ctor`.
@@ -1538,7 +1554,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
15381554
})?;
15391555

15401556
// Transform witnesses for `spec_matrix` into witnesses for `matrix`.
1541-
witnesses.apply_constructor(pcx, &missing_ctors, &ctor, report_individual_missing_ctors);
1557+
witnesses.apply_constructor(pcx, &missing_ctors, &ctor);
15421558
// Accumulate the found witnesses.
15431559
ret.extend(witnesses);
15441560

0 commit comments

Comments
 (0)