Skip to content

Commit fedee8d

Browse files
committed
Reorder
1 parent 75b064d commit fedee8d

File tree

1 file changed

+151
-148
lines changed

1 file changed

+151
-148
lines changed

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

Lines changed: 151 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,111 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
572572
}
573573
}
574574

575+
/// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
576+
/// Check that there are no borrow or move conflicts in `binding @ subpat` patterns.
577+
///
578+
/// For example, this would reject:
579+
/// - `ref x @ Some(ref mut y)`,
580+
/// - `ref mut x @ Some(ref y)`,
581+
/// - `ref mut x @ Some(ref mut y)`,
582+
/// - `ref mut? x @ Some(y)`, and
583+
/// - `x @ Some(ref mut? y)`.
584+
///
585+
/// This analysis is *not* subsumed by NLL.
586+
fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, '_, 'tcx>, pat: &Pat<'tcx>) {
587+
// Extract `sub` in `binding @ sub`.
588+
let PatKind::Binding { name, mode, ty, subpattern: Some(box ref sub), .. } = pat.kind else {
589+
return;
590+
};
591+
592+
let is_binding_by_move = |ty: Ty<'tcx>| !ty.is_copy_modulo_regions(cx.tcx, cx.param_env);
593+
594+
let sess = cx.tcx.sess;
595+
596+
// Get the binding move, extract the mutability if by-ref.
597+
let mut_outer = match mode {
598+
BindingMode::ByValue if is_binding_by_move(ty) => {
599+
// We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`.
600+
let mut conflicts_ref = Vec::new();
601+
sub.each_binding(|_, mode, _, span| match mode {
602+
BindingMode::ByValue => {}
603+
BindingMode::ByRef(_) => conflicts_ref.push(span),
604+
});
605+
if !conflicts_ref.is_empty() {
606+
sess.emit_err(BorrowOfMovedValue {
607+
binding_span: pat.span,
608+
conflicts_ref,
609+
name,
610+
ty,
611+
suggest_borrowing: Some(pat.span.shrink_to_lo()),
612+
});
613+
}
614+
return;
615+
}
616+
BindingMode::ByValue => return,
617+
BindingMode::ByRef(m) => m.mutability(),
618+
};
619+
620+
// We now have `ref $mut_outer binding @ sub` (semantically).
621+
// Recurse into each binding in `sub` and find mutability or move conflicts.
622+
let mut conflicts_move = Vec::new();
623+
let mut conflicts_mut_mut = Vec::new();
624+
let mut conflicts_mut_ref = Vec::new();
625+
sub.each_binding(|name, mode, ty, span| {
626+
match mode {
627+
BindingMode::ByRef(mut_inner) => match (mut_outer, mut_inner.mutability()) {
628+
// Both sides are `ref`.
629+
(Mutability::Not, Mutability::Not) => {}
630+
// 2x `ref mut`.
631+
(Mutability::Mut, Mutability::Mut) => {
632+
conflicts_mut_mut.push(Conflict::Mut { span, name })
633+
}
634+
(Mutability::Not, Mutability::Mut) => {
635+
conflicts_mut_ref.push(Conflict::Mut { span, name })
636+
}
637+
(Mutability::Mut, Mutability::Not) => {
638+
conflicts_mut_ref.push(Conflict::Ref { span, name })
639+
}
640+
},
641+
BindingMode::ByValue if is_binding_by_move(ty) => {
642+
conflicts_move.push(Conflict::Moved { span, name }) // `ref mut?` + by-move conflict.
643+
}
644+
BindingMode::ByValue => {} // `ref mut?` + by-copy is fine.
645+
}
646+
});
647+
648+
let report_mut_mut = !conflicts_mut_mut.is_empty();
649+
let report_mut_ref = !conflicts_mut_ref.is_empty();
650+
let report_move_conflict = !conflicts_move.is_empty();
651+
652+
let mut occurrences = match mut_outer {
653+
Mutability::Mut => vec![Conflict::Mut { span: pat.span, name }],
654+
Mutability::Not => vec![Conflict::Ref { span: pat.span, name }],
655+
};
656+
occurrences.extend(conflicts_mut_mut);
657+
occurrences.extend(conflicts_mut_ref);
658+
occurrences.extend(conflicts_move);
659+
660+
// Report errors if any.
661+
if report_mut_mut {
662+
// Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
663+
sess.emit_err(MultipleMutBorrows { span: pat.span, occurrences });
664+
} else if report_mut_ref {
665+
// Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
666+
match mut_outer {
667+
Mutability::Mut => {
668+
sess.emit_err(AlreadyMutBorrowed { span: pat.span, occurrences });
669+
}
670+
Mutability::Not => {
671+
sess.emit_err(AlreadyBorrowed { span: pat.span, occurrences });
672+
}
673+
};
674+
} else if report_move_conflict {
675+
// Report by-ref and by-move conflicts, e.g. `ref x @ y`.
676+
sess.emit_err(MovedWhileBorrowed { span: pat.span, occurrences });
677+
}
678+
}
679+
575680
fn check_for_bindings_named_same_as_variants(
576681
cx: &MatchVisitor<'_, '_, '_>,
577682
pat: &Pat<'_>,
@@ -616,25 +721,6 @@ fn check_for_bindings_named_same_as_variants(
616721
});
617722
}
618723

