Skip to content

Commit

Permalink
Unrolled build for rust-lang#118822
Browse files Browse the repository at this point in the history
Rollup merge of rust-lang#118822 - Nadrieril:librarify, r=compiler-errors

Extract exhaustiveness into its own crate

It now makes sense to extract exhaustiveness into its own crate! This was much-requested by rust-analyzer (they currently maintain by hand a copy of the algorithm), and I hope this can serve other projects e.g. clippy.

This is the churny PR: it exclusively moves code around. It's not yet useable outside of rustc but I wanted the churny parts to be out of the way.

r? `@compiler-errors`
  • Loading branch information
rust-timer authored Dec 11, 2023
2 parents 5701093 + 43714ed commit b812d86
Show file tree
Hide file tree
Showing 19 changed files with 2,614 additions and 2,460 deletions.
22 changes: 22 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3756,6 +3756,7 @@ dependencies = [
"rustc_monomorphize",
"rustc_parse",
"rustc_passes",
"rustc_pattern_analysis",
"rustc_privacy",
"rustc_query_system",
"rustc_resolve",
Expand Down Expand Up @@ -4229,6 +4230,7 @@ dependencies = [
"rustc_infer",
"rustc_macros",
"rustc_middle",
"rustc_pattern_analysis",
"rustc_session",
"rustc_span",
"rustc_target",
Expand Down Expand Up @@ -4364,6 +4366,26 @@ dependencies = [
"tracing",
]

[[package]]
name = "rustc_pattern_analysis"
version = "0.0.0"
dependencies = [
"rustc_apfloat",
"rustc_arena",
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
"rustc_hir",
"rustc_index",
"rustc_macros",
"rustc_middle",
"rustc_session",
"rustc_span",
"rustc_target",
"smallvec",
"tracing",
]

[[package]]
name = "rustc_privacy"
version = "0.0.0"
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_driver_impl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ rustc_mir_transform = { path = "../rustc_mir_transform" }
rustc_monomorphize = { path = "../rustc_monomorphize" }
rustc_parse = { path = "../rustc_parse" }
rustc_passes = { path = "../rustc_passes" }
rustc_pattern_analysis = { path = "../rustc_pattern_analysis" }
rustc_privacy = { path = "../rustc_privacy" }
rustc_query_system = { path = "../rustc_query_system" }
rustc_resolve = { path = "../rustc_resolve" }
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_driver_impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[
rustc_monomorphize::DEFAULT_LOCALE_RESOURCE,
rustc_parse::DEFAULT_LOCALE_RESOURCE,
rustc_passes::DEFAULT_LOCALE_RESOURCE,
rustc_pattern_analysis::DEFAULT_LOCALE_RESOURCE,
rustc_privacy::DEFAULT_LOCALE_RESOURCE,
rustc_query_system::DEFAULT_LOCALE_RESOURCE,
rustc_resolve::DEFAULT_LOCALE_RESOURCE,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_mir_build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ rustc_index = { path = "../rustc_index" }
rustc_infer = { path = "../rustc_infer" }
rustc_macros = { path = "../rustc_macros" }
rustc_middle = { path = "../rustc_middle" }
rustc_pattern_analysis = { path = "../rustc_pattern_analysis" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
Expand Down
20 changes: 0 additions & 20 deletions compiler/rustc_mir_build/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -237,15 +237,6 @@ mir_build_non_const_path = runtime values cannot be referenced in patterns
mir_build_non_exhaustive_match_all_arms_guarded =
match arms with guards don't count towards exhaustivity
mir_build_non_exhaustive_omitted_pattern = some variants are not matched explicitly
.help = ensure that all variants are matched explicitly by adding the suggested match arms
.note = the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found
mir_build_non_exhaustive_omitted_pattern_lint_on_arm = the lint level must be set on the whole match
.help = it no longer has any effect to set the lint level on an individual match arm
.label = remove this attribute
.suggestion = set the lint level on the whole match
mir_build_non_exhaustive_patterns_type_not_empty = non-exhaustive patterns: type `{$ty}` is non-empty
.def_note = `{$peeled_ty}` defined here
.type_note = the matched value is of type `{$ty}`
Expand All @@ -260,10 +251,6 @@ mir_build_non_partial_eq_match =
mir_build_nontrivial_structural_match =
to use a constant of type `{$non_sm_ty}` in a pattern, the constant's initializer must be trivial or `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]`
mir_build_overlapping_range_endpoints = multiple patterns overlap on their endpoints
.range = ... with this range
.note = you likely meant to write mutually exclusive ranges
mir_build_pattern_not_covered = refutable pattern in {$origin}
.pattern_ty = the matched value is of type `{$pattern_ty}`
Expand Down Expand Up @@ -317,13 +304,6 @@ mir_build_unconditional_recursion = function cannot return without recursing
mir_build_unconditional_recursion_call_site_label = recursive call site
mir_build_uncovered = {$count ->
[1] pattern `{$witness_1}`
[2] patterns `{$witness_1}` and `{$witness_2}`
[3] patterns `{$witness_1}`, `{$witness_2}` and `{$witness_3}`
*[other] patterns `{$witness_1}`, `{$witness_2}`, `{$witness_3}` and {$remainder} more
} not covered
mir_build_union_field_requires_unsafe =
access to union field is unsafe and requires unsafe block
.note = the field may not be properly initialized: using uninitialized data will cause undefined behavior
Expand Down
95 changes: 2 additions & 93 deletions compiler/rustc_mir_build/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
use crate::{
fluent_generated as fluent,
thir::pattern::{deconstruct_pat::WitnessPat, MatchCheckCtxt},
};
use crate::fluent_generated as fluent;
use rustc_errors::DiagnosticArgValue;
use rustc_errors::{
error_code, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
Handler, IntoDiagnostic, MultiSpan, SubdiagnosticMessage,
};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::thir::Pat;
use rustc_middle::ty::{self, Ty};
use rustc_pattern_analysis::{cx::MatchCheckCtxt, errors::Uncovered};
use rustc_span::symbol::Symbol;
use rustc_span::Span;

Expand Down Expand Up @@ -812,94 +809,6 @@ pub struct NonPartialEqMatch<'tcx> {
pub non_peq_ty: Ty<'tcx>,
}

#[derive(LintDiagnostic)]
#[diag(mir_build_overlapping_range_endpoints)]
#[note]
pub struct OverlappingRangeEndpoints<'tcx> {
#[label(mir_build_range)]
pub range: Span,
#[subdiagnostic]
pub overlap: Vec<Overlap<'tcx>>,
}

pub struct Overlap<'tcx> {
pub span: Span,
pub range: Pat<'tcx>,
}

impl<'tcx> AddToDiagnostic for Overlap<'tcx> {
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
where
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
{
let Overlap { span, range } = self;

// FIXME(mejrs) unfortunately `#[derive(LintDiagnostic)]`
// does not support `#[subdiagnostic(eager)]`...
let message = format!("this range overlaps on `{range}`...");
diag.span_label(span, message);
}
}

#[derive(LintDiagnostic)]
#[diag(mir_build_non_exhaustive_omitted_pattern)]
#[help]
#[note]
pub(crate) struct NonExhaustiveOmittedPattern<'tcx> {
pub scrut_ty: Ty<'tcx>,
#[subdiagnostic]
pub uncovered: Uncovered<'tcx>,
}

