Skip to content

Commit

Permalink
Auto merge of rust-lang#118842 - Nadrieril:librarify-further, r=compi…
Browse files Browse the repository at this point in the history
…ler-errors

Make exhaustiveness usable outside of rustc

With this PR, `rustc_pattern_analysis` compiles on stable (with the `stable` feature)! `rust-analyzer` will be able to use it to provide match-related diagnostics and refactors.

Two questions:
- Should I name the feature `nightly` instead of `rustc` for consistency with other crates? `rustc` makes more sense imo.
- `typed-arena` is an optional dependency but tidy made me add it to the allow-list anyway. Can I avoid that somehow?

r? `@compiler-errors`
  • Loading branch information
bors committed Dec 19, 2023
2 parents c9c9f52 + 3016c29 commit 3a539c0
Show file tree
Hide file tree
Showing 14 changed files with 720 additions and 473 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4354,6 +4354,7 @@ dependencies = [
"rustc_target",
"smallvec",
"tracing",
"typed-arena",
]

[[package]]
Expand Down Expand Up @@ -5689,6 +5690,12 @@ 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
48 changes: 37 additions & 11 deletions compiler/rustc_index/src/bit_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::slice;
use arrayvec::ArrayVec;
use smallvec::{smallvec, SmallVec};

#[cfg(feature = "nightly")]
use rustc_macros::{Decodable, Encodable};

