Skip to content

Commit

Permalink
Move constructor selection logic to PlaceInfo
Browse files Browse the repository at this point in the history
  • Loading branch information
Nadrieril committed Feb 6, 2024
1 parent 481bb33 commit 2b2a74c
Showing 1 changed file with 81 additions and 58 deletions.
139 changes: 81 additions & 58 deletions compiler/rustc_pattern_analysis/src/usefulness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -767,9 +767,6 @@ impl<'a, Cx: TypeCx> PlaceCtxt<'a, Cx> {
fn ctor_arity(&self, ctor: &Constructor<Cx>) -> usize {
self.cx.ctor_arity(ctor, self.ty)
}
fn ctors_for_ty(&self) -> Result<ConstructorSet<Cx>, Cx::Error> {
self.cx.ctors_for_ty(self.ty)
}
fn wild_from_ctor(&self, ctor: Constructor<Cx>) -> WitnessPat<Cx> {
WitnessPat::wild_from_ctor(self.cx, ctor, self.ty.clone())
}
Expand Down Expand Up @@ -822,7 +819,8 @@ impl fmt::Display for ValidityConstraint {
}
}

/// Data about a place under investigation.
/// Data about a place under investigation. Its methods contain a lot of the logic used to analyze
/// the constructors in the matrix.
struct PlaceInfo<Cx: TypeCx> {
/// The type of the place.
ty: Cx::Ty,
Expand All @@ -833,6 +831,8 @@ struct PlaceInfo<Cx: TypeCx> {
}

impl<Cx: TypeCx> PlaceInfo<Cx> {
/// Given a constructor for the current place, we return one `PlaceInfo` for each field of the
/// constructor.
fn specialize<'a>(
&'a self,
cx: &'a Cx,
Expand All @@ -846,6 +846,79 @@ impl<Cx: TypeCx> PlaceInfo<Cx> {
is_scrutinee: false,
})
}

/// This analyzes a column of constructors corresponding to the current place. It returns a pair
/// `(split_ctors, missing_ctors)`.
///
/// `split_ctors` is a splitted list of constructors that cover the whole type. This will be
/// used to specialize the matrix.
///
/// `missing_ctors` is a list of the constructors not found in the column, for reporting
/// purposes.
fn split_column_ctors<'a>(
&self,
cx: &Cx,
ctors: impl Iterator<Item = &'a Constructor<Cx>> + Clone,
) -> Result<(SmallVec<[Constructor<Cx>; 1]>, Vec<Constructor<Cx>>), Cx::Error>
where
Cx: 'a,
{
let ctors_for_ty = cx.ctors_for_ty(&self.ty)?;

// We treat match scrutinees of type `!` or `EmptyEnum` differently.
let is_toplevel_exception =
self.is_scrutinee && matches!(ctors_for_ty, ConstructorSet::NoConstructors);
// Whether empty patterns are counted as useful or not. We only warn an empty arm unreachable if
// it is guaranteed unreachable by the opsem (i.e. if the place is `known_valid`).
let empty_arms_are_unreachable = self.validity.is_known_valid()
&& (is_toplevel_exception
|| cx.is_exhaustive_patterns_feature_on()
|| cx.is_min_exhaustive_patterns_feature_on());
// Whether empty patterns can be omitted for exhaustiveness. We ignore place validity in the
// toplevel exception and `exhaustive_patterns` cases for backwards compatibility.
let can_omit_empty_arms = empty_arms_are_unreachable
|| is_toplevel_exception
|| cx.is_exhaustive_patterns_feature_on();

// Analyze the constructors present in this column.
let mut split_set = ctors_for_ty.split(ctors);
let all_missing = split_set.present.is_empty();

// Build the set of constructors we will specialize with. It must cover the whole type, so
// we add `Missing` to represent the missing ones. This is explained under "Constructor
// Splitting" at the top of this file.
let mut split_ctors = split_set.present;
if !(split_set.missing.is_empty()
&& (split_set.missing_empty.is_empty() || empty_arms_are_unreachable))
{
split_ctors.push(Constructor::Missing);
}

// Which empty constructors are considered missing. We ensure that
// `!missing_ctors.is_empty() => split_ctors.contains(Missing)`. The converse usually holds
// except when `!self.validity.is_known_valid()`.
let mut missing_ctors = split_set.missing;
if !can_omit_empty_arms {
missing_ctors.append(&mut split_set.missing_empty);
}

// Decide what constructors to report.
let is_integers = matches!(ctors_for_ty, ConstructorSet::Integers { .. });
let always_report_all = self.is_scrutinee && !is_integers;
// Whether we should report "Enum::A and Enum::C are missing" or "_ is missing".
let report_individual_missing_ctors = always_report_all || !all_missing;
if !missing_ctors.is_empty() && !report_individual_missing_ctors {
// Report `_` as missing.
missing_ctors = vec![Constructor::Wildcard];
} else if missing_ctors.iter().any(|c| c.is_non_exhaustive()) {
// We need to report a `_` anyway, so listing other constructors would be redundant.
// `NonExhaustive` is displayed as `_` just like `Wildcard`, but it will be picked
// up by diagnostics to add a note about why `_` is required here.
missing_ctors = vec![Constructor::NonExhaustive];
}

Ok((split_ctors, missing_ctors))
}
}