#[derive(LintDiagnostic)]
#[diag(mir_build_non_exhaustive_omitted_pattern_lint_on_arm)]
#[help]
pub(crate) struct NonExhaustiveOmittedPatternLintOnArm {
#[label]
pub lint_span: Span,
#[suggestion(code = "#[{lint_level}({lint_name})]\n", applicability = "maybe-incorrect")]
pub suggest_lint_on_match: Option<Span>,
pub lint_level: &'static str,
pub lint_name: &'static str,
}

#[derive(Subdiagnostic)]
#[label(mir_build_uncovered)]
pub(crate) struct Uncovered<'tcx> {
#[primary_span]
span: Span,
count: usize,
witness_1: Pat<'tcx>,
witness_2: Pat<'tcx>,
witness_3: Pat<'tcx>,
remainder: usize,
}

impl<'tcx> Uncovered<'tcx> {
pub fn new<'p>(
span: Span,
cx: &MatchCheckCtxt<'p, 'tcx>,
witnesses: Vec<WitnessPat<'tcx>>,
) -> Self {
let witness_1 = witnesses.get(0).unwrap().to_diagnostic_pat(cx);
Self {
span,
count: witnesses.len(),
// Substitute dummy values if witnesses is smaller than 3. These will never be read.
witness_2: witnesses
.get(1)
.map(|w| w.to_diagnostic_pat(cx))
.unwrap_or_else(|| witness_1.clone()),
witness_3: witnesses
.get(2)
.map(|w| w.to_diagnostic_pat(cx))
.unwrap_or_else(|| witness_1.clone()),
witness_1,
remainder: witnesses.len().saturating_sub(3),
}
}
}