use crate::{Idx, IndexVec};
Expand Down Expand Up @@ -111,7 +112,8 @@ macro_rules! bit_relations_inherent_impls {
/// to or greater than the domain size. All operations that involve two bitsets
/// will panic if the bitsets have differing domain sizes.
///
#[derive(Eq, PartialEq, Hash, Decodable, Encodable)]
#[cfg_attr(feature = "nightly", derive(Decodable, Encodable))]
#[derive(Eq, PartialEq, Hash)]
pub struct BitSet<T> {
domain_size: usize,
words: SmallVec<[Word; 2]>,
Expand Down Expand Up @@ -491,10 +493,21 @@ impl<T: Idx> ChunkedBitSet<T> {
match *chunk {
Zeros(chunk_domain_size) => {
if chunk_domain_size > 1 {
// We take some effort to avoid copying the words.
let words = Rc::<[Word; CHUNK_WORDS]>::new_zeroed();
// SAFETY: `words` can safely be all zeroes.
let mut words = unsafe { words.assume_init() };
#[cfg(feature = "nightly")]
let mut words = {
// We take some effort to avoid copying the words.
let words = Rc::<[Word; CHUNK_WORDS]>::new_zeroed();
// SAFETY: `words` can safely be all zeroes.
unsafe { words.assume_init() }
};
#[cfg(not(feature = "nightly"))]
let mut words = {
let words = mem::MaybeUninit::<[Word; CHUNK_WORDS]>::zeroed();
// SAFETY: `words` can safely be all zeroes.
let words = unsafe { words.assume_init() };
// Unfortunate possibly-large copy
Rc::new(words)
};
let words_ref = Rc::get_mut(&mut words).unwrap();

let (word_index, mask) = chunk_word_index_and_mask(elem);
Expand Down Expand Up @@ -545,10 +558,21 @@ impl<T: Idx> ChunkedBitSet<T> {
Zeros(_) => false,
Ones(chunk_domain_size) => {
if chunk_domain_size > 1 {
// We take some effort to avoid copying the words.
let words = Rc::<[Word; CHUNK_WORDS]>::new_zeroed();
// SAFETY: `words` can safely be all zeroes.
let mut words = unsafe { words.assume_init() };
#[cfg(feature = "nightly")]
let mut words = {
// We take some effort to avoid copying the words.
let words = Rc::<[Word; CHUNK_WORDS]>::new_zeroed();
// SAFETY: `words` can safely be all zeroes.
unsafe { words.assume_init() }
};
#[cfg(not(feature = "nightly"))]
let mut words = {
let words = mem::MaybeUninit::<[Word; CHUNK_WORDS]>::zeroed();
// SAFETY: `words` can safely be all zeroes.
let words = unsafe { words.assume_init() };
// Unfortunate possibly-large copy
Rc::new(words)
};
let words_ref = Rc::get_mut(&mut words).unwrap();

// Set only the bits in use.
Expand Down Expand Up @@ -1564,7 +1588,8 @@ impl<T: Idx> From<BitSet<T>> for GrowableBitSet<T> {
///
/// All operations that involve a row and/or column index will panic if the
/// index exceeds the relevant bound.
#[derive(Clone, Eq, PartialEq, Hash, Decodable, Encodable)]
#[cfg_attr(feature = "nightly", derive(Decodable, Encodable))]
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct BitMatrix<R: Idx, C: Idx> {
num_rows: usize,
num_columns: usize,
Expand Down Expand Up @@ -1993,7 +2018,8 @@ impl std::fmt::Debug for FiniteBitSet<u32> {

/// A fixed-sized bitset type represented by an integer type. Indices outwith than the range
/// representable by `T` are considered set.
#[derive(Copy, Clone, Eq, PartialEq, Decodable, Encodable)]
#[cfg_attr(feature = "nightly", derive(Decodable, Encodable))]
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct FiniteBitSet<T: FiniteBitSetTy>(pub T);

impl<T: FiniteBitSetTy> FiniteBitSet<T> {
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_index/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
)]
#![cfg_attr(feature = "nightly", allow(internal_features))]

#[cfg(feature = "nightly")]
pub mod bit_set;
#[cfg(feature = "nightly")]
pub mod interval;
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_mir_build/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use rustc_errors::{
};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::{self, Ty};
use rustc_pattern_analysis::{cx::MatchCheckCtxt, errors::Uncovered};
use rustc_pattern_analysis::{errors::Uncovered, rustc::RustcMatchCheckCtxt};
use rustc_span::symbol::Symbol;
use rustc_span::Span;

Expand Down Expand Up @@ -454,7 +454,7 @@ pub enum UnusedUnsafeEnclosing {
}

pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> {
pub cx: &'m MatchCheckCtxt<'p, 'tcx>,
pub cx: &'m RustcMatchCheckCtxt<'p, 'tcx>,
pub expr_span: Span,
pub span: Span,
pub ty: Ty<'tcx>,
Expand Down
60 changes: 32 additions & 28 deletions compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use rustc_pattern_analysis::constructor::Constructor;
use rustc_pattern_analysis::cx::MatchCheckCtxt;
use rustc_pattern_analysis::errors::Uncovered;
use rustc_pattern_analysis::pat::{DeconstructedPat, WitnessPat};
use rustc_pattern_analysis::usefulness::{Usefulness, UsefulnessReport};
use rustc_pattern_analysis::rustc::{
Constructor, DeconstructedPat, RustcMatchCheckCtxt as MatchCheckCtxt, Usefulness,
UsefulnessReport, WitnessPat,
};
use rustc_pattern_analysis::{analyze_match, MatchArm};

use crate::errors::*;

use rustc_arena::TypedArena;
use rustc_arena::{DroplessArena, TypedArena};
use rustc_ast::Mutability;
use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::stack::ensure_sufficient_stack;
Expand All @@ -31,13 +31,15 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err
let (thir, expr) = tcx.thir_body(def_id)?;
let thir = thir.borrow();
let pattern_arena = TypedArena::default();
let dropless_arena = DroplessArena::default();
let mut visitor = MatchVisitor {
tcx,
thir: &*thir,
param_env: tcx.param_env(def_id),
lint_level: tcx.local_def_id_to_hir_id(def_id),
let_source: LetSource::None,
pattern_arena: &pattern_arena,
dropless_arena: &dropless_arena,
error: Ok(()),
};
visitor.visit_expr(&thir[expr]);
Expand Down Expand Up @@ -82,6 +84,7 @@ struct MatchVisitor<'thir, 'p, 'tcx> {
lint_level: HirId,
let_source: LetSource,
pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
dropless_arena: &'p DroplessArena,
/// Tracks if we encountered an error while checking this body. That the first function to
/// report it stores it here. Some functions return `Result` to allow callers to short-circuit
/// on error, but callers don't need to store it here again.
Expand Down Expand Up @@ -382,6 +385,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
param_env: self.param_env,
module: self.tcx.parent_module(self.lint_level).to_def_id(),
pattern_arena: self.pattern_arena,
dropless_arena: self.dropless_arena,
match_lint_level: self.lint_level,
whole_match_span,
scrut_span,
Expand Down Expand Up @@ -425,7 +429,8 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
let arm = &self.thir.arms[arm];
let got_error = self.with_lint_level(arm.lint_level, |this| {
let Ok(pat) = this.lower_pattern(&cx, &arm.pattern) else { return true };
let arm = MatchArm { pat, hir_id: this.lint_level, has_guard: arm.guard.is_some() };
let arm =
MatchArm { pat, arm_data: this.lint_level, has_guard: arm.guard.is_some() };
tarms.push(arm);
false
});
Expand Down Expand Up @@ -548,7 +553,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
) -> Result<(MatchCheckCtxt<'p, 'tcx>, UsefulnessReport<'p, 'tcx>), ErrorGuaranteed> {
let cx = self.new_cx(refutability, None, scrut, pat.span);
let pat = self.lower_pattern(&cx, pat)?;
let arms = [MatchArm { pat, hir_id: self.lint_level, has_guard: false }];
let arms = [MatchArm { pat, arm_data: self.lint_level, has_guard: false }];
let report = analyze_match(&cx, &arms, pat.ty());
Ok((cx, report))
}
Expand Down Expand Up @@ -847,34 +852,34 @@ fn report_arm_reachability<'p, 'tcx>(
);
};

use Usefulness::*;
let mut catchall = None;
for (arm, is_useful) in report.arm_usefulness.iter() {
match is_useful {
Redundant => report_unreachable_pattern(arm.pat.span(), arm.hir_id, catchall),
Useful(redundant_spans) if redundant_spans.is_empty() => {}
Usefulness::Redundant => {
report_unreachable_pattern(*arm.pat.data(), arm.arm_data, catchall)
}
Usefulness::Useful(redundant_subpats) if redundant_subpats.is_empty() => {}
// The arm is reachable, but contains redundant subpatterns (from or-patterns).
Useful(redundant_spans) => {
let mut redundant_spans = redundant_spans.clone();
Usefulness::Useful(redundant_subpats) => {
let mut redundant_subpats = redundant_subpats.clone();
// Emit lints in the order in which they occur in the file.
redundant_spans.sort_unstable();
for span in redundant_spans {
report_unreachable_pattern(span, arm.hir_id, None);
redundant_subpats.sort_unstable_by_key(|pat| pat.data());
for pat in redundant_subpats {
report_unreachable_pattern(*pat.data(), arm.arm_data, None);
}
}
}
if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) {
catchall = Some(arm.pat.span());
catchall = Some(*arm.pat.data());
}
}
}

/// Checks for common cases of "catchall" patterns that may not be intended as such.
fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
use Constructor::*;
match pat.ctor() {
Wildcard => true,
Single => pat.iter_fields().all(|pat| pat_is_catchall(pat)),
Constructor::Wildcard => true,
Constructor::Struct | Constructor::Ref => pat.iter_fields().all(|pat| pat_is_catchall(pat)),
_ => false,
}
}
Expand All @@ -885,7 +890,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
thir: &Thir<'tcx>,
scrut_ty: Ty<'tcx>,
sp: Span,
witnesses: Vec<WitnessPat<'tcx>>,
witnesses: Vec<WitnessPat<'p, 'tcx>>,
arms: &[ArmId],
expr_span: Span,
) -> ErrorGuaranteed {
Expand Down Expand Up @@ -1082,10 +1087,10 @@ fn report_non_exhaustive_match<'p, 'tcx>(

fn joined_uncovered_patterns<'p, 'tcx>(
cx: &MatchCheckCtxt<'p, 'tcx>,
witnesses: &[WitnessPat<'tcx>],
witnesses: &[WitnessPat<'p, 'tcx>],
) -> String {
const LIMIT: usize = 3;
let pat_to_str = |pat: &WitnessPat<'tcx>| cx.hoist_witness_pat(pat).to_string();
let pat_to_str = |pat: &WitnessPat<'p, 'tcx>| cx.hoist_witness_pat(pat).to_string();
match witnesses {
[] => bug!(),
[witness] => format!("`{}`", cx.hoist_witness_pat(witness)),
Expand All @@ -1103,7 +1108,7 @@ fn joined_uncovered_patterns<'p, 'tcx>(

fn collect_non_exhaustive_tys<'tcx>(
cx: &MatchCheckCtxt<'_, 'tcx>,
pat: &WitnessPat<'tcx>,
pat: &WitnessPat<'_, 'tcx>,
non_exhaustive_tys: &mut FxIndexSet<Ty<'tcx>>,
) {
if matches!(pat.ctor(), Constructor::NonExhaustive) {
Expand All @@ -1122,7 +1127,7 @@ fn collect_non_exhaustive_tys<'tcx>(
fn report_adt_defined_here<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
witnesses: &[WitnessPat<'tcx>],
witnesses: &[WitnessPat<'_, 'tcx>],
point_at_non_local_ty: bool,
) -> Option<AdtDefinedHere<'tcx>> {
let ty = ty.peel_refs();
Expand All @@ -1144,15 +1149,14 @@ fn report_adt_defined_here<'tcx>(
Some(AdtDefinedHere { adt_def_span, ty, variants })
}

fn maybe_point_at_variant<'a, 'tcx: 'a>(
fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'p>(
tcx: TyCtxt<'tcx>,
def: AdtDef<'tcx>,
patterns: impl Iterator<Item = &'a WitnessPat<'tcx>>,
patterns: impl Iterator<Item = &'a WitnessPat<'p, 'tcx>>,
) -> Vec<Span> {
use Constructor::*;
let mut covered = vec![];
for pattern in patterns {
if let Variant(variant_index) = pattern.ctor() {
if let Constructor::Variant(variant_index) = pattern.ctor() {
if let ty::Adt(this_def, _) = pattern.ty().kind()
&& this_def.did() != def.did()
{
Expand Down
47 changes: 35 additions & 12 deletions compiler/rustc_pattern_analysis/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,40 @@ edition = "2021"
[dependencies]
# tidy-alphabetical-start
rustc_apfloat = "0.2.0"
rustc_arena = { path = "../rustc_arena" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" }
rustc_macros = { path = "../rustc_macros" }
rustc_middle = { path = "../rustc_middle" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
rustc_arena = { path = "../rustc_arena", optional = true }
rustc_data_structures = { path = "../rustc_data_structures", optional = true }
rustc_errors = { path = "../rustc_errors", optional = true }
rustc_fluent_macro = { path = "../rustc_fluent_macro", optional = true }
rustc_hir = { path = "../rustc_hir", optional = true }
rustc_index = { path = "../rustc_index", default-features = false }
rustc_macros = { path = "../rustc_macros", optional = true }
rustc_middle = { path = "../rustc_middle", optional = true }
rustc_session = { path = "../rustc_session", optional = true }
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]
default = ["rustc"]
# 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.
rustc = [
"dep:rustc_arena",
"dep:rustc_data_structures",
"dep:rustc_errors",
"dep:rustc_fluent_macro",
"dep:rustc_hir",
"dep:rustc_macros",
"dep:rustc_middle",
"dep:rustc_session",
"dep:rustc_span",
"dep:rustc_target",
"smallvec/may_dangle",
"rustc_index/nightly",
]
stable = [
"dep:typed-arena",
]
Loading

0 comments on commit 3a539c0

Please sign in to comment.