Skip to content

Commit 174e73a

Browse files
committed
Auto merge of #119396 - Nadrieril:intersection-tracking, r=WaffleLapkin
Exhaustiveness: track overlapping ranges precisely The `overlapping_range_endpoints` lint has false positives, e.g. #117648. I expected that removing these false positives would have too much of a perf impact but never measured it. This PR is an experiment to see if the perf loss is manageable. r? `@ghost`
2 parents bfd799f + a24f4db commit 174e73a

File tree

7 files changed

+206
-135
lines changed

7 files changed

+206
-135
lines changed

compiler/rustc_pattern_analysis/src/lib.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,10 @@ pub fn analyze_match<'p, 'tcx>(
124124

125125
let pat_column = PatternColumn::new(arms);
126126

127-
// Lint on ranges that overlap on their endpoints, which is likely a mistake.
128-
lint_overlapping_range_endpoints(cx, &pat_column)?;
127+
// Lint ranges that overlap on their endpoints, which is likely a mistake.
128+
if !report.overlapping_range_endpoints.is_empty() {
129+
lint_overlapping_range_endpoints(cx, &report.overlapping_range_endpoints);
130+
}
129131

130132
// Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
131133
// `if let`s. Only run if the match is exhaustive otherwise the error is redundant.

compiler/rustc_pattern_analysis/src/lints.rs

+22-85
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,14 @@
1-
use smallvec::SmallVec;
2-
3-
use rustc_data_structures::captures::Captures;
4-
use rustc_middle::ty;
51
use rustc_session::lint;
62
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
7-
use rustc_span::{ErrorGuaranteed, Span};
3+
use rustc_span::ErrorGuaranteed;
84

9-
use crate::constructor::{IntRange, MaybeInfiniteInt};
105
use crate::errors::{
11-
NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Overlap,
12-
OverlappingRangeEndpoints, Uncovered,
6+
self, NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Uncovered,
137
};
148
use crate::pat::PatOrWild;
159
use crate::rustc::{
16-
Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RevealedTy, RustcMatchCheckCtxt,
17-
SplitConstructorSet, WitnessPat,
10+
self, Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RevealedTy,
11+
RustcMatchCheckCtxt, SplitConstructorSet, WitnessPat,
1812
};
1913

2014
/// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that
@@ -68,10 +62,6 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
6862
Ok(ctors_for_ty.split(pcx, column_ctors))
6963
}
7064

71-
fn iter(&self) -> impl Iterator<Item = &'p DeconstructedPat<'p, 'tcx>> + Captures<'_> {
72-
self.patterns.iter().copied()
73-
}
74-
7565
/// Does specialization: given a constructor, this takes the patterns from the column that match
7666
/// the constructor, and outputs their fields.
7767
/// This returns one column per field of the constructor. They usually all have the same length
@@ -207,78 +197,25 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
207197
Ok(())
208198
}
209199

