Skip to content

Commit 3f10e5e

Browse files
committed
Auto merge of #120331 - Nadrieril:no-arena, r=<try>
Experiment: use a plain `Vec` in `DeconstructedPat` The use of an arena-allocated slice in `DeconstructedPat` dates to when we needed the arena anyway for lifetime reasons. Now that we don't, I'm thinking that if `thir::Pat` can use plain old `Vec`s, maybe so can I. r? `@ghost`
2 parents 7ffc697 + 7d27cad commit 3f10e5e

File tree

7 files changed

+208
-217
lines changed

7 files changed

+208
-217
lines changed

compiler/rustc_pattern_analysis/src/constructor.rs

+12-14
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,6 @@ use self::MaybeInfiniteInt::*;
162162
use self::SliceKind::*;
163163

164164
use crate::index;
165-
use crate::usefulness::PlaceCtxt;
166165
use crate::TypeCx;
167166

168167
/// Whether we have seen a constructor in the column or not.
@@ -718,21 +717,20 @@ impl<Cx: TypeCx> Constructor<Cx> {
718717

719718
/// The number of fields for this constructor. This must be kept in sync with
720719
/// `Fields::wildcards`.
721-
pub(crate) fn arity(&self, pcx: &PlaceCtxt<'_, Cx>) -> usize {
722-
pcx.ctor_arity(self)
720+
pub(crate) fn arity(&self, cx: &Cx, ty: &Cx::Ty) -> usize {
721+
cx.ctor_arity(self, ty)
723722
}
724723

725724
/// Returns whether `self` is covered by `other`, i.e. whether `self` is a subset of `other`.
726725
/// For the simple cases, this is simply checking for equality. For the "grouped" constructors,
727726
/// this checks for inclusion.
728727
// We inline because this has a single call site in `Matrix::specialize_constructor`.
729728
#[inline]
730-
pub(crate) fn is_covered_by(&self, pcx: &PlaceCtxt<'_, Cx>, other: &Self) -> bool {
729+
pub(crate) fn is_covered_by(&self, cx: &Cx, other: &Self) -> bool {
731730
match (self, other) {
732-
(Wildcard, _) => pcx
733-
.mcx
734-
.tycx
735-
.bug(format_args!("Constructor splitting should not have returned `Wildcard`")),
731+
(Wildcard, _) => {
732+
cx.bug(format_args!("Constructor splitting should not have returned `Wildcard`"))
733+
}
736734
// Wildcards cover anything
737735
(_, Wildcard) => true,
738736
// Only a wildcard pattern can match these special constructors.
@@ -773,7 +771,7 @@ impl<Cx: TypeCx> Constructor<Cx> {
773771
(Opaque(self_id), Opaque(other_id)) => self_id == other_id,
774772
(Opaque(..), _) | (_, Opaque(..)) => false,
775773

776-
_ => pcx.mcx.tycx.bug(format_args!(
774+
_ => cx.bug(format_args!(
777775
"trying to compare incompatible constructors {self:?} and {other:?}"
778776
)),
779777
}
@@ -850,10 +848,10 @@ pub enum ConstructorSet<Cx: TypeCx> {
850848
/// of the `ConstructorSet` for the type, yet if we forgot to include them in `present` we would be
851849
/// ignoring any row with `Opaque`s in the algorithm. Hence the importance of point 4.
852850
#[derive(Debug)]
853-
pub(crate) struct SplitConstructorSet<Cx: TypeCx> {
854-
pub(crate) present: SmallVec<[Constructor<Cx>; 1]>,
855-
pub(crate) missing: Vec<Constructor<Cx>>,
856-
pub(crate) missing_empty: Vec<Constructor<Cx>>,
851+
pub struct SplitConstructorSet<Cx: TypeCx> {
852+
pub present: SmallVec<[Constructor<Cx>; 1]>,
853+
pub missing: Vec<Constructor<Cx>>,
854+
pub missing_empty: Vec<Constructor<Cx>>,
857855
}
858856

859857
impl<Cx: TypeCx> ConstructorSet<Cx> {
@@ -862,7 +860,7 @@ impl<Cx: TypeCx> ConstructorSet<Cx> {
862860
/// or slices. This can get subtle; see [`SplitConstructorSet`] for details of this operation
863861
/// and its invariants.
864862
#[instrument(level = "debug", skip(self, ctors), ret)]
865-
pub(crate) fn split<'a>(
863+
pub fn split<'a>(
866864
&self,
867865
ctors: impl Iterator<Item = &'a Constructor<Cx>> + Clone,
868866
) -> SplitConstructorSet<Cx>

compiler/rustc_pattern_analysis/src/lib.rs

+9-17
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub mod errors;
66
#[cfg(feature = "rustc")]
77
pub(crate) mod lints;
88
pub mod pat;
9+
pub mod pat_column;
910
#[cfg(feature = "rustc")]
1011
pub mod rustc;
1112
pub mod usefulness;
@@ -67,8 +68,9 @@ use rustc_span::ErrorGuaranteed;
6768

6869
use crate::constructor::{Constructor, ConstructorSet, IntRange};
6970
#[cfg(feature = "rustc")]
70-
use crate::lints::{lint_nonexhaustive_missing_variants, PatternColumn};
71+
use crate::lints::lint_nonexhaustive_missing_variants;
7172
use crate::pat::DeconstructedPat;
73+
use crate::pat_column::PatternColumn;
7274
#[cfg(feature = "rustc")]
7375
use crate::rustc::RustcMatchCheckCtxt;
7476
#[cfg(feature = "rustc")]
@@ -109,7 +111,7 @@ pub trait TypeCx: Sized + fmt::Debug {
109111
fn ctors_for_ty(&self, ty: &Self::Ty) -> Result<ConstructorSet<Self>, Self::Error>;
110112

111113
/// Best-effort `Debug` implementation.
112-
fn debug_pat(f: &mut fmt::Formatter<'_>, pat: &DeconstructedPat<'_, Self>) -> fmt::Result;
114+
fn debug_pat(f: &mut fmt::Formatter<'_>, pat: &DeconstructedPat<Self>) -> fmt::Result;
113115

114116
/// Raise a bug.
115117
fn bug(&self, fmt: fmt::Arguments<'_>) -> !;
@@ -119,27 +121,19 @@ pub trait TypeCx: Sized + fmt::Debug {
119121
/// The default implementation does nothing.
120122
fn lint_overlapping_range_endpoints(
121123
&self,
122-
_pat: &DeconstructedPat<'_, Self>,
124+
_pat: &DeconstructedPat<Self>,
123125
_overlaps_on: IntRange,
124-
_overlaps_with: &[&DeconstructedPat<'_, Self>],
126+
_overlaps_with: &[&DeconstructedPat<Self>],
125127
) {
126128
}
127129
}
128130

129-
/// Context that provides information global to a match.
130-
#[derive(derivative::Derivative)]
131-
#[derivative(Clone(bound = ""), Copy(bound = ""))]
132-
pub struct MatchCtxt<'a, Cx: TypeCx> {
133-
/// The context for type information.
134-
pub tycx: &'a Cx,
135-
}
136-
137131
/// The arm of a match expression.
138132
#[derive(Debug)]
139133
#[derive(derivative::Derivative)]
140134
#[derivative(Clone(bound = ""), Copy(bound = ""))]
141135
pub struct MatchArm<'p, Cx: TypeCx> {
142-
pub pat: &'p DeconstructedPat<'p, Cx>,
136+
pub pat: &'p DeconstructedPat<Cx>,
143137
pub has_guard: bool,
144138
pub arm_data: Cx::ArmData,
145139
}
@@ -154,15 +148,13 @@ pub fn analyze_match<'p, 'tcx>(
154148
) -> Result<rustc::UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
155149
let scrut_ty = tycx.reveal_opaque_ty(scrut_ty);
156150
let scrut_validity = ValidityConstraint::from_bool(tycx.known_valid_scrutinee);
157-
let cx = MatchCtxt { tycx };
158-
159-
let report = compute_match_usefulness(cx, arms, scrut_ty, scrut_validity)?;
151+
let report = compute_match_usefulness(tycx, arms, scrut_ty, scrut_validity)?;
160152

161153
// Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
162154
// `if let`s. Only run if the match is exhaustive otherwise the error is redundant.
163155
if tycx.refutable && report.non_exhaustiveness_witnesses.is_empty() {
164156
let pat_column = PatternColumn::new(arms);
165-
lint_nonexhaustive_missing_variants(cx, arms, &pat_column, scrut_ty)?;
157+
lint_nonexhaustive_missing_variants(tycx, arms, &pat_column, scrut_ty)?;
166158
}
167159

168160
Ok(report)

compiler/rustc_pattern_analysis/src/lints.rs

+17-103
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,24 @@
11
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
22
use rustc_span::ErrorGuaranteed;
33

4+
use crate::constructor::Constructor;
45
use crate::errors::{NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Uncovered};
5-
use crate::pat::PatOrWild;
6-
use crate::rustc::{
7-
Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RevealedTy, RustcMatchCheckCtxt,
8-
SplitConstructorSet, WitnessPat,
9-
};
10-
11-
/// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that
12-
/// inspect the same subvalue/place".
13-
/// This is used to traverse patterns column-by-column for lints. Despite similarities with the
14-
/// algorithm in [`crate::usefulness`], this does a different traversal. Notably this is linear in
15-
/// the depth of patterns, whereas `compute_exhaustiveness_and_usefulness` is worst-case exponential
16-
/// (exhaustiveness is NP-complete). The core difference is that we treat sub-columns separately.
17-
///
18-
/// This must not contain an or-pattern. `expand_and_push` takes care to expand them.
19-
///
20-
/// This is not used in the usefulness algorithm; only in lints.
21-
#[derive(Debug)]
22-
pub(crate) struct PatternColumn<'p, 'tcx> {
23-
patterns: Vec<&'p DeconstructedPat<'p, 'tcx>>,
24-
}
25-
26-
impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
27-
pub(crate) fn new(arms: &[MatchArm<'p, 'tcx>]) -> Self {
28-
let patterns = Vec::with_capacity(arms.len());
29-
let mut column = PatternColumn { patterns };
30-
for arm in arms {
31-
column.expand_and_push(PatOrWild::Pat(arm.pat));
32-
}
33-
column
34-
}
35-
/// Pushes a pattern onto the column, expanding any or-patterns into its subpatterns.
36-
/// Internal method, prefer [`PatternColumn::new`].
37-
fn expand_and_push(&mut self, pat: PatOrWild<'p, RustcMatchCheckCtxt<'p, 'tcx>>) {
38-
// We flatten or-patterns and skip algorithm-generated wildcards.
39-
if pat.is_or_pat() {
40-
self.patterns.extend(
41-
pat.flatten_or_pat().into_iter().filter_map(|pat_or_wild| pat_or_wild.as_pat()),
42-
)
43-
} else if let Some(pat) = pat.as_pat() {
44-
self.patterns.push(pat)
45-
}
46-
}
47-
48-
fn head_ty(&self) -> Option<RevealedTy<'tcx>> {
49-
self.patterns.first().map(|pat| *pat.ty())
50-
}
51-
52-
/// Do constructor splitting on the constructors of the column.
53-
fn analyze_ctors(
54-
&self,
55-
pcx: &PlaceCtxt<'_, 'p, 'tcx>,
56-
) -> Result<SplitConstructorSet<'p, 'tcx>, ErrorGuaranteed> {
57-
let column_ctors = self.patterns.iter().map(|p| p.ctor());
58-
let ctors_for_ty = &pcx.ctors_for_ty()?;
59-
Ok(ctors_for_ty.split(column_ctors))
60-
}
61-
62-
/// Does specialization: given a constructor, this takes the patterns from the column that match
63-
/// the constructor, and outputs their fields.
64-
/// This returns one column per field of the constructor. They usually all have the same length
65-
/// (the number of patterns in `self` that matched `ctor`), except that we expand or-patterns
66-
/// which may change the lengths.
67-
fn specialize(
68-
&self,
69-
pcx: &PlaceCtxt<'_, 'p, 'tcx>,
70-
ctor: &Constructor<'p, 'tcx>,
71-
) -> Vec<PatternColumn<'p, 'tcx>> {
72-
let arity = ctor.arity(pcx);
73-
if arity == 0 {
74-
return Vec::new();
75-
}
76-
77-
// We specialize the column by `ctor`. This gives us `arity`-many columns of patterns. These
78-
// columns may have different lengths in the presence of or-patterns (this is why we can't
79-
// reuse `Matrix`).
80-
let mut specialized_columns: Vec<_> =
81-
(0..arity).map(|_| Self { patterns: Vec::new() }).collect();
82-
let relevant_patterns =
83-
self.patterns.iter().filter(|pat| ctor.is_covered_by(pcx, pat.ctor()));
84-
for pat in relevant_patterns {
85-
let specialized = pat.specialize(ctor, arity);
86-
for (subpat, column) in specialized.into_iter().zip(&mut specialized_columns) {
87-
column.expand_and_push(subpat);
88-
}
89-
}
90-
specialized_columns
91-
}
92-
}
6+
use crate::pat_column::PatternColumn;
7+
use crate::rustc::{RevealedTy, RustcMatchCheckCtxt, WitnessPat};
8+
use crate::MatchArm;
939

9410
/// Traverse the patterns to collect any variants of a non_exhaustive enum that fail to be mentioned
9511
/// in a given column.
9612
#[instrument(level = "debug", skip(cx), ret)]
9713
fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
98-
cx: MatchCtxt<'a, 'p, 'tcx>,
99-
column: &PatternColumn<'p, 'tcx>,
14+
cx: &RustcMatchCheckCtxt<'p, 'tcx>,
15+
column: &PatternColumn<'p, RustcMatchCheckCtxt<'p, 'tcx>>,
10016
) -> Result<Vec<WitnessPat<'p, 'tcx>>, ErrorGuaranteed> {
101-
let Some(ty) = column.head_ty() else {
17+
let Some(&ty) = column.head_ty() else {
10218
return Ok(Vec::new());
10319
};
104-
let pcx = &PlaceCtxt::new_dummy(cx, &ty);
10520

106-
let set = column.analyze_ctors(pcx)?;
21+
let set = column.analyze_ctors(cx, &ty)?;
10722
if set.present.is_empty() {
10823
// We can't consistently handle the case where no constructors are present (since this would
10924
// require digging deep through any type in case there's a non_exhaustive enum somewhere),
@@ -112,20 +27,20 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
11227
}
11328

11429
let mut witnesses = Vec::new();
115-
if cx.tycx.is_foreign_non_exhaustive_enum(ty) {
30+
if cx.is_foreign_non_exhaustive_enum(ty) {
11631
witnesses.extend(
11732
set.missing
11833
.into_iter()
11934
// This will list missing visible variants.
12035
.filter(|c| !matches!(c, Constructor::Hidden | Constructor::NonExhaustive))
121-
.map(|missing_ctor| WitnessPat::wild_from_ctor(pcx, missing_ctor)),
36+
.map(|missing_ctor| WitnessPat::wild_from_ctor(cx, missing_ctor, ty)),
12237
)
12338
}
12439

12540
// Recurse into the fields.
12641
for ctor in set.present {
127-
let specialized_columns = column.specialize(pcx, &ctor);
128-
let wild_pat = WitnessPat::wild_from_ctor(pcx, ctor);
42+
let specialized_columns = column.specialize(cx, &ty, &ctor);
43+
let wild_pat = WitnessPat::wild_from_ctor(cx, ctor, ty);
12944
for (i, col_i) in specialized_columns.iter().enumerate() {
13045
// Compute witnesses for each column.
13146
let wits_for_col_i = collect_nonexhaustive_missing_variants(cx, col_i)?;
@@ -141,18 +56,17 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
14156
Ok(witnesses)
14257
}
14358

144-
pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
145-
cx: MatchCtxt<'a, 'p, 'tcx>,
146-
arms: &[MatchArm<'p, 'tcx>],
147-
pat_column: &PatternColumn<'p, 'tcx>,
59+
pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>(
60+
rcx: &RustcMatchCheckCtxt<'p, 'tcx>,
61+
arms: &[MatchArm<'p, RustcMatchCheckCtxt<'p, 'tcx>>],
62+
pat_column: &PatternColumn<'p, RustcMatchCheckCtxt<'p, 'tcx>>,
14863
scrut_ty: RevealedTy<'tcx>,
14964
) -> Result<(), ErrorGuaranteed> {
150-
let rcx: &RustcMatchCheckCtxt<'_, '_> = cx.tycx;
15165
if !matches!(
15266
rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, rcx.match_lint_level).0,
15367
rustc_session::lint::Level::Allow
15468
) {
155-
let witnesses = collect_nonexhaustive_missing_variants(cx, pat_column)?;
69+
let witnesses = collect_nonexhaustive_missing_variants(rcx, pat_column)?;
15670
if !witnesses.is_empty() {
15771
// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
15872
// is not exhaustive enough.

0 commit comments

Comments
 (0)