Skip to content

Commit

Permalink
Auto merge of #119581 - Nadrieril:detangle-arena, r=<try>
Browse files Browse the repository at this point in the history
Exhaustiveness: remove the need for arena-allocation within the algorithm

WARNING: skip the first commit, it's from #119329 which is getting merged.

This nicely cleans up the lifetime story: after this PR, all the `&'p DeconstructedPat` ever handled in the algorithm are coming from user input; we never build one ourselves.

r? `@compiler-errors`
  • Loading branch information
bors committed Jan 4, 2024
2 parents 090d5ea 044ff22 commit da5b704
Show file tree
Hide file tree
Showing 9 changed files with 275 additions and 207 deletions.
7 changes: 0 additions & 7 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4355,7 4355,6 @@ dependencies = [
"rustc_target",
"smallvec",
"tracing",
"typed-arena",
]

[[package]]
Expand Down Expand Up @@ -5692,12 5691,6 @@ dependencies = [
"rustc-hash",
]

[[package]]
name = "typed-arena"
version = "2.0.2"
source = "registry https://github.com/rust-lang/crates.io-index"
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"

[[package]]
name = "typenum"
version = "1.16.0"
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 554,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
let cx = self.new_cx(refutability, None, scrut, pat.span);
let pat = self.lower_pattern(&cx, pat)?;
let arms = [MatchArm { pat, arm_data: self.lint_level, has_guard: false }];
let report = analyze_match(&cx, &arms, pat.ty());
let report = analyze_match(&cx, &arms, pat.ty().inner());
Ok((cx, report))
}

