Skip to content

Commit 71208c5

Browse files
committed
Track redundant subpatterns without interior mutability
1 parent de13a09 commit 71208c5

File tree

1 file changed

+52
-15
lines changed

1 file changed

+52
-15
lines changed

compiler/rustc_pattern_analysis/src/usefulness.rs

+52-15
Original file line numberDiff line numberDiff line change
@@ -712,9 +712,11 @@
712712
//! I (Nadrieril) prefer to put new tests in `ui/pattern/usefulness` unless there's a specific
713713
//! reason not to, for example if they crucially depend on a particular feature like `or_patterns`.
714714
715+
use rustc_hash::FxHashSet;
715716
use rustc_index::bit_set::BitSet;
716717
use smallvec::{smallvec, SmallVec};
717718
use std::fmt;
719+
use std::ops::Deref;
718720

719721
use crate::constructor::{Constructor, ConstructorSet, IntRange};
720722
use crate::pat::{DeconstructedPat, PatOrWild, WitnessPat};
@@ -729,12 +731,36 @@ pub fn ensure_sufficient_stack<R>(f: impl FnOnce() -> R) -> R {
729731
f()
730732
}
731733

734+
/// Wrapper type for by-address hashing. Comparison and hashing of the wrapped pointer type will be
735+
/// based on the address of its contents, rather than their value.
736+
struct ByAddress<T>(T);
737+
738+
impl<T: Deref> ByAddress<T> {
739+
fn addr(&self) -> *const T::Target {
740+
(&*self.0) as *const _
741+
}
742+
}
743+
/// Raw pointer hashing and comparison.
744+
impl<T: Deref> std::hash::Hash for ByAddress<T> {
745+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
746+
self.addr().hash(state)
747+
}
748+
}
749+
impl<T: Deref> PartialEq for ByAddress<T> {
750+
fn eq(&self, other: &Self) -> bool {
751+
std::ptr::eq(self.addr(), other.addr())
752+
}
753+
}
754+
impl<T: Deref> Eq for ByAddress<T> {}
755+
732756
/// Context that provides information for usefulness checking.
733-
#[derive(derivative::Derivative)]
734-
#[derivative(Clone(bound = ""), Copy(bound = ""))]
735-
pub struct UsefulnessCtxt<'a, Cx: TypeCx> {
757+
struct UsefulnessCtxt<'a, 'p, Cx: TypeCx> {
736758
/// The context for type information.
737-
pub tycx: &'a Cx,
759+
tycx: &'a Cx,
760+
/// Collect the patterns found useful during usefulness checking. This is used to lint
761+
/// unreachable (sub)patterns. We distinguish patterns by their address to avoid needing to
762+
/// inspect the contents. They'll all be distinct anyway since they carry a `Span`.
763+
useful_subpatterns: FxHashSet<ByAddress<&'p DeconstructedPat<'p, Cx>>>,
738764
}
739765

