Skip to content

Commit dd0887c

Browse files
authored
Rollup merge of #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`
2 parents dadecea + 43714ed commit dd0887c

File tree

19 files changed

+2614
-2460
lines changed

19 files changed

+2614
-2460
lines changed

Cargo.lock

+22
Original file line numberDiff line numberDiff line change
@@ -3756,6 +3756,7 @@ dependencies = [
37563756
"rustc_monomorphize",
37573757
"rustc_parse",
37583758
"rustc_passes",
3759+
"rustc_pattern_analysis",
37593760
"rustc_privacy",
37603761
"rustc_query_system",
37613762
"rustc_resolve",
@@ -4229,6 +4230,7 @@ dependencies = [
42294230
"rustc_infer",
42304231
"rustc_macros",
42314232
"rustc_middle",
4233+
"rustc_pattern_analysis",
42324234
"rustc_session",
42334235
"rustc_span",
42344236
"rustc_target",
@@ -4364,6 +4366,26 @@ dependencies = [
43644366
"tracing",
43654367
]
43664368

4369+
[[package]]
4370+
name = "rustc_pattern_analysis"
4371+
version = "0.0.0"
4372+
dependencies = [
4373+
"rustc_apfloat",
4374+
"rustc_arena",
4375+
"rustc_data_structures",
4376+
"rustc_errors",
4377+
"rustc_fluent_macro",
4378+
"rustc_hir",
4379+
"rustc_index",
4380+
"rustc_macros",
4381+
"rustc_middle",
4382+
"rustc_session",
4383+
"rustc_span",
4384+
"rustc_target",
4385+
"smallvec",
4386+
"tracing",
4387+
]
4388+
43674389
[[package]]
43684390
name = "rustc_privacy"
43694391
version = "0.0.0"

compiler/rustc_driver_impl/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ rustc_mir_transform = { path = "../rustc_mir_transform" }
3838
rustc_monomorphize = { path = "../rustc_monomorphize" }
3939
rustc_parse = { path = "../rustc_parse" }
4040
rustc_passes = { path = "../rustc_passes" }
41+
rustc_pattern_analysis = { path = "../rustc_pattern_analysis" }
4142
rustc_privacy = { path = "../rustc_privacy" }
4243
rustc_query_system = { path = "../rustc_query_system" }
4344
rustc_resolve = { path = "../rustc_resolve" }

compiler/rustc_driver_impl/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[
128128
rustc_monomorphize::DEFAULT_LOCALE_RESOURCE,
129129
rustc_parse::DEFAULT_LOCALE_RESOURCE,
130130
rustc_passes::DEFAULT_LOCALE_RESOURCE,
131+
rustc_pattern_analysis::DEFAULT_LOCALE_RESOURCE,
131132
rustc_privacy::DEFAULT_LOCALE_RESOURCE,
132133
rustc_query_system::DEFAULT_LOCALE_RESOURCE,
133134
rustc_resolve::DEFAULT_LOCALE_RESOURCE,

compiler/rustc_mir_build/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ rustc_index = { path = "../rustc_index" }
1717
rustc_infer = { path = "../rustc_infer" }
1818
rustc_macros = { path = "../rustc_macros" }
1919
rustc_middle = { path = "../rustc_middle" }
20+
rustc_pattern_analysis = { path = "../rustc_pattern_analysis" }
2021
rustc_session = { path = "../rustc_session" }
2122
rustc_span = { path = "../rustc_span" }
2223
rustc_target = { path = "../rustc_target" }

compiler/rustc_mir_build/messages.ftl

-20
Original file line numberDiff line numberDiff line change
@@ -237,15 +237,6 @@ mir_build_non_const_path = runtime values cannot be referenced in patterns
237237
mir_build_non_exhaustive_match_all_arms_guarded =
238238
match arms with guards don't count towards exhaustivity
239239
240-
mir_build_non_exhaustive_omitted_pattern = some variants are not matched explicitly
241-
.help = ensure that all variants are matched explicitly by adding the suggested match arms
242-
.note = the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found
243-
244-
mir_build_non_exhaustive_omitted_pattern_lint_on_arm = the lint level must be set on the whole match
245-
.help = it no longer has any effect to set the lint level on an individual match arm
246-
.label = remove this attribute
247-
.suggestion = set the lint level on the whole match
248-
249240
mir_build_non_exhaustive_patterns_type_not_empty = non-exhaustive patterns: type `{$ty}` is non-empty
250241
.def_note = `{$peeled_ty}` defined here
251242
.type_note = the matched value is of type `{$ty}`
@@ -260,10 +251,6 @@ mir_build_non_partial_eq_match =
260251
mir_build_nontrivial_structural_match =
261252
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)]`
262253
263-
mir_build_overlapping_range_endpoints = multiple patterns overlap on their endpoints
264-
.range = ... with this range
265-
.note = you likely meant to write mutually exclusive ranges
266-
267254
mir_build_pattern_not_covered = refutable pattern in {$origin}
268255
.pattern_ty = the matched value is of type `{$pattern_ty}`
269256
@@ -317,13 +304,6 @@ mir_build_unconditional_recursion = function cannot return without recursing
317304
318305
mir_build_unconditional_recursion_call_site_label = recursive call site
319306
320-
mir_build_uncovered = {$count ->
321-
[1] pattern `{$witness_1}`
322-
[2] patterns `{$witness_1}` and `{$witness_2}`
323-
[3] patterns `{$witness_1}`, `{$witness_2}` and `{$witness_3}`
324-
*[other] patterns `{$witness_1}`, `{$witness_2}`, `{$witness_3}` and {$remainder} more
325-
} not covered
326-
327307
mir_build_union_field_requires_unsafe =
328308
access to union field is unsafe and requires unsafe block
329309
.note = the field may not be properly initialized: using uninitialized data will cause undefined behavior

compiler/rustc_mir_build/src/errors.rs

+2-93
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
1-
use crate::{
2-
fluent_generated as fluent,
3-
thir::pattern::{deconstruct_pat::WitnessPat, MatchCheckCtxt},
4-
};
1+
use crate::fluent_generated as fluent;
52
use rustc_errors::DiagnosticArgValue;
63
use rustc_errors::{
74
error_code, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
85
Handler, IntoDiagnostic, MultiSpan, SubdiagnosticMessage,
96
};
107
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
11-
use rustc_middle::thir::Pat;
128
use rustc_middle::ty::{self, Ty};
9+
use rustc_pattern_analysis::{cx::MatchCheckCtxt, errors::Uncovered};
1310
use rustc_span::symbol::Symbol;
1411
use rustc_span::Span;
1512

@@ -812,94 +809,6 @@ pub struct NonPartialEqMatch<'tcx> {
812809
pub non_peq_ty: Ty<'tcx>,
813810
}
814811

815-
#[derive(LintDiagnostic)]
816-
#[diag(mir_build_overlapping_range_endpoints)]
817-
#[note]
818-
pub struct OverlappingRangeEndpoints<'tcx> {
819-
#[label(mir_build_range)]
820-
pub range: Span,
821-
#[subdiagnostic]
822-
pub overlap: Vec<Overlap<'tcx>>,
823-
}
824-
825-
pub struct Overlap<'tcx> {
826-
pub span: Span,
827-
pub range: Pat<'tcx>,
828-
}
829-
830-
impl<'tcx> AddToDiagnostic for Overlap<'tcx> {
831-
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
832-
where
833-
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
834-
{
835-
let Overlap { span, range } = self;
836-
837-
// FIXME(mejrs) unfortunately `#[derive(LintDiagnostic)]`
838-
// does not support `#[subdiagnostic(eager)]`...
839-
let message = format!("this range overlaps on `{range}`...");
840-
diag.span_label(span, message);
841-
}
842-
}
843-
844-
#[derive(LintDiagnostic)]
845-
#[diag(mir_build_non_exhaustive_omitted_pattern)]
846-
#[help]
847-
#[note]
848-
pub(crate) struct NonExhaustiveOmittedPattern<'tcx> {
849-
pub scrut_ty: Ty<'tcx>,
850-
#[subdiagnostic]
851-
pub uncovered: Uncovered<'tcx>,
852-
}
853-
854-
#[derive(LintDiagnostic)]
855-
#[diag(mir_build_non_exhaustive_omitted_pattern_lint_on_arm)]
856-
#[help]
857-
pub(crate) struct NonExhaustiveOmittedPatternLintOnArm {
858-
#[label]
859-
pub lint_span: Span,
860-
#[suggestion(code = "#[{lint_level}({lint_name})]\n", applicability = "maybe-incorrect")]
861-
pub suggest_lint_on_match: Option<Span>,
862-
pub lint_level: &'static str,
863-
pub lint_name: &'static str,
864-
}
865-
866-
#[derive(Subdiagnostic)]
867-
#[label(mir_build_uncovered)]
868-
pub(crate) struct Uncovered<'tcx> {
869-
#[primary_span]
870-
span: Span,
871-
count: usize,
872-
witness_1: Pat<'tcx>,
873-
witness_2: Pat<'tcx>,
874-
witness_3: Pat<'tcx>,
875-
remainder: usize,
876-
}
877-
878-
impl<'tcx> Uncovered<'tcx> {
879-
pub fn new<'p>(
880-
span: Span,
881-
cx: &MatchCheckCtxt<'p, 'tcx>,
882-
witnesses: Vec<WitnessPat<'tcx>>,
883-
) -> Self {
884-
let witness_1 = witnesses.get(0).unwrap().to_diagnostic_pat(cx);
885-
Self {
886-
span,
887-
count: witnesses.len(),
888-
// Substitute dummy values if witnesses is smaller than 3. These will never be read.
889-
witness_2: witnesses
890-
.get(1)
891-
.map(|w| w.to_diagnostic_pat(cx))
892-
.unwrap_or_else(|| witness_1.clone()),
893-
witness_3: witnesses
894-
.get(2)
895-
.map(|w| w.to_diagnostic_pat(cx))
896-
.unwrap_or_else(|| witness_1.clone()),
897-
witness_1,
898-
remainder: witnesses.len().saturating_sub(3),
899-
}
900-
}
901-
}
902-
903812
#[derive(Diagnostic)]
904813
#[diag(mir_build_pattern_not_covered, code = "E0005")]
905814
pub(crate) struct PatternNotCovered<'s, 'tcx> {

compiler/rustc_mir_build/src/thir/pattern/check_match.rs

+17-15
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
use super::deconstruct_pat::{Constructor, DeconstructedPat, WitnessPat};
2-
use super::usefulness::{
3-
compute_match_usefulness, MatchArm, MatchCheckCtxt, Usefulness, UsefulnessReport,
4-
};
1+
use rustc_pattern_analysis::constructor::Constructor;
2+
use rustc_pattern_analysis::cx::MatchCheckCtxt;
3+
use rustc_pattern_analysis::errors::Uncovered;
4+
use rustc_pattern_analysis::pat::{DeconstructedPat, WitnessPat};
5+
use rustc_pattern_analysis::usefulness::{Usefulness, UsefulnessReport};
6+
use rustc_pattern_analysis::{analyze_match, MatchArm};
57

68
use crate::errors::*;
79

@@ -284,7 +286,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
284286
check_borrow_conflicts_in_at_patterns(self, pat);
285287
check_for_bindings_named_same_as_variants(self, pat, refutable);
286288
});
287-
Ok(cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, pat)))
289+
Ok(cx.pattern_arena.alloc(cx.lower_pat(pat)))
288290
}
289291
}
290292

