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