Skip to content

Commit 8a85b7f

Browse files
committed
Track redundant subpatterns without interior mutability
1 parent 49906f0 commit 8a85b7f

File tree

1 file changed

+52
-20
lines changed

1 file changed

+52
-20
lines changed

compiler/rustc_pattern_analysis/src/usefulness.rs

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

720722
use crate::constructor::{Constructor, ConstructorSet, IntRange};
721723
use crate::pat::{DeconstructedPat, PatOrWild, WitnessPat};
@@ -730,18 +732,37 @@ pub fn ensure_sufficient_stack<R>(f: impl FnOnce() -> R) -> R {
730732
f()
731733
}
732734

733-
/// Context that provides information for usefulness checking.
734-
pub struct UsefulnessCtxt<'a, Cx: TypeCx> {
735-
/// The context for type information.
736-
pub tycx: &'a Cx,
737-
}
735+
/// Wrapper type for by-address hashing. Comparison and hashing of the wrapped pointer type will be
736+
/// based on the address of its contents, rather than their value.
737+
struct ByAddress<T>(T);
738738

739-
impl<'a, Cx: TypeCx> Copy for UsefulnessCtxt<'a, Cx> {}
740-
impl<'a, Cx: TypeCx> Clone for UsefulnessCtxt<'a, Cx> {
741-
fn clone(&self) -> Self {
742-
Self { tycx: self.tycx }
739+
impl<T: Deref> ByAddress<T> {
740+
fn addr(&self) -> *const T::Target {
741+
(&*self.0) as *const _
742+
}
743+
}
744+
/// Raw pointer hashing and comparison.
745+
impl<T: Deref> std::hash::Hash for ByAddress<T> {
746+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
747+
self.addr().hash(state)
748+
}
749+
}
750+
impl<T: Deref> PartialEq for ByAddress<T> {
751+
fn eq(&self, other: &Self) -> bool {
752+
std::ptr::eq(self.addr(), other.addr())
743753
}
744754
}
755+
impl<T: Deref> Eq for ByAddress<T> {}
756+
757+
/// Context that provides information for usefulness checking.
758+
struct UsefulnessCtxt<'a, 'p, Cx: TypeCx> {
759+
/// The context for type information.
760+
tycx: &'a Cx,
761+
/// Collect the patterns found useful during usefulness checking. This is used to lint
762+
/// unreachable (sub)patterns. We distinguish patterns by their address to avoid needing to
763+
/// inspect the contents. They'll all be distinct anyway since they carry a `Span`.
764+
useful_subpatterns: FxHashSet<ByAddress<&'p DeconstructedPat<'p, Cx>>>,
765+
}
745766

746767
/// Context that provides information local to a place under investigation.
747768
struct PlaceCtxt<'a, Cx: TypeCx> {
@@ -1371,7 +1392,7 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> {
13711392
/// We can however get false negatives because exhaustiveness does not explore all cases. See the
13721393
/// section on relevancy at the top of the file.
13731394
fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>(
1374-
mcx: UsefulnessCtxt<'_, Cx>,
1395+
mcx: &mut UsefulnessCtxt<'_, 'p, Cx>,
13751396
overlap_range: IntRange,
13761397
matrix: &Matrix<'p, Cx>,
13771398
specialized_matrix: &Matrix<'p, Cx>,
@@ -1444,7 +1465,7 @@ fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>(
14441465
/// This is all explained at the top of the file.
14451466
#[instrument(level = "debug", skip(mcx, is_top_level), ret)]
14461467
fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
1447-
mcx: UsefulnessCtxt<'a, Cx>,
1468+
mcx: &mut UsefulnessCtxt<'a, 'p, Cx>,
14481469
matrix: &mut Matrix<'p, Cx>,
14491470
is_top_level: bool,
14501471
) -> Result<WitnessMatrix<Cx>, Cx::Error> {
@@ -1572,7 +1593,9 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
15721593
// Record usefulness in the patterns.
15731594
for row in matrix.rows() {
15741595
if row.useful {
1575-
row.head().set_useful();
1596+
if let PatOrWild::Pat(pat) = row.head() {
1597+
mcx.useful_subpatterns.insert(ByAddress(pat));
1598+
}
15761599
}
15771600
}
15781601

@@ -1593,12 +1616,17 @@ pub enum Usefulness<'p, Cx: TypeCx> {
15931616

15941617
/// Report whether this pattern was found useful, and its subpatterns that were not useful if any.
15951618
fn collect_pattern_usefulness<'p, Cx: TypeCx>(
1619+
useful_subpatterns: &FxHashSet<ByAddress<&'p DeconstructedPat<'p, Cx>>>,
15961620
pat: &'p DeconstructedPat<'p, Cx>,
15971621
) -> Usefulness<'p, Cx> {
1598-
fn pat_is_useful<'p, Cx: TypeCx>(pat: &'p DeconstructedPat<'p, Cx>) -> bool {
1599-
if pat.useful.get() {
1622+
fn pat_is_useful<'p, Cx: TypeCx>(
1623+
useful_subpatterns: &FxHashSet<ByAddress<&'p DeconstructedPat<'p, Cx>>>,
1624+
pat: &'p DeconstructedPat<'p, Cx>,
1625+
) -> bool {
1626+
if useful_subpatterns.contains(&ByAddress(pat)) {
16001627
true
1601-
} else if pat.is_or_pat() && pat.iter_fields().any(|f| pat_is_useful(f)) {
1628+
} else if pat.is_or_pat() && pat.iter_fields().any(|f| pat_is_useful(useful_subpatterns, f))
1629+
{
16021630
// We always expand or patterns in the matrix, so we will never see the actual
16031631
// or-pattern (the one with constructor `Or`) in the column. As such, it will not be
16041632
// marked as useful itself, only its children will. We recover this information here.
@@ -1610,7 +1638,7 @@ fn collect_pattern_usefulness<'p, Cx: TypeCx>(
16101638

16111639
let mut subpats = Vec::new();
16121640
pat.walk(&mut |p| {
1613-
if pat_is_useful(p) {
1641+
if pat_is_useful(useful_subpatterns, p) {
16141642
// The pattern is useful, so we recurse to find redundant subpatterns.
16151643
true
16161644
} else {
@@ -1620,7 +1648,11 @@ fn collect_pattern_usefulness<'p, Cx: TypeCx>(
16201648
}
16211649
});
16221650

1623-
if pat_is_useful(pat) { Usefulness::Useful(subpats) } else { Usefulness::Redundant }
1651+
if pat_is_useful(useful_subpatterns, pat) {
1652+
Usefulness::Useful(subpats)
1653+
} else {
1654+
Usefulness::Redundant
1655+
}
16241656
}
16251657

16261658
/// The output of checking a match for exhaustiveness and arm usefulness.
@@ -1640,18 +1672,18 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>(
16401672
scrut_ty: Cx::Ty,
16411673
scrut_validity: ValidityConstraint,
16421674
) -> Result<UsefulnessReport<'p, Cx>, Cx::Error> {
1643-
let cx = UsefulnessCtxt { tycx };
1675+
let mut cx = UsefulnessCtxt { tycx, useful_subpatterns: FxHashSet::default() };
16441676
let mut matrix = Matrix::new(arms, scrut_ty, scrut_validity);
16451677
let non_exhaustiveness_witnesses =
1646-
compute_exhaustiveness_and_usefulness(cx, &mut matrix, true)?;
1678+
compute_exhaustiveness_and_usefulness(&mut cx, &mut matrix, true)?;
16471679

16481680
let non_exhaustiveness_witnesses: Vec<_> = non_exhaustiveness_witnesses.single_column();
16491681
let arm_usefulness: Vec<_> = arms
16501682
.iter()
16511683
.copied()
16521684
.map(|arm| {
16531685
debug!(?arm);
1654-
let usefulness = collect_pattern_usefulness(arm.pat);
1686+
let usefulness = collect_pattern_usefulness(&cx.useful_subpatterns, arm.pat);
16551687
(arm, usefulness)
16561688
})
16571689
.collect();

0 commit comments

Comments
 (0)