]>
Commit | Line | Data |
---|---|---|
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] | |
19 | extern crate rustc_middle; | |
20 | #[macro_use] | |
21 | extern crate tracing; | |
22 | ||
353b0b11 | 23 | use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; |
0bf4aa26 | 24 | use rustc_data_structures::graph::dominators::Dominators; |
4b012472 | 25 | use rustc_errors::{Diagnostic, DiagnosticBuilder}; |
dfeec247 | 26 | use rustc_hir as hir; |
f9f354fc | 27 | use rustc_hir::def_id::LocalDefId; |
add651ee | 28 | use rustc_index::bit_set::{BitSet, ChunkedBitSet}; |
49aad941 | 29 | use rustc_index::{IndexSlice, IndexVec}; |
9ffffee4 | 30 | use rustc_infer::infer::{ |
49aad941 | 31 | InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt, |
9ffffee4 | 32 | }; |
ed00b5ec FG |
33 | use rustc_middle::mir::tcx::PlaceTy; |
34 | use rustc_middle::mir::*; | |
49aad941 FG |
35 | use rustc_middle::query::Providers; |
36 | use rustc_middle::traits::DefiningAnchor; | |
4b012472 | 37 | use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt}; |
04454e1e FG |
38 | use rustc_session::lint::builtin::UNUSED_MUT; |
39 | use rustc_span::{Span, Symbol}; | |
353b0b11 | 40 | use rustc_target::abi::FieldIdx; |
ff7c6d11 | 41 | |
dfeec247 XL |
42 | use smallvec::SmallVec; |
43 | use std::cell::RefCell; | |
0bf4aa26 | 44 | use std::collections::BTreeMap; |
4b012472 | 45 | use std::marker::PhantomData; |
9ffffee4 | 46 | use std::ops::Deref; |
532ac7d7 | 47 | use std::rc::Rc; |
ff7c6d11 | 48 | |
c295e0f8 XL |
49 | use rustc_mir_dataflow::impls::{ |
50 | EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces, | |
f9f354fc | 51 | }; |
c295e0f8 | 52 | use rustc_mir_dataflow::move_paths::{InitIndex, MoveOutIndex, MovePathIndex}; |
ed00b5ec | 53 | use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult, MoveData}; |
c295e0f8 XL |
54 | use rustc_mir_dataflow::Analysis; |
55 | use rustc_mir_dataflow::MoveDataParamEnv; | |
ff7c6d11 | 56 | |
f2b60f7d FG |
57 | use crate::session_diagnostics::VarNeedNotMut; |
58 | ||
ed00b5ec | 59 | use self::diagnostics::{AccessKind, IllegalMoveOriginKind, MoveError, RegionName}; |
83c7162d | 60 | use self::location::LocationTable; |
ff7c6d11 | 61 | use self::prefixes::PrefixSet; |
49aad941 | 62 | use consumers::{BodyWithBorrowckFacts, ConsumerOptions}; |
ff7c6d11 | 63 | |
94b46f34 XL |
64 | use self::path_utils::*; |
65 | ||
3c0e092e | 66 | pub mod borrow_set; |
c295e0f8 | 67 | mod borrowck_errors; |
dfeec247 | 68 | mod constraints; |
c295e0f8 | 69 | mod dataflow; |
f9f354fc | 70 | mod def_use; |
60c5eb7d | 71 | mod diagnostics; |
dfeec247 | 72 | mod facts; |
83c7162d | 73 | mod location; |
dfeec247 XL |
74 | mod member_constraints; |
75 | mod nll; | |
8faf50e0 | 76 | mod path_utils; |
dfeec247 XL |
77 | mod place_ext; |
78 | mod places_conflict; | |
4b012472 | 79 | mod polonius; |
ff7c6d11 | 80 | mod prefixes; |
dfeec247 | 81 | mod region_infer; |
60c5eb7d | 82 | mod renumber; |
064997fb | 83 | mod session_diagnostics; |
60c5eb7d | 84 | mod type_check; |
dfeec247 XL |
85 | mod universal_regions; |
86 | mod used_muts; | |
49aad941 | 87 | mod util; |
60c5eb7d | 88 | |
487cf647 | 89 | /// A public API provided for the Rust compiler consumers. |
c295e0f8 XL |
90 | pub mod consumers; |
91 | ||
92 | use borrow_set::{BorrowData, BorrowSet}; | |
93 | use dataflow::{BorrowIndex, BorrowckFlowState as Flows, BorrowckResults, Borrows}; | |
353b0b11 | 94 | use nll::PoloniusOutput; |
c295e0f8 XL |
95 | use place_ext::PlaceExt; |
96 | use places_conflict::{places_conflict, PlaceConflictBias}; | |
97 | use region_infer::RegionInferenceContext; | |
9ffffee4 FG |
98 | use renumber::RegionCtxt; |
99 | ||
4b012472 | 100 | rustc_fluent_macro::fluent_messages! { "../messages.ftl" } |
48663c56 | 101 | |
923072b8 | 102 | /// Associate some local constants with the `'tcx` lifetime |
4b012472 | 103 | struct TyCtxtConsts<'tcx>(PhantomData<&'tcx ()>); |
923072b8 FG |
104 | impl<'tcx> TyCtxtConsts<'tcx> { |
105 | const DEREF_PROJECTION: &'tcx [PlaceElem<'tcx>; 1] = &[ProjectionElem::Deref]; | |
106 | } | |
74b04a01 | 107 | |
f035d41b | 108 | pub fn provide(providers: &mut Providers) { |
49aad941 | 109 | *providers = Providers { mir_borrowck, ..*providers }; |
ff7c6d11 XL |
110 | } |
111 | ||
49aad941 | 112 | fn 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 |
146 | fn 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, ®ioncx, &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 | ®ioncx, |
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, ®ioncx, &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(®ioncx), |
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 |
449 | pub 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 | ||
454 | impl<'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 | ||
503 | impl<'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 | 511 | struct 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 | 588 | impl<'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 | 820 | use self::AccessDepth::{Deep, Shallow}; |
dfeec247 | 821 | use self::ReadOrWrite::{Activation, Read, Reservation, Write}; |
ff7c6d11 XL |
822 | |
823 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
824 | enum ArtificialField { | |
ff7c6d11 | 825 | ArrayLength, |
781aab86 | 826 | FakeBorrow, |
ff7c6d11 XL |
827 | } |
828 | ||
829 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
0bf4aa26 | 830 | enum 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)] | |
851 | enum 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)] | |
871 | enum 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)] | |
879 | enum 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)] |
895 | enum 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 | 904 | enum 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 |
913 | struct RootPlace<'tcx> { |
914 | place_local: Local, | |
915 | place_projection: &'tcx [PlaceElem<'tcx>], | |
83c7162d XL |
916 | is_local_mutation_allowed: LocalMutationIsAllowed, |
917 | } | |
918 | ||
ff7c6d11 XL |
919 | impl 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 | 951 | impl<'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 | 2388 | mod 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. |
2527 | enum 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 | } |