]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_mir/src/borrow_check/mod.rs
New upstream version 1.54.0+dfsg1
[rustc.git] / compiler / rustc_mir / src / borrow_check / mod.rs
CommitLineData
ff7c6d11
XL
1//! This query borrow-checks the MIR to (further) ensure it is not broken.
2
532ac7d7 3use rustc_data_structures::fx::{FxHashMap, FxHashSet};
0bf4aa26 4use rustc_data_structures::graph::dominators::Dominators;
ba9703b0 5use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorReported};
dfeec247 6use rustc_hir as hir;
f9f354fc 7use rustc_hir::def_id::LocalDefId;
5869c6ff 8use rustc_hir::Node;
dfeec247 9use rustc_index::bit_set::BitSet;
e74abb32 10use rustc_index::vec::IndexVec;
74b04a01 11use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
ba9703b0 12use rustc_middle::mir::{
f9f354fc 13 traversal, Body, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem,
fc512014 14 PlaceRef, VarDebugInfoContents,
ba9703b0
XL
15};
16use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind};
17use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind};
f9f354fc 18use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind};
ba9703b0 19use rustc_middle::ty::query::Providers;
5869c6ff 20use rustc_middle::ty::{self, CapturedPlace, ParamEnv, RegionVid, TyCtxt};
ba9703b0 21use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT};
f9f354fc 22use rustc_span::{Span, Symbol, DUMMY_SP};
ff7c6d11 23
74b04a01 24use either::Either;
dfeec247
XL
25use smallvec::SmallVec;
26use std::cell::RefCell;
0bf4aa26 27use std::collections::BTreeMap;
cdc7bbd5 28use std::iter;
532ac7d7
XL
29use std::mem;
30use std::rc::Rc;
ff7c6d11 31
74b04a01 32use crate::dataflow;
f9f354fc
XL
33use crate::dataflow::impls::{
34 Borrows, EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
35};
9fa01778 36use crate::dataflow::indexes::{BorrowIndex, InitIndex, MoveOutIndex, MovePathIndex};
74b04a01 37use crate::dataflow::move_paths::{InitLocation, LookupResult, MoveData, MoveError};
9fa01778 38use crate::dataflow::MoveDataParamEnv;
ba9703b0 39use crate::dataflow::{Analysis, BorrowckFlowState as Flows, BorrowckResults};
ff7c6d11 40
dfeec247 41use self::diagnostics::{AccessKind, RegionName};
83c7162d 42use self::location::LocationTable;
ff7c6d11
XL
43use self::prefixes::PrefixSet;
44use self::MutateMode::{JustWrite, WriteAndRead};
45
94b46f34
XL
46use self::path_utils::*;
47
dfeec247
XL
48mod borrow_set;
49mod constraint_generation;
50mod constraints;
f9f354fc 51mod def_use;
60c5eb7d 52mod diagnostics;
dfeec247 53mod facts;
dfeec247 54mod invalidation;
83c7162d 55mod location;
dfeec247
XL
56mod member_constraints;
57mod nll;
8faf50e0 58mod path_utils;
dfeec247
XL
59mod place_ext;
60mod places_conflict;
ff7c6d11 61mod prefixes;
dfeec247 62mod region_infer;
60c5eb7d 63mod renumber;
60c5eb7d 64mod type_check;
dfeec247
XL
65mod universal_regions;
66mod used_muts;
60c5eb7d 67
dfeec247 68crate use borrow_set::{BorrowData, BorrowSet};
74b04a01 69crate use nll::{PoloniusOutput, ToRegionVid};
dfeec247
XL
70crate use place_ext::PlaceExt;
71crate use places_conflict::{places_conflict, PlaceConflictBias};
72crate use region_infer::RegionInferenceContext;
ff7c6d11 73
48663c56
XL
74// FIXME(eddyb) perhaps move this somewhere more centrally.
75#[derive(Debug)]
5869c6ff 76crate struct Upvar<'tcx> {
5869c6ff 77 place: CapturedPlace<'tcx>,
48663c56
XL
78
79 /// If true, the capture is behind a reference.
80 by_ref: bool,
48663c56
XL
81}
82
74b04a01
XL
83const DEREF_PROJECTION: &[PlaceElem<'_>; 1] = &[ProjectionElem::Deref];
84
f035d41b 85pub fn provide(providers: &mut Providers) {
3dfed10e
XL
86 *providers = Providers {
87 mir_borrowck: |tcx, did| {
88 if let Some(def) = ty::WithOptConstParam::try_lookup(did, tcx) {
89 tcx.mir_borrowck_const_arg(def)
90 } else {
91 mir_borrowck(tcx, ty::WithOptConstParam::unknown(did))
92 }
93 },
94 mir_borrowck_const_arg: |tcx, (did, param_did)| {
95 mir_borrowck(tcx, ty::WithOptConstParam { did, const_param_did: Some(param_did) })
96 },
97 ..*providers
98 };
ff7c6d11
XL
99}
100
3dfed10e
XL
101fn mir_borrowck<'tcx>(
102 tcx: TyCtxt<'tcx>,
103 def: ty::WithOptConstParam<LocalDefId>,
104) -> &'tcx BorrowCheckResult<'tcx> {
105 let (input_body, promoted) = tcx.mir_promoted(def);
106 debug!("run query mir_borrowck: {}", tcx.def_path_str(def.did.to_def_id()));
ff7c6d11 107
ff7c6d11 108 let opt_closure_req = tcx.infer_ctxt().enter(|infcx| {
dc9dc135 109 let input_body: &Body<'_> = &input_body.borrow();
e1599b0c 110 let promoted: &IndexVec<_, _> = &promoted.borrow();
29967ef6 111 do_mir_borrowck(&infcx, input_body, promoted)
ff7c6d11
XL
112 });
113 debug!("mir_borrowck done");
114
3dfed10e 115 tcx.arena.alloc(opt_closure_req)
ff7c6d11
XL
116}
117
dc9dc135
XL
118fn do_mir_borrowck<'a, 'tcx>(
119 infcx: &InferCtxt<'a, 'tcx>,
120 input_body: &Body<'tcx>,
f9f354fc 121 input_promoted: &IndexVec<Promoted, Body<'tcx>>,
dc9dc135 122) -> BorrowCheckResult<'tcx> {
29967ef6
XL
123 let def = input_body.source.with_opt_param().as_local().unwrap();
124
3dfed10e 125 debug!("do_mir_borrowck(def = {:?})", def);
8faf50e0 126
ff7c6d11 127 let tcx = infcx.tcx;
3dfed10e
XL
128 let param_env = tcx.param_env(def.did);
129 let id = tcx.hir().local_def_id_to_hir_id(def.did);
ff7c6d11 130
60c5eb7d
XL
131 let mut local_names = IndexVec::from_elem(None, &input_body.local_decls);
132 for var_debug_info in &input_body.var_debug_info {
fc512014
XL
133 if let VarDebugInfoContents::Place(place) = var_debug_info.value {
134 if let Some(local) = place.as_local() {
135 if let Some(prev_name) = local_names[local] {
136 if var_debug_info.name != prev_name {
137 span_bug!(
138 var_debug_info.source_info.span,
139 "local {:?} has many names (`{}` vs `{}`)",
140 local,
141 prev_name,
142 var_debug_info.name
143 );
144 }
60c5eb7d 145 }
fc512014 146 local_names[local] = Some(var_debug_info.name);
60c5eb7d 147 }
60c5eb7d
XL
148 }
149 }
150
48663c56 151 // Gather the upvars of a closure, if any.
3dfed10e 152 let tables = tcx.typeck_opt_const_arg(def);
ba9703b0 153 if let Some(ErrorReported) = tables.tainted_by_errors {
74b04a01
XL
154 infcx.set_tainted_by_errors();
155 }
48663c56 156 let upvars: Vec<_> = tables
fc512014
XL
157 .closure_min_captures_flattened(def.did.to_def_id())
158 .map(|captured_place| {
fc512014 159 let capture = captured_place.info.capture_kind;
48663c56 160 let by_ref = match capture {
1b1a35ee 161 ty::UpvarCapture::ByValue(_) => false,
48663c56
XL
162 ty::UpvarCapture::ByRef(..) => true,
163 };
17df50a5 164 Upvar { place: captured_place.clone(), by_ref }
48663c56
XL
165 })
166 .collect();
167
83c7162d
XL
168 // Replace all regions with fresh inference variables. This
169 // requires first making our own copy of the MIR. This copy will
170 // be modified (in place) to contain non-lexical lifetimes. It
171 // will have a lifetime tied to the inference context.
f9f354fc 172 let mut body = input_body.clone();
60c5eb7d 173 let mut promoted = input_promoted.clone();
29967ef6 174 let free_regions = nll::replace_regions_in_mir(infcx, param_env, &mut body, &mut promoted);
f9f354fc 175 let body = &body; // no further changes
60c5eb7d
XL
176
177 let location_table = &LocationTable::new(&body);
ff7c6d11 178
8faf50e0 179 let mut errors_buffer = Vec::new();
f9f354fc 180 let (move_data, move_errors): (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>) =
60c5eb7d 181 match MoveData::gather_moves(&body, tcx, param_env) {
f9f354fc
XL
182 Ok(move_data) => (move_data, Vec::new()),
183 Err((move_data, move_errors)) => (move_data, move_errors),
8faf50e0 184 };
f9f354fc
XL
185 let promoted_errors = promoted
186 .iter_enumerated()
187 .map(|(idx, body)| (idx, MoveData::gather_moves(&body, tcx, param_env)));
ff7c6d11 188
dfeec247 189 let mdpe = MoveDataParamEnv { move_data, param_env };
ff7c6d11 190
74b04a01 191 let mut flow_inits = MaybeInitializedPlaces::new(tcx, &body, &mdpe)
29967ef6 192 .into_engine(tcx, &body)
1b1a35ee 193 .pass_name("borrowck")
74b04a01
XL
194 .iterate_to_fixpoint()
195 .into_results_cursor(&body);
ff7c6d11 196
dc9dc135 197 let locals_are_invalidated_at_exit = tcx.hir().body_owner_kind(id).is_fn_or_closure();
dfeec247
XL
198 let borrow_set =
199 Rc::new(BorrowSet::build(tcx, body, locals_are_invalidated_at_exit, &mdpe.move_data));
200
201 // Compute non-lexical lifetimes.
74b04a01
XL
202 let nll::NllOutput {
203 regioncx,
204 opaque_type_values,
205 polonius_output,
206 opt_closure_req,
207 nll_errors,
208 } = nll::compute_regions(
209 infcx,
74b04a01
XL
210 free_regions,
211 body,
212 &promoted,
213 location_table,
214 param_env,
215 &mut flow_inits,
216 &mdpe.move_data,
217 &borrow_set,
f035d41b 218 &upvars,
74b04a01 219 );
83c7162d 220
dfeec247
XL
221 // Dump MIR results into a file, if that is enabled. This let us
222 // write unit-tests, as well as helping with debugging.
29967ef6 223 nll::dump_mir_results(infcx, &body, &regioncx, &opt_closure_req);
dfeec247 224
74b04a01 225 // We also have a `#[rustc_regions]` annotation that causes us to dump
dfeec247 226 // information.
74b04a01
XL
227 nll::dump_annotation(
228 infcx,
229 &body,
74b04a01
XL
230 &regioncx,
231 &opt_closure_req,
232 &opaque_type_values,
233 &mut errors_buffer,
234 );
b7449926
XL
235
236 // The various `flow_*` structures can be large. We drop `flow_inits` here
237 // so it doesn't overlap with the others below. This reduces peak memory
238 // usage significantly on some benchmarks.
239 drop(flow_inits);
240
83c7162d 241 let regioncx = Rc::new(regioncx);
ff7c6d11 242
6a06907d 243 let flow_borrows = Borrows::new(tcx, &body, &regioncx, &borrow_set)
29967ef6 244 .into_engine(tcx, &body)
1b1a35ee 245 .pass_name("borrowck")
74b04a01
XL
246 .iterate_to_fixpoint();
247 let flow_uninits = MaybeUninitializedPlaces::new(tcx, &body, &mdpe)
29967ef6 248 .into_engine(tcx, &body)
1b1a35ee 249 .pass_name("borrowck")
74b04a01
XL
250 .iterate_to_fixpoint();
251 let flow_ever_inits = EverInitializedPlaces::new(tcx, &body, &mdpe)
29967ef6 252 .into_engine(tcx, &body)
1b1a35ee 253 .pass_name("borrowck")
74b04a01 254 .iterate_to_fixpoint();
0531ce1d 255
dc9dc135 256 let movable_generator = match tcx.hir().get(id) {
b7449926 257 Node::Expr(&hir::Expr {
60c5eb7d 258 kind: hir::ExprKind::Closure(.., Some(hir::Movability::Static)),
2c00a5a8 259 ..
83c7162d
XL
260 }) => false,
261 _ => true,
2c00a5a8
XL
262 };
263
f9f354fc
XL
264 for (idx, move_data_results) in promoted_errors {
265 let promoted_body = &promoted[idx];
f9f354fc
XL
266
267 if let Err((move_data, move_errors)) = move_data_results {
268 let mut promoted_mbcx = MirBorrowckCtxt {
269 infcx,
1b1a35ee 270 param_env,
f9f354fc 271 body: promoted_body,
f9f354fc 272 move_data: &move_data,
6a06907d 273 location_table, // no need to create a real one for the promoted, it is not used
f9f354fc 274 movable_generator,
f035d41b 275 fn_self_span_reported: Default::default(),
f9f354fc
XL
276 locals_are_invalidated_at_exit,
277 access_place_error_reported: Default::default(),
278 reservation_error_reported: Default::default(),
279 reservation_warnings: Default::default(),
280 move_error_reported: BTreeMap::new(),
281 uninitialized_error_reported: Default::default(),
282 errors_buffer,
283 regioncx: regioncx.clone(),
284 used_mut: Default::default(),
285 used_mut_upvars: SmallVec::new(),
6a06907d
XL
286 borrow_set: Rc::clone(&borrow_set),
287 dominators: Dominators::dummy(), // not used
f9f354fc
XL
288 upvars: Vec::new(),
289 local_names: IndexVec::from_elem(None, &promoted_body.local_decls),
290 region_names: RefCell::default(),
291 next_region_name: RefCell::new(1),
292 polonius_output: None,
293 };
294 promoted_mbcx.report_move_errors(move_errors);
295 errors_buffer = promoted_mbcx.errors_buffer;
296 };
297 }
298
dc9dc135 299 let dominators = body.dominators();
83c7162d 300
ff7c6d11 301 let mut mbcx = MirBorrowckCtxt {
0bf4aa26 302 infcx,
1b1a35ee 303 param_env,
dc9dc135 304 body,
ff7c6d11 305 move_data: &mdpe.move_data,
8faf50e0 306 location_table,
2c00a5a8 307 movable_generator,
b7449926 308 locals_are_invalidated_at_exit,
f035d41b 309 fn_self_span_reported: Default::default(),
0bf4aa26
XL
310 access_place_error_reported: Default::default(),
311 reservation_error_reported: Default::default(),
532ac7d7 312 reservation_warnings: Default::default(),
0bf4aa26
XL
313 move_error_reported: BTreeMap::new(),
314 uninitialized_error_reported: Default::default(),
8faf50e0 315 errors_buffer,
6a06907d 316 regioncx: Rc::clone(&regioncx),
0bf4aa26 317 used_mut: Default::default(),
83c7162d 318 used_mut_upvars: SmallVec::new(),
6a06907d 319 borrow_set: Rc::clone(&borrow_set),
83c7162d 320 dominators,
48663c56 321 upvars,
60c5eb7d 322 local_names,
dfeec247
XL
323 region_names: RefCell::default(),
324 next_region_name: RefCell::new(1),
74b04a01 325 polonius_output,
ff7c6d11
XL
326 };
327
dfeec247
XL
328 // Compute and report region errors, if any.
329 mbcx.report_region_errors(nll_errors);
330
74b04a01
XL
331 let results = BorrowckResults {
332 ever_inits: flow_ever_inits,
333 uninits: flow_uninits,
334 borrows: flow_borrows,
335 };
ff7c6d11 336
f9f354fc 337 mbcx.report_move_errors(move_errors);
74b04a01 338
ba9703b0
XL
339 dataflow::visit_results(
340 &body,
341 traversal::reverse_postorder(&body).map(|(bb, _)| bb),
74b04a01
XL
342 &results,
343 &mut mbcx,
344 );
ff7c6d11 345
532ac7d7 346 // Convert any reservation warnings into lints.
416331ca 347 let reservation_warnings = mem::take(&mut mbcx.reservation_warnings);
48663c56 348 for (_, (place, span, location, bk, borrow)) in reservation_warnings {
ba9703b0 349 let mut initial_diag = mbcx.report_conflicting_borrow(location, (place, span), bk, &borrow);
532ac7d7 350
60c5eb7d
XL
351 let scope = mbcx.body.source_info(location).scope;
352 let lint_root = match &mbcx.body.source_scopes[scope].local_data {
353 ClearCrossCrate::Set(data) => data.lint_root,
354 _ => id,
532ac7d7
XL
355 };
356
357 // Span and message don't matter; we overwrite them below anyway
74b04a01 358 mbcx.infcx.tcx.struct_span_lint_hir(
dfeec247
XL
359 MUTABLE_BORROW_RESERVATION_CONFLICT,
360 lint_root,
361 DUMMY_SP,
74b04a01
XL
362 |lint| {
363 let mut diag = lint.build("");
532ac7d7 364
74b04a01
XL
365 diag.message = initial_diag.styled_message().clone();
366 diag.span = initial_diag.span.clone();
532ac7d7 367
74b04a01
XL
368 diag.buffer(&mut mbcx.errors_buffer);
369 },
370 );
532ac7d7 371 initial_diag.cancel();
532ac7d7
XL
372 }
373
83c7162d
XL
374 // For each non-user used mutable variable, check if it's been assigned from
375 // a user-declared local. If so, then put that local into the used_mut set.
376 // Note that this set is expected to be small - only upvars from closures
377 // would have a chance of erroneously adding non-user-defined mutable vars
378 // to the set.
dfeec247
XL
379 let temporary_used_locals: FxHashSet<Local> = mbcx
380 .used_mut
381 .iter()
60c5eb7d 382 .filter(|&local| !mbcx.body.local_decls[*local].is_user_variable())
8faf50e0
XL
383 .cloned()
384 .collect();
a1dfa0c6
XL
385 // For the remaining unused locals that are marked as mutable, we avoid linting any that
386 // were never initialized. These locals may have been removed as unreachable code; or will be
387 // linted as unused variables.
dfeec247
XL
388 let unused_mut_locals =
389 mbcx.body.mut_vars_iter().filter(|local| !mbcx.used_mut.contains(local)).collect();
a1dfa0c6 390 mbcx.gather_used_muts(temporary_used_locals, unused_mut_locals);
83c7162d
XL
391
392 debug!("mbcx.used_mut: {:?}", mbcx.used_mut);
8faf50e0 393 let used_mut = mbcx.used_mut;
dfeec247 394 for local in mbcx.body.mut_vars_and_args_iter().filter(|local| !used_mut.contains(local)) {
60c5eb7d
XL
395 let local_decl = &mbcx.body.local_decls[local];
396 let lint_root = match &mbcx.body.source_scopes[local_decl.source_info.scope].local_data {
397 ClearCrossCrate::Set(data) => data.lint_root,
398 _ => continue,
399 };
83c7162d 400
60c5eb7d
XL
401 // Skip over locals that begin with an underscore or have no name
402 match mbcx.local_names[local] {
dfeec247 403 Some(name) => {
74b04a01 404 if name.as_str().starts_with('_') {
dfeec247
XL
405 continue;
406 }
407 }
60c5eb7d
XL
408 None => continue,
409 }
83c7162d 410
60c5eb7d
XL
411 let span = local_decl.source_info.span;
412 if span.desugaring_kind().is_some() {
413 // If the `mut` arises as part of a desugaring, we should ignore it.
414 continue;
8faf50e0 415 }
60c5eb7d 416
74b04a01
XL
417 tcx.struct_span_lint_hir(UNUSED_MUT, lint_root, span, |lint| {
418 let mut_span = tcx.sess.source_map().span_until_non_whitespace(span);
419 lint.build("variable does not need to be mutable")
420 .span_suggestion_short(
421 mut_span,
422 "remove this `mut`",
423 String::new(),
424 Applicability::MachineApplicable,
425 )
426 .emit();
427 })
8faf50e0
XL
428 }
429
0bf4aa26
XL
430 // Buffer any move errors that we collected and de-duplicated.
431 for (_, (_, diag)) in mbcx.move_error_reported {
432 diag.buffer(&mut mbcx.errors_buffer);
433 }
434
435 if !mbcx.errors_buffer.is_empty() {
60c5eb7d 436 mbcx.errors_buffer.sort_by_key(|diag| diag.sort_span);
b7449926 437
8faf50e0 438 for diag in mbcx.errors_buffer.drain(..) {
e1599b0c 439 mbcx.infcx.tcx.sess.diagnostic().emit_diagnostic(&diag);
83c7162d
XL
440 }
441 }
442
8faf50e0 443 let result = BorrowCheckResult {
74b04a01 444 concrete_opaque_types: opaque_type_values,
83c7162d
XL
445 closure_requirements: opt_closure_req,
446 used_mut_upvars: mbcx.used_mut_upvars,
8faf50e0
XL
447 };
448
449 debug!("do_mir_borrowck: result = {:#?}", result);
450
451 result
ff7c6d11
XL
452}
453
416331ca
XL
454crate struct MirBorrowckCtxt<'cx, 'tcx> {
455 crate infcx: &'cx InferCtxt<'cx, 'tcx>,
1b1a35ee 456 param_env: ParamEnv<'tcx>,
f9f354fc 457 body: &'cx Body<'tcx>,
ff7c6d11 458 move_data: &'cx MoveData<'tcx>,
8faf50e0
XL
459
460 /// Map from MIR `Location` to `LocationIndex`; created
461 /// when MIR borrowck begins.
462 location_table: &'cx LocationTable,
463
2c00a5a8 464 movable_generator: bool,
ff7c6d11
XL
465 /// This keeps track of whether local variables are free-ed when the function
466 /// exits even without a `StorageDead`, which appears to be the case for
467 /// constants.
468 ///
469 /// I'm not sure this is the right approach - @eddyb could you try and
470 /// figure this out?
471 locals_are_invalidated_at_exit: bool,
2c00a5a8
XL
472 /// This field keeps track of when borrow errors are reported in the access_place function
473 /// so that there is no duplicate reporting. This field cannot also be used for the conflicting
474 /// borrow errors that is handled by the `reservation_error_reported` field as the inclusion
475 /// of the `Span` type (while required to mute some errors) stops the muting of the reservation
476 /// errors.
477 access_place_error_reported: FxHashSet<(Place<'tcx>, Span)>,
ff7c6d11
XL
478 /// This field keeps track of when borrow conflict errors are reported
479 /// for reservations, so that we don't report seemingly duplicate
9fa01778
XL
480 /// errors for corresponding activations.
481 //
482 // FIXME: ideally this would be a set of `BorrowIndex`, not `Place`s,
483 // but it is currently inconvenient to track down the `BorrowIndex`
484 // at the time we detect and report a reservation error.
ff7c6d11 485 reservation_error_reported: FxHashSet<Place<'tcx>>,
f035d41b
XL
486 /// This fields keeps track of the `Span`s that we have
487 /// used to report extra information for `FnSelfUse`, to avoid
488 /// unnecessarily verbose errors.
489 fn_self_span_reported: FxHashSet<Span>,
532ac7d7
XL
490 /// Migration warnings to be reported for #56254. We delay reporting these
491 /// so that we can suppress the warning if there's a corresponding error
492 /// for the activation of the borrow.
dfeec247
XL
493 reservation_warnings:
494 FxHashMap<BorrowIndex, (Place<'tcx>, Span, Location, BorrowKind, BorrowData<'tcx>)>,
74b04a01 495 /// This field keeps track of move errors that are to be reported for given move indices.
0bf4aa26
XL
496 ///
497 /// There are situations where many errors can be reported for a single move out (see #53807)
498 /// and we want only the best of those errors.
499 ///
500 /// The `report_use_of_moved_or_uninitialized` function checks this map and replaces the
501 /// diagnostic (if there is one) if the `Place` of the error being reported is a prefix of the
502 /// `Place` of the previous most diagnostic. This happens instead of buffering the error. Once
503 /// all move errors have been reported, any diagnostics in this map are added to the buffer
504 /// to be emitted.
505 ///
506 /// `BTreeMap` is used to preserve the order of insertions when iterating. This is necessary
507 /// when errors in the map are being re-added to the error buffer so that errors with the
508 /// same primary span come out in a consistent order.
74b04a01 509 move_error_reported: BTreeMap<Vec<MoveOutIndex>, (PlaceRef<'tcx>, DiagnosticBuilder<'cx>)>,
0bf4aa26 510 /// This field keeps track of errors reported in the checking of uninitialized variables,
8faf50e0 511 /// so that we don't report seemingly duplicate errors.
74b04a01 512 uninitialized_error_reported: FxHashSet<PlaceRef<'tcx>>,
8faf50e0
XL
513 /// Errors to be reported buffer
514 errors_buffer: Vec<Diagnostic>,
83c7162d
XL
515 /// This field keeps track of all the local variables that are declared mut and are mutated.
516 /// Used for the warning issued by an unused mutable local variable.
517 used_mut: FxHashSet<Local>,
518 /// If the function we're checking is a closure, then we'll need to report back the list of
519 /// mutable upvars that have been used. This field keeps track of them.
520 used_mut_upvars: SmallVec<[Field; 8]>,
dfeec247 521 /// Region inference context. This contains the results from region inference and lets us e.g.
ff7c6d11 522 /// find out which CFG points are contained in each borrow region.
dfeec247 523 regioncx: Rc<RegionInferenceContext<'tcx>>,
83c7162d
XL
524
525 /// The set of borrows extracted from the MIR
526 borrow_set: Rc<BorrowSet<'tcx>>,
527
528 /// Dominators for MIR
529 dominators: Dominators<BasicBlock>,
48663c56
XL
530
531 /// Information about upvars not necessarily preserved in types or MIR
5869c6ff 532 upvars: Vec<Upvar<'tcx>>,
60c5eb7d
XL
533
534 /// Names of local (user) variables (extracted from `var_debug_info`).
f9f354fc 535 local_names: IndexVec<Local, Option<Symbol>>,
dfeec247
XL
536
537 /// Record the region names generated for each region in the given
538 /// MIR def so that we can reuse them later in help/error messages.
539 region_names: RefCell<FxHashMap<RegionVid, RegionName>>,
540
541 /// The counter for generating new region names.
542 next_region_name: RefCell<usize>,
74b04a01
XL
543
544 /// Results of Polonius analysis.
545 polonius_output: Option<Rc<PoloniusOutput>>,
ff7c6d11
XL
546}
547
548// Check that:
549// 1. assignments are always made to mutable locations (FIXME: does that still really go here?)
550// 2. loans made in overlapping scopes do not conflict
551// 3. assignments do not affect things loaned out as immutable
552// 4. moves do not affect things loaned out in any way
ba9703b0 553impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tcx> {
dc9dc135 554 type FlowState = Flows<'cx, 'tcx>;
ff7c6d11 555
f9f354fc 556 fn visit_statement_before_primary_effect(
ff7c6d11 557 &mut self,
74b04a01 558 flow_state: &Flows<'cx, 'tcx>,
416331ca 559 stmt: &'cx Statement<'tcx>,
74b04a01 560 location: Location,
ff7c6d11 561 ) {
74b04a01 562 debug!("MirBorrowckCtxt::process_statement({:?}, {:?}): {:?}", location, stmt, flow_state);
ff7c6d11
XL
563 let span = stmt.source_info.span;
564
565 self.check_activations(location, span, flow_state);
566
ba9703b0
XL
567 match &stmt.kind {
568 StatementKind::Assign(box (lhs, ref rhs)) => {
dfeec247 569 self.consume_rvalue(location, (rhs, span), flow_state);
2c00a5a8 570
ba9703b0 571 self.mutate_place(location, (*lhs, span), Shallow(None), JustWrite, flow_state);
ff7c6d11 572 }
cdc7bbd5 573 StatementKind::FakeRead(box (_, ref place)) => {
0bf4aa26
XL
574 // Read for match doesn't access any memory and is used to
575 // assert that a place is safe and live. So we don't have to
576 // do any checks here.
577 //
578 // FIXME: Remove check that the place is initialized. This is
579 // needed for now because matches don't have never patterns yet.
580 // So this is the only place we prevent
581 // let x: !;
582 // match x {};
583 // from compiling.
584 self.check_if_path_or_subpath_is_moved(
48663c56 585 location,
0bf4aa26 586 InitializationRequiringAction::Use,
416331ca 587 (place.as_ref(), span),
8faf50e0
XL
588 flow_state,
589 );
94b46f34 590 }
ba9703b0
XL
591 StatementKind::SetDiscriminant { place, variant_index: _ } => {
592 self.mutate_place(location, (**place, span), Shallow(None), JustWrite, flow_state);
ff7c6d11 593 }
ba9703b0 594 StatementKind::LlvmInlineAsm(ref asm) => {
cdc7bbd5 595 for (o, output) in iter::zip(&asm.asm.outputs, &*asm.outputs) {
ff7c6d11
XL
596 if o.is_indirect {
597 // FIXME(eddyb) indirect inline asm outputs should
532ac7d7 598 // be encoded through MIR place derefs instead.
ff7c6d11 599 self.access_place(
48663c56 600 location,
ba9703b0 601 (*output, o.span),
ff7c6d11
XL
602 (Deep, Read(ReadKind::Copy)),
603 LocalMutationIsAllowed::No,
604 flow_state,
605 );
83c7162d 606 self.check_if_path_or_subpath_is_moved(
48663c56 607 location,
ff7c6d11 608 InitializationRequiringAction::Use,
416331ca 609 (output.as_ref(), o.span),
ff7c6d11
XL
610 flow_state,
611 );
612 } else {
613 self.mutate_place(
48663c56 614 location,
ba9703b0 615 (*output, o.span),
ff7c6d11
XL
616 if o.is_rw { Deep } else { Shallow(None) },
617 if o.is_rw { WriteAndRead } else { JustWrite },
618 flow_state,
619 );
620 }
621 }
532ac7d7 622 for (_, input) in asm.inputs.iter() {
48663c56 623 self.consume_operand(location, (input, span), flow_state);
ff7c6d11
XL
624 }
625 }
6a06907d
XL
626
627 StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping {
628 ..
629 }) => {
630 span_bug!(
631 span,
632 "Unexpected CopyNonOverlapping, should only appear after lower_intrinsics",
633 )
634 }
8faf50e0 635 StatementKind::Nop
3dfed10e 636 | StatementKind::Coverage(..)
b7449926 637 | StatementKind::AscribeUserType(..)
a1dfa0c6 638 | StatementKind::Retag { .. }
8faf50e0 639 | StatementKind::StorageLive(..) => {
a1dfa0c6 640 // `Nop`, `AscribeUserType`, `Retag`, and `StorageLive` are irrelevant
ff7c6d11
XL
641 // to borrow check.
642 }
ff7c6d11
XL
643 StatementKind::StorageDead(local) => {
644 self.access_place(
48663c56 645 location,
ba9703b0 646 (Place::from(*local), span),
ff7c6d11
XL
647 (Shallow(None), Write(WriteKind::StorageDeadOrDrop)),
648 LocalMutationIsAllowed::Yes,
649 flow_state,
650 );
651 }
652 }
653 }
654
f9f354fc 655 fn visit_terminator_before_primary_effect(
ff7c6d11 656 &mut self,
74b04a01 657 flow_state: &Flows<'cx, 'tcx>,
416331ca 658 term: &'cx Terminator<'tcx>,
74b04a01 659 loc: Location,
ff7c6d11 660 ) {
74b04a01 661 debug!("MirBorrowckCtxt::process_terminator({:?}, {:?}): {:?}", loc, term, flow_state);
ff7c6d11
XL
662 let span = term.source_info.span;
663
74b04a01 664 self.check_activations(loc, span, flow_state);
ff7c6d11
XL
665
666 match term.kind {
29967ef6 667 TerminatorKind::SwitchInt { ref discr, switch_ty: _, targets: _ } => {
48663c56 668 self.consume_operand(loc, (discr, span), flow_state);
ff7c6d11 669 }
29967ef6 670 TerminatorKind::Drop { place, target: _, unwind: _ } => {
dfeec247
XL
671 debug!(
672 "visit_terminator_drop \
29967ef6
XL
673 loc: {:?} term: {:?} place: {:?} span: {:?}",
674 loc, term, place, span
dfeec247 675 );
b7449926 676
0bf4aa26 677 self.access_place(
48663c56 678 loc,
29967ef6 679 (place, span),
0bf4aa26
XL
680 (AccessDepth::Drop, Write(WriteKind::StorageDeadOrDrop)),
681 LocalMutationIsAllowed::Yes,
682 flow_state,
683 );
ff7c6d11
XL
684 }
685 TerminatorKind::DropAndReplace {
f035d41b 686 place: drop_place,
ff7c6d11
XL
687 value: ref new_value,
688 target: _,
689 unwind: _,
690 } => {
dfeec247
XL
691 self.mutate_place(loc, (drop_place, span), Deep, JustWrite, flow_state);
692 self.consume_operand(loc, (new_value, span), flow_state);
ff7c6d11
XL
693 }
694 TerminatorKind::Call {
695 ref func,
696 ref args,
697 ref destination,
698 cleanup: _,
0bf4aa26 699 from_hir_call: _,
f035d41b 700 fn_span: _,
ff7c6d11 701 } => {
48663c56 702 self.consume_operand(loc, (func, span), flow_state);
ff7c6d11 703 for arg in args {
dfeec247 704 self.consume_operand(loc, (arg, span), flow_state);
ff7c6d11 705 }
ba9703b0 706 if let Some((dest, _ /*bb*/)) = *destination {
dfeec247 707 self.mutate_place(loc, (dest, span), Deep, JustWrite, flow_state);
ff7c6d11
XL
708 }
709 }
dfeec247 710 TerminatorKind::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ } => {
48663c56 711 self.consume_operand(loc, (cond, span), flow_state);
ba9703b0 712 use rustc_middle::mir::AssertKind;
74b04a01 713 if let AssertKind::BoundsCheck { ref len, ref index } = *msg {
48663c56
XL
714 self.consume_operand(loc, (len, span), flow_state);
715 self.consume_operand(loc, (index, span), flow_state);
ff7c6d11
XL
716 }
717 }
718
ba9703b0 719 TerminatorKind::Yield { ref value, resume: _, resume_arg, drop: _ } => {
48663c56 720 self.consume_operand(loc, (value, span), flow_state);
74b04a01
XL
721 self.mutate_place(loc, (resume_arg, span), Deep, JustWrite, flow_state);
722 }
2c00a5a8 723
f9f354fc
XL
724 TerminatorKind::InlineAsm {
725 template: _,
726 ref operands,
727 options: _,
728 line_spans: _,
729 destination: _,
730 } => {
731 for op in operands {
732 match *op {
cdc7bbd5 733 InlineAsmOperand::In { reg: _, ref value } => {
f9f354fc
XL
734 self.consume_operand(loc, (value, span), flow_state);
735 }
736 InlineAsmOperand::Out { reg: _, late: _, place, .. } => {
737 if let Some(place) = place {
738 self.mutate_place(
739 loc,
740 (place, span),
741 Shallow(None),
742 JustWrite,
743 flow_state,
744 );
745 }
746 }
747 InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => {
748 self.consume_operand(loc, (in_value, span), flow_state);
749 if let Some(out_place) = out_place {
750 self.mutate_place(
751 loc,
752 (out_place, span),
753 Shallow(None),
754 JustWrite,
755 flow_state,
756 );
757 }
758 }
cdc7bbd5
XL
759 InlineAsmOperand::Const { value: _ }
760 | InlineAsmOperand::SymFn { value: _ }
f035d41b 761 | InlineAsmOperand::SymStatic { def_id: _ } => {}
f9f354fc
XL
762 }
763 }
764 }
765
74b04a01
XL
766 TerminatorKind::Goto { target: _ }
767 | TerminatorKind::Abort
768 | TerminatorKind::Unreachable
769 | TerminatorKind::Resume
770 | TerminatorKind::Return
771 | TerminatorKind::GeneratorDrop
f035d41b 772 | TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ }
74b04a01
XL
773 | TerminatorKind::FalseUnwind { real_target: _, unwind: _ } => {
774 // no data used, thus irrelevant to borrowck
775 }
776 }
777 }
778
f9f354fc 779 fn visit_terminator_after_primary_effect(
74b04a01
XL
780 &mut self,
781 flow_state: &Flows<'cx, 'tcx>,
782 term: &'cx Terminator<'tcx>,
783 loc: Location,
784 ) {
785 let span = term.source_info.span;
786
787 match term.kind {
788 TerminatorKind::Yield { value: _, resume: _, resume_arg: _, drop: _ } => {
2c00a5a8
XL
789 if self.movable_generator {
790 // Look for any active borrows to locals
83c7162d 791 let borrow_set = self.borrow_set.clone();
74b04a01
XL
792 for i in flow_state.borrows.iter() {
793 let borrow = &borrow_set[i];
794 self.check_for_local_borrow(borrow, span);
795 }
2c00a5a8 796 }
ff7c6d11
XL
797 }
798
799 TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => {
800 // Returning from the function implicitly kills storage for all locals and statics.
801 // Often, the storage will already have been killed by an explicit
802 // StorageDead, but we don't always emit those (notably on unwind paths),
803 // so this "extra check" serves as a kind of backup.
83c7162d 804 let borrow_set = self.borrow_set.clone();
74b04a01
XL
805 for i in flow_state.borrows.iter() {
806 let borrow = &borrow_set[i];
807 self.check_for_invalidation_at_exit(loc, borrow, span);
808 }
ff7c6d11 809 }
74b04a01
XL
810
811 TerminatorKind::Abort
812 | TerminatorKind::Assert { .. }
813 | TerminatorKind::Call { .. }
814 | TerminatorKind::Drop { .. }
815 | TerminatorKind::DropAndReplace { .. }
f035d41b 816 | TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ }
74b04a01
XL
817 | TerminatorKind::FalseUnwind { real_target: _, unwind: _ }
818 | TerminatorKind::Goto { .. }
819 | TerminatorKind::SwitchInt { .. }
f9f354fc
XL
820 | TerminatorKind::Unreachable
821 | TerminatorKind::InlineAsm { .. } => {}
ff7c6d11
XL
822 }
823 }
824}
825
826#[derive(Copy, Clone, PartialEq, Eq, Debug)]
827enum MutateMode {
828 JustWrite,
829 WriteAndRead,
830}
831
0bf4aa26 832use self::AccessDepth::{Deep, Shallow};
dfeec247 833use self::ReadOrWrite::{Activation, Read, Reservation, Write};
ff7c6d11
XL
834
835#[derive(Copy, Clone, PartialEq, Eq, Debug)]
836enum ArtificialField {
ff7c6d11 837 ArrayLength,
0bf4aa26 838 ShallowBorrow,
ff7c6d11
XL
839}
840
841#[derive(Copy, Clone, PartialEq, Eq, Debug)]
0bf4aa26 842enum AccessDepth {
ff7c6d11 843 /// From the RFC: "A *shallow* access means that the immediate
2c00a5a8 844 /// fields reached at P are accessed, but references or pointers
ff7c6d11
XL
845 /// found within are not dereferenced. Right now, the only access
846 /// that is shallow is an assignment like `x = ...;`, which would
847 /// be a *shallow write* of `x`."
848 Shallow(Option<ArtificialField>),
849
850 /// From the RFC: "A *deep* access means that all data reachable
851 /// through the given place may be invalidated or accesses by
852 /// this action."
853 Deep,
0bf4aa26
XL
854
855 /// Access is Deep only when there is a Drop implementation that
856 /// can reach the data behind the reference.
857 Drop,
ff7c6d11
XL
858}
859
860/// Kind of access to a value: read or write
861/// (For informational purposes only)
862#[derive(Copy, Clone, PartialEq, Eq, Debug)]
863enum ReadOrWrite {
864 /// From the RFC: "A *read* means that the existing data may be
865 /// read, but will not be changed."
866 Read(ReadKind),
867
868 /// From the RFC: "A *write* means that the data may be mutated to
869 /// new values or otherwise invalidated (for example, it could be
870 /// de-initialized, as in a move operation).
871 Write(WriteKind),
872
873 /// For two-phase borrows, we distinguish a reservation (which is treated
874 /// like a Read) from an activation (which is treated like a write), and
875 /// each of those is furthermore distinguished from Reads/Writes above.
876 Reservation(WriteKind),
877 Activation(WriteKind, BorrowIndex),
878}
879
880/// Kind of read access to a value
881/// (For informational purposes only)
882#[derive(Copy, Clone, PartialEq, Eq, Debug)]
883enum ReadKind {
884 Borrow(BorrowKind),
885 Copy,
886}
887
888/// Kind of write access to a value
889/// (For informational purposes only)
890#[derive(Copy, Clone, PartialEq, Eq, Debug)]
891enum WriteKind {
892 StorageDeadOrDrop,
893 MutableBorrow(BorrowKind),
894 Mutate,
895 Move,
896}
897
898/// When checking permissions for a place access, this flag is used to indicate that an immutable
899/// local place can be mutated.
9fa01778
XL
900//
901// FIXME: @nikomatsakis suggested that this flag could be removed with the following modifications:
902// - Merge `check_access_permissions()` and `check_if_reassignment_to_immutable_state()`.
903// - Split `is_mutable()` into `is_assignable()` (can be directly assigned) and
904// `is_declared_mutable()`.
905// - Take flow state into consideration in `is_assignable()` for local variables.
ff7c6d11
XL
906#[derive(Copy, Clone, PartialEq, Eq, Debug)]
907enum LocalMutationIsAllowed {
908 Yes,
909 /// We want use of immutable upvars to cause a "write to immutable upvar"
910 /// error, not an "reassignment" error.
911 ExceptUpvars,
912 No,
913}
914
0bf4aa26 915#[derive(Copy, Clone, Debug)]
ff7c6d11
XL
916enum InitializationRequiringAction {
917 Update,
918 Borrow,
0bf4aa26 919 MatchOn,
ff7c6d11
XL
920 Use,
921 Assignment,
0bf4aa26 922 PartialAssignment,
ff7c6d11
XL
923}
924
74b04a01
XL
925struct RootPlace<'tcx> {
926 place_local: Local,
927 place_projection: &'tcx [PlaceElem<'tcx>],
83c7162d
XL
928 is_local_mutation_allowed: LocalMutationIsAllowed,
929}
930
ff7c6d11
XL
931impl InitializationRequiringAction {
932 fn as_noun(self) -> &'static str {
933 match self {
934 InitializationRequiringAction::Update => "update",
935 InitializationRequiringAction::Borrow => "borrow",
0bf4aa26 936 InitializationRequiringAction::MatchOn => "use", // no good noun
ff7c6d11
XL
937 InitializationRequiringAction::Use => "use",
938 InitializationRequiringAction::Assignment => "assign",
0bf4aa26 939 InitializationRequiringAction::PartialAssignment => "assign to part",
ff7c6d11
XL
940 }
941 }
942
943 fn as_verb_in_past_tense(self) -> &'static str {
944 match self {
945 InitializationRequiringAction::Update => "updated",
946 InitializationRequiringAction::Borrow => "borrowed",
0bf4aa26 947 InitializationRequiringAction::MatchOn => "matched on",
ff7c6d11
XL
948 InitializationRequiringAction::Use => "used",
949 InitializationRequiringAction::Assignment => "assigned",
0bf4aa26 950 InitializationRequiringAction::PartialAssignment => "partially assigned",
b7449926
XL
951 }
952 }
953}
954
dc9dc135 955impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
74b04a01 956 fn body(&self) -> &'cx Body<'tcx> {
f9f354fc 957 self.body
74b04a01
XL
958 }
959
ff7c6d11
XL
960 /// Checks an access to the given place to see if it is allowed. Examines the set of borrows
961 /// that are in scope, as well as which paths have been initialized, to ensure that (a) the
962 /// place is initialized and (b) it is not borrowed in some way that would prevent this
963 /// access.
964 ///
9fa01778 965 /// Returns `true` if an error is reported.
ff7c6d11
XL
966 fn access_place(
967 &mut self,
48663c56 968 location: Location,
ba9703b0 969 place_span: (Place<'tcx>, Span),
0bf4aa26 970 kind: (AccessDepth, ReadOrWrite),
ff7c6d11 971 is_local_mutation_allowed: LocalMutationIsAllowed,
dc9dc135 972 flow_state: &Flows<'cx, 'tcx>,
b7449926 973 ) {
ff7c6d11
XL
974 let (sd, rw) = kind;
975
976 if let Activation(_, borrow_index) = rw {
977 if self.reservation_error_reported.contains(&place_span.0) {
0531ce1d
XL
978 debug!(
979 "skipping access_place for activation of invalid reservation \
980 place: {:?} borrow_index: {:?}",
981 place_span.0, borrow_index
982 );
b7449926 983 return;
ff7c6d11
XL
984 }
985 }
986
b7449926
XL
987 // Check is_empty() first because it's the common case, and doing that
988 // way we avoid the clone() call.
dfeec247 989 if !self.access_place_error_reported.is_empty()
ba9703b0 990 && self.access_place_error_reported.contains(&(place_span.0, place_span.1))
0531ce1d
XL
991 {
992 debug!(
993 "access_place: suppressing error place_span=`{:?}` kind=`{:?}`",
994 place_span, kind
995 );
b7449926 996 return;
2c00a5a8
XL
997 }
998
dfeec247
XL
999 let mutability_error = self.check_access_permissions(
1000 place_span,
1001 rw,
1002 is_local_mutation_allowed,
1003 flow_state,
1004 location,
1005 );
ff7c6d11 1006 let conflict_error =
48663c56 1007 self.check_access_for_conflict(location, place_span, sd, rw, flow_state);
ff7c6d11 1008
532ac7d7 1009 if let (Activation(_, borrow_idx), true) = (kind.1, conflict_error) {
60c5eb7d 1010 // Suppress this warning when there's an error being emitted for the
532ac7d7
XL
1011 // same borrow: fixing the error is likely to fix the warning.
1012 self.reservation_warnings.remove(&borrow_idx);
1013 }
1014
2c00a5a8 1015 if conflict_error || mutability_error {
dfeec247 1016 debug!("access_place: logging error place_span=`{:?}` kind=`{:?}`", place_span, kind);
532ac7d7 1017
ba9703b0 1018 self.access_place_error_reported.insert((place_span.0, place_span.1));
2c00a5a8 1019 }
ff7c6d11
XL
1020 }
1021
1022 fn check_access_for_conflict(
1023 &mut self,
48663c56 1024 location: Location,
ba9703b0 1025 place_span: (Place<'tcx>, Span),
0bf4aa26 1026 sd: AccessDepth,
ff7c6d11 1027 rw: ReadOrWrite,
dc9dc135 1028 flow_state: &Flows<'cx, 'tcx>,
ff7c6d11 1029 ) -> bool {
83c7162d 1030 debug!(
48663c56
XL
1031 "check_access_for_conflict(location={:?}, place_span={:?}, sd={:?}, rw={:?})",
1032 location, place_span, sd, rw,
83c7162d
XL
1033 );
1034
ff7c6d11 1035 let mut error_reported = false;
0bf4aa26 1036 let tcx = self.infcx.tcx;
dc9dc135 1037 let body = self.body;
94b46f34 1038 let borrow_set = self.borrow_set.clone();
74b04a01
XL
1039
1040 // Use polonius output if it has been enabled.
1041 let polonius_output = self.polonius_output.clone();
1042 let borrows_in_scope = if let Some(polonius) = &polonius_output {
1043 let location = self.location_table.start_index(location);
1044 Either::Left(polonius.errors_at(location).iter().copied())
1045 } else {
1046 Either::Right(flow_state.borrows.iter())
1047 };
1048
94b46f34
XL
1049 each_borrow_involving_path(
1050 self,
1051 tcx,
dc9dc135 1052 body,
48663c56 1053 location,
ff7c6d11 1054 (sd, place_span.0),
94b46f34 1055 &borrow_set,
74b04a01 1056 borrows_in_scope,
8faf50e0 1057 |this, borrow_index, borrow| match (rw, borrow.kind) {
ff7c6d11
XL
1058 // Obviously an activation is compatible with its own
1059 // reservation (or even prior activating uses of same
1060 // borrow); so don't check if they interfere.
1061 //
1062 // NOTE: *reservations* do conflict with themselves;
1063 // thus aren't injecting unsoundenss w/ this check.)
83c7162d 1064 (Activation(_, activating), _) if activating == borrow_index => {
ff7c6d11
XL
1065 debug!(
1066 "check_access_for_conflict place_span: {:?} sd: {:?} rw: {:?} \
83c7162d 1067 skipping {:?} b/c activation of same borrow_index",
ff7c6d11
XL
1068 place_span,
1069 sd,
1070 rw,
83c7162d 1071 (borrow_index, borrow),
ff7c6d11
XL
1072 );
1073 Control::Continue
1074 }
1075
ba9703b0
XL
1076 (Read(_), BorrowKind::Shared | BorrowKind::Shallow)
1077 | (
1078 Read(ReadKind::Borrow(BorrowKind::Shallow)),
1079 BorrowKind::Unique | BorrowKind::Mut { .. },
1080 ) => Control::Continue,
0bf4aa26
XL
1081
1082 (Write(WriteKind::Move), BorrowKind::Shallow) => {
1083 // Handled by initialization checks.
ff7c6d11
XL
1084 Control::Continue
1085 }
1086
ba9703b0 1087 (Read(kind), BorrowKind::Unique | BorrowKind::Mut { .. }) => {
ff7c6d11 1088 // Reading from mere reservations of mutable-borrows is OK.
48663c56
XL
1089 if !is_active(&this.dominators, borrow, location) {
1090 assert!(allow_two_phase_borrow(borrow.kind));
ff7c6d11
XL
1091 return Control::Continue;
1092 }
1093
0bf4aa26 1094 error_reported = true;
ff7c6d11 1095 match kind {
dfeec247 1096 ReadKind::Copy => {
48663c56 1097 this.report_use_while_mutably_borrowed(location, place_span, borrow)
532ac7d7 1098 .buffer(&mut this.errors_buffer);
ff7c6d11
XL
1099 }
1100 ReadKind::Borrow(bk) => {
48663c56 1101 this.report_conflicting_borrow(location, place_span, bk, borrow)
532ac7d7 1102 .buffer(&mut this.errors_buffer);
ff7c6d11
XL
1103 }
1104 }
1105 Control::Break
1106 }
1107
ba9703b0
XL
1108 (
1109 Reservation(WriteKind::MutableBorrow(bk)),
1110 BorrowKind::Shallow | BorrowKind::Shared,
3dfed10e
XL
1111 ) if { tcx.migrate_borrowck() && this.borrow_set.contains(&location) } => {
1112 let bi = this.borrow_set.get_index_of(&location).unwrap();
532ac7d7
XL
1113 debug!(
1114 "recording invalid reservation of place: {:?} with \
1115 borrow index {:?} as warning",
dfeec247 1116 place_span.0, bi,
532ac7d7
XL
1117 );
1118 // rust-lang/rust#56254 - This was previously permitted on
1119 // the 2018 edition so we emit it as a warning. We buffer
1120 // these sepately so that we only emit a warning if borrow
1121 // checking was otherwise successful.
dfeec247 1122 this.reservation_warnings
ba9703b0 1123 .insert(bi, (place_span.0, place_span.1, location, bk, borrow.clone()));
532ac7d7
XL
1124
1125 // Don't suppress actual errors.
1126 Control::Continue
1127 }
1128
ba9703b0 1129 (Reservation(kind) | Activation(kind, _) | Write(kind), _) => {
ff7c6d11 1130 match rw {
532ac7d7 1131 Reservation(..) => {
ff7c6d11
XL
1132 debug!(
1133 "recording invalid reservation of \
1134 place: {:?}",
1135 place_span.0
1136 );
ba9703b0 1137 this.reservation_error_reported.insert(place_span.0);
0531ce1d 1138 }
ff7c6d11
XL
1139 Activation(_, activating) => {
1140 debug!(
1141 "observing check_place for activation of \
1142 borrow_index: {:?}",
1143 activating
1144 );
0531ce1d
XL
1145 }
1146 Read(..) | Write(..) => {}
ff7c6d11
XL
1147 }
1148
0bf4aa26 1149 error_reported = true;
ff7c6d11
XL
1150 match kind {
1151 WriteKind::MutableBorrow(bk) => {
48663c56 1152 this.report_conflicting_borrow(location, place_span, bk, borrow)
532ac7d7 1153 .buffer(&mut this.errors_buffer);
ff7c6d11 1154 }
dfeec247
XL
1155 WriteKind::StorageDeadOrDrop => this
1156 .report_borrowed_value_does_not_live_long_enough(
48663c56 1157 location,
ff7c6d11 1158 borrow,
94b46f34 1159 place_span,
dfeec247
XL
1160 Some(kind),
1161 ),
ff7c6d11 1162 WriteKind::Mutate => {
48663c56 1163 this.report_illegal_mutation_of_borrowed(location, place_span, borrow)
ff7c6d11
XL
1164 }
1165 WriteKind::Move => {
48663c56 1166 this.report_move_out_while_borrowed(location, place_span, borrow)
ff7c6d11
XL
1167 }
1168 }
1169 Control::Break
1170 }
1171 },
1172 );
1173
1174 error_reported
1175 }
1176
1177 fn mutate_place(
1178 &mut self,
48663c56 1179 location: Location,
ba9703b0 1180 place_span: (Place<'tcx>, Span),
0bf4aa26 1181 kind: AccessDepth,
ff7c6d11 1182 mode: MutateMode,
dc9dc135 1183 flow_state: &Flows<'cx, 'tcx>,
ff7c6d11
XL
1184 ) {
1185 // Write of P[i] or *P, or WriteAndRead of any P, requires P init'd.
1186 match mode {
1187 MutateMode::WriteAndRead => {
83c7162d 1188 self.check_if_path_or_subpath_is_moved(
48663c56 1189 location,
ff7c6d11 1190 InitializationRequiringAction::Update,
416331ca 1191 (place_span.0.as_ref(), place_span.1),
ff7c6d11
XL
1192 flow_state,
1193 );
1194 }
1195 MutateMode::JustWrite => {
48663c56 1196 self.check_if_assigned_path_is_moved(location, place_span, flow_state);
ff7c6d11
XL
1197 }
1198 }
1199
b7449926
XL
1200 // Special case: you can assign a immutable local variable
1201 // (e.g., `x = ...`) so long as it has never been initialized
1202 // before (at this point in the flow).
e74abb32
XL
1203 if let Some(local) = place_span.0.as_local() {
1204 if let Mutability::Not = self.body.local_decls[local].mutability {
b7449926
XL
1205 // check for reassignments to immutable local variables
1206 self.check_if_reassignment_to_immutable_state(
dfeec247 1207 location, local, place_span, flow_state,
b7449926
XL
1208 );
1209 return;
1210 }
1211 }
1212
1213 // Otherwise, use the normal access permission rules.
1214 self.access_place(
48663c56 1215 location,
ff7c6d11
XL
1216 place_span,
1217 (kind, Write(WriteKind::Mutate)),
b7449926 1218 LocalMutationIsAllowed::No,
ff7c6d11
XL
1219 flow_state,
1220 );
ff7c6d11
XL
1221 }
1222
1223 fn consume_rvalue(
1224 &mut self,
48663c56 1225 location: Location,
416331ca 1226 (rvalue, span): (&'cx Rvalue<'tcx>, Span),
dc9dc135 1227 flow_state: &Flows<'cx, 'tcx>,
ff7c6d11
XL
1228 ) {
1229 match *rvalue {
ba9703b0 1230 Rvalue::Ref(_ /*rgn*/, bk, place) => {
ff7c6d11 1231 let access_kind = match bk {
0bf4aa26
XL
1232 BorrowKind::Shallow => {
1233 (Shallow(Some(ArtificialField::ShallowBorrow)), Read(ReadKind::Borrow(bk)))
dfeec247 1234 }
ff7c6d11 1235 BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
2c00a5a8 1236 BorrowKind::Unique | BorrowKind::Mut { .. } => {
ff7c6d11 1237 let wk = WriteKind::MutableBorrow(bk);
48663c56 1238 if allow_two_phase_borrow(bk) {
ff7c6d11
XL
1239 (Deep, Reservation(wk))
1240 } else {
1241 (Deep, Write(wk))
1242 }
1243 }
1244 };
1245
1246 self.access_place(
48663c56 1247 location,
ff7c6d11
XL
1248 (place, span),
1249 access_kind,
1250 LocalMutationIsAllowed::No,
1251 flow_state,
1252 );
1253
0bf4aa26
XL
1254 let action = if bk == BorrowKind::Shallow {
1255 InitializationRequiringAction::MatchOn
1256 } else {
1257 InitializationRequiringAction::Borrow
1258 };
1259
83c7162d 1260 self.check_if_path_or_subpath_is_moved(
48663c56 1261 location,
0bf4aa26 1262 action,
416331ca 1263 (place.as_ref(), span),
ff7c6d11
XL
1264 flow_state,
1265 );
1266 }
1267
ba9703b0 1268 Rvalue::AddressOf(mutability, place) => {
dfeec247
XL
1269 let access_kind = match mutability {
1270 Mutability::Mut => (
1271 Deep,
1272 Write(WriteKind::MutableBorrow(BorrowKind::Mut {
1273 allow_two_phase_borrow: false,
1274 })),
1275 ),
1276 Mutability::Not => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))),
1277 };
1278
1279 self.access_place(
1280 location,
1281 (place, span),
1282 access_kind,
1283 LocalMutationIsAllowed::No,
1284 flow_state,
1285 );
1286
1287 self.check_if_path_or_subpath_is_moved(
1288 location,
1289 InitializationRequiringAction::Borrow,
1290 (place.as_ref(), span),
1291 flow_state,
1292 );
1293 }
1294
f9f354fc
XL
1295 Rvalue::ThreadLocalRef(_) => {}
1296
ff7c6d11
XL
1297 Rvalue::Use(ref operand)
1298 | Rvalue::Repeat(ref operand, _)
1299 | Rvalue::UnaryOp(_ /*un_op*/, ref operand)
1300 | Rvalue::Cast(_ /*cast_kind*/, ref operand, _ /*ty*/) => {
48663c56 1301 self.consume_operand(location, (operand, span), flow_state)
ff7c6d11
XL
1302 }
1303
ba9703b0 1304 Rvalue::Len(place) | Rvalue::Discriminant(place) => {
ff7c6d11 1305 let af = match *rvalue {
0731742a
XL
1306 Rvalue::Len(..) => Some(ArtificialField::ArrayLength),
1307 Rvalue::Discriminant(..) => None,
ff7c6d11
XL
1308 _ => unreachable!(),
1309 };
1310 self.access_place(
48663c56 1311 location,
ff7c6d11 1312 (place, span),
0731742a 1313 (Shallow(af), Read(ReadKind::Copy)),
ff7c6d11
XL
1314 LocalMutationIsAllowed::No,
1315 flow_state,
1316 );
83c7162d 1317 self.check_if_path_or_subpath_is_moved(
48663c56 1318 location,
ff7c6d11 1319 InitializationRequiringAction::Use,
416331ca 1320 (place.as_ref(), span),
ff7c6d11
XL
1321 flow_state,
1322 );
1323 }
1324
6a06907d
XL
1325 Rvalue::BinaryOp(_bin_op, box (ref operand1, ref operand2))
1326 | Rvalue::CheckedBinaryOp(_bin_op, box (ref operand1, ref operand2)) => {
48663c56
XL
1327 self.consume_operand(location, (operand1, span), flow_state);
1328 self.consume_operand(location, (operand2, span), flow_state);
ff7c6d11
XL
1329 }
1330
1331 Rvalue::NullaryOp(_op, _ty) => {
1332 // nullary ops take no dynamic input; no borrowck effect.
1333 //
1334 // FIXME: is above actually true? Do we want to track
1335 // the fact that uninitialized data can be created via
1336 // `NullOp::Box`?
1337 }
1338
83c7162d
XL
1339 Rvalue::Aggregate(ref aggregate_kind, ref operands) => {
1340 // We need to report back the list of mutable upvars that were
1341 // moved into the closure and subsequently used by the closure,
1342 // in order to populate our used_mut set.
8faf50e0 1343 match **aggregate_kind {
dfeec247
XL
1344 AggregateKind::Closure(def_id, _) | AggregateKind::Generator(def_id, _, _) => {
1345 let BorrowCheckResult { used_mut_upvars, .. } =
f9f354fc 1346 self.infcx.tcx.mir_borrowck(def_id.expect_local());
8faf50e0
XL
1347 debug!("{:?} used_mut_upvars={:?}", def_id, used_mut_upvars);
1348 for field in used_mut_upvars {
48663c56 1349 self.propagate_closure_used_mut_upvar(&operands[field.index()]);
83c7162d
XL
1350 }
1351 }
8faf50e0
XL
1352 AggregateKind::Adt(..)
1353 | AggregateKind::Array(..)
1354 | AggregateKind::Tuple { .. } => (),
83c7162d
XL
1355 }
1356
1357 for operand in operands {
48663c56
XL
1358 self.consume_operand(location, (operand, span), flow_state);
1359 }
1360 }
1361 }
1362 }
1363
1364 fn propagate_closure_used_mut_upvar(&mut self, operand: &Operand<'tcx>) {
ba9703b0 1365 let propagate_closure_used_mut_place = |this: &mut Self, place: Place<'tcx>| {
5869c6ff
XL
1366 // We have three possibilities here:
1367 // a. We are modifying something through a mut-ref
1368 // b. We are modifying something that is local to our parent
1369 // c. Current body is a nested closure, and we are modifying path starting from
1370 // a Place captured by our parent closure.
1371
1372 // Handle (c), the path being modified is exactly the path captured by our parent
1373 if let Some(field) = this.is_upvar_field_projection(place.as_ref()) {
1374 this.used_mut_upvars.push(field);
1375 return;
1376 }
1377
1378 for (place_ref, proj) in place.iter_projections().rev() {
1379 // Handle (a)
1380 if proj == ProjectionElem::Deref {
1381 match place_ref.ty(this.body(), this.infcx.tcx).ty.kind() {
1382 // We aren't modifying a variable directly
1383 ty::Ref(_, _, hir::Mutability::Mut) => return,
1384
1385 _ => {}
1386 }
1387 }
1388
1389 // Handle (c)
1390 if let Some(field) = this.is_upvar_field_projection(place_ref) {
416331ca 1391 this.used_mut_upvars.push(field);
5869c6ff 1392 return;
48663c56 1393 }
48663c56 1394 }
5869c6ff
XL
1395
1396 // Handle(b)
1397 this.used_mut.insert(place.local);
48663c56
XL
1398 };
1399
1400 // This relies on the current way that by-value
1401 // captures of a closure are copied/moved directly
1402 // when generating MIR.
1403 match *operand {
ba9703b0 1404 Operand::Move(place) | Operand::Copy(place) => {
e74abb32 1405 match place.as_local() {
60c5eb7d 1406 Some(local) if !self.body.local_decls[local].is_user_variable() => {
e74abb32
XL
1407 if self.body.local_decls[local].ty.is_mutable_ptr() {
1408 // The variable will be marked as mutable by the borrow.
1409 return;
1410 }
1411 // This is an edge case where we have a `move` closure
1412 // inside a non-move closure, and the inner closure
1413 // contains a mutation:
1414 //
1415 // let mut i = 0;
1416 // || { move || { i += 1; }; };
1417 //
1418 // In this case our usual strategy of assuming that the
1419 // variable will be captured by mutable reference is
1420 // wrong, since `i` can be copied into the inner
1421 // closure from a shared reference.
1422 //
1423 // As such we have to search for the local that this
1424 // capture comes from and mark it as being used as mut.
1425
1426 let temp_mpi = self.move_data.rev_lookup.find_local(local);
1427 let init = if let [init_index] = *self.move_data.init_path_map[temp_mpi] {
1428 &self.move_data.inits[init_index]
1429 } else {
1430 bug!("temporary should be initialized exactly once")
1431 };
48663c56 1432
e74abb32
XL
1433 let loc = match init.location {
1434 InitLocation::Statement(stmt) => stmt,
1435 _ => bug!("temporary initialized in arguments"),
1436 };
48663c56 1437
60c5eb7d
XL
1438 let body = self.body;
1439 let bbd = &body[loc.block];
e74abb32
XL
1440 let stmt = &bbd.statements[loc.statement_index];
1441 debug!("temporary assigned in: stmt={:?}", stmt);
48663c56 1442
ba9703b0 1443 if let StatementKind::Assign(box (_, Rvalue::Ref(_, _, source))) = stmt.kind
e74abb32
XL
1444 {
1445 propagate_closure_used_mut_place(self, source);
1446 } else {
1447 bug!(
1448 "closures should only capture user variables \
1449 or references to user variables"
1450 );
1451 }
1452 }
1453 _ => propagate_closure_used_mut_place(self, place),
83c7162d
XL
1454 }
1455 }
48663c56 1456 Operand::Constant(..) => {}
ff7c6d11
XL
1457 }
1458 }
1459
1460 fn consume_operand(
1461 &mut self,
48663c56 1462 location: Location,
416331ca 1463 (operand, span): (&'cx Operand<'tcx>, Span),
dc9dc135 1464 flow_state: &Flows<'cx, 'tcx>,
ff7c6d11
XL
1465 ) {
1466 match *operand {
ba9703b0 1467 Operand::Copy(place) => {
ff7c6d11
XL
1468 // copy of place: check if this is "copy of frozen path"
1469 // (FIXME: see check_loans.rs)
1470 self.access_place(
48663c56 1471 location,
ff7c6d11
XL
1472 (place, span),
1473 (Deep, Read(ReadKind::Copy)),
1474 LocalMutationIsAllowed::No,
1475 flow_state,
1476 );
1477
1478 // Finally, check if path was already moved.
83c7162d 1479 self.check_if_path_or_subpath_is_moved(
48663c56 1480 location,
ff7c6d11 1481 InitializationRequiringAction::Use,
416331ca 1482 (place.as_ref(), span),
ff7c6d11
XL
1483 flow_state,
1484 );
1485 }
ba9703b0 1486 Operand::Move(place) => {
ff7c6d11
XL
1487 // move of place: check if this is move of already borrowed path
1488 self.access_place(
48663c56 1489 location,
ff7c6d11
XL
1490 (place, span),
1491 (Deep, Write(WriteKind::Move)),
1492 LocalMutationIsAllowed::Yes,
1493 flow_state,
1494 );
1495
1496 // Finally, check if path was already moved.
83c7162d 1497 self.check_if_path_or_subpath_is_moved(
48663c56 1498 location,
ff7c6d11 1499 InitializationRequiringAction::Use,
416331ca 1500 (place.as_ref(), span),
ff7c6d11
XL
1501 flow_state,
1502 );
1503 }
1504 Operand::Constant(_) => {}
1505 }
1506 }
1507
0bf4aa26 1508 /// Checks whether a borrow of this place is invalidated when the function
ff7c6d11
XL
1509 /// exits
1510 fn check_for_invalidation_at_exit(
1511 &mut self,
48663c56 1512 location: Location,
ff7c6d11
XL
1513 borrow: &BorrowData<'tcx>,
1514 span: Span,
ff7c6d11
XL
1515 ) {
1516 debug!("check_for_invalidation_at_exit({:?})", borrow);
ba9703b0 1517 let place = borrow.borrowed_place;
74b04a01 1518 let mut root_place = PlaceRef { local: place.local, projection: &[] };
ff7c6d11
XL
1519
1520 // FIXME(nll-rfc#40): do more precise destructor tracking here. For now
1521 // we just know that all locals are dropped at function exit (otherwise
1522 // we'll have a memory leak) and assume that all statics have a destructor.
1523 //
1524 // FIXME: allow thread-locals to borrow other thread locals?
416331ca 1525
dfeec247 1526 let (might_be_alive, will_be_dropped) =
74b04a01 1527 if self.body.local_decls[root_place.local].is_ref_to_thread_local() {
dfeec247
XL
1528 // Thread-locals might be dropped after the function exits
1529 // We have to dereference the outer reference because
1530 // borrows don't conflict behind shared references.
74b04a01 1531 root_place.projection = DEREF_PROJECTION;
dfeec247
XL
1532 (true, true)
1533 } else {
1534 (false, self.locals_are_invalidated_at_exit)
1535 };
ff7c6d11
XL
1536
1537 if !will_be_dropped {
dfeec247 1538 debug!("place_is_invalidated_at_exit({:?}) - won't be dropped", place);
ff7c6d11
XL
1539 return;
1540 }
1541
ff7c6d11
XL
1542 let sd = if might_be_alive { Deep } else { Shallow(None) };
1543
0bf4aa26
XL
1544 if places_conflict::borrow_conflicts_with_place(
1545 self.infcx.tcx,
60c5eb7d 1546 &self.body,
0bf4aa26
XL
1547 place,
1548 borrow.kind,
1549 root_place,
0731742a
XL
1550 sd,
1551 places_conflict::PlaceConflictBias::Overlap,
0bf4aa26 1552 ) {
ff7c6d11
XL
1553 debug!("check_for_invalidation_at_exit({:?}): INVALID", place);
1554 // FIXME: should be talking about the region lifetime instead
1555 // of just a span here.
0bf4aa26 1556 let span = self.infcx.tcx.sess.source_map().end_point(span);
ff7c6d11 1557 self.report_borrowed_value_does_not_live_long_enough(
48663c56 1558 location,
ff7c6d11 1559 borrow,
94b46f34
XL
1560 (place, span),
1561 None,
ff7c6d11
XL
1562 )
1563 }
1564 }
1565
2c00a5a8 1566 /// Reports an error if this is a borrow of local data.
dfeec247 1567 /// This is called for all Yield expressions on movable generators
0531ce1d 1568 fn check_for_local_borrow(&mut self, borrow: &BorrowData<'tcx>, yield_span: Span) {
2c00a5a8
XL
1569 debug!("check_for_local_borrow({:?})", borrow);
1570
ba9703b0 1571 if borrow_of_local_data(borrow.borrowed_place) {
416331ca 1572 let err = self.cannot_borrow_across_generator_yield(
dfeec247
XL
1573 self.retrieve_borrow_spans(borrow).var_or_use(),
1574 yield_span,
1575 );
8faf50e0
XL
1576
1577 err.buffer(&mut self.errors_buffer);
2c00a5a8
XL
1578 }
1579 }
1580
dc9dc135 1581 fn check_activations(&mut self, location: Location, span: Span, flow_state: &Flows<'cx, 'tcx>) {
ff7c6d11
XL
1582 // Two-phase borrow support: For each activation that is newly
1583 // generated at this statement, check if it interferes with
1584 // another borrow.
83c7162d
XL
1585 let borrow_set = self.borrow_set.clone();
1586 for &borrow_index in borrow_set.activations_at_location(location) {
1587 let borrow = &borrow_set[borrow_index];
1588
1589 // only mutable borrows should be 2-phase
1590 assert!(match borrow.kind {
0bf4aa26 1591 BorrowKind::Shared | BorrowKind::Shallow => false,
83c7162d
XL
1592 BorrowKind::Unique | BorrowKind::Mut { .. } => true,
1593 });
1594
1595 self.access_place(
48663c56 1596 location,
ba9703b0 1597 (borrow.borrowed_place, span),
dfeec247 1598 (Deep, Activation(WriteKind::MutableBorrow(borrow.kind), borrow_index)),
83c7162d
XL
1599 LocalMutationIsAllowed::No,
1600 flow_state,
1601 );
1602 // We do not need to call `check_if_path_or_subpath_is_moved`
1603 // again, as we already called it when we made the
1604 // initial reservation.
1605 }
ff7c6d11 1606 }
ff7c6d11 1607
ff7c6d11
XL
1608 fn check_if_reassignment_to_immutable_state(
1609 &mut self,
48663c56 1610 location: Location,
b7449926 1611 local: Local,
ba9703b0 1612 place_span: (Place<'tcx>, Span),
dc9dc135 1613 flow_state: &Flows<'cx, 'tcx>,
ff7c6d11 1614 ) {
b7449926
XL
1615 debug!("check_if_reassignment_to_immutable_state({:?})", local);
1616
1617 // Check if any of the initializiations of `local` have happened yet:
0bf4aa26 1618 if let Some(init_index) = self.is_local_ever_initialized(local, flow_state) {
b7449926
XL
1619 // And, if so, report an error.
1620 let init = &self.move_data.inits[init_index];
dc9dc135 1621 let span = init.span(&self.body);
dfeec247 1622 self.report_illegal_reassignment(location, place_span, span, place_span.0);
ff7c6d11
XL
1623 }
1624 }
1625
83c7162d 1626 fn check_if_full_path_is_moved(
ff7c6d11 1627 &mut self,
48663c56 1628 location: Location,
ff7c6d11 1629 desired_action: InitializationRequiringAction,
74b04a01 1630 place_span: (PlaceRef<'tcx>, Span),
dc9dc135 1631 flow_state: &Flows<'cx, 'tcx>,
ff7c6d11 1632 ) {
ff7c6d11 1633 let maybe_uninits = &flow_state.uninits;
ff7c6d11
XL
1634
1635 // Bad scenarios:
1636 //
1637 // 1. Move of `a.b.c`, use of `a.b.c`
1638 // 2. Move of `a.b.c`, use of `a.b.c.d` (without first reinitializing `a.b.c.d`)
83c7162d 1639 // 3. Uninitialized `(a.b.c: &_)`, use of `*a.b.c`; note that with
ff7c6d11
XL
1640 // partial initialization support, one might have `a.x`
1641 // initialized but not `a.b`.
1642 //
1643 // OK scenarios:
1644 //
83c7162d
XL
1645 // 4. Move of `a.b.c`, use of `a.b.d`
1646 // 5. Uninitialized `a.x`, initialized `a.b`, use of `a.b`
1647 // 6. Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b`
ff7c6d11 1648 // must have been initialized for the use to be sound.
83c7162d 1649 // 7. Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d`
ff7c6d11
XL
1650
1651 // The dataflow tracks shallow prefixes distinctly (that is,
1652 // field-accesses on P distinctly from P itself), in order to
1653 // track substructure initialization separately from the whole
1654 // structure.
1655 //
1656 // E.g., when looking at (*a.b.c).d, if the closest prefix for
1657 // which we have a MovePath is `a.b`, then that means that the
1658 // initialization state of `a.b` is all we need to inspect to
1659 // know if `a.b.c` is valid (and from that we infer that the
1660 // dereference and `.d` access is also valid, since we assume
1661 // `a.b.c` is assigned a reference to a initialized and
1662 // well-formed record structure.)
1663
1664 // Therefore, if we seek out the *closest* prefix for which we
1665 // have a MovePath, that should capture the initialization
1666 // state for the place scenario.
1667 //
83c7162d 1668 // This code covers scenarios 1, 2, and 3.
ff7c6d11 1669
b7449926 1670 debug!("check_if_full_path_is_moved place: {:?}", place_span.0);
dfeec247
XL
1671 let (prefix, mpi) = self.move_path_closest_to(place_span.0);
1672 if maybe_uninits.contains(mpi) {
1673 self.report_use_of_moved_or_uninitialized(
1674 location,
1675 desired_action,
1676 (prefix, place_span.0, place_span.1),
1677 mpi,
1678 );
1679 } // Only query longest prefix with a MovePath, not further
1680 // ancestors; dataflow recurs on children when parents
1681 // move (to support partial (re)inits).
1682 //
1683 // (I.e., querying parents breaks scenario 7; but may want
1684 // to do such a query based on partial-init feature-gate.)
83c7162d
XL
1685 }
1686
60c5eb7d
XL
1687 /// Subslices correspond to multiple move paths, so we iterate through the
1688 /// elements of the base array. For each element we check
1689 ///
1690 /// * Does this element overlap with our slice.
1691 /// * Is any part of it uninitialized.
1692 fn check_if_subslice_element_is_moved(
1693 &mut self,
1694 location: Location,
1695 desired_action: InitializationRequiringAction,
74b04a01
XL
1696 place_span: (PlaceRef<'tcx>, Span),
1697 maybe_uninits: &BitSet<MovePathIndex>,
1b1a35ee
XL
1698 from: u64,
1699 to: u64,
60c5eb7d
XL
1700 ) {
1701 if let Some(mpi) = self.move_path_for_place(place_span.0) {
74b04a01
XL
1702 let move_paths = &self.move_data.move_paths;
1703
1704 let root_path = &move_paths[mpi];
1705 for (child_mpi, child_move_path) in root_path.children(move_paths) {
1706 let last_proj = child_move_path.place.projection.last().unwrap();
60c5eb7d
XL
1707 if let ProjectionElem::ConstantIndex { offset, from_end, .. } = last_proj {
1708 debug_assert!(!from_end, "Array constant indexing shouldn't be `from_end`.");
1709
1710 if (from..to).contains(offset) {
74b04a01
XL
1711 let uninit_child =
1712 self.move_data.find_in_move_path_or_its_descendants(child_mpi, |mpi| {
1713 maybe_uninits.contains(mpi)
1714 });
1715
1716 if let Some(uninit_child) = uninit_child {
60c5eb7d
XL
1717 self.report_use_of_moved_or_uninitialized(
1718 location,
1719 desired_action,
1720 (place_span.0, place_span.0, place_span.1),
1721 uninit_child,
1722 );
1723 return; // don't bother finding other problems.
1724 }
1725 }
1726 }
60c5eb7d
XL
1727 }
1728 }
1729 }
1730
83c7162d
XL
1731 fn check_if_path_or_subpath_is_moved(
1732 &mut self,
48663c56 1733 location: Location,
83c7162d 1734 desired_action: InitializationRequiringAction,
74b04a01 1735 place_span: (PlaceRef<'tcx>, Span),
dc9dc135 1736 flow_state: &Flows<'cx, 'tcx>,
83c7162d 1737 ) {
83c7162d 1738 let maybe_uninits = &flow_state.uninits;
83c7162d
XL
1739
1740 // Bad scenarios:
1741 //
1742 // 1. Move of `a.b.c`, use of `a` or `a.b`
1743 // partial initialization support, one might have `a.x`
1744 // initialized but not `a.b`.
1745 // 2. All bad scenarios from `check_if_full_path_is_moved`
1746 //
1747 // OK scenarios:
1748 //
1749 // 3. Move of `a.b.c`, use of `a.b.d`
1750 // 4. Uninitialized `a.x`, initialized `a.b`, use of `a.b`
1751 // 5. Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b`
1752 // must have been initialized for the use to be sound.
1753 // 6. Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d`
1754
48663c56 1755 self.check_if_full_path_is_moved(location, desired_action, place_span, flow_state);
ff7c6d11 1756
5869c6ff
XL
1757 if let Some((place_base, ProjectionElem::Subslice { from, to, from_end: false })) =
1758 place_span.0.last_projection()
dfeec247 1759 {
5869c6ff 1760 let place_ty = place_base.ty(self.body(), self.infcx.tcx);
1b1a35ee 1761 if let ty::Array(..) = place_ty.ty.kind() {
60c5eb7d
XL
1762 self.check_if_subslice_element_is_moved(
1763 location,
1764 desired_action,
5869c6ff 1765 (place_base, place_span.1),
60c5eb7d 1766 maybe_uninits,
5869c6ff
XL
1767 from,
1768 to,
60c5eb7d
XL
1769 );
1770 return;
1771 }
1772 }
1773
ff7c6d11
XL
1774 // A move of any shallow suffix of `place` also interferes
1775 // with an attempt to use `place`. This is scenario 3 above.
1776 //
1777 // (Distinct from handling of scenarios 1+2+4 above because
1778 // `place` does not interfere with suffixes of its prefixes,
0731742a 1779 // e.g., `a.b.c` does not interfere with `a.b.d`)
83c7162d
XL
1780 //
1781 // This code covers scenario 1.
ff7c6d11 1782
b7449926
XL
1783 debug!("check_if_path_or_subpath_is_moved place: {:?}", place_span.0);
1784 if let Some(mpi) = self.move_path_for_place(place_span.0) {
74b04a01
XL
1785 let uninit_mpi = self
1786 .move_data
1787 .find_in_move_path_or_its_descendants(mpi, |mpi| maybe_uninits.contains(mpi));
1788
1789 if let Some(uninit_mpi) = uninit_mpi {
ff7c6d11 1790 self.report_use_of_moved_or_uninitialized(
48663c56 1791 location,
ff7c6d11 1792 desired_action,
0bf4aa26 1793 (place_span.0, place_span.0, place_span.1),
74b04a01 1794 uninit_mpi,
ff7c6d11
XL
1795 );
1796 return; // don't bother finding other problems.
1797 }
1798 }
1799 }
1800
1801 /// Currently MoveData does not store entries for all places in
1802 /// the input MIR. For example it will currently filter out
1803 /// places that are Copy; thus we do not track places of shared
1804 /// reference type. This routine will walk up a place along its
1805 /// prefixes, searching for a foundational place that *is*
1806 /// tracked in the MoveData.
1807 ///
1808 /// An Err result includes a tag indicated why the search failed.
0531ce1d 1809 /// Currently this can only occur if the place is built off of a
ff7c6d11 1810 /// static variable, as we do not track those in the MoveData.
74b04a01 1811 fn move_path_closest_to(&mut self, place: PlaceRef<'tcx>) -> (PlaceRef<'tcx>, MovePathIndex) {
60c5eb7d 1812 match self.move_data.rev_lookup.find(place) {
dfeec247
XL
1813 LookupResult::Parent(Some(mpi)) | LookupResult::Exact(mpi) => {
1814 (self.move_data.move_paths[mpi].place.as_ref(), mpi)
1815 }
1816 LookupResult::Parent(None) => panic!("should have move path for every Local"),
ff7c6d11
XL
1817 }
1818 }
1819
74b04a01 1820 fn move_path_for_place(&mut self, place: PlaceRef<'tcx>) -> Option<MovePathIndex> {
ff7c6d11
XL
1821 // If returns None, then there is no move path corresponding
1822 // to a direct owner of `place` (which means there is nothing
1823 // that borrowck tracks for its analysis).
1824
1825 match self.move_data.rev_lookup.find(place) {
1826 LookupResult::Parent(_) => None,
1827 LookupResult::Exact(mpi) => Some(mpi),
1828 }
1829 }
1830
1831 fn check_if_assigned_path_is_moved(
1832 &mut self,
48663c56 1833 location: Location,
ba9703b0 1834 (place, span): (Place<'tcx>, Span),
dc9dc135 1835 flow_state: &Flows<'cx, 'tcx>,
ff7c6d11 1836 ) {
83c7162d 1837 debug!("check_if_assigned_path_is_moved place: {:?}", place);
416331ca
XL
1838
1839 // None case => assigning to `x` does not require `x` be initialized.
5869c6ff 1840 for (place_base, elem) in place.iter_projections().rev() {
e1599b0c 1841 match elem {
416331ca
XL
1842 ProjectionElem::Index(_/*operand*/) |
1843 ProjectionElem::ConstantIndex { .. } |
1844 // assigning to P[i] requires P to be valid.
1845 ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) =>
1846 // assigning to (P->variant) is okay if assigning to `P` is okay
1847 //
1848 // FIXME: is this true even if P is a adt with a dtor?
1849 { }
1850
1851 // assigning to (*P) requires P to be initialized
1852 ProjectionElem::Deref => {
1853 self.check_if_full_path_is_moved(
1854 location, InitializationRequiringAction::Use,
5869c6ff 1855 (place_base, span), flow_state);
416331ca
XL
1856 // (base initialized; no need to
1857 // recur further)
ff7c6d11
XL
1858 break;
1859 }
416331ca
XL
1860
1861 ProjectionElem::Subslice { .. } => {
1862 panic!("we don't allow assignments to subslices, location: {:?}",
1863 location);
1864 }
1865
1866 ProjectionElem::Field(..) => {
1867 // if type of `P` has a dtor, then
1868 // assigning to `P.f` requires `P` itself
1869 // be already initialized
1870 let tcx = self.infcx.tcx;
5869c6ff 1871 let base_ty = place_base.ty(self.body(), tcx).ty;
1b1a35ee 1872 match base_ty.kind() {
416331ca
XL
1873 ty::Adt(def, _) if def.has_dtor(tcx) => {
1874 self.check_if_path_or_subpath_is_moved(
1875 location, InitializationRequiringAction::Assignment,
5869c6ff 1876 (place_base, span), flow_state);
416331ca 1877
83c7162d
XL
1878 // (base initialized; no need to
1879 // recur further)
1880 break;
1881 }
1882
416331ca
XL
1883 // Once `let s; s.x = V; read(s.x);`,
1884 // is allowed, remove this match arm.
1885 ty::Adt(..) | ty::Tuple(..) => {
5869c6ff 1886 check_parent_of_field(self, location, place_base, span, flow_state);
416331ca 1887
dfeec247
XL
1888 // rust-lang/rust#21232, #54499, #54986: during period where we reject
1889 // partial initialization, do not complain about unnecessary `mut` on
1890 // an attempt to do a partial initialization.
1891 self.used_mut.insert(place.local);
ff7c6d11 1892 }
ff7c6d11 1893
416331ca
XL
1894 _ => {}
1895 }
ff7c6d11
XL
1896 }
1897 }
1898 }
ff7c6d11 1899
dc9dc135
XL
1900 fn check_parent_of_field<'cx, 'tcx>(
1901 this: &mut MirBorrowckCtxt<'cx, 'tcx>,
48663c56 1902 location: Location,
74b04a01 1903 base: PlaceRef<'tcx>,
a1dfa0c6 1904 span: Span,
dc9dc135 1905 flow_state: &Flows<'cx, 'tcx>,
a1dfa0c6 1906 ) {
0bf4aa26
XL
1907 // rust-lang/rust#21232: Until Rust allows reads from the
1908 // initialized parts of partially initialized structs, we
1909 // will, starting with the 2018 edition, reject attempts
1910 // to write to structs that are not fully initialized.
1911 //
1912 // In other words, *until* we allow this:
1913 //
1914 // 1. `let mut s; s.x = Val; read(s.x);`
1915 //
1916 // we will for now disallow this:
1917 //
1918 // 2. `let mut s; s.x = Val;`
1919 //
1920 // and also this:
1921 //
1922 // 3. `let mut s = ...; drop(s); s.x=Val;`
1923 //
1924 // This does not use check_if_path_or_subpath_is_moved,
1925 // because we want to *allow* reinitializations of fields:
0731742a 1926 // e.g., want to allow
0bf4aa26
XL
1927 //
1928 // `let mut s = ...; drop(s.x); s.x=Val;`
1929 //
1930 // This does not use check_if_full_path_is_moved on
1931 // `base`, because that would report an error about the
1932 // `base` as a whole, but in this scenario we *really*
1933 // want to report an error about the actual thing that was
1934 // moved, which may be some prefix of `base`.
1935
1936 // Shallow so that we'll stop at any dereference; we'll
1937 // report errors about issues with such bases elsewhere.
1938 let maybe_uninits = &flow_state.uninits;
1939
1940 // Find the shortest uninitialized prefix you can reach
1941 // without going over a Deref.
1942 let mut shortest_uninit_seen = None;
1943 for prefix in this.prefixes(base, PrefixSet::Shallow) {
1944 let mpi = match this.move_path_for_place(prefix) {
dfeec247
XL
1945 Some(mpi) => mpi,
1946 None => continue,
0bf4aa26
XL
1947 };
1948
1949 if maybe_uninits.contains(mpi) {
dfeec247
XL
1950 debug!(
1951 "check_parent_of_field updating shortest_uninit_seen from {:?} to {:?}",
1952 shortest_uninit_seen,
1953 Some((prefix, mpi))
1954 );
0bf4aa26
XL
1955 shortest_uninit_seen = Some((prefix, mpi));
1956 } else {
1957 debug!("check_parent_of_field {:?} is definitely initialized", (prefix, mpi));
1958 }
1959 }
1960
1961 if let Some((prefix, mpi)) = shortest_uninit_seen {
a1dfa0c6
XL
1962 // Check for a reassignment into a uninitialized field of a union (for example,
1963 // after a move out). In this case, do not report a error here. There is an
1964 // exception, if this is the first assignment into the union (that is, there is
1965 // no move out from an earlier location) then this is an attempt at initialization
1966 // of the union - we should error in that case.
1967 let tcx = this.infcx.tcx;
17df50a5
XL
1968 if base.ty(this.body(), tcx).ty.is_union() {
1969 if this.move_data.path_map[mpi].iter().any(|moi| {
1970 this.move_data.moves[*moi].source.is_predecessor_of(location, this.body)
1971 }) {
1972 return;
a1dfa0c6
XL
1973 }
1974 }
1975
0bf4aa26 1976 this.report_use_of_moved_or_uninitialized(
48663c56 1977 location,
0bf4aa26
XL
1978 InitializationRequiringAction::PartialAssignment,
1979 (prefix, base, span),
1980 mpi,
1981 );
1982 }
1983 }
1984 }
8faf50e0 1985
9fa01778 1986 /// Checks the permissions for the given place and read or write kind
ff7c6d11 1987 ///
9fa01778 1988 /// Returns `true` if an error is reported.
ff7c6d11 1989 fn check_access_permissions(
83c7162d 1990 &mut self,
ba9703b0 1991 (place, span): (Place<'tcx>, Span),
ff7c6d11
XL
1992 kind: ReadOrWrite,
1993 is_local_mutation_allowed: LocalMutationIsAllowed,
dc9dc135 1994 flow_state: &Flows<'cx, 'tcx>,
8faf50e0 1995 location: Location,
ff7c6d11
XL
1996 ) -> bool {
1997 debug!(
0bf4aa26 1998 "check_access_permissions({:?}, {:?}, is_local_mutation_allowed: {:?})",
0531ce1d 1999 place, kind, is_local_mutation_allowed
ff7c6d11 2000 );
94b46f34 2001
94b46f34
XL
2002 let error_access;
2003 let the_place_err;
2004
ff7c6d11 2005 match kind {
ba9703b0
XL
2006 Reservation(WriteKind::MutableBorrow(
2007 borrow_kind @ (BorrowKind::Unique | BorrowKind::Mut { .. }),
2008 ))
2009 | Write(WriteKind::MutableBorrow(
2010 borrow_kind @ (BorrowKind::Unique | BorrowKind::Mut { .. }),
2011 )) => {
94b46f34
XL
2012 let is_local_mutation_allowed = match borrow_kind {
2013 BorrowKind::Unique => LocalMutationIsAllowed::Yes,
2014 BorrowKind::Mut { .. } => is_local_mutation_allowed,
0bf4aa26 2015 BorrowKind::Shared | BorrowKind::Shallow => unreachable!(),
94b46f34 2016 };
416331ca 2017 match self.is_mutable(place.as_ref(), is_local_mutation_allowed) {
94b46f34
XL
2018 Ok(root_place) => {
2019 self.add_used_mut(root_place, flow_state);
2020 return false;
2021 }
83c7162d 2022 Err(place_err) => {
94b46f34
XL
2023 error_access = AccessKind::MutableBorrow;
2024 the_place_err = place_err;
ff7c6d11
XL
2025 }
2026 }
83c7162d 2027 }
ff7c6d11 2028 Reservation(WriteKind::Mutate) | Write(WriteKind::Mutate) => {
416331ca 2029 match self.is_mutable(place.as_ref(), is_local_mutation_allowed) {
94b46f34
XL
2030 Ok(root_place) => {
2031 self.add_used_mut(root_place, flow_state);
2032 return false;
2033 }
83c7162d 2034 Err(place_err) => {
94b46f34
XL
2035 error_access = AccessKind::Mutate;
2036 the_place_err = place_err;
ff7c6d11 2037 }
ff7c6d11
XL
2038 }
2039 }
94b46f34 2040
ba9703b0
XL
2041 Reservation(
2042 WriteKind::Move
2043 | WriteKind::StorageDeadOrDrop
2044 | WriteKind::MutableBorrow(BorrowKind::Shared)
2045 | WriteKind::MutableBorrow(BorrowKind::Shallow),
2046 )
2047 | Write(
2048 WriteKind::Move
2049 | WriteKind::StorageDeadOrDrop
2050 | WriteKind::MutableBorrow(BorrowKind::Shared)
2051 | WriteKind::MutableBorrow(BorrowKind::Shallow),
2052 ) => {
e74abb32 2053 if let (Err(_), true) = (
416331ca 2054 self.is_mutable(place.as_ref(), is_local_mutation_allowed),
dfeec247 2055 self.errors_buffer.is_empty(),
a1dfa0c6 2056 ) {
e74abb32
XL
2057 // rust-lang/rust#46908: In pure NLL mode this code path should be
2058 // unreachable, but we use `delay_span_bug` because we can hit this when
2059 // dereferencing a non-Copy raw pointer *and* have `-Ztreat-err-as-bug`
2060 // enabled. We don't want to ICE for that case, as other errors will have
2061 // been emitted (#52262).
dfeec247
XL
2062 self.infcx.tcx.sess.delay_span_bug(
2063 span,
2064 &format!(
2065 "Accessing `{:?}` with the kind `{:?}` shouldn't be possible",
2066 place, kind,
2067 ),
2068 );
ff7c6d11 2069 }
94b46f34
XL
2070 return false;
2071 }
2072 Activation(..) => {
2073 // permission checks are done at Reservation point.
2074 return false;
ff7c6d11 2075 }
ba9703b0
XL
2076 Read(
2077 ReadKind::Borrow(
2078 BorrowKind::Unique
2079 | BorrowKind::Mut { .. }
2080 | BorrowKind::Shared
2081 | BorrowKind::Shallow,
2082 )
2083 | ReadKind::Copy,
2084 ) => {
94b46f34
XL
2085 // Access authorized
2086 return false;
2087 }
ff7c6d11
XL
2088 }
2089
60c5eb7d
XL
2090 // rust-lang/rust#21232, #54986: during period where we reject
2091 // partial initialization, do not complain about mutability
2092 // errors except for actual mutation (as opposed to an attempt
2093 // to do a partial initialization).
dfeec247
XL
2094 let previously_initialized =
2095 self.is_local_ever_initialized(place.local, flow_state).is_some();
60c5eb7d 2096
94b46f34 2097 // at this point, we have set up the error reporting state.
60c5eb7d 2098 if previously_initialized {
dfeec247 2099 self.report_mutability_error(place, span, the_place_err, error_access, location);
0bf4aa26
XL
2100 true
2101 } else {
2102 false
60c5eb7d 2103 }
0bf4aa26
XL
2104 }
2105
dc9dc135
XL
2106 fn is_local_ever_initialized(
2107 &self,
2108 local: Local,
2109 flow_state: &Flows<'cx, 'tcx>,
2110 ) -> Option<InitIndex> {
0bf4aa26
XL
2111 let mpi = self.move_data.rev_lookup.find_local(local);
2112 let ii = &self.move_data.init_path_map[mpi];
2113 for &index in ii {
2114 if flow_state.ever_inits.contains(index) {
2115 return Some(index);
2116 }
2117 }
2118 None
ff7c6d11
XL
2119 }
2120
83c7162d 2121 /// Adds the place into the used mutable variables set
74b04a01 2122 fn add_used_mut(&mut self, root_place: RootPlace<'tcx>, flow_state: &Flows<'cx, 'tcx>) {
83c7162d 2123 match root_place {
dfeec247 2124 RootPlace { place_local: local, place_projection: [], is_local_mutation_allowed } => {
0bf4aa26
XL
2125 // If the local may have been initialized, and it is now currently being
2126 // mutated, then it is justified to be annotated with the `mut`
2127 // keyword, since the mutation may be a possible reassignment.
dfeec247 2128 if is_local_mutation_allowed != LocalMutationIsAllowed::Yes
74b04a01 2129 && self.is_local_ever_initialized(local, flow_state).is_some()
0bf4aa26 2130 {
74b04a01 2131 self.used_mut.insert(local);
83c7162d
XL
2132 }
2133 }
8faf50e0 2134 RootPlace {
dfeec247 2135 place_local: _,
416331ca 2136 place_projection: _,
8faf50e0
XL
2137 is_local_mutation_allowed: LocalMutationIsAllowed::Yes,
2138 } => {}
83c7162d 2139 RootPlace {
dfeec247 2140 place_local,
e1599b0c 2141 place_projection: place_projection @ [.., _],
83c7162d
XL
2142 is_local_mutation_allowed: _,
2143 } => {
416331ca 2144 if let Some(field) = self.is_upvar_field_projection(PlaceRef {
dfeec247
XL
2145 local: place_local,
2146 projection: place_projection,
416331ca 2147 }) {
83c7162d
XL
2148 self.used_mut_upvars.push(field);
2149 }
2150 }
83c7162d
XL
2151 }
2152 }
2153
0bf4aa26 2154 /// Whether this value can be written or borrowed mutably.
83c7162d 2155 /// Returns the root place if the place passed in is a projection.
74b04a01 2156 fn is_mutable(
ff7c6d11 2157 &self,
74b04a01 2158 place: PlaceRef<'tcx>,
ff7c6d11 2159 is_local_mutation_allowed: LocalMutationIsAllowed,
74b04a01 2160 ) -> Result<RootPlace<'tcx>, PlaceRef<'tcx>> {
5869c6ff
XL
2161 debug!("is_mutable: place={:?}, is_local...={:?}", place, is_local_mutation_allowed);
2162 match place.last_projection() {
2163 None => {
2164 let local = &self.body.local_decls[place.local];
ff7c6d11
XL
2165 match local.mutability {
2166 Mutability::Not => match is_local_mutation_allowed {
8faf50e0 2167 LocalMutationIsAllowed::Yes => Ok(RootPlace {
dfeec247 2168 place_local: place.local,
416331ca 2169 place_projection: place.projection,
8faf50e0
XL
2170 is_local_mutation_allowed: LocalMutationIsAllowed::Yes,
2171 }),
2172 LocalMutationIsAllowed::ExceptUpvars => Ok(RootPlace {
dfeec247 2173 place_local: place.local,
416331ca 2174 place_projection: place.projection,
8faf50e0
XL
2175 is_local_mutation_allowed: LocalMutationIsAllowed::ExceptUpvars,
2176 }),
ff7c6d11
XL
2177 LocalMutationIsAllowed::No => Err(place),
2178 },
8faf50e0 2179 Mutability::Mut => Ok(RootPlace {
dfeec247 2180 place_local: place.local,
416331ca 2181 place_projection: place.projection,
8faf50e0
XL
2182 is_local_mutation_allowed,
2183 }),
ff7c6d11
XL
2184 }
2185 }
5869c6ff 2186 Some((place_base, elem)) => {
e1599b0c 2187 match elem {
ff7c6d11 2188 ProjectionElem::Deref => {
5869c6ff 2189 let base_ty = place_base.ty(self.body(), self.infcx.tcx).ty;
ff7c6d11
XL
2190
2191 // Check the kind of deref to decide
1b1a35ee 2192 match base_ty.kind() {
b7449926 2193 ty::Ref(_, _, mutbl) => {
94b46f34 2194 match mutbl {
ff7c6d11 2195 // Shared borrowed data is never mutable
dfeec247 2196 hir::Mutability::Not => Err(place),
ff7c6d11
XL
2197 // Mutably borrowed data is mutable, but only if we have a
2198 // unique path to the `&mut`
dfeec247 2199 hir::Mutability::Mut => {
48663c56 2200 let mode = match self.is_upvar_field_projection(place) {
dfeec247 2201 Some(field) if self.upvars[field.index()].by_ref => {
ff7c6d11
XL
2202 is_local_mutation_allowed
2203 }
2204 _ => LocalMutationIsAllowed::Yes,
2205 };
2206
5869c6ff 2207 self.is_mutable(place_base, mode)
ff7c6d11
XL
2208 }
2209 }
2210 }
b7449926 2211 ty::RawPtr(tnm) => {
ff7c6d11
XL
2212 match tnm.mutbl {
2213 // `*const` raw pointers are not mutable
dfeec247 2214 hir::Mutability::Not => Err(place),
83c7162d
XL
2215 // `*mut` raw pointers are always mutable, regardless of
2216 // context. The users have to check by themselves.
dfeec247
XL
2217 hir::Mutability::Mut => Ok(RootPlace {
2218 place_local: place.local,
2219 place_projection: place.projection,
2220 is_local_mutation_allowed,
2221 }),
ff7c6d11
XL
2222 }
2223 }
2224 // `Box<T>` owns its content, so mutable if its location is mutable
5869c6ff
XL
2225 _ if base_ty.is_box() => {
2226 self.is_mutable(place_base, is_local_mutation_allowed)
2227 }
ff7c6d11
XL
2228 // Deref should only be for reference, pointers or boxes
2229 _ => bug!("Deref of unexpected type: {:?}", base_ty),
2230 }
2231 }
2232 // All other projections are owned by their base path, so mutable if
2233 // base path is mutable
2234 ProjectionElem::Field(..)
2235 | ProjectionElem::Index(..)
2236 | ProjectionElem::ConstantIndex { .. }
2237 | ProjectionElem::Subslice { .. }
2238 | ProjectionElem::Downcast(..) => {
48663c56 2239 let upvar_field_projection = self.is_upvar_field_projection(place);
8faf50e0 2240 if let Some(field) = upvar_field_projection {
48663c56 2241 let upvar = &self.upvars[field.index()];
ff7c6d11 2242 debug!(
5869c6ff
XL
2243 "is_mutable: upvar.mutability={:?} local_mutation_is_allowed={:?} \
2244 place={:?}, place_base={:?}",
2245 upvar, is_local_mutation_allowed, place, place_base
ff7c6d11 2246 );
5869c6ff 2247 match (upvar.place.mutability, is_local_mutation_allowed) {
ba9703b0
XL
2248 (
2249 Mutability::Not,
2250 LocalMutationIsAllowed::No
2251 | LocalMutationIsAllowed::ExceptUpvars,
2252 ) => Err(place),
ff7c6d11
XL
2253 (Mutability::Not, LocalMutationIsAllowed::Yes)
2254 | (Mutability::Mut, _) => {
83c7162d
XL
2255 // Subtle: this is an upvar
2256 // reference, so it looks like
2257 // `self.foo` -- we want to double
48663c56 2258 // check that the location `*self`
83c7162d
XL
2259 // is mutable (i.e., this is not a
2260 // `Fn` closure). But if that
2261 // check succeeds, we want to
2262 // *blame* the mutability on
2263 // `place` (that is,
2264 // `self.foo`). This is used to
2265 // propagate the info about
2266 // whether mutability declarations
2267 // are used outwards, so that we register
2268 // the outer variable as mutable. Otherwise a
2269 // test like this fails to record the `mut`
2270 // as needed:
2271 //
2272 // ```
2273 // fn foo<F: FnOnce()>(_f: F) { }
2274 // fn main() {
2275 // let var = Vec::new();
2276 // foo(move || {
2277 // var.push(1);
2278 // });
2279 // }
2280 // ```
5869c6ff
XL
2281 let _ =
2282 self.is_mutable(place_base, is_local_mutation_allowed)?;
8faf50e0 2283 Ok(RootPlace {
dfeec247 2284 place_local: place.local,
416331ca 2285 place_projection: place.projection,
8faf50e0
XL
2286 is_local_mutation_allowed,
2287 })
ff7c6d11
XL
2288 }
2289 }
2290 } else {
5869c6ff 2291 self.is_mutable(place_base, is_local_mutation_allowed)
ff7c6d11
XL
2292 }
2293 }
2294 }
2295 }
2296 }
2297 }
48663c56
XL
2298
2299 /// If `place` is a field projection, and the field is being projected from a closure type,
2300 /// then returns the index of the field being projected. Note that this closure will always
2301 /// be `self` in the current MIR, because that is the only time we directly access the fields
2302 /// of a closure type.
74b04a01 2303 pub fn is_upvar_field_projection(&self, place_ref: PlaceRef<'tcx>) -> Option<Field> {
f035d41b 2304 path_utils::is_upvar_field_projection(self.infcx.tcx, &self.upvars, place_ref, self.body())
48663c56 2305 }
ff7c6d11
XL
2306}
2307
ff7c6d11
XL
2308/// The degree of overlap between 2 places for borrow-checking.
2309enum Overlap {
2310 /// The places might partially overlap - in this case, we give
2311 /// up and say that they might conflict. This occurs when
2312 /// different fields of a union are borrowed. For example,
2313 /// if `u` is a union, we have no way of telling how disjoint
2314 /// `u.a.x` and `a.b.y` are.
2315 Arbitrary,
2316 /// The places have the same type, and are either completely disjoint
0731742a 2317 /// or equal - i.e., they can't "partially" overlap as can occur with
ff7c6d11
XL
2318 /// unions. This is the "base case" on which we recur for extensions
2319 /// of the place.
2320 EqualOrDisjoint,
2321 /// The places are disjoint, so we know all extensions of them
2322 /// will also be disjoint.
2323 Disjoint,
2324}