Skip to content

Commit 4bcf66f

Browse files
committed
Introduce MatchCtxt
1 parent 60ea14b commit 4bcf66f

File tree

6 files changed

+93
-77
lines changed

6 files changed

+93
-77
lines changed

Diff for: compiler/rustc_pattern_analysis/src/constructor.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,7 @@ impl<Cx: MatchCx> Constructor<Cx> {
718718
/// The number of fields for this constructor. This must be kept in sync with
719719
/// `Fields::wildcards`.
720720
pub(crate) fn arity(&self, pcx: &PlaceCtxt<'_, '_, Cx>) -> usize {
721-
pcx.cx.ctor_arity(self, pcx.ty)
721+
pcx.ctor_arity(self)
722722
}
723723

724724
/// Returns whether `self` is covered by `other`, i.e. whether `self` is a subset of `other`.
@@ -729,7 +729,8 @@ impl<Cx: MatchCx> Constructor<Cx> {
729729
pub(crate) fn is_covered_by<'p>(&self, pcx: &PlaceCtxt<'_, 'p, Cx>, other: &Self) -> bool {
730730
match (self, other) {
731731
(Wildcard, _) => pcx
732-
.cx
732+
.mcx
733+
.tycx
733734
.bug(format_args!("Constructor splitting should not have returned `Wildcard`")),
734735
// Wildcards cover anything
735736
(_, Wildcard) => true,
@@ -771,7 +772,7 @@ impl<Cx: MatchCx> Constructor<Cx> {
771772
(Opaque(self_id), Opaque(other_id)) => self_id == other_id,
772773
(Opaque(..), _) | (_, Opaque(..)) => false,
773774

774-
_ => pcx.cx.bug(format_args!(
775+
_ => pcx.mcx.tycx.bug(format_args!(
775776
"trying to compare incompatible constructors {self:?} and {other:?}"
776777
)),
777778
}
@@ -1007,7 +1008,7 @@ impl<Cx: MatchCx> ConstructorSet<Cx> {
10071008
// We have now grouped all the constructors into 3 buckets: present, missing, missing_empty.
10081009
// In the absence of the `exhaustive_patterns` feature however, we don't count nested empty
10091010
// types as empty. Only non-nested `!` or `enum Foo {}` are considered empty.
1010-
if !pcx.cx.is_exhaustive_patterns_feature_on()
1011+
if !pcx.mcx.tycx.is_exhaustive_patterns_feature_on()
10111012
&& !(pcx.is_scrutinee && matches!(self, Self::NoConstructors))
10121013
{
10131014
// Treat all missing constructors as nonempty.

Diff for: compiler/rustc_pattern_analysis/src/lib.rs

+34-12
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,19 @@ use crate::rustc::RustcMatchCheckCtxt;
3636
#[cfg(feature = "rustc")]
3737
use crate::usefulness::{compute_match_usefulness, ValidityConstraint};
3838

39+
// It's not possible to only enable the `typed_arena` dependency when the `rustc` feature is off, so
40+
// we use another feature instead. The crate won't compile if one of these isn't enabled.
41+
#[cfg(feature = "rustc")]
42+
pub(crate) use rustc_arena::TypedArena;
43+
#[cfg(feature = "stable")]
44+
pub(crate) use typed_arena::Arena as TypedArena;
45+
46+
pub trait Captures<'a> {}
47+
impl<'a, T: ?Sized> Captures<'a> for T {}
48+
49+
/// Context that provides type information about constructors.
50+
///
51+
/// Most of the crate is parameterized on a type that implements this trait.
3952
pub trait MatchCx: Sized + Clone + fmt::Debug {
4053
/// The type of a pattern.
4154
type Ty: Copy + Clone + fmt::Debug; // FIXME: remove Copy
@@ -71,42 +84,51 @@ pub trait MatchCx: Sized + Clone + fmt::Debug {
7184
fn bug(&self, fmt: fmt::Arguments<'_>) -> !;
7285
}
7386

87+
/// Context that provides information global to a match.
88+
#[derive(Clone)]
89+
pub struct MatchCtxt<'a, 'p, Cx: MatchCx> {
90+
/// The context for type information.
91+
pub tycx: &'a Cx,
92+
/// An arena to store the wildcards we produce during analysis.
93+
pub wildcard_arena: &'a TypedArena<DeconstructedPat<'p, Cx>>,
94+
}
95+
96+
impl<'a, 'p, Cx: MatchCx> Copy for MatchCtxt<'a, 'p, Cx> {}
97+
7498
/// The arm of a match expression.
7599
#[derive(Clone, Debug)]
76100
pub struct MatchArm<'p, Cx: MatchCx> {
77-
/// The pattern must have been lowered through `check_match::MatchVisitor::lower_pattern`.
78101
pub pat: &'p DeconstructedPat<'p, Cx>,
79102
pub has_guard: bool,
80103
pub arm_data: Cx::ArmData,
81104
}
82105

83106
impl<'p, Cx: MatchCx> Copy for MatchArm<'p, Cx> {}
84107

85-
pub trait Captures<'a> {}
86-
impl<'a, T: ?Sized> Captures<'a> for T {}
87-
88108
/// The entrypoint for this crate. Computes whether a match is exhaustive and which of its arms are
89109
/// useful, and runs some lints.
90110
#[cfg(feature = "rustc")]
91111
pub fn analyze_match<'p, 'tcx>(
92-
cx: &RustcMatchCheckCtxt<'p, 'tcx>,
112+
tycx: &RustcMatchCheckCtxt<'p, 'tcx>,
93113
arms: &[rustc::MatchArm<'p, 'tcx>],
94114
scrut_ty: Ty<'tcx>,
95115
) -> rustc::UsefulnessReport<'p, 'tcx> {
96116
// Arena to store the extra wildcards we construct during analysis.
97-
let wildcard_arena = cx.pattern_arena;
98-
let pat_column = PatternColumn::new(arms);
117+
let wildcard_arena = tycx.pattern_arena;
118+
let scrut_validity = ValidityConstraint::from_bool(tycx.known_valid_scrutinee);
119+
let cx = MatchCtxt { tycx, wildcard_arena };
120+
121+
let report = compute_match_usefulness(cx, arms, scrut_ty, scrut_validity);
99122

100-
let scrut_validity = ValidityConstraint::from_bool(cx.known_valid_scrutinee);
101-
let report = compute_match_usefulness(cx, arms, scrut_ty, scrut_validity, wildcard_arena);
123+
let pat_column = PatternColumn::new(arms);
102124

103125
// Lint on ranges that overlap on their endpoints, which is likely a mistake.
104-
lint_overlapping_range_endpoints(cx, &pat_column, wildcard_arena);
126+
lint_overlapping_range_endpoints(cx, &pat_column);
105127

106128
// Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
107129
// `if let`s. Only run if the match is exhaustive otherwise the error is redundant.
108-
if cx.refutable && report.non_exhaustiveness_witnesses.is_empty() {
109-
lint_nonexhaustive_missing_variants(cx, arms, &pat_column, scrut_ty, wildcard_arena)
130+
if tycx.refutable && report.non_exhaustiveness_witnesses.is_empty() {
131+
lint_nonexhaustive_missing_variants(cx, arms, &pat_column, scrut_ty)
110132
}
111133

112134
report

Diff for: compiler/rustc_pattern_analysis/src/lints.rs

+27-29
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use rustc_arena::TypedArena;
21
use smallvec::SmallVec;
32

43
use rustc_data_structures::captures::Captures;
@@ -13,8 +12,8 @@ use crate::errors::{
1312
OverlappingRangeEndpoints, Uncovered,
1413
};
1514
use crate::rustc::{
16-
Constructor, DeconstructedPat, MatchArm, PlaceCtxt, RustcMatchCheckCtxt, SplitConstructorSet,
17-
WitnessPat,
15+
Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RustcMatchCheckCtxt,
16+
SplitConstructorSet, WitnessPat,
1817
};
1918
use crate::MatchCx;
2019

@@ -70,7 +69,7 @@ impl<'a, 'p, 'tcx> PatternColumn<'a, 'p, 'tcx> {
7069
/// Do constructor splitting on the constructors of the column.
7170
fn analyze_ctors(&self, pcx: &PlaceCtxt<'_, 'p, 'tcx>) -> SplitConstructorSet<'p, 'tcx> {
7271
let column_ctors = self.patterns.iter().map(|p| p.ctor());
73-
pcx.cx.ctors_for_ty(pcx.ty).split(pcx, column_ctors)
72+
pcx.ctors_for_ty().split(pcx, column_ctors)
7473
}
7574

7675
fn iter<'b>(&'b self) -> impl Iterator<Item = &'a DeconstructedPat<'p, 'tcx>> + Captures<'b> {
@@ -121,16 +120,15 @@ impl<'a, 'p, 'tcx> PatternColumn<'a, 'p, 'tcx> {
121120

122121
/// Traverse the patterns to collect any variants of a non_exhaustive enum that fail to be mentioned
123122
/// in a given column.
124-
#[instrument(level = "debug", skip(cx, wildcard_arena), ret)]
123+
#[instrument(level = "debug", skip(cx), ret)]
125124
fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
126-
cx: &RustcMatchCheckCtxt<'p, 'tcx>,
125+
cx: MatchCtxt<'a, 'p, 'tcx>,
127126
column: &PatternColumn<'a, 'p, 'tcx>,
128-
wildcard_arena: &TypedArena<DeconstructedPat<'p, 'tcx>>,
129127
) -> Vec<WitnessPat<'p, 'tcx>> {
130128
let Some(ty) = column.head_ty() else {
131129
return Vec::new();
132130
};
133-
let pcx = &PlaceCtxt::new_dummy(cx, ty, wildcard_arena);
131+
let pcx = &PlaceCtxt::new_dummy(cx, ty);
134132

135133
let set = column.analyze_ctors(pcx);
136134
if set.present.is_empty() {
@@ -141,7 +139,7 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
141139
}
142140

143141
let mut witnesses = Vec::new();
144-
if cx.is_foreign_non_exhaustive_enum(ty) {
142+
if cx.tycx.is_foreign_non_exhaustive_enum(ty) {
145143
witnesses.extend(
146144
set.missing
147145
.into_iter()
@@ -157,7 +155,7 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
157155
let wild_pat = WitnessPat::wild_from_ctor(pcx, ctor);
158156
for (i, col_i) in specialized_columns.iter().enumerate() {
159157
// Compute witnesses for each column.
160-
let wits_for_col_i = collect_nonexhaustive_missing_variants(cx, col_i, wildcard_arena);
158+
let wits_for_col_i = collect_nonexhaustive_missing_variants(cx, col_i);
161159
// For each witness, we build a new pattern in the shape of `ctor(_, _, wit, _, _)`,
162160
// adding enough wildcards to match `arity`.
163161
for wit in wits_for_col_i {
@@ -171,29 +169,29 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
171169
}
172170

173171
pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
174-
cx: &RustcMatchCheckCtxt<'p, 'tcx>,
172+
cx: MatchCtxt<'a, 'p, 'tcx>,
175173
arms: &[MatchArm<'p, 'tcx>],
176174
pat_column: &PatternColumn<'a, 'p, 'tcx>,
177175
scrut_ty: Ty<'tcx>,
178-
wildcard_arena: &TypedArena<DeconstructedPat<'p, 'tcx>>,
179176
) {
177+
let rcx: &RustcMatchCheckCtxt<'_, '_> = cx.tycx;
180178
if !matches!(
181-
cx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, cx.match_lint_level).0,
179+
rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, rcx.match_lint_level).0,
182180
rustc_session::lint::Level::Allow
183181
) {
184-
let witnesses = collect_nonexhaustive_missing_variants(cx, pat_column, wildcard_arena);
182+
let witnesses = collect_nonexhaustive_missing_variants(cx, pat_column);
185183
if !witnesses.is_empty() {
186184
// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
187185
// is not exhaustive enough.
188186
//
189187
// NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`.
190-
cx.tcx.emit_spanned_lint(
188+
rcx.tcx.emit_spanned_lint(
191189
NON_EXHAUSTIVE_OMITTED_PATTERNS,
192-
cx.match_lint_level,
193-
cx.scrut_span,
190+
rcx.match_lint_level,
191+
rcx.scrut_span,
194192
NonExhaustiveOmittedPattern {
195193
scrut_ty,
196-
uncovered: Uncovered::new(cx.scrut_span, cx, witnesses),
194+
uncovered: Uncovered::new(rcx.scrut_span, rcx, witnesses),
197195
},
198196
);
199197
}
@@ -203,17 +201,17 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
203201
// usage of the lint.
204202
for arm in arms {
205203
let (lint_level, lint_level_source) =
206-
cx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.arm_data);
204+
rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.arm_data);
207205
if !matches!(lint_level, rustc_session::lint::Level::Allow) {
208206
let decorator = NonExhaustiveOmittedPatternLintOnArm {
209207
lint_span: lint_level_source.span(),
210-
suggest_lint_on_match: cx.whole_match_span.map(|span| span.shrink_to_lo()),
208+
suggest_lint_on_match: rcx.whole_match_span.map(|span| span.shrink_to_lo()),
211209
lint_level: lint_level.as_str(),
212210
lint_name: "non_exhaustive_omitted_patterns",
213211
};
214212

215213
use rustc_errors::DecorateLint;
216-
let mut err = cx.tcx.sess.struct_span_warn(*arm.pat.data(), "");
214+
let mut err = rcx.tcx.sess.struct_span_warn(*arm.pat.data(), "");
217215
err.set_primary_message(decorator.msg());
218216
decorator.decorate_lint(&mut err);
219217
err.emit();
@@ -223,30 +221,30 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
223221
}
224222

225223
/// Traverse the patterns to warn the user about ranges that overlap on their endpoints.
226-
#[instrument(level = "debug", skip(cx, wildcard_arena))]
224+
#[instrument(level = "debug", skip(cx))]
227225
pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
228-
cx: &RustcMatchCheckCtxt<'p, 'tcx>,
226+
cx: MatchCtxt<'a, 'p, 'tcx>,
229227
column: &PatternColumn<'a, 'p, 'tcx>,
230-
wildcard_arena: &TypedArena<DeconstructedPat<'p, 'tcx>>,
231228
) {
232229
let Some(ty) = column.head_ty() else {
233230
return;
234231
};
235-
let pcx = &PlaceCtxt::new_dummy(cx, ty, wildcard_arena);
232+
let pcx = &PlaceCtxt::new_dummy(cx, ty);
233+
let rcx: &RustcMatchCheckCtxt<'_, '_> = cx.tycx;
236234

237235
let set = column.analyze_ctors(pcx);
238236

239237
if matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_)) {
240238
let emit_lint = |overlap: &IntRange, this_span: Span, overlapped_spans: &[Span]| {
241-
let overlap_as_pat = cx.hoist_pat_range(overlap, ty);
239+
let overlap_as_pat = rcx.hoist_pat_range(overlap, ty);
242240
let overlaps: Vec<_> = overlapped_spans
243241
.iter()
244242
.copied()
245243
.map(|span| Overlap { range: overlap_as_pat.clone(), span })
246244
.collect();
247-
cx.tcx.emit_spanned_lint(
245+
rcx.tcx.emit_spanned_lint(
248246
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
249-
cx.match_lint_level,
247+
rcx.match_lint_level,
250248
this_span,
251249
OverlappingRangeEndpoints { overlap: overlaps, range: this_span },
252250
);
@@ -291,7 +289,7 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
291289
// Recurse into the fields.
292290
for ctor in set.present {
293291
for col in column.specialize(pcx, &ctor) {
294-
lint_overlapping_range_endpoints(cx, &col, wildcard_arena);
292+
lint_overlapping_range_endpoints(cx, &col);
295293
}
296294
}
297295
}

Diff for: compiler/rustc_pattern_analysis/src/pat.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,10 @@ impl<'p, Cx: MatchCx> DeconstructedPat<'p, Cx> {
8181
other_ctor: &Constructor<Cx>,
8282
) -> SmallVec<[&'a DeconstructedPat<'p, Cx>; 2]> {
8383
let wildcard_sub_tys = || {
84-
let tys = pcx.cx.ctor_sub_tys(other_ctor, pcx.ty);
84+
let tys = pcx.ctor_sub_tys(other_ctor);
8585
tys.iter()
8686
.map(|ty| DeconstructedPat::wildcard(*ty, Cx::PatData::default()))
87-
.map(|pat| pcx.wildcard_arena.alloc(pat) as &_)
87+
.map(|pat| pcx.mcx.wildcard_arena.alloc(pat) as &_)
8888
.collect()
8989
};
9090
match (&self.ctor, other_ctor) {
@@ -179,7 +179,7 @@ impl<Cx: MatchCx> WitnessPat<Cx> {
179179
/// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern
180180
/// `Some(_)`.
181181
pub(crate) fn wild_from_ctor(pcx: &PlaceCtxt<'_, '_, Cx>, ctor: Constructor<Cx>) -> Self {
182-
let field_tys = pcx.cx.ctor_sub_tys(&ctor, pcx.ty);
182+
let field_tys = pcx.ctor_sub_tys(&ctor);
183183
let fields = field_tys.iter().map(|ty| Self::wildcard(*ty)).collect();
184184
Self::new(ctor, fields, pcx.ty)
185185
}

Diff for: compiler/rustc_pattern_analysis/src/rustc.rs

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub type ConstructorSet<'p, 'tcx> =
3131
pub type DeconstructedPat<'p, 'tcx> =
3232
crate::pat::DeconstructedPat<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
3333
pub type MatchArm<'p, 'tcx> = crate::MatchArm<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
34+
pub type MatchCtxt<'a, 'p, 'tcx> = crate::MatchCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>;
3435
pub(crate) type PlaceCtxt<'a, 'p, 'tcx> =
3536
crate::usefulness::PlaceCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>;
3637
pub(crate) type SplitConstructorSet<'p, 'tcx> =

0 commit comments

Comments
 (0)