impl<Cx: TypeCx> Clone for PlaceInfo<Cx> {
Expand Down Expand Up @@ -1469,63 +1542,13 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
};
};

let ty = &place.ty.clone(); // Clone it out so we can mutate `matrix` later.
let pcx = &PlaceCtxt { cx: mcx.tycx, ty };
debug!("ty: {:?}", pcx.ty);
let ctors_for_ty = pcx.ctors_for_ty()?;

// We treat match scrutinees of type `!` or `EmptyEnum` differently.
let is_toplevel_exception =
place.is_scrutinee && matches!(ctors_for_ty, ConstructorSet::NoConstructors);
// Whether empty patterns are counted as useful or not. We only warn an empty arm unreachable if
// it is guaranteed unreachable by the opsem (i.e. if the place is `known_valid`).
let empty_arms_are_unreachable = place.validity.is_known_valid()
&& (is_toplevel_exception
|| mcx.tycx.is_exhaustive_patterns_feature_on()
|| mcx.tycx.is_min_exhaustive_patterns_feature_on());
// Whether empty patterns can be omitted for exhaustiveness. We ignore place validity in the
// toplevel exception and `exhaustive_patterns` cases for backwards compatibility.
let can_omit_empty_arms = empty_arms_are_unreachable
|| is_toplevel_exception
|| mcx.tycx.is_exhaustive_patterns_feature_on();

// Analyze the constructors present in this column.
debug!("ty: {:?}", place.ty);
let ctors = matrix.heads().map(|p| p.ctor());
let mut split_set = ctors_for_ty.split(ctors);
let all_missing = split_set.present.is_empty();
// Build the set of constructors we will specialize with. It must cover the whole type.
// We need to iterate over a full set of constructors, so we add `Missing` to represent the
// missing ones. This is explained under "Constructor Splitting" at the top of this file.
let mut split_ctors = split_set.present;
if !(split_set.missing.is_empty()
&& (split_set.missing_empty.is_empty() || empty_arms_are_unreachable))
{
split_ctors.push(Constructor::Missing);
}

// Which constructors are considered missing. We ensure that `!missing_ctors.is_empty() =>
// split_ctors.contains(Missing)`. The converse usually holds except when
// `!place_validity.is_known_valid()`.
let mut missing_ctors = split_set.missing;
if !can_omit_empty_arms {
missing_ctors.append(&mut split_set.missing_empty);
}

// Decide what constructors to report.
let is_integers = matches!(ctors_for_ty, ConstructorSet::Integers { .. });
let always_report_all = place.is_scrutinee && !is_integers;
// Whether we should report "Enum::A and Enum::C are missing" or "_ is missing".
let report_individual_missing_ctors = always_report_all || !all_missing;
if !missing_ctors.is_empty() && !report_individual_missing_ctors {
// Report `_` as missing.
missing_ctors = vec![Constructor::Wildcard];
} else if missing_ctors.iter().any(|c| c.is_non_exhaustive()) {
// We need to report a `_` anyway, so listing other constructors would be redundant.
// `NonExhaustive` is displayed as `_` just like `Wildcard`, but it will be picked
// up by diagnostics to add a note about why `_` is required here.
missing_ctors = vec![Constructor::NonExhaustive];
}
let (split_ctors, missing_ctors) = place.split_column_ctors(mcx.tycx, ctors)?;

let ty = &place.ty.clone(); // Clone it out so we can mutate `matrix` later.
let pcx = &PlaceCtxt { cx: mcx.tycx, ty };
let mut ret = WitnessMatrix::empty();
for ctor in split_ctors {
// Dig into rows that match `ctor`.
Expand Down

0 comments on commit 2b2a74c

Please sign in to comment.