Expand Down Expand Up @@ -972,7 972,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
}
} else if ty == cx.tcx.types.str_ {
err.note("`&str` cannot be matched exhaustively, so a wildcard `_` is necessary");
} else if cx.is_foreign_non_exhaustive_enum(ty) {
} else if cx.is_foreign_non_exhaustive_enum(cx.reveal_opaque_ty(ty)) {
err.note(format!("`{ty}` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively"));
}
}
Expand Down Expand Up @@ -1112,12 1112,12 @@ fn collect_non_exhaustive_tys<'tcx>(
non_exhaustive_tys: &mut FxIndexSet<Ty<'tcx>>,
) {
if matches!(pat.ctor(), Constructor::NonExhaustive) {
non_exhaustive_tys.insert(pat.ty());
non_exhaustive_tys.insert(pat.ty().inner());
}
if let Constructor::IntRange(range) = pat.ctor() {
if cx.is_range_beyond_boundaries(range, pat.ty()) {
// The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`.
non_exhaustive_tys.insert(pat.ty());
non_exhaustive_tys.insert(pat.ty().inner());
}
}
pat.iter_fields()
Expand Down
4 changes: 0 additions & 4 deletions compiler/rustc_pattern_analysis/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 20,6 @@ rustc_span = { path = "../rustc_span", optional = true }
rustc_target = { path = "../rustc_target", optional = true }
smallvec = { version = "1.8.1", features = ["union"] }
tracing = "0.1"
typed-arena = { version = "2.0.2", optional = true }
# tidy-alphabetical-end

[features]
Expand All @@ -41,6 40,3 @@ rustc = [
"smallvec/may_dangle",
"rustc_index/nightly",
]
stable = [
"dep:typed-arena",
]
6 changes: 3 additions & 3 deletions compiler/rustc_pattern_analysis/src/constructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 718,7 @@ impl<Cx: TypeCx> Constructor<Cx> {

/// The number of fields for this constructor. This must be kept in sync with
/// `Fields::wildcards`.
pub(crate) fn arity(&self, pcx: &PlaceCtxt<'_, '_, Cx>) -> usize {
pub(crate) fn arity(&self, pcx: &PlaceCtxt<'_, Cx>) -> usize {
pcx.ctor_arity(self)
}

Expand All @@ -727,7 727,7 @@ impl<Cx: TypeCx> Constructor<Cx> {
/// this checks for inclusion.
// We inline because this has a single call site in `Matrix::specialize_constructor`.
#[inline]
pub(crate) fn is_covered_by<'p>(&self, pcx: &PlaceCtxt<'_, 'p, Cx>, other: &Self) -> bool {
pub(crate) fn is_covered_by(&self, pcx: &PlaceCtxt<'_, Cx>, other: &Self) -> bool {
match (self, other) {
(Wildcard, _) => pcx
.mcx
Expand Down Expand Up @@ -861,7 861,7 @@ impl<Cx: TypeCx> ConstructorSet<Cx> {
#[instrument(level = "debug", skip(self, pcx, ctors), ret)]
pub(crate) fn split<'a>(
&self,
pcx: &PlaceCtxt<'a, '_, Cx>,
pcx: &PlaceCtxt<'a, Cx>,
ctors: impl Iterator<Item = &'a Constructor<Cx>> Clone,
) -> SplitConstructorSet<Cx> {
let mut present: SmallVec<[_; 1]> = SmallVec::new();
Expand Down
18 changes: 3 additions & 15 deletions compiler/rustc_pattern_analysis/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 36,6 @@ use crate::rustc::RustcMatchCheckCtxt;
#[cfg(feature = "rustc")]
use crate::usefulness::{compute_match_usefulness, ValidityConstraint};

// It's not possible to only enable the `typed_arena` dependency when the `rustc` feature is off, so
// we use another feature instead. The crate won't compile if one of these isn't enabled.
#[cfg(feature = "rustc")]
pub(crate) use rustc_arena::TypedArena;
#[cfg(feature = "stable")]
pub(crate) use typed_arena::Arena as TypedArena;

pub trait Captures<'a> {}
impl<'a, T: ?Sized> Captures<'a> for T {}

Expand All @@ -61,8 54,6 @@ pub trait TypeCx: Sized fmt::Debug {
/// Extra data to store in a pattern.
type PatData: Clone;

/// FIXME(Nadrieril): `Cx` should only give us revealed types.
fn reveal_opaque_ty(&self, ty: Self::Ty) -> Self::Ty;
fn is_exhaustive_patterns_feature_on(&self) -> bool;

/// The number of fields for this constructor.
Expand All @@ -87,11 78,9 @@ pub trait TypeCx: Sized fmt::Debug {
/// Context that provides information global to a match.
#[derive(derivative::Derivative)]
#[derivative(Clone(bound = ""), Copy(bound = ""))]
pub struct MatchCtxt<'a, 'p, Cx: TypeCx> {
pub struct MatchCtxt<'a, Cx: TypeCx> {
/// The context for type information.
pub tycx: &'a Cx,
/// An arena to store the wildcards we produce during analysis.
pub wildcard_arena: &'p TypedArena<DeconstructedPat<'p, Cx>>,
}

/// The arm of a match expression.
Expand All @@ -112,10 101,9 @@ pub fn analyze_match<'p, 'tcx>(
arms: &[rustc::MatchArm<'p, 'tcx>],
scrut_ty: Ty<'tcx>,
) -> rustc::UsefulnessReport<'p, 'tcx> {
// Arena to store the extra wildcards we construct during analysis.
let wildcard_arena = tycx.pattern_arena;
let scrut_ty = tycx.reveal_opaque_ty(scrut_ty);
let scrut_validity = ValidityConstraint::from_bool(tycx.known_valid_scrutinee);
let cx = MatchCtxt { tycx, wildcard_arena };
let cx = MatchCtxt { tycx };

let report = compute_match_usefulness(cx, arms, scrut_ty, scrut_validity);

Expand Down
60 changes: 23 additions & 37 deletions compiler/rustc_pattern_analysis/src/lints.rs
Original file line number Diff line number Diff line change
@@ -1,7 1,7 @@
use smallvec::SmallVec;

use rustc_data_structures::captures::Captures;
use rustc_middle::ty::{self, Ty};
use rustc_middle::ty;
use rustc_session::lint;
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
use rustc_span::Span;
Expand All @@ -11,11 11,11 @@ use crate::errors::{
NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Overlap,
OverlappingRangeEndpoints, Uncovered,
};
use crate::pat::PatOrWild;
use crate::rustc::{
Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RustcMatchCheckCtxt,
Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RevealedTy, RustcMatchCheckCtxt,
SplitConstructorSet, WitnessPat,
};
use crate::TypeCx;

/// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that
/// inspect the same subvalue/place".
Expand All @@ -34,28 34,24 @@ pub(crate) struct PatternColumn<'p, 'tcx> {

impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
pub(crate) fn new(arms: &[MatchArm<'p, 'tcx>]) -> Self {
let mut patterns = Vec::with_capacity(arms.len());
let mut column = PatternColumn { patterns: Vec::with_capacity(arms.len()) };
for arm in arms {
if arm.pat.is_or_pat() {
patterns.extend(arm.pat.flatten_or_pat())
} else {
patterns.push(arm.pat)
}
column.expand_and_push(PatOrWild::Pat(arm.pat));
}
Self { patterns }
}

fn is_empty(&self) -> bool {
self.patterns.is_empty()
column
}
fn head_ty(&self, cx: MatchCtxt<'_, 'p, 'tcx>) -> Option<Ty<'tcx>> {
if self.patterns.len() == 0 {
return None;
fn expand_and_push(&mut self, pat: PatOrWild<'p, RustcMatchCheckCtxt<'p, 'tcx>>) {
if pat.is_or_pat() {
self.patterns.extend(
pat.flatten_or_pat().into_iter().filter_map(|pat_or_wild| pat_or_wild.as_pat()),
)
} else if let Some(pat) = pat.as_pat() {
self.patterns.push(pat)
}
}

let ty = self.patterns[0].ty();
// FIXME(Nadrieril): `Cx` should only give us revealed types.
Some(cx.tycx.reveal_opaque_ty(ty))
fn head_ty(&self) -> Option<RevealedTy<'tcx>> {
self.patterns.first().map(|pat| pat.ty())
}

/// Do constructor splitting on the constructors of the column.
Expand Down Expand Up @@ -91,21 87,11 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
let relevant_patterns =
self.patterns.iter().filter(|pat| ctor.is_covered_by(pcx, pat.ctor()));
for pat in relevant_patterns {
let specialized = pat.specialize(pcx, ctor);
for (subpat, column) in specialized.iter().zip(&mut specialized_columns) {
if subpat.is_or_pat() {
column.patterns.extend(subpat.flatten_or_pat())
} else {
column.patterns.push(subpat)
}
let specialized = pat.specialize(ctor, pcx.ctor_arity(ctor));
for (subpat, column) in specialized.into_iter().zip(&mut specialized_columns) {
column.expand_and_push(subpat);
}
}

assert!(
!specialized_columns[0].is_empty(),
"ctor {ctor:?} was listed as present but isn't;
there is an inconsistency between `Constructor::is_covered_by` and `ConstructorSet::split`"
);
specialized_columns
}
}
Expand All @@ -117,7 103,7 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
cx: MatchCtxt<'a, 'p, 'tcx>,
column: &PatternColumn<'p, 'tcx>,
) -> Vec<WitnessPat<'p, 'tcx>> {
let Some(ty) = column.head_ty(cx) else {
let Some(ty) = column.head_ty() else {
return Vec::new();
};
let pcx = &PlaceCtxt::new_dummy(cx, ty);
Expand Down Expand Up @@ -164,7 150,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
cx: MatchCtxt<'a, 'p, 'tcx>,
arms: &[MatchArm<'p, 'tcx>],
pat_column: &PatternColumn<'p, 'tcx>,
scrut_ty: Ty<'tcx>,
scrut_ty: RevealedTy<'tcx>,
) {
let rcx: &RustcMatchCheckCtxt<'_, '_> = cx.tycx;
if !matches!(
Expand All @@ -182,7 168,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
rcx.match_lint_level,
rcx.scrut_span,
NonExhaustiveOmittedPattern {
scrut_ty,
scrut_ty: scrut_ty.inner(),
uncovered: Uncovered::new(rcx.scrut_span, rcx, witnesses),
},
);
Expand Down Expand Up @@ -218,7 204,7 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
cx: MatchCtxt<'a, 'p, 'tcx>,
column: &PatternColumn<'p, 'tcx>,
) {
let Some(ty) = column.head_ty(cx) else {
let Some(ty) = column.head_ty() else {
return;
};
let pcx = &PlaceCtxt::new_dummy(cx, ty);
Expand Down
Loading

0 comments on commit da5b704

Please sign in to comment.