1use rustc_data_structures::fx::FxIndexMap;
2use rustc_middle::mir::*;
3use rustc_middle::ty::Ty;
4use rustc_span::Span;
5use tracing::debug;
67use crate::builder::Builder;
8use crate::builder::expr::as_place::PlaceBase;
9use crate::builder::matches::{Binding, Candidate, FlatPat, MatchPairTree, TestableCase};
1011impl<'a, 'tcx> Builder<'a, 'tcx> {
12/// Creates a false edge to `imaginary_target` and a real edge to
13 /// real_target. If `imaginary_target` is none, or is the same as the real
14 /// target, a Goto is generated instead to simplify the generated MIR.
15pub(crate) fn false_edges(
16&mut self,
17 from_block: BasicBlock,
18 real_target: BasicBlock,
19 imaginary_target: BasicBlock,
20 source_info: SourceInfo,
21 ) {
22if imaginary_target != real_target {
23self.cfg.terminate(
24from_block,
25source_info,
26 TerminatorKind::FalseEdge { real_target, imaginary_target },
27 );
28 } else {
29self.cfg.goto(from_block, source_info, real_target)
30 }
31 }
32}
3334/// Determine the set of places that have to be stable across match guards.
35///
36/// Returns a list of places that need a fake borrow along with a local to store it.
37///
38/// Match exhaustiveness checking is not able to handle the case where the place being matched on is
39/// mutated in the guards. We add "fake borrows" to the guards that prevent any mutation of the
40/// place being matched. There are a some subtleties:
41///
42/// 1. Borrowing `*x` doesn't prevent assigning to `x`. If `x` is a shared reference, the borrow
43/// isn't even tracked. As such we have to add fake borrows of any prefixes of a place.
44/// 2. We don't want `match x { (Some(_), _) => (), .. }` to conflict with mutable borrows of `x.1`, so we
45/// only add fake borrows for places which are bound or tested by the match.
46/// 3. We don't want `match x { Some(_) => (), .. }` to conflict with mutable borrows of `(x as
47/// Some).0`, so the borrows are a special shallow borrow that only affects the place and not its
48/// projections.
49/// ```rust
50/// let mut x = (Some(0), true);
51/// match x {
52/// (Some(_), false) => {}
53/// _ if { if let Some(ref mut y) = x.0 { *y += 1 }; true } => {}
54/// _ => {}
55/// }
56/// ```
57/// 4. The fake borrows may be of places in inactive variants, e.g. here we need to fake borrow `x`
58/// and `(x as Some).0`, but when we reach the guard `x` may not be `Some`.
59/// ```rust
60/// let mut x = (Some(Some(0)), true);
61/// match x {
62/// (Some(Some(_)), false) => {}
63/// _ if { if let Some(Some(ref mut y)) = x.0 { *y += 1 }; true } => {}
64/// _ => {}
65/// }
66/// ```
67/// So it would be UB to generate code for the fake borrows. They therefore have to be removed by
68/// a MIR pass run after borrow checking.
69pub(super) fn collect_fake_borrows<'tcx>(
70 cx: &mut Builder<'_, 'tcx>,
71 candidates: &[Candidate<'tcx>],
72 temp_span: Span,
73 scrutinee_base: PlaceBase,
74) -> Vec<(Place<'tcx>, Local, FakeBorrowKind)> {
75if candidates.iter().all(|candidate| !candidate.has_guard) {
76// Fake borrows are only used when there is a guard.
77return Vec::new();
78 }
79let mut collector =
80FakeBorrowCollector { cx, scrutinee_base, fake_borrows: FxIndexMap::default() };
81for candidate in candidates.iter() {
82 collector.visit_candidate(candidate);
83 }
84let fake_borrows = collector.fake_borrows;
85{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_mir_build/src/builder/matches/util.rs:85",
"rustc_mir_build::builder::matches::util",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/builder/matches/util.rs"),
::tracing_core::__macro_support::Option::Some(85u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::matches::util"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("add_fake_borrows fake_borrows = {0:?}",
fake_borrows) as &dyn Value))])
});
} else { ; }
};debug!("add_fake_borrows fake_borrows = {:?}", fake_borrows);
86let tcx = cx.tcx;
87fake_borrows88 .iter()
89 .map(|(matched_place, borrow_kind)| {
90let fake_borrow_deref_ty = matched_place.ty(&cx.local_decls, tcx).ty;
91let fake_borrow_ty =
92Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, fake_borrow_deref_ty);
93let mut fake_borrow_temp = LocalDecl::new(fake_borrow_ty, temp_span);
94fake_borrow_temp.local_info = ClearCrossCrate::Set(Box::new(LocalInfo::FakeBorrow));
95let fake_borrow_temp = cx.local_decls.push(fake_borrow_temp);
96 (*matched_place, fake_borrow_temp, *borrow_kind)
97 })
98 .collect()
99}
100101pub(super) struct FakeBorrowCollector<'a, 'b, 'tcx> {
102 cx: &'a mut Builder<'b, 'tcx>,
103/// Base of the scrutinee place. Used to distinguish bindings inside the scrutinee place from
104 /// bindings inside deref patterns.
105scrutinee_base: PlaceBase,
106/// Store for each place the kind of borrow to take. In case of conflicts, we take the strongest
107 /// borrow (i.e. Deep > Shallow).
108 /// Invariant: for any place in `fake_borrows`, all the prefixes of this place that are
109 /// dereferences are also borrowed with the same of stronger borrow kind.
110fake_borrows: FxIndexMap<Place<'tcx>, FakeBorrowKind>,
111}
112113impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
114// Fake borrow this place and its dereference prefixes.
115fn fake_borrow(&mut self, place: Place<'tcx>, kind: FakeBorrowKind) {
116if self.fake_borrows.get(&place).is_some_and(|k| *k >= kind) {
117return;
118 }
119self.fake_borrows.insert(place, kind);
120// Also fake borrow the prefixes of any fake borrow.
121self.fake_borrow_deref_prefixes(place, kind);
122 }
123124// Fake borrow the prefixes of this place that are dereferences.
125fn fake_borrow_deref_prefixes(&mut self, place: Place<'tcx>, kind: FakeBorrowKind) {
126for (place_ref, elem) in place.as_ref().iter_projections().rev() {
127if let ProjectionElem::Deref = elem {
128// Insert a shallow borrow after a deref. For other projections the borrow of
129 // `place_ref` will conflict with any mutation of `place.base`.
130let place = place_ref.to_place(self.cx.tcx);
131if self.fake_borrows.get(&place).is_some_and(|k| *k >= kind) {
132return;
133 }
134self.fake_borrows.insert(place, kind);
135 }
136 }
137 }
138139fn visit_candidate(&mut self, candidate: &Candidate<'tcx>) {
140for binding in &candidate.extra_data.bindings {
141if let super::SubpatternBindings::One(binding) = binding {
142self.visit_binding(binding);
143 }
144 }
145for match_pair in &candidate.match_pairs {
146self.visit_match_pair(match_pair);
147 }
148 }
149150fn visit_flat_pat(&mut self, flat_pat: &FlatPat<'tcx>) {
151for binding in &flat_pat.extra_data.bindings {
152if let super::SubpatternBindings::One(binding) = binding {
153self.visit_binding(binding);
154 }
155 }
156for match_pair in &flat_pat.match_pairs {
157self.visit_match_pair(match_pair);
158 }
159 }
160161fn visit_match_pair(&mut self, match_pair: &MatchPairTree<'tcx>) {
162if let TestableCase::Or { pats, .. } = &match_pair.testable_case {
163for flat_pat in pats.iter() {
164self.visit_flat_pat(flat_pat)
165 }
166 } else if #[allow(non_exhaustive_omitted_patterns)] match match_pair.testable_case {
TestableCase::Deref { .. } => true,
_ => false,
}matches!(match_pair.testable_case, TestableCase::Deref { .. }) {
167// The subpairs of a deref pattern are all places relative to the deref temporary, so we
168 // don't fake borrow them. Problem is, if we only shallowly fake-borrowed
169 // `match_pair.place`, this would allow:
170 // ```
171 // let mut b = Box::new(false);
172 // match b {
173 // deref!(true) => {} // not reached because `*b == false`
174 // _ if { *b = true; false } => {} // not reached because the guard is `false`
175 // deref!(false) => {} // not reached because the guard changed it
176 // // UB because we reached the unreachable.
177 // }
178 // ```
179 // Hence we fake borrow using a deep borrow.
180if let Some(place) = match_pair.place {
181self.fake_borrow(place, FakeBorrowKind::Deep);
182 }
183 } else {
184// Insert a Shallow borrow of any place that is switched on.
185if let Some(place) = match_pair.place {
186self.fake_borrow(place, FakeBorrowKind::Shallow);
187 }
188189for subpair in &match_pair.subpairs {
190self.visit_match_pair(subpair);
191 }
192 }
193 }
194195fn visit_binding(&mut self, Binding { source, .. }: &Binding<'tcx>) {
196if let PlaceBase::Local(l) = self.scrutinee_base
197 && l != source.local
198 {
199// The base of this place is a temporary created for deref patterns. We don't emit fake
200 // borrows for these as they are not initialized in all branches.
201return;
202 }
203204// Insert a borrows of prefixes of places that are bound and are
205 // behind a dereference projection.
206 //
207 // These borrows are taken to avoid situations like the following:
208 //
209 // match x[10] {
210 // _ if { x = &[0]; false } => (),
211 // y => (), // Out of bounds array access!
212 // }
213 //
214 // match *x {
215 // // y is bound by reference in the guard and then by copy in the
216 // // arm, so y is 2 in the arm!
217 // y if { y == 1 && (x = &2) == () } => y,
218 // _ => 3,
219 // }
220 //
221 // We don't just fake borrow the whole place because this is allowed:
222 // match u {
223 // _ if { u = true; false } => (),
224 // x => (),
225 // }
226self.fake_borrow_deref_prefixes(*source, FakeBorrowKind::Shallow);
227 }
228}
229230#[must_use]
231pub(crate) fn ref_pat_borrow_kind(ref_mutability: Mutability) -> BorrowKind {
232match ref_mutability {
233 Mutability::Mut => BorrowKind::Mut { kind: MutBorrowKind::Default },
234 Mutability::Not => BorrowKind::Shared,
235 }
236}