210-
/// Traverse the patterns to warn the user about ranges that overlap on their endpoints.
211-
#[instrument(level = "debug", skip(cx))]
212200
pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
213201
cx: MatchCtxt<'a, 'p, 'tcx>,
214-
column: &PatternColumn<'p, 'tcx>,
215-
) -> Result<(), ErrorGuaranteed> {
216-
let Some(ty) = column.head_ty() else {
217-
return Ok(());
218-
};
219-
let pcx = &PlaceCtxt::new_dummy(cx, ty);
220-
let rcx: &RustcMatchCheckCtxt<'_, '_> = cx.tycx;
221-
222-
let set = column.analyze_ctors(pcx)?;
223-
224-
if matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_)) {
225-
let emit_lint = |overlap: &IntRange, this_span: Span, overlapped_spans: &[Span]| {
226-
let overlap_as_pat = rcx.hoist_pat_range(overlap, ty);
227-
let overlaps: Vec<_> = overlapped_spans
228-
.iter()
229-
.copied()
230-
.map(|span| Overlap { range: overlap_as_pat.clone(), span })
231-
.collect();
232-
rcx.tcx.emit_spanned_lint(
233-
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
234-
rcx.match_lint_level,
235-
this_span,
236-
OverlappingRangeEndpoints { overlap: overlaps, range: this_span },
237-
);
238-
};
239-
240-
// If two ranges overlapped, the split set will contain their intersection as a singleton.
241-
let split_int_ranges = set.present.iter().filter_map(|c| c.as_int_range());
242-
for overlap_range in split_int_ranges.clone() {
243-
if overlap_range.is_singleton() {
244-
let overlap: MaybeInfiniteInt = overlap_range.lo;
245-
// Ranges that look like `lo..=overlap`.
246-
let mut prefixes: SmallVec<[_; 1]> = Default::default();
247-
// Ranges that look like `overlap..=hi`.
248-
let mut suffixes: SmallVec<[_; 1]> = Default::default();
249-
// Iterate on patterns that contained `overlap`.
250-
for pat in column.iter() {
251-
let Constructor::IntRange(this_range) = pat.ctor() else { continue };
252-
let this_span = pat.data().unwrap().span;
253-
if this_range.is_singleton() {
254-
// Don't lint when one of the ranges is a singleton.
255-
continue;
256-
}
257-
if this_range.lo == overlap {
258-
// `this_range` looks like `overlap..=this_range.hi`; it overlaps with any
259-
// ranges that look like `lo..=overlap`.
260-
if !prefixes.is_empty() {
261-
emit_lint(overlap_range, this_span, &prefixes);
262-
}
263-
suffixes.push(this_span)
264-
} else if this_range.hi == overlap.plus_one() {
265-
// `this_range` looks like `this_range.lo..=overlap`; it overlaps with any
266-
// ranges that look like `overlap..=hi`.
267-
if !suffixes.is_empty() {
268-
emit_lint(overlap_range, this_span, &suffixes);
269-
}
270-
prefixes.push(this_span)
271-
}
272-
}
273-
}
274-
}
275-
} else {
276-
// Recurse into the fields.
277-
for ctor in set.present {
278-
for col in column.specialize(pcx, &ctor) {
279-
lint_overlapping_range_endpoints(cx, &col)?;
280-
}
281-
}
202+
overlapping_range_endpoints: &[rustc::OverlappingRanges<'p, 'tcx>],
203+
) {
204+
let rcx = cx.tycx;
205+
for overlap in overlapping_range_endpoints {
206+
let overlap_as_pat = rcx.hoist_pat_range(&overlap.overlaps_on, overlap.pat.ty());
207+
let overlaps: Vec<_> = overlap
208+
.overlaps_with
209+
.iter()
210+
.map(|pat| pat.data().unwrap().span)
211+
.map(|span| errors::Overlap { range: overlap_as_pat.clone(), span })
212+
.collect();
213+
let pat_span = overlap.pat.data().unwrap().span;
214+
rcx.tcx.emit_spanned_lint(
215+
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
216+
rcx.match_lint_level,
217+
pat_span,
218+
errors::OverlappingRangeEndpoints { overlap: overlaps, range: pat_span },
219+
);
282220
}
283-
Ok(())
284221
}

compiler/rustc_pattern_analysis/src/rustc.rs

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ pub type DeconstructedPat<'p, 'tcx> =
3434
crate::pat::DeconstructedPat<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
3535
pub type MatchArm<'p, 'tcx> = crate::MatchArm<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
3636
pub type MatchCtxt<'a, 'p, 'tcx> = crate::MatchCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>;
37+
pub type OverlappingRanges<'p, 'tcx> =
38+
crate::usefulness::OverlappingRanges<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
3739
pub(crate) type PlaceCtxt<'a, 'p, 'tcx> =
3840
crate::usefulness::PlaceCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>;
3941
pub(crate) type SplitConstructorSet<'p, 'tcx> =

0 commit comments

Comments
 (0)