diff options
Diffstat (limited to 'compiler/rustc_pattern_analysis/src')
| -rw-r--r-- | compiler/rustc_pattern_analysis/src/constructor.rs | 19 | ||||
| -rw-r--r-- | compiler/rustc_pattern_analysis/src/lib.rs | 8 | ||||
| -rw-r--r-- | compiler/rustc_pattern_analysis/src/lints.rs | 8 | ||||
| -rw-r--r-- | compiler/rustc_pattern_analysis/src/pat.rs | 12 | ||||
| -rw-r--r-- | compiler/rustc_pattern_analysis/src/rustc.rs | 22 | ||||
| -rw-r--r-- | compiler/rustc_pattern_analysis/src/usefulness.rs | 72 |
6 files changed, 60 insertions, 81 deletions
diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 76098505b79..eba71a23435 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -861,12 +861,14 @@ impl<Cx: TypeCx> ConstructorSet<Cx> { /// any) are missing; 2/ split constructors to handle non-trivial intersections e.g. on ranges /// or slices. This can get subtle; see [`SplitConstructorSet`] for details of this operation /// and its invariants. - #[instrument(level = "debug", skip(self, pcx, ctors), ret)] + #[instrument(level = "debug", skip(self, ctors), ret)] pub(crate) fn split<'a>( &self, - pcx: &PlaceCtxt<'a, Cx>, ctors: impl Iterator<Item = &'a Constructor<Cx>> + Clone, - ) -> SplitConstructorSet<Cx> { + ) -> SplitConstructorSet<Cx> + where + Cx: 'a, + { let mut present: SmallVec<[_; 1]> = SmallVec::new(); // Empty constructors found missing. let mut missing_empty = Vec::new(); @@ -1006,17 +1008,6 @@ impl<Cx: TypeCx> ConstructorSet<Cx> { } } - // We have now grouped all the constructors into 3 buckets: present, missing, missing_empty. - // In the absence of the `exhaustive_patterns` feature however, we don't count nested empty - // types as empty. Only non-nested `!` or `enum Foo {}` are considered empty. - if !pcx.mcx.tycx.is_exhaustive_patterns_feature_on() - && !(pcx.is_scrutinee && matches!(self, Self::NoConstructors)) - { - // Treat all missing constructors as nonempty. - // This clears `missing_empty`. - missing.append(&mut missing_empty); - } - SplitConstructorSet { present, missing, missing_empty } } } diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 21fa8e68d82..4fd01b5e638 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -82,7 +82,7 @@ impl<'a, T: ?Sized> Captures<'a> for T {} /// Most of the crate is parameterized on a type that implements this trait. pub trait TypeCx: Sized + fmt::Debug { /// The type of a pattern. - type Ty: Copy + Clone + fmt::Debug; // FIXME: remove Copy + type Ty: Clone + fmt::Debug; /// Errors that can abort analysis. type Error: fmt::Debug; /// The index of an enum variant. @@ -97,16 +97,16 @@ pub trait TypeCx: Sized + fmt::Debug { fn is_exhaustive_patterns_feature_on(&self) -> bool; /// The number of fields for this constructor. - fn ctor_arity(&self, ctor: &Constructor<Self>, ty: Self::Ty) -> usize; + fn ctor_arity(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> usize; /// The types of the fields for this constructor. The result must have a length of /// `ctor_arity()`. - fn ctor_sub_tys(&self, ctor: &Constructor<Self>, ty: Self::Ty) -> &[Self::Ty]; + fn ctor_sub_tys(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> &[Self::Ty]; /// The set of all the constructors for `ty`. /// /// This must follow the invariants of `ConstructorSet` - fn ctors_for_ty(&self, ty: Self::Ty) -> Result<ConstructorSet<Self>, Self::Error>; + fn ctors_for_ty(&self, ty: &Self::Ty) -> Result<ConstructorSet<Self>, Self::Error>; /// Best-effort `Debug` implementation. fn debug_pat(f: &mut fmt::Formatter<'_>, pat: &DeconstructedPat<'_, Self>) -> fmt::Result; diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index 4266e2a405e..4bfe7dfb072 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -46,7 +46,7 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> { } fn head_ty(&self) -> Option<RevealedTy<'tcx>> { - self.patterns.first().map(|pat| pat.ty()) + self.patterns.first().map(|pat| *pat.ty()) } /// Do constructor splitting on the constructors of the column. @@ -56,7 +56,7 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> { ) -> Result<SplitConstructorSet<'p, 'tcx>, ErrorGuaranteed> { let column_ctors = self.patterns.iter().map(|p| p.ctor()); let ctors_for_ty = &pcx.ctors_for_ty()?; - Ok(ctors_for_ty.split(pcx, column_ctors)) + Ok(ctors_for_ty.split(column_ctors)) } /// Does specialization: given a constructor, this takes the patterns from the column that match @@ -101,7 +101,7 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>( let Some(ty) = column.head_ty() else { return Ok(Vec::new()); }; - let pcx = &PlaceCtxt::new_dummy(cx, ty); + let pcx = &PlaceCtxt::new_dummy(cx, &ty); let set = column.analyze_ctors(pcx)?; if set.present.is_empty() { @@ -158,7 +158,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>( // is not exhaustive enough. // // NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`. - rcx.tcx.emit_spanned_lint( + rcx.tcx.emit_node_span_lint( NON_EXHAUSTIVE_OMITTED_PATTERNS, rcx.match_lint_level, rcx.scrut_span, diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index 75fe59edf88..8cd0ecb073c 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -54,8 +54,8 @@ impl<'p, Cx: TypeCx> DeconstructedPat<'p, Cx> { pub fn ctor(&self) -> &Constructor<Cx> { &self.ctor } - pub fn ty(&self) -> Cx::Ty { - self.ty + pub fn ty(&self) -> &Cx::Ty { + &self.ty } /// Returns the extra data stored in a pattern. Returns `None` if the pattern is a wildcard that /// does not correspond to a user-supplied pattern. @@ -242,15 +242,15 @@ impl<Cx: TypeCx> WitnessPat<Cx> { /// `Some(_)`. pub(crate) fn wild_from_ctor(pcx: &PlaceCtxt<'_, Cx>, ctor: Constructor<Cx>) -> Self { let field_tys = pcx.ctor_sub_tys(&ctor); - let fields = field_tys.iter().map(|ty| Self::wildcard(*ty)).collect(); - Self::new(ctor, fields, pcx.ty) + let fields = field_tys.iter().cloned().map(|ty| Self::wildcard(ty)).collect(); + Self::new(ctor, fields, pcx.ty.clone()) } pub fn ctor(&self) -> &Constructor<Cx> { &self.ctor } - pub fn ty(&self) -> Cx::Ty { - self.ty + pub fn ty(&self) -> &Cx::Ty { + &self.ty } pub fn iter_fields(&self) -> impl Iterator<Item = &WitnessPat<Cx>> { diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 87e70d68c1b..d8c3c010a2a 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -766,7 +766,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { let mut subpatterns = pat.iter_fields().map(|p| Box::new(cx.hoist_witness_pat(p))); let kind = match pat.ctor() { Bool(b) => PatKind::Constant { value: mir::Const::from_bool(cx.tcx, *b) }, - IntRange(range) => return self.hoist_pat_range(range, pat.ty()), + IntRange(range) => return self.hoist_pat_range(range, *pat.ty()), Struct | Variant(_) | UnionField => match pat.ty().kind() { ty::Tuple(..) => PatKind::Leaf { subpatterns: subpatterns @@ -785,7 +785,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { RustcMatchCheckCtxt::variant_index_for_adt(&pat.ctor(), *adt_def); let variant = &adt_def.variant(variant_index); let subpatterns = cx - .list_variant_nonhidden_fields(pat.ty(), variant) + .list_variant_nonhidden_fields(*pat.ty(), variant) .zip(subpatterns) .map(|((field, _ty), pattern)| FieldPat { field, pattern }) .collect(); @@ -796,7 +796,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { PatKind::Leaf { subpatterns } } } - _ => bug!("unexpected ctor for type {:?} {:?}", pat.ctor(), pat.ty()), + _ => bug!("unexpected ctor for type {:?} {:?}", pat.ctor(), *pat.ty()), }, // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should // be careful to reconstruct the correct constant pattern here. However a string @@ -961,21 +961,21 @@ impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> { self.tcx.features().exhaustive_patterns } - fn ctor_arity(&self, ctor: &crate::constructor::Constructor<Self>, ty: Self::Ty) -> usize { - self.ctor_arity(ctor, ty) + fn ctor_arity(&self, ctor: &crate::constructor::Constructor<Self>, ty: &Self::Ty) -> usize { + self.ctor_arity(ctor, *ty) } fn ctor_sub_tys( &self, ctor: &crate::constructor::Constructor<Self>, - ty: Self::Ty, + ty: &Self::Ty, ) -> &[Self::Ty] { - self.ctor_sub_tys(ctor, ty) + self.ctor_sub_tys(ctor, *ty) } fn ctors_for_ty( &self, - ty: Self::Ty, + ty: &Self::Ty, ) -> Result<crate::constructor::ConstructorSet<Self>, Self::Error> { - self.ctors_for_ty(ty) + self.ctors_for_ty(*ty) } fn debug_pat( @@ -994,14 +994,14 @@ impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> { overlaps_on: IntRange, overlaps_with: &[&crate::pat::DeconstructedPat<'_, Self>], ) { - let overlap_as_pat = self.hoist_pat_range(&overlaps_on, pat.ty()); + let overlap_as_pat = self.hoist_pat_range(&overlaps_on, *pat.ty()); let overlaps: Vec<_> = overlaps_with .iter() .map(|pat| pat.data().unwrap().span) .map(|span| errors::Overlap { range: overlap_as_pat.clone(), span }) .collect(); let pat_span = pat.data().unwrap().span; - self.tcx.emit_spanned_lint( + self.tcx.emit_node_span_lint( lint::builtin::OVERLAPPING_RANGE_ENDPOINTS, self.match_lint_level, pat_span, diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index c7894994213..d7852a2b2cb 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -736,16 +736,15 @@ pub(crate) struct PlaceCtxt<'a, Cx: TypeCx> { #[derivative(Debug = "ignore")] pub(crate) mcx: MatchCtxt<'a, Cx>, /// Type of the place under investigation. - pub(crate) ty: Cx::Ty, - /// Whether the place is the original scrutinee place, as opposed to a subplace of it. - pub(crate) is_scrutinee: bool, + #[derivative(Clone(clone_with = "Clone::clone"))] // See rust-derivative#90 + pub(crate) ty: &'a Cx::Ty, } impl<'a, Cx: TypeCx> PlaceCtxt<'a, Cx> { /// A `PlaceCtxt` when code other than `is_useful` needs one. #[cfg_attr(not(feature = "rustc"), allow(dead_code))] - pub(crate) fn new_dummy(mcx: MatchCtxt<'a, Cx>, ty: Cx::Ty) -> Self { - PlaceCtxt { mcx, ty, is_scrutinee: false } + pub(crate) fn new_dummy(mcx: MatchCtxt<'a, Cx>, ty: &'a Cx::Ty) -> Self { + PlaceCtxt { mcx, ty } } pub(crate) fn ctor_arity(&self, ctor: &Constructor<Cx>) -> usize { @@ -768,9 +767,6 @@ impl<'a, Cx: TypeCx> PlaceCtxt<'a, Cx> { pub enum ValidityConstraint { ValidOnly, MaybeInvalid, - /// Option for backwards compatibility: the place is not known to be valid but we allow omitting - /// `useful && !reachable` arms anyway. - MaybeInvalidButAllowOmittingArms, } impl ValidityConstraint { @@ -778,20 +774,9 @@ impl ValidityConstraint { if is_valid_only { ValidOnly } else { MaybeInvalid } } - fn allow_omitting_side_effecting_arms(self) -> Self { - match self { - MaybeInvalid | MaybeInvalidButAllowOmittingArms => MaybeInvalidButAllowOmittingArms, - // There are no side-effecting empty arms here, nothing to do. - ValidOnly => ValidOnly, - } - } - fn is_known_valid(self) -> bool { matches!(self, ValidOnly) } - fn allows_omitting_empty_arms(self) -> bool { - matches!(self, ValidOnly | MaybeInvalidButAllowOmittingArms) - } /// If the place has validity given by `self` and we read that the value at the place has /// constructor `ctor`, this computes what we can assume about the validity of the constructor @@ -814,7 +799,7 @@ impl fmt::Display for ValidityConstraint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let s = match self { ValidOnly => "✓", - MaybeInvalid | MaybeInvalidButAllowOmittingArms => "?", + MaybeInvalid => "?", }; write!(f, "{s}") } @@ -1039,8 +1024,8 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> { matrix } - fn head_ty(&self) -> Option<Cx::Ty> { - self.place_ty.first().copied() + fn head_ty(&self) -> Option<&Cx::Ty> { + self.place_ty.first() } fn column_count(&self) -> usize { self.place_ty.len() @@ -1074,7 +1059,7 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> { let ctor_sub_tys = pcx.ctor_sub_tys(ctor); let arity = ctor_sub_tys.len(); let specialized_place_ty = - ctor_sub_tys.iter().chain(self.place_ty[1..].iter()).copied().collect(); + ctor_sub_tys.iter().chain(self.place_ty[1..].iter()).cloned().collect(); let ctor_sub_validity = self.place_validity[0].specialize(ctor); let specialized_place_validity = std::iter::repeat(ctor_sub_validity) .take(arity) @@ -1230,7 +1215,7 @@ impl<Cx: TypeCx> WitnessStack<Cx> { let len = self.0.len(); let arity = ctor.arity(pcx); let fields = self.0.drain((len - arity)..).rev().collect(); - let pat = WitnessPat::new(ctor.clone(), fields, pcx.ty); + let pat = WitnessPat::new(ctor.clone(), fields, pcx.ty.clone()); self.0.push(pat); } } @@ -1426,7 +1411,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( return Ok(WitnessMatrix::empty()); } - let Some(ty) = matrix.head_ty() else { + let Some(ty) = matrix.head_ty().cloned() else { // The base case: there are no columns in the matrix. We are morally pattern-matching on (). // A row is useful iff it has no (unguarded) rows above it. let mut useful = true; // Whether the next row is useful. @@ -1447,41 +1432,44 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( }; debug!("ty: {ty:?}"); - let pcx = &PlaceCtxt { mcx, ty, is_scrutinee: is_top_level }; + let pcx = &PlaceCtxt { mcx, ty: &ty }; + let ctors_for_ty = pcx.ctors_for_ty()?; // Whether the place/column we are inspecting is known to contain valid data. let place_validity = matrix.place_validity[0]; - // For backwards compability we allow omitting some empty arms that we ideally shouldn't. - let place_validity = place_validity.allow_omitting_side_effecting_arms(); + // We treat match scrutinees of type `!` or `EmptyEnum` differently. + let is_toplevel_exception = + is_top_level && matches!(ctors_for_ty, ConstructorSet::NoConstructors); + // Whether empty patterns can be omitted for exhaustiveness. + let can_omit_empty_arms = is_toplevel_exception || mcx.tycx.is_exhaustive_patterns_feature_on(); + // Whether empty patterns are counted as useful or not. + let empty_arms_are_unreachable = place_validity.is_known_valid() && can_omit_empty_arms; // Analyze the constructors present in this column. let ctors = matrix.heads().map(|p| p.ctor()); - let ctors_for_ty = pcx.ctors_for_ty()?; - let is_integers = matches!(ctors_for_ty, ConstructorSet::Integers { .. }); // For diagnostics. - let split_set = ctors_for_ty.split(pcx, ctors); + 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() { - // 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. - split_ctors.push(Constructor::Missing); - } else if !split_set.missing_empty.is_empty() && !place_validity.is_known_valid() { - // The missing empty constructors are reachable if the place can contain invalid data. + if !(split_set.missing.is_empty() + && (split_set.missing_empty.is_empty() || empty_arms_are_unreachable)) + { split_ctors.push(Constructor::Missing); } // Decide what constructors to report. + let is_integers = matches!(ctors_for_ty, ConstructorSet::Integers { .. }); let always_report_all = is_top_level && !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; // Which constructors are considered missing. We ensure that `!missing_ctors.is_empty() => - // split_ctors.contains(Missing)`. The converse usually holds except in the - // `MaybeInvalidButAllowOmittingArms` backwards-compatibility case. + // split_ctors.contains(Missing)`. The converse usually holds except when + // `!place_validity.is_known_valid()`. let mut missing_ctors = split_set.missing; - if !place_validity.allows_omitting_empty_arms() { - missing_ctors.extend(split_set.missing_empty); + if !can_omit_empty_arms { + missing_ctors.append(&mut split_set.missing_empty); } let mut ret = WitnessMatrix::empty(); |