#[derive(Diagnostic)]
#[diag(mir_build_pattern_not_covered, code = "E0005")]
pub(crate) struct PatternNotCovered<'s, 'tcx> {
Expand Down
32 changes: 17 additions & 15 deletions compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use super::deconstruct_pat::{Constructor, DeconstructedPat, WitnessPat};
use super::usefulness::{
compute_match_usefulness, MatchArm, MatchCheckCtxt, Usefulness, UsefulnessReport,
};
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::{analyze_match, MatchArm};

use crate::errors::*;

Expand Down Expand Up @@ -284,7 +286,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
check_borrow_conflicts_in_at_patterns(self, pat);
check_for_bindings_named_same_as_variants(self, pat, refutable);
});
Ok(cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, pat)))
Ok(cx.pattern_arena.alloc(cx.lower_pat(pat)))
}
}

Expand Down Expand Up @@ -433,7 +435,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
}

let scrut_ty = scrut.ty;
let report = compute_match_usefulness(&cx, &tarms, scrut_ty);
let report = analyze_match(&cx, &tarms, scrut_ty);

match source {
// Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }`
Expand Down Expand Up @@ -547,7 +549,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
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 report = compute_match_usefulness(&cx, &arms, pat.ty());
let report = analyze_match(&cx, &arms, pat.ty());
Ok((cx, report))
}

Expand Down Expand Up @@ -924,7 +926,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
pattern = if witnesses.len() < 4 {
witnesses
.iter()
.map(|witness| witness.to_diagnostic_pat(cx).to_string())
.map(|witness| cx.hoist_witness_pat(witness).to_string())
.collect::<Vec<String>>()
.join(" | ")
} else {
Expand All @@ -948,7 +950,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
if !is_empty_match {
let mut non_exhaustive_tys = FxHashSet::default();
// Look at the first witness.
collect_non_exhaustive_tys(cx.tcx, &witnesses[0], &mut non_exhaustive_tys);
collect_non_exhaustive_tys(cx, &witnesses[0], &mut non_exhaustive_tys);

for ty in non_exhaustive_tys {
if ty.is_ptr_sized_integral() {
Expand Down Expand Up @@ -1083,13 +1085,13 @@ fn joined_uncovered_patterns<'p, 'tcx>(
witnesses: &[WitnessPat<'tcx>],
) -> String {
const LIMIT: usize = 3;
let pat_to_str = |pat: &WitnessPat<'tcx>| pat.to_diagnostic_pat(cx).to_string();
let pat_to_str = |pat: &WitnessPat<'tcx>| cx.hoist_witness_pat(pat).to_string();
match witnesses {
[] => bug!(),
[witness] => format!("`{}`", witness.to_diagnostic_pat(cx)),
[witness] => format!("`{}`", cx.hoist_witness_pat(witness)),
[head @ .., tail] if head.len() < LIMIT => {
let head: Vec<_> = head.iter().map(pat_to_str).collect();
format!("`{}` and `{}`", head.join("`, `"), tail.to_diagnostic_pat(cx))
format!("`{}` and `{}`", head.join("`, `"), cx.hoist_witness_pat(tail))
}
_ => {
let (head, tail) = witnesses.split_at(LIMIT);
Expand All @@ -1100,21 +1102,21 @@ fn joined_uncovered_patterns<'p, 'tcx>(
}

fn collect_non_exhaustive_tys<'tcx>(
tcx: TyCtxt<'tcx>,
cx: &MatchCheckCtxt<'_, 'tcx>,
pat: &WitnessPat<'tcx>,
non_exhaustive_tys: &mut FxHashSet<Ty<'tcx>>,
) {
if matches!(pat.ctor(), Constructor::NonExhaustive) {
non_exhaustive_tys.insert(pat.ty());
}
if let Constructor::IntRange(range) = pat.ctor() {
if range.is_beyond_boundaries(pat.ty(), tcx) {
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());
}
}
pat.iter_fields()
.for_each(|field_pat| collect_non_exhaustive_tys(tcx, field_pat, non_exhaustive_tys))
.for_each(|field_pat| collect_non_exhaustive_tys(cx, field_pat, non_exhaustive_tys))
}

fn report_adt_defined_here<'tcx>(
Expand Down
Loading

0 comments on commit b812d86

Please sign in to comment.