740766
/// Context that provides information local to a place under investigation.
@@ -1330,7 +1356,7 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> {
13301356
/// We can however get false negatives because exhaustiveness does not explore all cases. See the
13311357
/// section on relevancy at the top of the file.
13321358
fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>(
1333-
mcx: UsefulnessCtxt<'_, Cx>,
1359+
mcx: &mut UsefulnessCtxt<'_, 'p, Cx>,
13341360
overlap_range: IntRange,
13351361
matrix: &Matrix<'p, Cx>,
13361362
specialized_matrix: &Matrix<'p, Cx>,
@@ -1403,7 +1429,7 @@ fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>(
14031429
/// This is all explained at the top of the file.
14041430
#[instrument(level = "debug", skip(mcx, is_top_level), ret)]
14051431
fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
1406-
mcx: UsefulnessCtxt<'a, Cx>,
1432+
mcx: &mut UsefulnessCtxt<'a, 'p, Cx>,
14071433
matrix: &mut Matrix<'p, Cx>,
14081434
is_top_level: bool,
14091435
) -> Result<WitnessMatrix<Cx>, Cx::Error> {
@@ -1524,7 +1550,9 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
15241550
// Record usefulness in the patterns.
15251551
for row in matrix.rows() {
15261552
if row.useful {
1527-
row.head().set_useful();
1553+
if let PatOrWild::Pat(pat) = row.head() {
1554+
mcx.useful_subpatterns.insert(ByAddress(pat));
1555+
}
15281556
}
15291557
}
15301558

@@ -1545,12 +1573,17 @@ pub enum Usefulness<'p, Cx: TypeCx> {
15451573

15461574
/// Report whether this pattern was found useful, and its subpatterns that were not useful if any.
15471575
fn collect_pattern_usefulness<'p, Cx: TypeCx>(
1576+
useful_subpatterns: &FxHashSet<ByAddress<&'p DeconstructedPat<'p, Cx>>>,
15481577
pat: &'p DeconstructedPat<'p, Cx>,
15491578
) -> Usefulness<'p, Cx> {
1550-
fn pat_is_useful<'p, Cx: TypeCx>(pat: &'p DeconstructedPat<'p, Cx>) -> bool {
1551-
if pat.useful.get() {
1579+
fn pat_is_useful<'p, Cx: TypeCx>(
1580+
useful_subpatterns: &FxHashSet<ByAddress<&'p DeconstructedPat<'p, Cx>>>,
1581+
pat: &'p DeconstructedPat<'p, Cx>,
1582+
) -> bool {
1583+
if useful_subpatterns.contains(&ByAddress(pat)) {
15521584
true
1553-
} else if pat.is_or_pat() && pat.iter_fields().any(|f| pat_is_useful(f)) {
1585+
} else if pat.is_or_pat() && pat.iter_fields().any(|f| pat_is_useful(useful_subpatterns, f))
1586+
{
15541587
// We always expand or patterns in the matrix, so we will never see the actual
15551588
// or-pattern (the one with constructor `Or`) in the column. As such, it will not be
15561589
// marked as useful itself, only its children will. We recover this information here.
@@ -1562,7 +1595,7 @@ fn collect_pattern_usefulness<'p, Cx: TypeCx>(
15621595

15631596
let mut subpats = Vec::new();
15641597
pat.walk(&mut |p| {
1565-
if pat_is_useful(p) {
1598+
if pat_is_useful(useful_subpatterns, p) {
15661599
// The pattern is useful, so we recurse to find redundant subpatterns.
15671600
true
15681601
} else {
@@ -1572,7 +1605,11 @@ fn collect_pattern_usefulness<'p, Cx: TypeCx>(
15721605
}
15731606
});
15741607

1575-
if pat_is_useful(pat) { Usefulness::Useful(subpats) } else { Usefulness::Redundant }
1608+
if pat_is_useful(useful_subpatterns, pat) {
1609+
Usefulness::Useful(subpats)
1610+
} else {
1611+
Usefulness::Redundant
1612+
}
15761613
}
15771614

15781615
/// The output of checking a match for exhaustiveness and arm usefulness.
@@ -1592,18 +1629,18 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>(
15921629
scrut_ty: Cx::Ty,
15931630
scrut_validity: ValidityConstraint,
15941631
) -> Result<UsefulnessReport<'p, Cx>, Cx::Error> {
1595-
let cx = UsefulnessCtxt { tycx };
1632+
let mut cx = UsefulnessCtxt { tycx, useful_subpatterns: FxHashSet::default() };
15961633
let mut matrix = Matrix::new(arms, scrut_ty, scrut_validity);
15971634
let non_exhaustiveness_witnesses =
1598-
compute_exhaustiveness_and_usefulness(cx, &mut matrix, true)?;
1635+
compute_exhaustiveness_and_usefulness(&mut cx, &mut matrix, true)?;
15991636

16001637
let non_exhaustiveness_witnesses: Vec<_> = non_exhaustiveness_witnesses.single_column();
16011638
let arm_usefulness: Vec<_> = arms
16021639
.iter()
16031640
.copied()
16041641
.map(|arm| {
16051642
debug!(?arm);
1606-
let usefulness = collect_pattern_usefulness(arm.pat);
1643+
let usefulness = collect_pattern_usefulness(&cx.useful_subpatterns, arm.pat);
16071644
(arm, usefulness)
16081645
})
16091646
.collect();

0 commit comments

Comments
 (0)