@@ -433,7 +435,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
433435
}
434436

435437
let scrut_ty = scrut.ty;
436-
let report = compute_match_usefulness(&cx, &tarms, scrut_ty);
438+
let report = analyze_match(&cx, &tarms, scrut_ty);
437439

438440
match source {
439441
// Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }`
@@ -547,7 +549,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
547549
let cx = self.new_cx(refutability, None, scrut, pat.span);
548550
let pat = self.lower_pattern(&cx, pat)?;
549551
let arms = [MatchArm { pat, hir_id: self.lint_level, has_guard: false }];
550-
let report = compute_match_usefulness(&cx, &arms, pat.ty());
552+
let report = analyze_match(&cx, &arms, pat.ty());
551553
Ok((cx, report))
552554
}
553555

@@ -924,7 +926,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
924926
pattern = if witnesses.len() < 4 {
925927
witnesses
926928
.iter()
927-
.map(|witness| witness.to_diagnostic_pat(cx).to_string())
929+
.map(|witness| cx.hoist_witness_pat(witness).to_string())
928930
.collect::<Vec<String>>()
929931
.join(" | ")
930932
} else {
@@ -948,7 +950,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
948950
if !is_empty_match {
949951
let mut non_exhaustive_tys = FxHashSet::default();
950952
// Look at the first witness.
951-
collect_non_exhaustive_tys(cx.tcx, &witnesses[0], &mut non_exhaustive_tys);
953+
collect_non_exhaustive_tys(cx, &witnesses[0], &mut non_exhaustive_tys);
952954

953955
for ty in non_exhaustive_tys {
954956
if ty.is_ptr_sized_integral() {
@@ -1083,13 +1085,13 @@ fn joined_uncovered_patterns<'p, 'tcx>(
10831085
witnesses: &[WitnessPat<'tcx>],
10841086
) -> String {
10851087
const LIMIT: usize = 3;
1086-
let pat_to_str = |pat: &WitnessPat<'tcx>| pat.to_diagnostic_pat(cx).to_string();
1088+
let pat_to_str = |pat: &WitnessPat<'tcx>| cx.hoist_witness_pat(pat).to_string();
10871089
match witnesses {
10881090
[] => bug!(),
1089-
[witness] => format!("`{}`", witness.to_diagnostic_pat(cx)),
1091+
[witness] => format!("`{}`", cx.hoist_witness_pat(witness)),
10901092
[head @ .., tail] if head.len() < LIMIT => {
10911093
let head: Vec<_> = head.iter().map(pat_to_str).collect();
1092-
format!("`{}` and `{}`", head.join("`, `"), tail.to_diagnostic_pat(cx))
1094+
format!("`{}` and `{}`", head.join("`, `"), cx.hoist_witness_pat(tail))
10931095
}
10941096
_ => {
10951097
let (head, tail) = witnesses.split_at(LIMIT);
@@ -1100,21 +1102,21 @@ fn joined_uncovered_patterns<'p, 'tcx>(
11001102
}
11011103

11021104
fn collect_non_exhaustive_tys<'tcx>(
1103-
tcx: TyCtxt<'tcx>,
1105+
cx: &MatchCheckCtxt<'_, 'tcx>,
11041106
pat: &WitnessPat<'tcx>,
11051107
non_exhaustive_tys: &mut FxHashSet<Ty<'tcx>>,
11061108
) {
11071109
if matches!(pat.ctor(), Constructor::NonExhaustive) {
11081110
non_exhaustive_tys.insert(pat.ty());
11091111
}
11101112
if let Constructor::IntRange(range) = pat.ctor() {
1111-
if range.is_beyond_boundaries(pat.ty(), tcx) {
1113+
if cx.is_range_beyond_boundaries(range, pat.ty()) {
11121114
// The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`.
11131115
non_exhaustive_tys.insert(pat.ty());
11141116
}
11151117
}
11161118
pat.iter_fields()
1117-
.for_each(|field_pat| collect_non_exhaustive_tys(tcx, field_pat, non_exhaustive_tys))
1119+
.for_each(|field_pat| collect_non_exhaustive_tys(cx, field_pat, non_exhaustive_tys))
11181120
}
11191121

11201122
fn report_adt_defined_here<'tcx>(

0 commit comments

Comments
 (0)