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