Skip to content

Commit 71abc95

Browse files
committed
Replace Pat with a new intermediate representation
1 parent fde45e9 commit 71abc95

File tree

6 files changed

+667
-504
lines changed

6 files changed

+667
-504
lines changed

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

+74-84
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
use super::deconstruct_pat::{Constructor, DeconstructedPat};
12
use super::usefulness::{
2-
compute_match_usefulness, expand_pattern, is_wildcard, MatchArm, MatchCheckCtxt, Reachability,
3-
UsefulnessReport,
3+
compute_match_usefulness, MatchArm, MatchCheckCtxt, Reachability, UsefulnessReport,
44
};
55
use super::{PatCtxt, PatternError};
66

@@ -12,26 +12,25 @@ use rustc_hir::def::*;
1212
use rustc_hir::def_id::DefId;
1313
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
1414
use rustc_hir::{HirId, Pat};
15-
use rustc_middle::thir::PatKind;
16-
use rustc_middle::ty::{self, Ty, TyCtxt};
15+
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
1716
use rustc_session::lint::builtin::{
1817
BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS,
1918
};
2019
use rustc_session::Session;
2120
use rustc_span::{DesugaringKind, ExpnKind, Span};
22-
use std::slice;
2321

2422
crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) {
2523
let body_id = match def_id.as_local() {
2624
None => return,
2725
Some(id) => tcx.hir().body_owned_by(tcx.hir().local_def_id_to_hir_id(id)),
2826
};
2927

28+
let pattern_arena = TypedArena::default();
3029
let mut visitor = MatchVisitor {
3130
tcx,
3231
typeck_results: tcx.typeck_body(body_id),
3332
param_env: tcx.param_env(def_id),
34-
pattern_arena: TypedArena::default(),
33+
pattern_arena: &pattern_arena,
3534
};
3635
visitor.visit_body(tcx.hir().body(body_id));
3736
}
@@ -40,14 +39,14 @@ fn create_e0004(sess: &Session, sp: Span, error_message: String) -> DiagnosticBu
4039
struct_span_err!(sess, sp, E0004, "{}", &error_message)
4140
}
4241

43-
struct MatchVisitor<'a, 'tcx> {
42+
struct MatchVisitor<'a, 'p, 'tcx> {
4443
tcx: TyCtxt<'tcx>,
4544
typeck_results: &'a ty::TypeckResults<'tcx>,
4645
param_env: ty::ParamEnv<'tcx>,
47-
pattern_arena: TypedArena<super::Pat<'tcx>>,
46+
pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
4847
}
4948

50-
impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
49+
impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> {
5150
type Map = intravisit::ErasedMap<'tcx>;
5251

5352
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
@@ -113,31 +112,30 @@ impl PatCtxt<'_, '_> {
113112
}
114113
}
115114

116-
impl<'tcx> MatchVisitor<'_, 'tcx> {
115+
impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
117116
fn check_patterns(&self, pat: &Pat<'_>) {
118117
pat.walk_always(|pat| check_borrow_conflicts_in_at_patterns(self, pat));
119118
check_for_bindings_named_same_as_variants(self, pat);
120119
}
121120

122-
fn lower_pattern<'p>(
121+
fn lower_pattern(
123122
&self,
124123
cx: &mut MatchCheckCtxt<'p, 'tcx>,
125124
pat: &'tcx hir::Pat<'tcx>,
126125
have_errors: &mut bool,
127-
) -> (&'p super::Pat<'tcx>, Ty<'tcx>) {
126+
) -> &'p DeconstructedPat<'p, 'tcx> {
128127
let mut patcx = PatCtxt::new(self.tcx, self.param_env, self.typeck_results);
129128
patcx.include_lint_checks();
130129
let pattern = patcx.lower_pattern(pat);
131-
let pattern_ty = pattern.ty;
132-
let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(pattern));
130+
let pattern: &_ = cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, &pattern));
133131
if !patcx.errors.is_empty() {
134132
*have_errors = true;
135133
patcx.report_inlining_errors();
136134
}
137-
(pattern, pattern_ty)
135+
pattern
138136
}
139137