619-
/// Checks for common cases of "catchall" patterns that may not be intended as such.
620-
fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
621-
use Constructor::*;
622-
match pat.ctor() {
623-
Wildcard => true,
624-
Single => pat.iter_fields().all(|pat| pat_is_catchall(pat)),
625-
_ => false,
626-
}
627-
}
628-
629-
fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>) {
630-
tcx.emit_spanned_lint(
631-
UNREACHABLE_PATTERNS,
632-
id,
633-
span,
634-
UnreachablePattern { span: if catchall.is_some() { Some(span) } else { None }, catchall },
635-
);
636-
}
637-
638724
fn irrefutable_let_patterns(
639725
tcx: TyCtxt<'_>,
640726
id: HirId,
@@ -680,19 +766,31 @@ fn report_arm_reachability<'p, 'tcx>(
680766
cx: &MatchCheckCtxt<'p, 'tcx>,
681767
report: &UsefulnessReport<'p, 'tcx>,
682768
) {
769+
let report_unreachable_pattern = |span, hir_id, catchall: Option<Span>| {
770+
cx.tcx.emit_spanned_lint(
771+
UNREACHABLE_PATTERNS,
772+
hir_id,
773+
span,
774+
UnreachablePattern {
775+
span: if catchall.is_some() { Some(span) } else { None },
776+
catchall,
777+
},
778+
);
779+
};
780+
683781
use Reachability::*;
684782
let mut catchall = None;
685783
for (arm, is_useful) in report.arm_usefulness.iter() {
686784
match is_useful {
687-
Unreachable => unreachable_pattern(cx.tcx, arm.pat.span(), arm.hir_id, catchall),
785+
Unreachable => report_unreachable_pattern(arm.pat.span(), arm.hir_id, catchall),
688786
Reachable(unreachables) if unreachables.is_empty() => {}
689787
// The arm is reachable, but contains unreachable subpatterns (from or-patterns).
690788
Reachable(unreachables) => {
691789
let mut unreachables = unreachables.clone();
692790
// Emit lints in the order in which they occur in the file.
693791
unreachables.sort_unstable();
694792
for span in unreachables {
695-
unreachable_pattern(cx.tcx, span, arm.hir_id, None);
793+
report_unreachable_pattern(span, arm.hir_id, None);
696794
}
697795
}
698796
}
@@ -702,22 +800,14 @@ fn report_arm_reachability<'p, 'tcx>(
702800
}
703801
}
704802

705-
fn collect_non_exhaustive_tys<'tcx>(
706-
tcx: TyCtxt<'tcx>,
707-
pat: &WitnessPat<'tcx>,
708-
non_exhaustive_tys: &mut FxHashSet<Ty<'tcx>>,
709-
) {
710-
if matches!(pat.ctor(), Constructor::NonExhaustive) {
711-
non_exhaustive_tys.insert(pat.ty());
712-
}
713-
if let Constructor::IntRange(range) = pat.ctor() {
714-
if range.is_beyond_boundaries(pat.ty(), tcx) {
715-
// The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`.
716-
non_exhaustive_tys.insert(pat.ty());
717-
}
803+
/// Checks for common cases of "catchall" patterns that may not be intended as such.
804+
fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
805+
use Constructor::*;
806+
match pat.ctor() {
807+
Wildcard => true,
808+
Single => pat.iter_fields().all(|pat| pat_is_catchall(pat)),
809+
_ => false,
718810
}
719-
pat.iter_fields()
720-
.for_each(|field_pat| collect_non_exhaustive_tys(tcx, field_pat, non_exhaustive_tys))
721811
}
722812

723813
/// Report that a match is not exhaustive.
@@ -755,7 +845,14 @@ fn non_exhaustive_match<'p, 'tcx>(
755845
sp,
756846
format!("non-exhaustive patterns: {joined_patterns} not covered"),
757847
);
758-
err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
848+
err.span_label(
849+
sp,
850+
format!(
851+
"pattern{} {} not covered",
852+
rustc_errors::pluralize!(witnesses.len()),
853+
joined_patterns
854+
),
855+
);
759856
patterns_len = witnesses.len();
760857
pattern = if witnesses.len() < 4 {
761858
witnesses
@@ -910,7 +1007,7 @@ fn non_exhaustive_match<'p, 'tcx>(
9101007
err.emit()
9111008
}
9121009

913-
pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
1010+
fn joined_uncovered_patterns<'p, 'tcx>(
9141011
cx: &MatchCheckCtxt<'p, 'tcx>,
9151012
witnesses: &[WitnessPat<'tcx>],
9161013
) -> String {
@@ -931,11 +1028,22 @@ pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
9311028
}
9321029
}
9331030

934-
pub(crate) fn pattern_not_covered_label(
935-
witnesses: &[WitnessPat<'_>],
936-
joined_patterns: &str,
937-
) -> String {
938-
format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns)
1031+
fn collect_non_exhaustive_tys<'tcx>(
1032+
tcx: TyCtxt<'tcx>,
1033+
pat: &WitnessPat<'tcx>,
1034+
non_exhaustive_tys: &mut FxHashSet<Ty<'tcx>>,
1035+
) {
1036+
if matches!(pat.ctor(), Constructor::NonExhaustive) {
1037+
non_exhaustive_tys.insert(pat.ty());
1038+
}
1039+
if let Constructor::IntRange(range) = pat.ctor() {
1040+
if range.is_beyond_boundaries(pat.ty(), tcx) {
1041+
// The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`.
1042+
non_exhaustive_tys.insert(pat.ty());
1043+
}
1044+
}
1045+
pat.iter_fields()
1046+
.for_each(|field_pat| collect_non_exhaustive_tys(tcx, field_pat, non_exhaustive_tys))
9391047
}
9401048

9411049
/// Point at the definition of non-covered `enum` variants.
@@ -997,108 +1105,3 @@ fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>(
9971105
}
9981106
covered
9991107
}
1000-
1001-
/// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
1002-
/// Check that there are no borrow or move conflicts in `binding @ subpat` patterns.
1003-
///
1004-
/// For example, this would reject:
1005-
/// - `ref x @ Some(ref mut y)`,
1006-
/// - `ref mut x @ Some(ref y)`,
1007-
/// - `ref mut x @ Some(ref mut y)`,
1008-
/// - `ref mut? x @ Some(y)`, and
1009-
/// - `x @ Some(ref mut? y)`.
1010-
///
1011-
/// This analysis is *not* subsumed by NLL.
1012-
fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, '_, 'tcx>, pat: &Pat<'tcx>) {
1013-
// Extract `sub` in `binding @ sub`.
1014-
let PatKind::Binding { name, mode, ty, subpattern: Some(box ref sub), .. } = pat.kind else {
1015-
return;
1016-
};
1017-
1018-
let is_binding_by_move = |ty: Ty<'tcx>| !ty.is_copy_modulo_regions(cx.tcx, cx.param_env);
1019-
1020-
let sess = cx.tcx.sess;
1021-
1022-
// Get the binding move, extract the mutability if by-ref.
1023-
let mut_outer = match mode {
1024-
BindingMode::ByValue if is_binding_by_move(ty) => {
1025-
// We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`.
1026-
let mut conflicts_ref = Vec::new();
1027-
sub.each_binding(|_, mode, _, span| match mode {
1028-
BindingMode::ByValue => {}
1029-
BindingMode::ByRef(_) => conflicts_ref.push(span),
1030-
});
1031-
if !conflicts_ref.is_empty() {
1032-
sess.emit_err(BorrowOfMovedValue {
1033-
binding_span: pat.span,
1034-
conflicts_ref,
1035-
name,
1036-
ty,
1037-
suggest_borrowing: Some(pat.span.shrink_to_lo()),
1038-
});
1039-
}
1040-
return;
1041-
}
1042-
BindingMode::ByValue => return,
1043-
BindingMode::ByRef(m) => m.mutability(),
1044-
};
1045-
1046-
// We now have `ref $mut_outer binding @ sub` (semantically).
1047-
// Recurse into each binding in `sub` and find mutability or move conflicts.
1048-
let mut conflicts_move = Vec::new();
1049-
let mut conflicts_mut_mut = Vec::new();
1050-
let mut conflicts_mut_ref = Vec::new();
1051-
sub.each_binding(|name, mode, ty, span| {
1052-
match mode {
1053-
BindingMode::ByRef(mut_inner) => match (mut_outer, mut_inner.mutability()) {
1054-
// Both sides are `ref`.
1055-
(Mutability::Not, Mutability::Not) => {}
1056-
// 2x `ref mut`.
1057-
(Mutability::Mut, Mutability::Mut) => {
1058-
conflicts_mut_mut.push(Conflict::Mut { span, name })
1059-
}
1060-
(Mutability::Not, Mutability::Mut) => {
1061-
conflicts_mut_ref.push(Conflict::Mut { span, name })
1062-
}
1063-
(Mutability::Mut, Mutability::Not) => {
1064-
conflicts_mut_ref.push(Conflict::Ref { span, name })
1065-
}
1066-
},
1067-
BindingMode::ByValue if is_binding_by_move(ty) => {
1068-
conflicts_move.push(Conflict::Moved { span, name }) // `ref mut?` + by-move conflict.
1069-
}
1070-
BindingMode::ByValue => {} // `ref mut?` + by-copy is fine.
1071-
}
1072-
});
1073-
1074-
let report_mut_mut = !conflicts_mut_mut.is_empty();
1075-
let report_mut_ref = !conflicts_mut_ref.is_empty();
1076-
let report_move_conflict = !conflicts_move.is_empty();
1077-
1078-
let mut occurrences = match mut_outer {
1079-
Mutability::Mut => vec![Conflict::Mut { span: pat.span, name }],
1080-
Mutability::Not => vec![Conflict::Ref { span: pat.span, name }],
1081-
};
1082-
occurrences.extend(conflicts_mut_mut);
1083-
occurrences.extend(conflicts_mut_ref);
1084-
occurrences.extend(conflicts_move);
1085-
1086-
// Report errors if any.
1087-
if report_mut_mut {
1088-
// Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
1089-
sess.emit_err(MultipleMutBorrows { span: pat.span, occurrences });
1090-
} else if report_mut_ref {
1091-
// Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
1092-
match mut_outer {
1093-
Mutability::Mut => {
1094-
sess.emit_err(AlreadyMutBorrowed { span: pat.span, occurrences });
1095-
}
1096-
Mutability::Not => {
1097-
sess.emit_err(AlreadyBorrowed { span: pat.span, occurrences });
1098-
}
1099-
};
1100-
} else if report_move_conflict {
1101-
// Report by-ref and by-move conflicts, e.g. `ref x @ y`.
1102-
sess.emit_err(MovedWhileBorrowed { span: pat.span, occurrences });
1103-
}
1104-
}

0 commit comments

Comments
 (0)