140-
fn new_cx(&self, hir_id: HirId) -> MatchCheckCtxt<'_, 'tcx> {
138+
fn new_cx(&self, hir_id: HirId) -> MatchCheckCtxt<'p, 'tcx> {
141139
MatchCheckCtxt {
142140
tcx: self.tcx,
143141
param_env: self.param_env,
@@ -149,8 +147,8 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
149147
fn check_let(&mut self, pat: &'tcx hir::Pat<'tcx>, expr: &hir::Expr<'_>, span: Span) {
150148
self.check_patterns(pat);
151149
let mut cx = self.new_cx(expr.hir_id);
152-
let tpat = self.lower_pattern(&mut cx, pat, &mut false).0;
153-
check_let_reachability(&mut cx, pat.hir_id, &tpat, span);
150+
let tpat = self.lower_pattern(&mut cx, pat, &mut false);
151+
check_let_reachability(&mut cx, pat.hir_id, tpat, span);
154152
}
155153

156154
fn check_match(
@@ -166,8 +164,8 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
166164
self.check_patterns(&arm.pat);
167165
if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard {
168166
self.check_patterns(pat);
169-
let tpat = self.lower_pattern(&mut cx, pat, &mut false).0;
170-
check_let_reachability(&mut cx, pat.hir_id, &tpat, tpat.span);
167+
let tpat = self.lower_pattern(&mut cx, pat, &mut false);
168+
check_let_reachability(&mut cx, pat.hir_id, tpat, tpat.span());
171169
}
172170
}
173171

@@ -176,7 +174,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
176174
let arms: Vec<_> = arms
177175
.iter()
178176
.map(|hir::Arm { pat, guard, .. }| MatchArm {
179-
pat: self.lower_pattern(&mut cx, pat, &mut have_errors).0,
177+
pat: self.lower_pattern(&mut cx, pat, &mut have_errors),
180178
hir_id: pat.hir_id,
181179
has_guard: guard.is_some(),
182180
})
@@ -210,7 +208,8 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
210208
fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option<Span>) {
211209
let mut cx = self.new_cx(pat.hir_id);
212210

213-
let (pattern, pattern_ty) = self.lower_pattern(&mut cx, pat, &mut false);
211+
let pattern = self.lower_pattern(&mut cx, pat, &mut false);
212+
let pattern_ty = pattern.ty();
214213
let arms = vec![MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }];
215214
let report = compute_match_usefulness(&cx, &arms, pat.hir_id, pattern_ty);
216215

@@ -222,7 +221,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
222221
return;
223222
}
224223

225-
let joined_patterns = joined_uncovered_patterns(&witnesses);
224+
let joined_patterns = joined_uncovered_patterns(&cx, &witnesses);
226225
let mut err = struct_span_err!(
227226
self.tcx.sess,
228227
pat.span,
@@ -298,7 +297,7 @@ fn const_not_var(
298297
}
299298
}
300299

301-
fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pat<'_>) {
300+
fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_, '_>, pat: &Pat<'_>) {
302301
pat.walk_always(|p| {
303302
if let hir::PatKind::Binding(_, _, ident, None) = p.kind {
304303
if let Some(ty::BindByValue(hir::Mutability::Not)) =
@@ -340,12 +339,11 @@ fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pa
340339
}
341340

342341
/// Checks for common cases of "catchall" patterns that may not be intended as such.
343-
fn pat_is_catchall(pat: &super::Pat<'_>) -> bool {
344-
use PatKind::*;
345-
match &*pat.kind {
346-
Binding { subpattern: None, .. } => true,
347-
Binding { subpattern: Some(s), .. } | Deref { subpattern: s } => pat_is_catchall(s),
348-
Leaf { subpatterns: s } => s.iter().all(|p| pat_is_catchall(&p.pattern)),
342+
fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
343+
use Constructor::*;
344+
match pat.ctor() {
345+
Wildcard => true,
346+
Single => pat.iter_fields().all(|pat| pat_is_catchall(pat)),
349347
_ => false,
350348
}
351349
}
@@ -424,11 +422,11 @@ fn irrefutable_let_pattern(tcx: TyCtxt<'_>, id: HirId, span: Span) {
424422
fn check_let_reachability<'p, 'tcx>(
425423
cx: &mut MatchCheckCtxt<'p, 'tcx>,
426424
pat_id: HirId,
427-
pat: &'p super::Pat<'tcx>,
425+
pat: &'p DeconstructedPat<'p, 'tcx>,
428426
span: Span,
429427
) {
430428
let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }];
431-
let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty);
429+
let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty());
432430

433431
// Report if the pattern is unreachable, which can only occur when the type is uninhabited.
434432
// This also reports unreachable sub-patterns though, so we can't just replace it with an
@@ -450,7 +448,7 @@ fn report_arm_reachability<'p, 'tcx>(
450448
let mut catchall = None;
451449
for (arm, is_useful) in report.arm_usefulness.iter() {
452450
match is_useful {
453-
Unreachable => unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, catchall),
451+
Unreachable => unreachable_pattern(cx.tcx, arm.pat.span(), arm.hir_id, catchall),
454452
Reachable(unreachables) if unreachables.is_empty() => {}
455453
// The arm is reachable, but contains unreachable subpatterns (from or-patterns).
456454
Reachable(unreachables) => {
@@ -463,7 +461,7 @@ fn report_arm_reachability<'p, 'tcx>(
463461
}
464462
}
465463
if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) {
466-
catchall = Some(arm.pat.span);
464+
catchall = Some(arm.pat.span());
467465
}
468466
}
469467
}
@@ -473,7 +471,7 @@ fn non_exhaustive_match<'p, 'tcx>(
473471
cx: &MatchCheckCtxt<'p, 'tcx>,
474472
scrut_ty: Ty<'tcx>,
475473
sp: Span,
476-
witnesses: Vec<super::Pat<'tcx>>,
474+
witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
477475
is_empty_match: bool,
478476
) {
479477
let non_empty_enum = match scrut_ty.kind() {
@@ -490,7 +488,7 @@ fn non_exhaustive_match<'p, 'tcx>(
490488
format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty),
491489
);
492490
} else {
493-
let joined_patterns = joined_uncovered_patterns(&witnesses);
491+
let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
494492
err = create_e0004(
495493
cx.tcx.sess,
496494
sp,
@@ -517,7 +515,7 @@ fn non_exhaustive_match<'p, 'tcx>(
517515
if (scrut_ty == cx.tcx.types.usize || scrut_ty == cx.tcx.types.isize)
518516
&& !is_empty_match
519517
&& witnesses.len() == 1
520-
&& is_wildcard(&witnesses[0])
518+
&& matches!(witnesses[0].ctor(), Constructor::NonExhaustive)
521519
{
522520
err.note(&format!(
523521
"`{}` does not have a fixed maximum value, \
@@ -540,33 +538,40 @@ fn non_exhaustive_match<'p, 'tcx>(
540538
err.emit();
541539
}
542540

543-
crate fn joined_uncovered_patterns(witnesses: &[super::Pat<'_>]) -> String {
541+
crate fn joined_uncovered_patterns<'p, 'tcx>(
542+
cx: &MatchCheckCtxt<'p, 'tcx>,
543+
witnesses: &[DeconstructedPat<'p, 'tcx>],
544+
) -> String {
544545
const LIMIT: usize = 3;
546+
let pat_to_str = |pat: &DeconstructedPat<'p, 'tcx>| pat.to_pat(cx).to_string();
545547
match witnesses {
546548
[] => bug!(),
547-
[witness] => format!("`{}`", witness),
549+
[witness] => format!("`{}`", witness.to_pat(cx)),
548550
[head @ .., tail] if head.len() < LIMIT => {
549-
let head: Vec<_> = head.iter().map(<_>::to_string).collect();
550-
format!("`{}` and `{}`", head.join("`, `"), tail)
551+
let head: Vec<_> = head.iter().map(pat_to_str).collect();
552+
format!("`{}` and `{}`", head.join("`, `"), tail.to_pat(cx))
551553
}
552554
_ => {
553555
let (head, tail) = witnesses.split_at(LIMIT);
554-
let head: Vec<_> = head.iter().map(<_>::to_string).collect();
556+
let head: Vec<_> = head.iter().map(pat_to_str).collect();
555557
format!("`{}` and {} more", head.join("`, `"), tail.len())
556558
}
557559
}
558560
}
559561

560-
crate fn pattern_not_covered_label(witnesses: &[super::Pat<'_>], joined_patterns: &str) -> String {
562+
crate fn pattern_not_covered_label(
563+
witnesses: &[DeconstructedPat<'_, '_>],
564+
joined_patterns: &str,
565+
) -> String {
561566
format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns)
562567
}
563568

564569
/// Point at the definition of non-covered `enum` variants.
565-
fn adt_defined_here(
566-
cx: &MatchCheckCtxt<'_, '_>,
570+
fn adt_defined_here<'p, 'tcx>(
571+
cx: &MatchCheckCtxt<'p, 'tcx>,
567572
err: &mut DiagnosticBuilder<'_>,
568-
ty: Ty<'_>,
569-
witnesses: &[super::Pat<'_>],
573+
ty: Ty<'tcx>,
574+
witnesses: &[DeconstructedPat<'p, 'tcx>],
570575
) {
571576
let ty = ty.peel_refs();
572577
if let ty::Adt(def, _) = ty.kind() {
@@ -575,57 +580,42 @@ fn adt_defined_here(
575580
}
576581

577582
if witnesses.len() < 4 {
578-
for sp in maybe_point_at_variant(ty, &witnesses) {
583+
for sp in maybe_point_at_variant(cx, def, witnesses.iter()) {
579584
err.span_label(sp, "not covered");
580585
}
581586
}
582587
}
583588
}
584589

585-
fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[super::Pat<'_>]) -> Vec<Span> {
590+
fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>(
591+
cx: &MatchCheckCtxt<'p, 'tcx>,
592+
def: &AdtDef,
593+
patterns: impl Iterator<Item = &'a DeconstructedPat<'p, 'tcx>>,
594+
) -> Vec<Span> {
595+
use Constructor::*;
586596
let mut covered = vec![];
587-
if let ty::Adt(def, _) = ty.kind() {
588-
// Don't point at variants that have already been covered due to other patterns to avoid
589-
// visual clutter.
590-
for pattern in patterns {
591-
use PatKind::{AscribeUserType, Deref, Leaf, Or, Variant};
592-
match &*pattern.kind {
593-
AscribeUserType { subpattern, .. } | Deref { subpattern } => {
594-
covered.extend(maybe_point_at_variant(ty, slice::from_ref(&subpattern)));
595-
}
596-
Variant { adt_def, variant_index, subpatterns, .. } if adt_def.did == def.did => {
597-
let sp = def.variants[*variant_index].ident.span;
598-
if covered.contains(&sp) {
599-
continue;
600-
}
601-
covered.push(sp);
602-
603-
let pats = subpatterns
604-
.iter()
605-
.map(|field_pattern| field_pattern.pattern.clone())
606-
.collect::<Box<[_]>>();
607-
covered.extend(maybe_point_at_variant(ty, &pats));
608-
}
609-
Leaf { subpatterns } => {
610-
let pats = subpatterns
611-
.iter()
612-
.map(|field_pattern| field_pattern.pattern.clone())
613-
.collect::<Box<[_]>>();
614-
covered.extend(maybe_point_at_variant(ty, &pats));
597+
for pattern in patterns {
598+
if let Variant(variant_index) = pattern.ctor() {
599+
if let ty::Adt(this_def, _) = pattern.ty().kind() {
600+
if this_def.did != def.did {
601+
continue;
615602
}
616-
Or { pats } => {
617-
let pats = pats.iter().cloned().collect::<Box<[_]>>();
618-
covered.extend(maybe_point_at_variant(ty, &pats));
619-
}
620-
_ => {}
621603
}
604+
let sp = def.variants[*variant_index].ident.span;
605+
if covered.contains(&sp) {
606+
// Don't point at variants that have already been covered due to other patterns to avoid
607+
// visual clutter.
608+
continue;
609+
}
610+
covered.push(sp);
622611
}
612+
covered.extend(maybe_point_at_variant(cx, def, pattern.iter_fields()));
623613
}
624614
covered
625615
}
626616

627617
/// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
628-
fn is_binding_by_move(cx: &MatchVisitor<'_, '_>, hir_id: HirId, span: Span) -> bool {
618+
fn is_binding_by_move(cx: &MatchVisitor<'_, '_, '_>, hir_id: HirId, span: Span) -> bool {
629619
!cx.typeck_results.node_type(hir_id).is_copy_modulo_regions(cx.tcx.at(span), cx.param_env)
630620
}
631621

@@ -639,7 +629,7 @@ fn is_binding_by_move(cx: &MatchVisitor<'_, '_>, hir_id: HirId, span: Span) -> b
639629
/// - `x @ Some(ref mut? y)`.
640630
///
641631
/// This analysis is *not* subsumed by NLL.
642-
fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_>, pat: &Pat<'_>) {
632+
fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_, '_>, pat: &Pat<'_>) {
643633
// Extract `sub` in `binding @ sub`.
644634
let (name, sub) = match &pat.kind {
645635
hir::PatKind::Binding(.., name, Some(sub)) => (*name, sub),

0 commit comments

Comments
 (0)