]>
Commit | Line | Data |
---|---|---|
ff7c6d11 XL |
1 | // Copyright 2017 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | //! This query borrow-checks the MIR to (further) ensure it is not broken. | |
12 | ||
0531ce1d | 13 | use borrow_check::nll::region_infer::{RegionCausalInfo, RegionInferenceContext}; |
ff7c6d11 XL |
14 | use rustc::hir; |
15 | use rustc::hir::def_id::DefId; | |
16 | use rustc::hir::map::definitions::DefPathData; | |
17 | use rustc::infer::InferCtxt; | |
18 | use rustc::ty::{self, ParamEnv, TyCtxt}; | |
19 | use rustc::ty::maps::Providers; | |
2c00a5a8 | 20 | use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Location, Place}; |
ff7c6d11 XL |
21 | use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue}; |
22 | use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind}; | |
23 | use rustc::mir::ClosureRegionRequirements; | |
24 | ||
25 | use rustc_data_structures::fx::FxHashSet; | |
26 | use rustc_data_structures::indexed_set::IdxSetBuf; | |
27 | use rustc_data_structures::indexed_vec::Idx; | |
28 | ||
29 | use std::rc::Rc; | |
30 | ||
31 | use syntax::ast; | |
32 | use syntax_pos::Span; | |
33 | ||
34 | use dataflow::{do_dataflow, DebugFormatted}; | |
35 | use dataflow::FlowAtLocation; | |
36 | use dataflow::MoveDataParamEnv; | |
0531ce1d | 37 | use dataflow::{DataflowResultsConsumer}; |
2c00a5a8 XL |
38 | use dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces}; |
39 | use dataflow::{EverInitializedPlaces, MovingOutStatements}; | |
ff7c6d11 | 40 | use dataflow::{BorrowData, Borrows, ReserveOrActivateIndex}; |
ff7c6d11 XL |
41 | use dataflow::indexes::BorrowIndex; |
42 | use dataflow::move_paths::{IllegalMoveOriginKind, MoveError}; | |
43 | use dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MovePathIndex}; | |
44 | use util::borrowck_errors::{BorrowckErrors, Origin}; | |
45 | ||
46 | use std::iter; | |
47 | ||
48 | use self::flows::Flows; | |
49 | use self::prefixes::PrefixSet; | |
50 | use self::MutateMode::{JustWrite, WriteAndRead}; | |
51 | ||
52 | mod error_reporting; | |
53 | mod flows; | |
54 | mod prefixes; | |
55 | ||
ff7c6d11 XL |
56 | pub(crate) mod nll; |
57 | ||
58 | pub fn provide(providers: &mut Providers) { | |
59 | *providers = Providers { | |
60 | mir_borrowck, | |
61 | ..*providers | |
62 | }; | |
63 | } | |
64 | ||
65 | fn mir_borrowck<'a, 'tcx>( | |
66 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
67 | def_id: DefId, | |
68 | ) -> Option<ClosureRegionRequirements<'tcx>> { | |
69 | let input_mir = tcx.mir_validated(def_id); | |
70 | debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id)); | |
71 | ||
0531ce1d | 72 | if !tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.use_mir() { |
ff7c6d11 XL |
73 | return None; |
74 | } | |
75 | ||
76 | let opt_closure_req = tcx.infer_ctxt().enter(|infcx| { | |
77 | let input_mir: &Mir = &input_mir.borrow(); | |
78 | do_mir_borrowck(&infcx, input_mir, def_id) | |
79 | }); | |
80 | debug!("mir_borrowck done"); | |
81 | ||
82 | opt_closure_req | |
83 | } | |
84 | ||
85 | fn do_mir_borrowck<'a, 'gcx, 'tcx>( | |
86 | infcx: &InferCtxt<'a, 'gcx, 'tcx>, | |
87 | input_mir: &Mir<'gcx>, | |
88 | def_id: DefId, | |
89 | ) -> Option<ClosureRegionRequirements<'gcx>> { | |
90 | let tcx = infcx.tcx; | |
91 | let attributes = tcx.get_attrs(def_id); | |
92 | let param_env = tcx.param_env(def_id); | |
93 | let id = tcx.hir | |
94 | .as_local_node_id(def_id) | |
95 | .expect("do_mir_borrowck: non-local DefId"); | |
96 | ||
97 | // Make our own copy of the MIR. This copy will be modified (in place) to | |
98 | // contain non-lexical lifetimes. It will have a lifetime tied | |
99 | // to the inference context. | |
100 | let mut mir: Mir<'tcx> = input_mir.clone(); | |
0531ce1d | 101 | let free_regions = if !tcx.nll() { |
ff7c6d11 XL |
102 | None |
103 | } else { | |
104 | let mir = &mut mir; | |
105 | ||
106 | // Replace all regions with fresh inference variables. | |
107 | Some(nll::replace_regions_in_mir(infcx, def_id, param_env, mir)) | |
108 | }; | |
109 | let mir = &mir; | |
110 | ||
111 | let move_data: MoveData<'tcx> = match MoveData::gather_moves(mir, tcx) { | |
112 | Ok(move_data) => move_data, | |
113 | Err((move_data, move_errors)) => { | |
114 | for move_error in move_errors { | |
115 | let (span, kind): (Span, IllegalMoveOriginKind) = match move_error { | |
116 | MoveError::UnionMove { .. } => { | |
0531ce1d | 117 | unimplemented!("don't know how to report union move errors yet.") |
ff7c6d11 XL |
118 | } |
119 | MoveError::IllegalMove { | |
120 | cannot_move_out_of: o, | |
121 | } => (o.span, o.kind), | |
122 | }; | |
123 | let origin = Origin::Mir; | |
124 | let mut err = match kind { | |
125 | IllegalMoveOriginKind::Static => { | |
126 | tcx.cannot_move_out_of(span, "static item", origin) | |
127 | } | |
128 | IllegalMoveOriginKind::BorrowedContent => { | |
129 | tcx.cannot_move_out_of(span, "borrowed content", origin) | |
130 | } | |
131 | IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => { | |
132 | tcx.cannot_move_out_of_interior_of_drop(span, ty, origin) | |
133 | } | |
134 | IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => { | |
135 | tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin) | |
136 | } | |
137 | }; | |
138 | err.emit(); | |
139 | } | |
140 | move_data | |
141 | } | |
142 | }; | |
143 | ||
144 | let mdpe = MoveDataParamEnv { | |
145 | move_data: move_data, | |
146 | param_env: param_env, | |
147 | }; | |
148 | let body_id = match tcx.def_key(def_id).disambiguated_data.data { | |
149 | DefPathData::StructCtor | DefPathData::EnumVariant(_) => None, | |
150 | _ => Some(tcx.hir.body_owned_by(id)), | |
151 | }; | |
152 | ||
153 | let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); | |
154 | let mut flow_inits = FlowAtLocation::new(do_dataflow( | |
155 | tcx, | |
156 | mir, | |
157 | id, | |
158 | &attributes, | |
159 | &dead_unwinds, | |
2c00a5a8 | 160 | MaybeInitializedPlaces::new(tcx, mir, &mdpe), |
ff7c6d11 XL |
161 | |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]), |
162 | )); | |
163 | let flow_uninits = FlowAtLocation::new(do_dataflow( | |
164 | tcx, | |
165 | mir, | |
166 | id, | |
167 | &attributes, | |
168 | &dead_unwinds, | |
2c00a5a8 | 169 | MaybeUninitializedPlaces::new(tcx, mir, &mdpe), |
ff7c6d11 XL |
170 | |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]), |
171 | )); | |
172 | let flow_move_outs = FlowAtLocation::new(do_dataflow( | |
173 | tcx, | |
174 | mir, | |
175 | id, | |
176 | &attributes, | |
177 | &dead_unwinds, | |
178 | MovingOutStatements::new(tcx, mir, &mdpe), | |
179 | |bd, i| DebugFormatted::new(&bd.move_data().moves[i]), | |
180 | )); | |
181 | let flow_ever_inits = FlowAtLocation::new(do_dataflow( | |
182 | tcx, | |
183 | mir, | |
184 | id, | |
185 | &attributes, | |
186 | &dead_unwinds, | |
2c00a5a8 | 187 | EverInitializedPlaces::new(tcx, mir, &mdpe), |
ff7c6d11 XL |
188 | |bd, i| DebugFormatted::new(&bd.move_data().inits[i]), |
189 | )); | |
190 | ||
191 | // If we are in non-lexical mode, compute the non-lexical lifetimes. | |
192 | let (opt_regioncx, opt_closure_req) = if let Some(free_regions) = free_regions { | |
193 | let (regioncx, opt_closure_req) = nll::compute_regions( | |
194 | infcx, | |
195 | def_id, | |
196 | free_regions, | |
197 | mir, | |
198 | param_env, | |
199 | &mut flow_inits, | |
200 | &mdpe.move_data, | |
201 | ); | |
202 | (Some(Rc::new(regioncx)), opt_closure_req) | |
203 | } else { | |
0531ce1d | 204 | assert!(!tcx.nll()); |
ff7c6d11 XL |
205 | (None, None) |
206 | }; | |
207 | let flow_inits = flow_inits; // remove mut | |
208 | ||
0531ce1d XL |
209 | let flow_borrows = FlowAtLocation::new(do_dataflow( |
210 | tcx, | |
211 | mir, | |
212 | id, | |
213 | &attributes, | |
214 | &dead_unwinds, | |
215 | Borrows::new(tcx, mir, opt_regioncx.clone(), def_id, body_id), | |
216 | |rs, i| { | |
217 | DebugFormatted::new(&(i.kind(), rs.location(i.borrow_index()))) | |
218 | } | |
219 | )); | |
220 | ||
2c00a5a8 XL |
221 | let movable_generator = !match tcx.hir.get(id) { |
222 | hir::map::Node::NodeExpr(&hir::Expr { | |
223 | node: hir::ExprClosure(.., Some(hir::GeneratorMovability::Static)), | |
224 | .. | |
225 | }) => true, | |
226 | _ => false, | |
227 | }; | |
228 | ||
ff7c6d11 XL |
229 | let mut mbcx = MirBorrowckCtxt { |
230 | tcx: tcx, | |
231 | mir: mir, | |
232 | node_id: id, | |
233 | move_data: &mdpe.move_data, | |
234 | param_env: param_env, | |
2c00a5a8 | 235 | movable_generator, |
ff7c6d11 XL |
236 | locals_are_invalidated_at_exit: match tcx.hir.body_owner_kind(id) { |
237 | hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => false, | |
238 | hir::BodyOwnerKind::Fn => true, | |
239 | }, | |
2c00a5a8 | 240 | access_place_error_reported: FxHashSet(), |
ff7c6d11 | 241 | reservation_error_reported: FxHashSet(), |
0531ce1d XL |
242 | nonlexical_regioncx: opt_regioncx, |
243 | nonlexical_cause_info: None, | |
ff7c6d11 XL |
244 | }; |
245 | ||
246 | let mut state = Flows::new( | |
0531ce1d | 247 | flow_borrows, |
ff7c6d11 XL |
248 | flow_inits, |
249 | flow_uninits, | |
250 | flow_move_outs, | |
251 | flow_ever_inits, | |
252 | ); | |
253 | ||
254 | mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer | |
255 | ||
256 | opt_closure_req | |
257 | } | |
258 | ||
259 | #[allow(dead_code)] | |
260 | pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> { | |
261 | tcx: TyCtxt<'cx, 'gcx, 'tcx>, | |
262 | mir: &'cx Mir<'tcx>, | |
263 | node_id: ast::NodeId, | |
264 | move_data: &'cx MoveData<'tcx>, | |
265 | param_env: ParamEnv<'gcx>, | |
2c00a5a8 | 266 | movable_generator: bool, |
ff7c6d11 XL |
267 | /// This keeps track of whether local variables are free-ed when the function |
268 | /// exits even without a `StorageDead`, which appears to be the case for | |
269 | /// constants. | |
270 | /// | |
271 | /// I'm not sure this is the right approach - @eddyb could you try and | |
272 | /// figure this out? | |
273 | locals_are_invalidated_at_exit: bool, | |
2c00a5a8 XL |
274 | /// This field keeps track of when borrow errors are reported in the access_place function |
275 | /// so that there is no duplicate reporting. This field cannot also be used for the conflicting | |
276 | /// borrow errors that is handled by the `reservation_error_reported` field as the inclusion | |
277 | /// of the `Span` type (while required to mute some errors) stops the muting of the reservation | |
278 | /// errors. | |
279 | access_place_error_reported: FxHashSet<(Place<'tcx>, Span)>, | |
ff7c6d11 XL |
280 | /// This field keeps track of when borrow conflict errors are reported |
281 | /// for reservations, so that we don't report seemingly duplicate | |
282 | /// errors for corresponding activations | |
283 | /// | |
284 | /// FIXME: Ideally this would be a set of BorrowIndex, not Places, | |
285 | /// but it is currently inconvenient to track down the BorrowIndex | |
286 | /// at the time we detect and report a reservation error. | |
287 | reservation_error_reported: FxHashSet<Place<'tcx>>, | |
288 | /// Non-lexical region inference context, if NLL is enabled. This | |
289 | /// contains the results from region inference and lets us e.g. | |
290 | /// find out which CFG points are contained in each borrow region. | |
291 | nonlexical_regioncx: Option<Rc<RegionInferenceContext<'tcx>>>, | |
0531ce1d | 292 | nonlexical_cause_info: Option<RegionCausalInfo>, |
ff7c6d11 XL |
293 | } |
294 | ||
295 | // Check that: | |
296 | // 1. assignments are always made to mutable locations (FIXME: does that still really go here?) | |
297 | // 2. loans made in overlapping scopes do not conflict | |
298 | // 3. assignments do not affect things loaned out as immutable | |
299 | // 4. moves do not affect things loaned out in any way | |
300 | impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'gcx, 'tcx> { | |
301 | type FlowState = Flows<'cx, 'gcx, 'tcx>; | |
302 | ||
303 | fn mir(&self) -> &'cx Mir<'tcx> { | |
304 | self.mir | |
305 | } | |
306 | ||
307 | fn visit_block_entry(&mut self, bb: BasicBlock, flow_state: &Self::FlowState) { | |
308 | debug!("MirBorrowckCtxt::process_block({:?}): {}", bb, flow_state); | |
309 | } | |
310 | ||
311 | fn visit_statement_entry( | |
312 | &mut self, | |
313 | location: Location, | |
314 | stmt: &Statement<'tcx>, | |
315 | flow_state: &Self::FlowState, | |
316 | ) { | |
317 | debug!( | |
318 | "MirBorrowckCtxt::process_statement({:?}, {:?}): {}", | |
0531ce1d | 319 | location, stmt, flow_state |
ff7c6d11 XL |
320 | ); |
321 | let span = stmt.source_info.span; | |
322 | ||
323 | self.check_activations(location, span, flow_state); | |
324 | ||
325 | match stmt.kind { | |
326 | StatementKind::Assign(ref lhs, ref rhs) => { | |
2c00a5a8 XL |
327 | self.consume_rvalue( |
328 | ContextKind::AssignRhs.new(location), | |
329 | (rhs, span), | |
330 | location, | |
331 | flow_state, | |
332 | ); | |
333 | ||
ff7c6d11 XL |
334 | self.mutate_place( |
335 | ContextKind::AssignLhs.new(location), | |
336 | (lhs, span), | |
337 | Shallow(None), | |
338 | JustWrite, | |
339 | flow_state, | |
340 | ); | |
ff7c6d11 XL |
341 | } |
342 | StatementKind::SetDiscriminant { | |
343 | ref place, | |
344 | variant_index: _, | |
345 | } => { | |
346 | self.mutate_place( | |
347 | ContextKind::SetDiscrim.new(location), | |
348 | (place, span), | |
349 | Shallow(Some(ArtificialField::Discriminant)), | |
350 | JustWrite, | |
351 | flow_state, | |
352 | ); | |
353 | } | |
354 | StatementKind::InlineAsm { | |
355 | ref asm, | |
356 | ref outputs, | |
357 | ref inputs, | |
358 | } => { | |
359 | let context = ContextKind::InlineAsm.new(location); | |
360 | for (o, output) in asm.outputs.iter().zip(outputs) { | |
361 | if o.is_indirect { | |
362 | // FIXME(eddyb) indirect inline asm outputs should | |
363 | // be encoeded through MIR place derefs instead. | |
364 | self.access_place( | |
365 | context, | |
366 | (output, span), | |
367 | (Deep, Read(ReadKind::Copy)), | |
368 | LocalMutationIsAllowed::No, | |
369 | flow_state, | |
370 | ); | |
371 | self.check_if_path_is_moved( | |
372 | context, | |
373 | InitializationRequiringAction::Use, | |
374 | (output, span), | |
375 | flow_state, | |
376 | ); | |
377 | } else { | |
378 | self.mutate_place( | |
379 | context, | |
380 | (output, span), | |
381 | if o.is_rw { Deep } else { Shallow(None) }, | |
382 | if o.is_rw { WriteAndRead } else { JustWrite }, | |
383 | flow_state, | |
384 | ); | |
385 | } | |
386 | } | |
387 | for input in inputs { | |
388 | self.consume_operand(context, (input, span), flow_state); | |
389 | } | |
390 | } | |
391 | StatementKind::EndRegion(ref _rgn) => { | |
392 | // ignored when consuming results (update to | |
393 | // flow_state already handled). | |
394 | } | |
0531ce1d XL |
395 | StatementKind::Nop | |
396 | StatementKind::UserAssertTy(..) | | |
397 | StatementKind::Validate(..) | | |
398 | StatementKind::StorageLive(..) => { | |
399 | // `Nop`, `UserAssertTy`, `Validate`, and `StorageLive` are irrelevant | |
ff7c6d11 XL |
400 | // to borrow check. |
401 | } | |
ff7c6d11 XL |
402 | StatementKind::StorageDead(local) => { |
403 | self.access_place( | |
404 | ContextKind::StorageDead.new(location), | |
405 | (&Place::Local(local), span), | |
406 | (Shallow(None), Write(WriteKind::StorageDeadOrDrop)), | |
407 | LocalMutationIsAllowed::Yes, | |
408 | flow_state, | |
409 | ); | |
410 | } | |
411 | } | |
412 | } | |
413 | ||
414 | fn visit_terminator_entry( | |
415 | &mut self, | |
416 | location: Location, | |
417 | term: &Terminator<'tcx>, | |
418 | flow_state: &Self::FlowState, | |
419 | ) { | |
420 | let loc = location; | |
421 | debug!( | |
422 | "MirBorrowckCtxt::process_terminator({:?}, {:?}): {}", | |
0531ce1d | 423 | location, term, flow_state |
ff7c6d11 XL |
424 | ); |
425 | let span = term.source_info.span; | |
426 | ||
427 | self.check_activations(location, span, flow_state); | |
428 | ||
429 | match term.kind { | |
430 | TerminatorKind::SwitchInt { | |
431 | ref discr, | |
432 | switch_ty: _, | |
433 | values: _, | |
434 | targets: _, | |
435 | } => { | |
436 | self.consume_operand(ContextKind::SwitchInt.new(loc), (discr, span), flow_state); | |
437 | } | |
438 | TerminatorKind::Drop { | |
439 | location: ref drop_place, | |
440 | target: _, | |
441 | unwind: _, | |
442 | } => { | |
0531ce1d XL |
443 | let gcx = self.tcx.global_tcx(); |
444 | ||
445 | // Compute the type with accurate region information. | |
446 | let drop_place_ty = drop_place.ty(self.mir, self.tcx); | |
447 | ||
448 | // Erase the regions. | |
449 | let drop_place_ty = self.tcx.erase_regions(&drop_place_ty).to_ty(self.tcx); | |
450 | ||
451 | // "Lift" into the gcx -- once regions are erased, this type should be in the | |
452 | // global arenas; this "lift" operation basically just asserts that is true, but | |
453 | // that is useful later. | |
454 | let drop_place_ty = gcx.lift(&drop_place_ty).unwrap(); | |
455 | ||
456 | self.visit_terminator_drop(loc, term, flow_state, drop_place, drop_place_ty, span); | |
ff7c6d11 XL |
457 | } |
458 | TerminatorKind::DropAndReplace { | |
459 | location: ref drop_place, | |
460 | value: ref new_value, | |
461 | target: _, | |
462 | unwind: _, | |
463 | } => { | |
464 | self.mutate_place( | |
465 | ContextKind::DropAndReplace.new(loc), | |
466 | (drop_place, span), | |
467 | Deep, | |
468 | JustWrite, | |
469 | flow_state, | |
470 | ); | |
471 | self.consume_operand( | |
472 | ContextKind::DropAndReplace.new(loc), | |
473 | (new_value, span), | |
474 | flow_state, | |
475 | ); | |
476 | } | |
477 | TerminatorKind::Call { | |
478 | ref func, | |
479 | ref args, | |
480 | ref destination, | |
481 | cleanup: _, | |
482 | } => { | |
483 | self.consume_operand(ContextKind::CallOperator.new(loc), (func, span), flow_state); | |
484 | for arg in args { | |
485 | self.consume_operand( | |
486 | ContextKind::CallOperand.new(loc), | |
487 | (arg, span), | |
488 | flow_state, | |
489 | ); | |
490 | } | |
491 | if let Some((ref dest, _ /*bb*/)) = *destination { | |
492 | self.mutate_place( | |
493 | ContextKind::CallDest.new(loc), | |
494 | (dest, span), | |
495 | Deep, | |
496 | JustWrite, | |
497 | flow_state, | |
498 | ); | |
499 | } | |
500 | } | |
501 | TerminatorKind::Assert { | |
502 | ref cond, | |
503 | expected: _, | |
504 | ref msg, | |
505 | target: _, | |
506 | cleanup: _, | |
507 | } => { | |
508 | self.consume_operand(ContextKind::Assert.new(loc), (cond, span), flow_state); | |
509 | match *msg { | |
510 | AssertMessage::BoundsCheck { ref len, ref index } => { | |
511 | self.consume_operand(ContextKind::Assert.new(loc), (len, span), flow_state); | |
512 | self.consume_operand( | |
513 | ContextKind::Assert.new(loc), | |
514 | (index, span), | |
515 | flow_state, | |
516 | ); | |
517 | } | |
518 | AssertMessage::Math(_ /*const_math_err*/) => {} | |
519 | AssertMessage::GeneratorResumedAfterReturn => {} | |
520 | AssertMessage::GeneratorResumedAfterPanic => {} | |
521 | } | |
522 | } | |
523 | ||
524 | TerminatorKind::Yield { | |
525 | ref value, | |
526 | resume: _, | |
527 | drop: _, | |
528 | } => { | |
529 | self.consume_operand(ContextKind::Yield.new(loc), (value, span), flow_state); | |
2c00a5a8 XL |
530 | |
531 | if self.movable_generator { | |
532 | // Look for any active borrows to locals | |
533 | let domain = flow_state.borrows.operator(); | |
534 | let data = domain.borrows(); | |
0531ce1d | 535 | flow_state.borrows.with_iter_outgoing(|borrows| { |
2c00a5a8 XL |
536 | for i in borrows { |
537 | let borrow = &data[i.borrow_index()]; | |
538 | self.check_for_local_borrow(borrow, span); | |
539 | } | |
540 | }); | |
541 | } | |
ff7c6d11 XL |
542 | } |
543 | ||
544 | TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => { | |
545 | // Returning from the function implicitly kills storage for all locals and statics. | |
546 | // Often, the storage will already have been killed by an explicit | |
547 | // StorageDead, but we don't always emit those (notably on unwind paths), | |
548 | // so this "extra check" serves as a kind of backup. | |
549 | let domain = flow_state.borrows.operator(); | |
550 | let data = domain.borrows(); | |
0531ce1d | 551 | flow_state.borrows.with_iter_outgoing(|borrows| { |
ff7c6d11 XL |
552 | for i in borrows { |
553 | let borrow = &data[i.borrow_index()]; | |
554 | let context = ContextKind::StorageDead.new(loc); | |
555 | self.check_for_invalidation_at_exit(context, borrow, span, flow_state); | |
556 | } | |
557 | }); | |
558 | } | |
559 | TerminatorKind::Goto { target: _ } | |
560 | | TerminatorKind::Abort | |
561 | | TerminatorKind::Unreachable | |
0531ce1d XL |
562 | | TerminatorKind::FalseEdges { |
563 | real_target: _, | |
564 | imaginary_targets: _, | |
565 | } | |
566 | | TerminatorKind::FalseUnwind { | |
567 | real_target: _, | |
568 | unwind: _, | |
569 | } => { | |
ff7c6d11 XL |
570 | // no data used, thus irrelevant to borrowck |
571 | } | |
572 | } | |
573 | } | |
574 | } | |
575 | ||
576 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
577 | enum MutateMode { | |
578 | JustWrite, | |
579 | WriteAndRead, | |
580 | } | |
581 | ||
582 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
583 | enum Control { | |
584 | Continue, | |
585 | Break, | |
586 | } | |
587 | ||
588 | use self::ShallowOrDeep::{Deep, Shallow}; | |
589 | use self::ReadOrWrite::{Activation, Read, Reservation, Write}; | |
590 | ||
591 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
592 | enum ArtificialField { | |
593 | Discriminant, | |
594 | ArrayLength, | |
595 | } | |
596 | ||
597 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
598 | enum ShallowOrDeep { | |
599 | /// From the RFC: "A *shallow* access means that the immediate | |
2c00a5a8 | 600 | /// fields reached at P are accessed, but references or pointers |
ff7c6d11 XL |
601 | /// found within are not dereferenced. Right now, the only access |
602 | /// that is shallow is an assignment like `x = ...;`, which would | |
603 | /// be a *shallow write* of `x`." | |
604 | Shallow(Option<ArtificialField>), | |
605 | ||
606 | /// From the RFC: "A *deep* access means that all data reachable | |
607 | /// through the given place may be invalidated or accesses by | |
608 | /// this action." | |
609 | Deep, | |
610 | } | |
611 | ||
612 | /// Kind of access to a value: read or write | |
613 | /// (For informational purposes only) | |
614 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
615 | enum ReadOrWrite { | |
616 | /// From the RFC: "A *read* means that the existing data may be | |
617 | /// read, but will not be changed." | |
618 | Read(ReadKind), | |
619 | ||
620 | /// From the RFC: "A *write* means that the data may be mutated to | |
621 | /// new values or otherwise invalidated (for example, it could be | |
622 | /// de-initialized, as in a move operation). | |
623 | Write(WriteKind), | |
624 | ||
625 | /// For two-phase borrows, we distinguish a reservation (which is treated | |
626 | /// like a Read) from an activation (which is treated like a write), and | |
627 | /// each of those is furthermore distinguished from Reads/Writes above. | |
628 | Reservation(WriteKind), | |
629 | Activation(WriteKind, BorrowIndex), | |
630 | } | |
631 | ||
632 | /// Kind of read access to a value | |
633 | /// (For informational purposes only) | |
634 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
635 | enum ReadKind { | |
636 | Borrow(BorrowKind), | |
637 | Copy, | |
638 | } | |
639 | ||
640 | /// Kind of write access to a value | |
641 | /// (For informational purposes only) | |
642 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
643 | enum WriteKind { | |
644 | StorageDeadOrDrop, | |
645 | MutableBorrow(BorrowKind), | |
646 | Mutate, | |
647 | Move, | |
648 | } | |
649 | ||
650 | /// When checking permissions for a place access, this flag is used to indicate that an immutable | |
651 | /// local place can be mutated. | |
652 | /// | |
653 | /// FIXME: @nikomatsakis suggested that this flag could be removed with the following modifications: | |
654 | /// - Merge `check_access_permissions()` and `check_if_reassignment_to_immutable_state()` | |
655 | /// - Split `is_mutable()` into `is_assignable()` (can be directly assigned) and | |
656 | /// `is_declared_mutable()` | |
657 | /// - Take flow state into consideration in `is_assignable()` for local variables | |
658 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
659 | enum LocalMutationIsAllowed { | |
660 | Yes, | |
661 | /// We want use of immutable upvars to cause a "write to immutable upvar" | |
662 | /// error, not an "reassignment" error. | |
663 | ExceptUpvars, | |
664 | No, | |
665 | } | |
666 | ||
667 | struct AccessErrorsReported { | |
668 | mutability_error: bool, | |
0531ce1d XL |
669 | #[allow(dead_code)] |
670 | conflict_error: bool, | |
ff7c6d11 XL |
671 | } |
672 | ||
673 | #[derive(Copy, Clone)] | |
674 | enum InitializationRequiringAction { | |
675 | Update, | |
676 | Borrow, | |
677 | Use, | |
678 | Assignment, | |
679 | } | |
680 | ||
681 | impl InitializationRequiringAction { | |
682 | fn as_noun(self) -> &'static str { | |
683 | match self { | |
684 | InitializationRequiringAction::Update => "update", | |
685 | InitializationRequiringAction::Borrow => "borrow", | |
686 | InitializationRequiringAction::Use => "use", | |
687 | InitializationRequiringAction::Assignment => "assign", | |
688 | } | |
689 | } | |
690 | ||
691 | fn as_verb_in_past_tense(self) -> &'static str { | |
692 | match self { | |
693 | InitializationRequiringAction::Update => "updated", | |
694 | InitializationRequiringAction::Borrow => "borrowed", | |
695 | InitializationRequiringAction::Use => "used", | |
696 | InitializationRequiringAction::Assignment => "assigned", | |
697 | } | |
698 | } | |
699 | } | |
700 | ||
701 | impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { | |
2c00a5a8 XL |
702 | /// Returns true if the borrow represented by `kind` is |
703 | /// allowed to be split into separate Reservation and | |
704 | /// Activation phases. | |
705 | fn allow_two_phase_borrow(&self, kind: BorrowKind) -> bool { | |
0531ce1d XL |
706 | self.tcx.two_phase_borrows() |
707 | && (kind.allows_two_phase_borrow() | |
708 | || self.tcx.sess.opts.debugging_opts.two_phase_beyond_autoref) | |
709 | } | |
710 | ||
711 | /// Invokes `access_place` as appropriate for dropping the value | |
712 | /// at `drop_place`. Note that the *actual* `Drop` in the MIR is | |
713 | /// always for a variable (e.g., `Drop(x)`) -- but we recursively | |
714 | /// break this variable down into subpaths (e.g., `Drop(x.foo)`) | |
715 | /// to indicate more precisely which fields might actually be | |
716 | /// accessed by a destructor. | |
717 | fn visit_terminator_drop( | |
718 | &mut self, | |
719 | loc: Location, | |
720 | term: &Terminator<'tcx>, | |
721 | flow_state: &Flows<'cx, 'gcx, 'tcx>, | |
722 | drop_place: &Place<'tcx>, | |
723 | erased_drop_place_ty: ty::Ty<'gcx>, | |
724 | span: Span, | |
725 | ) { | |
726 | match erased_drop_place_ty.sty { | |
727 | // When a struct is being dropped, we need to check | |
728 | // whether it has a destructor, if it does, then we can | |
729 | // call it, if it does not then we need to check the | |
730 | // individual fields instead. This way if `foo` has a | |
731 | // destructor but `bar` does not, we will only check for | |
732 | // borrows of `x.foo` and not `x.bar`. See #47703. | |
733 | ty::TyAdt(def, substs) if def.is_struct() && !def.has_dtor(self.tcx) => { | |
734 | for (index, field) in def.all_fields().enumerate() { | |
735 | let gcx = self.tcx.global_tcx(); | |
736 | let field_ty = field.ty(gcx, substs); | |
737 | let field_ty = gcx.normalize_erasing_regions(self.param_env, field_ty); | |
738 | let place = drop_place.clone().field(Field::new(index), field_ty); | |
739 | ||
740 | self.visit_terminator_drop(loc, term, flow_state, &place, field_ty, span); | |
741 | } | |
742 | } | |
743 | _ => { | |
744 | // We have now refined the type of the value being | |
745 | // dropped (potentially) to just the type of a | |
746 | // subfield; so check whether that field's type still | |
747 | // "needs drop". If so, we assume that the destructor | |
748 | // may access any data it likes (i.e., a Deep Write). | |
749 | let gcx = self.tcx.global_tcx(); | |
750 | if erased_drop_place_ty.needs_drop(gcx, self.param_env) { | |
751 | self.access_place( | |
752 | ContextKind::Drop.new(loc), | |
753 | (drop_place, span), | |
754 | (Deep, Write(WriteKind::StorageDeadOrDrop)), | |
755 | LocalMutationIsAllowed::Yes, | |
756 | flow_state, | |
757 | ); | |
758 | } | |
759 | } | |
760 | } | |
2c00a5a8 XL |
761 | } |
762 | ||
ff7c6d11 XL |
763 | /// Checks an access to the given place to see if it is allowed. Examines the set of borrows |
764 | /// that are in scope, as well as which paths have been initialized, to ensure that (a) the | |
765 | /// place is initialized and (b) it is not borrowed in some way that would prevent this | |
766 | /// access. | |
767 | /// | |
768 | /// Returns true if an error is reported, false otherwise. | |
769 | fn access_place( | |
770 | &mut self, | |
771 | context: Context, | |
772 | place_span: (&Place<'tcx>, Span), | |
773 | kind: (ShallowOrDeep, ReadOrWrite), | |
774 | is_local_mutation_allowed: LocalMutationIsAllowed, | |
775 | flow_state: &Flows<'cx, 'gcx, 'tcx>, | |
776 | ) -> AccessErrorsReported { | |
777 | let (sd, rw) = kind; | |
778 | ||
779 | if let Activation(_, borrow_index) = rw { | |
780 | if self.reservation_error_reported.contains(&place_span.0) { | |
0531ce1d XL |
781 | debug!( |
782 | "skipping access_place for activation of invalid reservation \ | |
783 | place: {:?} borrow_index: {:?}", | |
784 | place_span.0, borrow_index | |
785 | ); | |
ff7c6d11 XL |
786 | return AccessErrorsReported { |
787 | mutability_error: false, | |
788 | conflict_error: true, | |
789 | }; | |
790 | } | |
791 | } | |
792 | ||
0531ce1d XL |
793 | if self.access_place_error_reported |
794 | .contains(&(place_span.0.clone(), place_span.1)) | |
795 | { | |
796 | debug!( | |
797 | "access_place: suppressing error place_span=`{:?}` kind=`{:?}`", | |
798 | place_span, kind | |
799 | ); | |
2c00a5a8 XL |
800 | return AccessErrorsReported { |
801 | mutability_error: false, | |
802 | conflict_error: true, | |
803 | }; | |
804 | } | |
805 | ||
ff7c6d11 XL |
806 | let mutability_error = |
807 | self.check_access_permissions(place_span, rw, is_local_mutation_allowed); | |
808 | let conflict_error = | |
809 | self.check_access_for_conflict(context, place_span, sd, rw, flow_state); | |
810 | ||
2c00a5a8 | 811 | if conflict_error || mutability_error { |
0531ce1d XL |
812 | debug!( |
813 | "access_place: logging error place_span=`{:?}` kind=`{:?}`", | |
814 | place_span, kind | |
815 | ); | |
816 | self.access_place_error_reported | |
817 | .insert((place_span.0.clone(), place_span.1)); | |
2c00a5a8 XL |
818 | } |
819 | ||
ff7c6d11 XL |
820 | AccessErrorsReported { |
821 | mutability_error, | |
822 | conflict_error, | |
823 | } | |
824 | } | |
825 | ||
826 | fn check_access_for_conflict( | |
827 | &mut self, | |
828 | context: Context, | |
829 | place_span: (&Place<'tcx>, Span), | |
830 | sd: ShallowOrDeep, | |
831 | rw: ReadOrWrite, | |
832 | flow_state: &Flows<'cx, 'gcx, 'tcx>, | |
833 | ) -> bool { | |
834 | let mut error_reported = false; | |
835 | self.each_borrow_involving_path( | |
836 | context, | |
837 | (sd, place_span.0), | |
838 | flow_state, | |
839 | |this, index, borrow| match (rw, borrow.kind) { | |
840 | // Obviously an activation is compatible with its own | |
841 | // reservation (or even prior activating uses of same | |
842 | // borrow); so don't check if they interfere. | |
843 | // | |
844 | // NOTE: *reservations* do conflict with themselves; | |
845 | // thus aren't injecting unsoundenss w/ this check.) | |
846 | (Activation(_, activating), _) if activating == index.borrow_index() => { | |
847 | debug!( | |
848 | "check_access_for_conflict place_span: {:?} sd: {:?} rw: {:?} \ | |
849 | skipping {:?} b/c activation of same borrow_index: {:?}", | |
850 | place_span, | |
851 | sd, | |
852 | rw, | |
853 | (index, borrow), | |
854 | index.borrow_index() | |
855 | ); | |
856 | Control::Continue | |
857 | } | |
858 | ||
859 | (Read(_), BorrowKind::Shared) | (Reservation(..), BorrowKind::Shared) => { | |
860 | Control::Continue | |
861 | } | |
862 | ||
2c00a5a8 | 863 | (Read(kind), BorrowKind::Unique) | (Read(kind), BorrowKind::Mut { .. }) => { |
ff7c6d11 | 864 | // Reading from mere reservations of mutable-borrows is OK. |
0531ce1d | 865 | if this.allow_two_phase_borrow(borrow.kind) && index.is_reservation() { |
ff7c6d11 XL |
866 | return Control::Continue; |
867 | } | |
868 | ||
869 | match kind { | |
870 | ReadKind::Copy => { | |
871 | error_reported = true; | |
872 | this.report_use_while_mutably_borrowed(context, place_span, borrow) | |
873 | } | |
874 | ReadKind::Borrow(bk) => { | |
875 | let end_issued_loan_span = flow_state | |
876 | .borrows | |
877 | .operator() | |
878 | .opt_region_end_span(&borrow.region); | |
879 | error_reported = true; | |
880 | this.report_conflicting_borrow( | |
881 | context, | |
882 | place_span, | |
883 | bk, | |
884 | &borrow, | |
885 | end_issued_loan_span, | |
886 | ) | |
887 | } | |
888 | } | |
889 | Control::Break | |
890 | } | |
891 | ||
892 | (Reservation(kind), BorrowKind::Unique) | |
2c00a5a8 | 893 | | (Reservation(kind), BorrowKind::Mut { .. }) |
ff7c6d11 XL |
894 | | (Activation(kind, _), _) |
895 | | (Write(kind), _) => { | |
896 | match rw { | |
897 | Reservation(_) => { | |
898 | debug!( | |
899 | "recording invalid reservation of \ | |
900 | place: {:?}", | |
901 | place_span.0 | |
902 | ); | |
903 | this.reservation_error_reported.insert(place_span.0.clone()); | |
0531ce1d | 904 | } |
ff7c6d11 XL |
905 | Activation(_, activating) => { |
906 | debug!( | |
907 | "observing check_place for activation of \ | |
908 | borrow_index: {:?}", | |
909 | activating | |
910 | ); | |
0531ce1d XL |
911 | } |
912 | Read(..) | Write(..) => {} | |
ff7c6d11 XL |
913 | } |
914 | ||
915 | match kind { | |
916 | WriteKind::MutableBorrow(bk) => { | |
917 | let end_issued_loan_span = flow_state | |
918 | .borrows | |
919 | .operator() | |
920 | .opt_region_end_span(&borrow.region); | |
921 | ||
922 | error_reported = true; | |
923 | this.report_conflicting_borrow( | |
924 | context, | |
925 | place_span, | |
926 | bk, | |
927 | &borrow, | |
928 | end_issued_loan_span, | |
929 | ) | |
930 | } | |
931 | WriteKind::StorageDeadOrDrop => { | |
932 | error_reported = true; | |
933 | this.report_borrowed_value_does_not_live_long_enough( | |
934 | context, | |
935 | borrow, | |
936 | place_span.1, | |
937 | flow_state.borrows.operator(), | |
938 | ); | |
939 | } | |
940 | WriteKind::Mutate => { | |
941 | error_reported = true; | |
942 | this.report_illegal_mutation_of_borrowed(context, place_span, borrow) | |
943 | } | |
944 | WriteKind::Move => { | |
945 | error_reported = true; | |
946 | this.report_move_out_while_borrowed(context, place_span, &borrow) | |
947 | } | |
948 | } | |
949 | Control::Break | |
950 | } | |
951 | }, | |
952 | ); | |
953 | ||
954 | error_reported | |
955 | } | |
956 | ||
957 | fn mutate_place( | |
958 | &mut self, | |
959 | context: Context, | |
960 | place_span: (&Place<'tcx>, Span), | |
961 | kind: ShallowOrDeep, | |
962 | mode: MutateMode, | |
963 | flow_state: &Flows<'cx, 'gcx, 'tcx>, | |
964 | ) { | |
965 | // Write of P[i] or *P, or WriteAndRead of any P, requires P init'd. | |
966 | match mode { | |
967 | MutateMode::WriteAndRead => { | |
968 | self.check_if_path_is_moved( | |
969 | context, | |
970 | InitializationRequiringAction::Update, | |
971 | place_span, | |
972 | flow_state, | |
973 | ); | |
974 | } | |
975 | MutateMode::JustWrite => { | |
976 | self.check_if_assigned_path_is_moved(context, place_span, flow_state); | |
977 | } | |
978 | } | |
979 | ||
980 | let errors_reported = self.access_place( | |
981 | context, | |
982 | place_span, | |
983 | (kind, Write(WriteKind::Mutate)), | |
984 | // We want immutable upvars to cause an "assignment to immutable var" | |
985 | // error, not an "reassignment of immutable var" error, because the | |
986 | // latter can't find a good previous assignment span. | |
987 | // | |
988 | // There's probably a better way to do this. | |
989 | LocalMutationIsAllowed::ExceptUpvars, | |
990 | flow_state, | |
991 | ); | |
992 | ||
993 | if !errors_reported.mutability_error { | |
994 | // check for reassignments to immutable local variables | |
995 | self.check_if_reassignment_to_immutable_state(context, place_span, flow_state); | |
996 | } | |
997 | } | |
998 | ||
999 | fn consume_rvalue( | |
1000 | &mut self, | |
1001 | context: Context, | |
1002 | (rvalue, span): (&Rvalue<'tcx>, Span), | |
1003 | _location: Location, | |
1004 | flow_state: &Flows<'cx, 'gcx, 'tcx>, | |
1005 | ) { | |
1006 | match *rvalue { | |
1007 | Rvalue::Ref(_ /*rgn*/, bk, ref place) => { | |
1008 | let access_kind = match bk { | |
1009 | BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))), | |
2c00a5a8 | 1010 | BorrowKind::Unique | BorrowKind::Mut { .. } => { |
ff7c6d11 | 1011 | let wk = WriteKind::MutableBorrow(bk); |
2c00a5a8 | 1012 | if self.allow_two_phase_borrow(bk) { |
ff7c6d11 XL |
1013 | (Deep, Reservation(wk)) |
1014 | } else { | |
1015 | (Deep, Write(wk)) | |
1016 | } | |
1017 | } | |
1018 | }; | |
1019 | ||
1020 | self.access_place( | |
1021 | context, | |
1022 | (place, span), | |
1023 | access_kind, | |
1024 | LocalMutationIsAllowed::No, | |
1025 | flow_state, | |
1026 | ); | |
1027 | ||
1028 | self.check_if_path_is_moved( | |
1029 | context, | |
1030 | InitializationRequiringAction::Borrow, | |
1031 | (place, span), | |
1032 | flow_state, | |
1033 | ); | |
1034 | } | |
1035 | ||
1036 | Rvalue::Use(ref operand) | |
1037 | | Rvalue::Repeat(ref operand, _) | |
1038 | | Rvalue::UnaryOp(_ /*un_op*/, ref operand) | |
1039 | | Rvalue::Cast(_ /*cast_kind*/, ref operand, _ /*ty*/) => { | |
1040 | self.consume_operand(context, (operand, span), flow_state) | |
1041 | } | |
1042 | ||
1043 | Rvalue::Len(ref place) | Rvalue::Discriminant(ref place) => { | |
1044 | let af = match *rvalue { | |
1045 | Rvalue::Len(..) => ArtificialField::ArrayLength, | |
1046 | Rvalue::Discriminant(..) => ArtificialField::Discriminant, | |
1047 | _ => unreachable!(), | |
1048 | }; | |
1049 | self.access_place( | |
1050 | context, | |
1051 | (place, span), | |
1052 | (Shallow(Some(af)), Read(ReadKind::Copy)), | |
1053 | LocalMutationIsAllowed::No, | |
1054 | flow_state, | |
1055 | ); | |
1056 | self.check_if_path_is_moved( | |
1057 | context, | |
1058 | InitializationRequiringAction::Use, | |
1059 | (place, span), | |
1060 | flow_state, | |
1061 | ); | |
1062 | } | |
1063 | ||
1064 | Rvalue::BinaryOp(_bin_op, ref operand1, ref operand2) | |
1065 | | Rvalue::CheckedBinaryOp(_bin_op, ref operand1, ref operand2) => { | |
1066 | self.consume_operand(context, (operand1, span), flow_state); | |
1067 | self.consume_operand(context, (operand2, span), flow_state); | |
1068 | } | |
1069 | ||
1070 | Rvalue::NullaryOp(_op, _ty) => { | |
1071 | // nullary ops take no dynamic input; no borrowck effect. | |
1072 | // | |
1073 | // FIXME: is above actually true? Do we want to track | |
1074 | // the fact that uninitialized data can be created via | |
1075 | // `NullOp::Box`? | |
1076 | } | |
1077 | ||
1078 | Rvalue::Aggregate(ref _aggregate_kind, ref operands) => for operand in operands { | |
1079 | self.consume_operand(context, (operand, span), flow_state); | |
1080 | }, | |
1081 | } | |
1082 | } | |
1083 | ||
1084 | fn consume_operand( | |
1085 | &mut self, | |
1086 | context: Context, | |
1087 | (operand, span): (&Operand<'tcx>, Span), | |
1088 | flow_state: &Flows<'cx, 'gcx, 'tcx>, | |
1089 | ) { | |
1090 | match *operand { | |
1091 | Operand::Copy(ref place) => { | |
1092 | // copy of place: check if this is "copy of frozen path" | |
1093 | // (FIXME: see check_loans.rs) | |
1094 | self.access_place( | |
1095 | context, | |
1096 | (place, span), | |
1097 | (Deep, Read(ReadKind::Copy)), | |
1098 | LocalMutationIsAllowed::No, | |
1099 | flow_state, | |
1100 | ); | |
1101 | ||
1102 | // Finally, check if path was already moved. | |
1103 | self.check_if_path_is_moved( | |
1104 | context, | |
1105 | InitializationRequiringAction::Use, | |
1106 | (place, span), | |
1107 | flow_state, | |
1108 | ); | |
1109 | } | |
1110 | Operand::Move(ref place) => { | |
1111 | // move of place: check if this is move of already borrowed path | |
1112 | self.access_place( | |
1113 | context, | |
1114 | (place, span), | |
1115 | (Deep, Write(WriteKind::Move)), | |
1116 | LocalMutationIsAllowed::Yes, | |
1117 | flow_state, | |
1118 | ); | |
1119 | ||
1120 | // Finally, check if path was already moved. | |
1121 | self.check_if_path_is_moved( | |
1122 | context, | |
1123 | InitializationRequiringAction::Use, | |
1124 | (place, span), | |
1125 | flow_state, | |
1126 | ); | |
1127 | } | |
1128 | Operand::Constant(_) => {} | |
1129 | } | |
1130 | } | |
1131 | ||
1132 | /// Returns whether a borrow of this place is invalidated when the function | |
1133 | /// exits | |
1134 | fn check_for_invalidation_at_exit( | |
1135 | &mut self, | |
1136 | context: Context, | |
1137 | borrow: &BorrowData<'tcx>, | |
1138 | span: Span, | |
1139 | flow_state: &Flows<'cx, 'gcx, 'tcx>, | |
1140 | ) { | |
1141 | debug!("check_for_invalidation_at_exit({:?})", borrow); | |
1142 | let place = &borrow.borrowed_place; | |
1143 | let root_place = self.prefixes(place, PrefixSet::All).last().unwrap(); | |
1144 | ||
1145 | // FIXME(nll-rfc#40): do more precise destructor tracking here. For now | |
1146 | // we just know that all locals are dropped at function exit (otherwise | |
1147 | // we'll have a memory leak) and assume that all statics have a destructor. | |
1148 | // | |
1149 | // FIXME: allow thread-locals to borrow other thread locals? | |
1150 | let (might_be_alive, will_be_dropped) = match root_place { | |
1151 | Place::Static(statik) => { | |
1152 | // Thread-locals might be dropped after the function exits, but | |
1153 | // "true" statics will never be. | |
1154 | let is_thread_local = self.tcx | |
1155 | .get_attrs(statik.def_id) | |
1156 | .iter() | |
1157 | .any(|attr| attr.check_name("thread_local")); | |
1158 | ||
1159 | (true, is_thread_local) | |
1160 | } | |
1161 | Place::Local(_) => { | |
1162 | // Locals are always dropped at function exit, and if they | |
1163 | // have a destructor it would've been called already. | |
1164 | (false, self.locals_are_invalidated_at_exit) | |
1165 | } | |
1166 | Place::Projection(..) => { | |
1167 | bug!("root of {:?} is a projection ({:?})?", place, root_place) | |
1168 | } | |
1169 | }; | |
1170 | ||
1171 | if !will_be_dropped { | |
1172 | debug!( | |
1173 | "place_is_invalidated_at_exit({:?}) - won't be dropped", | |
1174 | place | |
1175 | ); | |
1176 | return; | |
1177 | } | |
1178 | ||
1179 | // FIXME: replace this with a proper borrow_conflicts_with_place when | |
1180 | // that is merged. | |
1181 | let sd = if might_be_alive { Deep } else { Shallow(None) }; | |
1182 | ||
1183 | if self.places_conflict(place, root_place, sd) { | |
1184 | debug!("check_for_invalidation_at_exit({:?}): INVALID", place); | |
1185 | // FIXME: should be talking about the region lifetime instead | |
1186 | // of just a span here. | |
2c00a5a8 | 1187 | let span = self.tcx.sess.codemap().end_point(span); |
ff7c6d11 XL |
1188 | self.report_borrowed_value_does_not_live_long_enough( |
1189 | context, | |
1190 | borrow, | |
2c00a5a8 | 1191 | span, |
ff7c6d11 XL |
1192 | flow_state.borrows.operator(), |
1193 | ) | |
1194 | } | |
1195 | } | |
1196 | ||
2c00a5a8 XL |
1197 | /// Reports an error if this is a borrow of local data. |
1198 | /// This is called for all Yield statements on movable generators | |
0531ce1d | 1199 | fn check_for_local_borrow(&mut self, borrow: &BorrowData<'tcx>, yield_span: Span) { |
2c00a5a8 XL |
1200 | fn borrow_of_local_data<'tcx>(place: &Place<'tcx>) -> bool { |
1201 | match place { | |
1202 | Place::Static(..) => false, | |
1203 | Place::Local(..) => true, | |
1204 | Place::Projection(box proj) => { | |
1205 | match proj.elem { | |
1206 | // Reborrow of already borrowed data is ignored | |
1207 | // Any errors will be caught on the initial borrow | |
1208 | ProjectionElem::Deref => false, | |
1209 | ||
1210 | // For interior references and downcasts, find out if the base is local | |
0531ce1d XL |
1211 | ProjectionElem::Field(..) |
1212 | | ProjectionElem::Index(..) | |
1213 | | ProjectionElem::ConstantIndex { .. } | |
1214 | | ProjectionElem::Subslice { .. } | |
1215 | | ProjectionElem::Downcast(..) => borrow_of_local_data(&proj.base), | |
2c00a5a8 XL |
1216 | } |
1217 | } | |
1218 | } | |
1219 | } | |
1220 | ||
1221 | debug!("check_for_local_borrow({:?})", borrow); | |
1222 | ||
1223 | if borrow_of_local_data(&borrow.borrowed_place) { | |
0531ce1d XL |
1224 | self.tcx |
1225 | .cannot_borrow_across_generator_yield( | |
1226 | self.retrieve_borrow_span(borrow), | |
1227 | yield_span, | |
1228 | Origin::Mir, | |
1229 | ) | |
1230 | .emit(); | |
2c00a5a8 XL |
1231 | } |
1232 | } | |
1233 | ||
ff7c6d11 XL |
1234 | fn check_activations( |
1235 | &mut self, | |
1236 | location: Location, | |
1237 | span: Span, | |
1238 | flow_state: &Flows<'cx, 'gcx, 'tcx>, | |
1239 | ) { | |
0531ce1d | 1240 | if !self.tcx.two_phase_borrows() { |
ff7c6d11 XL |
1241 | return; |
1242 | } | |
1243 | ||
1244 | // Two-phase borrow support: For each activation that is newly | |
1245 | // generated at this statement, check if it interferes with | |
1246 | // another borrow. | |
1247 | let domain = flow_state.borrows.operator(); | |
1248 | let data = domain.borrows(); | |
1249 | flow_state.borrows.each_gen_bit(|gen| { | |
1250 | if gen.is_activation() { | |
1251 | let borrow_index = gen.borrow_index(); | |
1252 | let borrow = &data[borrow_index]; | |
1253 | // currently the flow analysis registers | |
1254 | // activations for both mutable and immutable | |
1255 | // borrows. So make sure we are talking about a | |
1256 | // mutable borrow before we check it. | |
1257 | match borrow.kind { | |
1258 | BorrowKind::Shared => return, | |
2c00a5a8 | 1259 | BorrowKind::Unique | BorrowKind::Mut { .. } => {} |
ff7c6d11 XL |
1260 | } |
1261 | ||
1262 | self.access_place( | |
1263 | ContextKind::Activation.new(location), | |
1264 | (&borrow.borrowed_place, span), | |
1265 | ( | |
1266 | Deep, | |
1267 | Activation(WriteKind::MutableBorrow(borrow.kind), borrow_index), | |
1268 | ), | |
1269 | LocalMutationIsAllowed::No, | |
1270 | flow_state, | |
1271 | ); | |
1272 | // We do not need to call `check_if_path_is_moved` | |
1273 | // again, as we already called it when we made the | |
1274 | // initial reservation. | |
1275 | } | |
1276 | }); | |
1277 | } | |
1278 | } | |
1279 | ||
1280 | impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { | |
1281 | fn check_if_reassignment_to_immutable_state( | |
1282 | &mut self, | |
1283 | context: Context, | |
1284 | (place, span): (&Place<'tcx>, Span), | |
1285 | flow_state: &Flows<'cx, 'gcx, 'tcx>, | |
1286 | ) { | |
1287 | debug!("check_if_reassignment_to_immutable_state({:?})", place); | |
1288 | // determine if this path has a non-mut owner (and thus needs checking). | |
1289 | if let Ok(()) = self.is_mutable(place, LocalMutationIsAllowed::No) { | |
1290 | return; | |
1291 | } | |
1292 | debug!( | |
1293 | "check_if_reassignment_to_immutable_state({:?}) - is an imm local", | |
1294 | place | |
1295 | ); | |
1296 | ||
0531ce1d | 1297 | for i in flow_state.ever_inits.iter_incoming() { |
ff7c6d11 XL |
1298 | let init = self.move_data.inits[i]; |
1299 | let init_place = &self.move_data.move_paths[init.path].place; | |
1300 | if self.places_conflict(&init_place, place, Deep) { | |
1301 | self.report_illegal_reassignment(context, (place, span), init.span); | |
1302 | break; | |
1303 | } | |
1304 | } | |
1305 | } | |
1306 | ||
1307 | fn check_if_path_is_moved( | |
1308 | &mut self, | |
1309 | context: Context, | |
1310 | desired_action: InitializationRequiringAction, | |
1311 | place_span: (&Place<'tcx>, Span), | |
1312 | flow_state: &Flows<'cx, 'gcx, 'tcx>, | |
1313 | ) { | |
1314 | // FIXME: analogous code in check_loans first maps `place` to | |
1315 | // its base_path ... but is that what we want here? | |
1316 | let place = self.base_path(place_span.0); | |
1317 | ||
1318 | let maybe_uninits = &flow_state.uninits; | |
1319 | let curr_move_outs = &flow_state.move_outs; | |
1320 | ||
1321 | // Bad scenarios: | |
1322 | // | |
1323 | // 1. Move of `a.b.c`, use of `a.b.c` | |
1324 | // 2. Move of `a.b.c`, use of `a.b.c.d` (without first reinitializing `a.b.c.d`) | |
1325 | // 3. Move of `a.b.c`, use of `a` or `a.b` | |
1326 | // 4. Uninitialized `(a.b.c: &_)`, use of `*a.b.c`; note that with | |
1327 | // partial initialization support, one might have `a.x` | |
1328 | // initialized but not `a.b`. | |
1329 | // | |
1330 | // OK scenarios: | |
1331 | // | |
1332 | // 5. Move of `a.b.c`, use of `a.b.d` | |
1333 | // 6. Uninitialized `a.x`, initialized `a.b`, use of `a.b` | |
1334 | // 7. Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b` | |
1335 | // must have been initialized for the use to be sound. | |
1336 | // 8. Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d` | |
1337 | ||
1338 | // The dataflow tracks shallow prefixes distinctly (that is, | |
1339 | // field-accesses on P distinctly from P itself), in order to | |
1340 | // track substructure initialization separately from the whole | |
1341 | // structure. | |
1342 | // | |
1343 | // E.g., when looking at (*a.b.c).d, if the closest prefix for | |
1344 | // which we have a MovePath is `a.b`, then that means that the | |
1345 | // initialization state of `a.b` is all we need to inspect to | |
1346 | // know if `a.b.c` is valid (and from that we infer that the | |
1347 | // dereference and `.d` access is also valid, since we assume | |
1348 | // `a.b.c` is assigned a reference to a initialized and | |
1349 | // well-formed record structure.) | |
1350 | ||
1351 | // Therefore, if we seek out the *closest* prefix for which we | |
1352 | // have a MovePath, that should capture the initialization | |
1353 | // state for the place scenario. | |
1354 | // | |
1355 | // This code covers scenarios 1, 2, and 4. | |
1356 | ||
1357 | debug!("check_if_path_is_moved part1 place: {:?}", place); | |
1358 | match self.move_path_closest_to(place) { | |
1359 | Ok(mpi) => { | |
1360 | if maybe_uninits.contains(&mpi) { | |
1361 | self.report_use_of_moved_or_uninitialized( | |
1362 | context, | |
1363 | desired_action, | |
1364 | place_span, | |
1365 | mpi, | |
1366 | curr_move_outs, | |
1367 | ); | |
1368 | return; // don't bother finding other problems. | |
1369 | } | |
1370 | } | |
1371 | Err(NoMovePathFound::ReachedStatic) => { | |
1372 | // Okay: we do not build MoveData for static variables | |
1373 | } // Only query longest prefix with a MovePath, not further | |
1374 | // ancestors; dataflow recurs on children when parents | |
1375 | // move (to support partial (re)inits). | |
1376 | // | |
1377 | // (I.e. querying parents breaks scenario 8; but may want | |
1378 | // to do such a query based on partial-init feature-gate.) | |
1379 | } | |
1380 | ||
1381 | // A move of any shallow suffix of `place` also interferes | |
1382 | // with an attempt to use `place`. This is scenario 3 above. | |
1383 | // | |
1384 | // (Distinct from handling of scenarios 1+2+4 above because | |
1385 | // `place` does not interfere with suffixes of its prefixes, | |
1386 | // e.g. `a.b.c` does not interfere with `a.b.d`) | |
1387 | ||
1388 | debug!("check_if_path_is_moved part2 place: {:?}", place); | |
1389 | if let Some(mpi) = self.move_path_for_place(place) { | |
1390 | if let Some(child_mpi) = maybe_uninits.has_any_child_of(mpi) { | |
1391 | self.report_use_of_moved_or_uninitialized( | |
1392 | context, | |
1393 | desired_action, | |
1394 | place_span, | |
1395 | child_mpi, | |
1396 | curr_move_outs, | |
1397 | ); | |
1398 | return; // don't bother finding other problems. | |
1399 | } | |
1400 | } | |
1401 | } | |
1402 | ||
1403 | /// Currently MoveData does not store entries for all places in | |
1404 | /// the input MIR. For example it will currently filter out | |
1405 | /// places that are Copy; thus we do not track places of shared | |
1406 | /// reference type. This routine will walk up a place along its | |
1407 | /// prefixes, searching for a foundational place that *is* | |
1408 | /// tracked in the MoveData. | |
1409 | /// | |
1410 | /// An Err result includes a tag indicated why the search failed. | |
0531ce1d | 1411 | /// Currently this can only occur if the place is built off of a |
ff7c6d11 XL |
1412 | /// static variable, as we do not track those in the MoveData. |
1413 | fn move_path_closest_to( | |
1414 | &mut self, | |
1415 | place: &Place<'tcx>, | |
1416 | ) -> Result<MovePathIndex, NoMovePathFound> { | |
1417 | let mut last_prefix = place; | |
1418 | for prefix in self.prefixes(place, PrefixSet::All) { | |
1419 | if let Some(mpi) = self.move_path_for_place(prefix) { | |
1420 | return Ok(mpi); | |
1421 | } | |
1422 | last_prefix = prefix; | |
1423 | } | |
1424 | match *last_prefix { | |
1425 | Place::Local(_) => panic!("should have move path for every Local"), | |
0531ce1d | 1426 | Place::Projection(_) => panic!("PrefixSet::All meant don't stop for Projection"), |
ff7c6d11 XL |
1427 | Place::Static(_) => return Err(NoMovePathFound::ReachedStatic), |
1428 | } | |
1429 | } | |
1430 | ||
1431 | fn move_path_for_place(&mut self, place: &Place<'tcx>) -> Option<MovePathIndex> { | |
1432 | // If returns None, then there is no move path corresponding | |
1433 | // to a direct owner of `place` (which means there is nothing | |
1434 | // that borrowck tracks for its analysis). | |
1435 | ||
1436 | match self.move_data.rev_lookup.find(place) { | |
1437 | LookupResult::Parent(_) => None, | |
1438 | LookupResult::Exact(mpi) => Some(mpi), | |
1439 | } | |
1440 | } | |
1441 | ||
1442 | fn check_if_assigned_path_is_moved( | |
1443 | &mut self, | |
1444 | context: Context, | |
1445 | (place, span): (&Place<'tcx>, Span), | |
1446 | flow_state: &Flows<'cx, 'gcx, 'tcx>, | |
1447 | ) { | |
1448 | // recur down place; dispatch to check_if_path_is_moved when necessary | |
1449 | let mut place = place; | |
1450 | loop { | |
1451 | match *place { | |
1452 | Place::Local(_) | Place::Static(_) => { | |
1453 | // assigning to `x` does not require `x` be initialized. | |
1454 | break; | |
1455 | } | |
1456 | Place::Projection(ref proj) => { | |
1457 | let Projection { ref base, ref elem } = **proj; | |
1458 | match *elem { | |
1459 | ProjectionElem::Deref | | |
1460 | // assigning to *P requires `P` initialized. | |
1461 | ProjectionElem::Index(_/*operand*/) | | |
1462 | ProjectionElem::ConstantIndex { .. } | | |
1463 | // assigning to P[i] requires `P` initialized. | |
1464 | ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) => | |
1465 | // assigning to (P->variant) is okay if assigning to `P` is okay | |
1466 | // | |
1467 | // FIXME: is this true even if P is a adt with a dtor? | |
1468 | { } | |
1469 | ||
1470 | ProjectionElem::Subslice { .. } => { | |
0531ce1d | 1471 | panic!("we don't allow assignments to subslices, context: {:?}", |
ff7c6d11 XL |
1472 | context); |
1473 | } | |
1474 | ||
1475 | ProjectionElem::Field(..) => { | |
1476 | // if type of `P` has a dtor, then | |
1477 | // assigning to `P.f` requires `P` itself | |
1478 | // be already initialized | |
1479 | let tcx = self.tcx; | |
1480 | match base.ty(self.mir, tcx).to_ty(tcx).sty { | |
1481 | ty::TyAdt(def, _) if def.has_dtor(tcx) => { | |
1482 | ||
1483 | // FIXME: analogous code in | |
1484 | // check_loans.rs first maps | |
1485 | // `base` to its base_path. | |
1486 | ||
1487 | self.check_if_path_is_moved( | |
1488 | context, InitializationRequiringAction::Assignment, | |
1489 | (base, span), flow_state); | |
1490 | ||
1491 | // (base initialized; no need to | |
1492 | // recur further) | |
1493 | break; | |
1494 | } | |
1495 | _ => {} | |
1496 | } | |
1497 | } | |
1498 | } | |
1499 | ||
1500 | place = base; | |
1501 | continue; | |
1502 | } | |
1503 | } | |
1504 | } | |
1505 | } | |
1506 | ||
1507 | /// Check the permissions for the given place and read or write kind | |
1508 | /// | |
1509 | /// Returns true if an error is reported, false otherwise. | |
1510 | fn check_access_permissions( | |
1511 | &self, | |
1512 | (place, span): (&Place<'tcx>, Span), | |
1513 | kind: ReadOrWrite, | |
1514 | is_local_mutation_allowed: LocalMutationIsAllowed, | |
1515 | ) -> bool { | |
1516 | debug!( | |
1517 | "check_access_permissions({:?}, {:?}, {:?})", | |
0531ce1d | 1518 | place, kind, is_local_mutation_allowed |
ff7c6d11 XL |
1519 | ); |
1520 | let mut error_reported = false; | |
1521 | match kind { | |
1522 | Reservation(WriteKind::MutableBorrow(BorrowKind::Unique)) | |
1523 | | Write(WriteKind::MutableBorrow(BorrowKind::Unique)) => { | |
1524 | if let Err(_place_err) = self.is_mutable(place, LocalMutationIsAllowed::Yes) { | |
1525 | span_bug!(span, "&unique borrow for {:?} should not fail", place); | |
1526 | } | |
1527 | } | |
2c00a5a8 XL |
1528 | Reservation(WriteKind::MutableBorrow(BorrowKind::Mut { .. })) |
1529 | | Write(WriteKind::MutableBorrow(BorrowKind::Mut { .. })) => if let Err(place_err) = | |
ff7c6d11 XL |
1530 | self.is_mutable(place, is_local_mutation_allowed) |
1531 | { | |
1532 | error_reported = true; | |
1533 | ||
1534 | let item_msg = match self.describe_place(place) { | |
1535 | Some(name) => format!("immutable item `{}`", name), | |
1536 | None => "immutable item".to_owned(), | |
1537 | }; | |
1538 | ||
1539 | let mut err = self.tcx | |
1540 | .cannot_borrow_path_as_mutable(span, &item_msg, Origin::Mir); | |
1541 | err.span_label(span, "cannot borrow as mutable"); | |
1542 | ||
1543 | if place != place_err { | |
1544 | if let Some(name) = self.describe_place(place_err) { | |
1545 | err.note(&format!("Value not mutable causing this error: `{}`", name)); | |
1546 | } | |
1547 | } | |
1548 | ||
1549 | err.emit(); | |
1550 | }, | |
1551 | Reservation(WriteKind::Mutate) | Write(WriteKind::Mutate) => { | |
1552 | if let Err(place_err) = self.is_mutable(place, is_local_mutation_allowed) { | |
1553 | error_reported = true; | |
1554 | ||
1555 | let item_msg = match self.describe_place(place) { | |
1556 | Some(name) => format!("immutable item `{}`", name), | |
1557 | None => "immutable item".to_owned(), | |
1558 | }; | |
1559 | ||
1560 | let mut err = self.tcx.cannot_assign(span, &item_msg, Origin::Mir); | |
1561 | err.span_label(span, "cannot mutate"); | |
1562 | ||
1563 | if place != place_err { | |
1564 | if let Some(name) = self.describe_place(place_err) { | |
1565 | err.note(&format!("Value not mutable causing this error: `{}`", name)); | |
1566 | } | |
1567 | } | |
1568 | ||
1569 | err.emit(); | |
1570 | } | |
1571 | } | |
1572 | Reservation(WriteKind::Move) | |
1573 | | Reservation(WriteKind::StorageDeadOrDrop) | |
1574 | | Reservation(WriteKind::MutableBorrow(BorrowKind::Shared)) | |
1575 | | Write(WriteKind::Move) | |
1576 | | Write(WriteKind::StorageDeadOrDrop) | |
1577 | | Write(WriteKind::MutableBorrow(BorrowKind::Shared)) => { | |
1578 | if let Err(_place_err) = self.is_mutable(place, is_local_mutation_allowed) { | |
1579 | self.tcx.sess.delay_span_bug( | |
1580 | span, | |
1581 | &format!( | |
1582 | "Accessing `{:?}` with the kind `{:?}` shouldn't be possible", | |
0531ce1d | 1583 | place, kind |
ff7c6d11 XL |
1584 | ), |
1585 | ); | |
1586 | } | |
1587 | } | |
1588 | ||
1589 | Activation(..) => {} // permission checks are done at Reservation point. | |
1590 | ||
1591 | Read(ReadKind::Borrow(BorrowKind::Unique)) | |
2c00a5a8 | 1592 | | Read(ReadKind::Borrow(BorrowKind::Mut { .. })) |
ff7c6d11 XL |
1593 | | Read(ReadKind::Borrow(BorrowKind::Shared)) |
1594 | | Read(ReadKind::Copy) => {} // Access authorized | |
1595 | } | |
1596 | ||
1597 | error_reported | |
1598 | } | |
1599 | ||
1600 | /// Can this value be written or borrowed mutably | |
1601 | fn is_mutable<'d>( | |
1602 | &self, | |
1603 | place: &'d Place<'tcx>, | |
1604 | is_local_mutation_allowed: LocalMutationIsAllowed, | |
1605 | ) -> Result<(), &'d Place<'tcx>> { | |
1606 | match *place { | |
1607 | Place::Local(local) => { | |
1608 | let local = &self.mir.local_decls[local]; | |
1609 | match local.mutability { | |
1610 | Mutability::Not => match is_local_mutation_allowed { | |
1611 | LocalMutationIsAllowed::Yes | LocalMutationIsAllowed::ExceptUpvars => { | |
1612 | Ok(()) | |
1613 | } | |
1614 | LocalMutationIsAllowed::No => Err(place), | |
1615 | }, | |
1616 | Mutability::Mut => Ok(()), | |
1617 | } | |
1618 | } | |
0531ce1d XL |
1619 | Place::Static(ref static_) => |
1620 | if self.tcx.is_static(static_.def_id) != Some(hir::Mutability::MutMutable) { | |
1621 | Err(place) | |
1622 | } else { | |
1623 | Ok(()) | |
1624 | }, | |
ff7c6d11 XL |
1625 | Place::Projection(ref proj) => { |
1626 | match proj.elem { | |
1627 | ProjectionElem::Deref => { | |
1628 | let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); | |
1629 | ||
1630 | // Check the kind of deref to decide | |
1631 | match base_ty.sty { | |
1632 | ty::TyRef(_, tnm) => { | |
1633 | match tnm.mutbl { | |
1634 | // Shared borrowed data is never mutable | |
1635 | hir::MutImmutable => Err(place), | |
1636 | // Mutably borrowed data is mutable, but only if we have a | |
1637 | // unique path to the `&mut` | |
1638 | hir::MutMutable => { | |
1639 | let mode = match self.is_upvar_field_projection(&proj.base) | |
1640 | { | |
1641 | Some(field) | |
1642 | if { | |
1643 | self.mir.upvar_decls[field.index()].by_ref | |
1644 | } => | |
1645 | { | |
1646 | is_local_mutation_allowed | |
1647 | } | |
1648 | _ => LocalMutationIsAllowed::Yes, | |
1649 | }; | |
1650 | ||
1651 | self.is_mutable(&proj.base, mode) | |
1652 | } | |
1653 | } | |
1654 | } | |
1655 | ty::TyRawPtr(tnm) => { | |
1656 | match tnm.mutbl { | |
1657 | // `*const` raw pointers are not mutable | |
1658 | hir::MutImmutable => return Err(place), | |
1659 | // `*mut` raw pointers are always mutable, regardless of context | |
1660 | // The users have to check by themselve. | |
1661 | hir::MutMutable => return Ok(()), | |
1662 | } | |
1663 | } | |
1664 | // `Box<T>` owns its content, so mutable if its location is mutable | |
1665 | _ if base_ty.is_box() => { | |
1666 | self.is_mutable(&proj.base, is_local_mutation_allowed) | |
1667 | } | |
1668 | // Deref should only be for reference, pointers or boxes | |
1669 | _ => bug!("Deref of unexpected type: {:?}", base_ty), | |
1670 | } | |
1671 | } | |
1672 | // All other projections are owned by their base path, so mutable if | |
1673 | // base path is mutable | |
1674 | ProjectionElem::Field(..) | |
1675 | | ProjectionElem::Index(..) | |
1676 | | ProjectionElem::ConstantIndex { .. } | |
1677 | | ProjectionElem::Subslice { .. } | |
1678 | | ProjectionElem::Downcast(..) => { | |
1679 | if let Some(field) = self.is_upvar_field_projection(place) { | |
1680 | let decl = &self.mir.upvar_decls[field.index()]; | |
1681 | debug!( | |
1682 | "decl.mutability={:?} local_mutation_is_allowed={:?} place={:?}", | |
0531ce1d | 1683 | decl, is_local_mutation_allowed, place |
ff7c6d11 XL |
1684 | ); |
1685 | match (decl.mutability, is_local_mutation_allowed) { | |
1686 | (Mutability::Not, LocalMutationIsAllowed::No) | |
1687 | | (Mutability::Not, LocalMutationIsAllowed::ExceptUpvars) => { | |
1688 | Err(place) | |
1689 | } | |
1690 | (Mutability::Not, LocalMutationIsAllowed::Yes) | |
1691 | | (Mutability::Mut, _) => { | |
1692 | self.is_mutable(&proj.base, is_local_mutation_allowed) | |
1693 | } | |
1694 | } | |
1695 | } else { | |
1696 | self.is_mutable(&proj.base, is_local_mutation_allowed) | |
1697 | } | |
1698 | } | |
1699 | } | |
1700 | } | |
1701 | } | |
1702 | } | |
1703 | ||
ff7c6d11 XL |
1704 | /// If this is a field projection, and the field is being projected from a closure type, |
1705 | /// then returns the index of the field being projected. Note that this closure will always | |
1706 | /// be `self` in the current MIR, because that is the only time we directly access the fields | |
1707 | /// of a closure type. | |
1708 | fn is_upvar_field_projection(&self, place: &Place<'tcx>) -> Option<Field> { | |
1709 | match *place { | |
1710 | Place::Projection(ref proj) => match proj.elem { | |
1711 | ProjectionElem::Field(field, _ty) => { | |
1712 | let is_projection_from_ty_closure = proj.base | |
1713 | .ty(self.mir, self.tcx) | |
1714 | .to_ty(self.tcx) | |
1715 | .is_closure(); | |
1716 | ||
1717 | if is_projection_from_ty_closure { | |
1718 | Some(field) | |
1719 | } else { | |
1720 | None | |
1721 | } | |
1722 | } | |
1723 | _ => None, | |
1724 | }, | |
1725 | _ => None, | |
1726 | } | |
1727 | } | |
1728 | } | |
1729 | ||
1730 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
1731 | enum NoMovePathFound { | |
1732 | ReachedStatic, | |
1733 | } | |
1734 | ||
1735 | /// The degree of overlap between 2 places for borrow-checking. | |
1736 | enum Overlap { | |
1737 | /// The places might partially overlap - in this case, we give | |
1738 | /// up and say that they might conflict. This occurs when | |
1739 | /// different fields of a union are borrowed. For example, | |
1740 | /// if `u` is a union, we have no way of telling how disjoint | |
1741 | /// `u.a.x` and `a.b.y` are. | |
1742 | Arbitrary, | |
1743 | /// The places have the same type, and are either completely disjoint | |
1744 | /// or equal - i.e. they can't "partially" overlap as can occur with | |
1745 | /// unions. This is the "base case" on which we recur for extensions | |
1746 | /// of the place. | |
1747 | EqualOrDisjoint, | |
1748 | /// The places are disjoint, so we know all extensions of them | |
1749 | /// will also be disjoint. | |
1750 | Disjoint, | |
1751 | } | |
1752 | ||
1753 | impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { | |
1754 | // Given that the bases of `elem1` and `elem2` are always either equal | |
1755 | // or disjoint (and have the same type!), return the overlap situation | |
1756 | // between `elem1` and `elem2`. | |
1757 | fn place_element_conflict(&self, elem1: &Place<'tcx>, elem2: &Place<'tcx>) -> Overlap { | |
1758 | match (elem1, elem2) { | |
1759 | (Place::Local(l1), Place::Local(l2)) => { | |
1760 | if l1 == l2 { | |
1761 | // the same local - base case, equal | |
1762 | debug!("place_element_conflict: DISJOINT-OR-EQ-LOCAL"); | |
1763 | Overlap::EqualOrDisjoint | |
1764 | } else { | |
1765 | // different locals - base case, disjoint | |
1766 | debug!("place_element_conflict: DISJOINT-LOCAL"); | |
1767 | Overlap::Disjoint | |
1768 | } | |
1769 | } | |
1770 | (Place::Static(static1), Place::Static(static2)) => { | |
1771 | if static1.def_id != static2.def_id { | |
1772 | debug!("place_element_conflict: DISJOINT-STATIC"); | |
1773 | Overlap::Disjoint | |
0531ce1d | 1774 | } else if self.tcx.is_static(static1.def_id) == Some(hir::Mutability::MutMutable) { |
ff7c6d11 XL |
1775 | // We ignore mutable statics - they can only be unsafe code. |
1776 | debug!("place_element_conflict: IGNORE-STATIC-MUT"); | |
1777 | Overlap::Disjoint | |
1778 | } else { | |
1779 | debug!("place_element_conflict: DISJOINT-OR-EQ-STATIC"); | |
1780 | Overlap::EqualOrDisjoint | |
1781 | } | |
1782 | } | |
1783 | (Place::Local(_), Place::Static(_)) | (Place::Static(_), Place::Local(_)) => { | |
1784 | debug!("place_element_conflict: DISJOINT-STATIC-LOCAL"); | |
1785 | Overlap::Disjoint | |
1786 | } | |
1787 | (Place::Projection(pi1), Place::Projection(pi2)) => { | |
1788 | match (&pi1.elem, &pi2.elem) { | |
1789 | (ProjectionElem::Deref, ProjectionElem::Deref) => { | |
1790 | // derefs (e.g. `*x` vs. `*x`) - recur. | |
1791 | debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF"); | |
1792 | Overlap::EqualOrDisjoint | |
1793 | } | |
1794 | (ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => { | |
1795 | if f1 == f2 { | |
1796 | // same field (e.g. `a.y` vs. `a.y`) - recur. | |
1797 | debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD"); | |
1798 | Overlap::EqualOrDisjoint | |
1799 | } else { | |
1800 | let ty = pi1.base.ty(self.mir, self.tcx).to_ty(self.tcx); | |
1801 | match ty.sty { | |
1802 | ty::TyAdt(def, _) if def.is_union() => { | |
1803 | // Different fields of a union, we are basically stuck. | |
1804 | debug!("place_element_conflict: STUCK-UNION"); | |
1805 | Overlap::Arbitrary | |
1806 | } | |
1807 | _ => { | |
1808 | // Different fields of a struct (`a.x` vs. `a.y`). Disjoint! | |
1809 | debug!("place_element_conflict: DISJOINT-FIELD"); | |
1810 | Overlap::Disjoint | |
1811 | } | |
1812 | } | |
1813 | } | |
1814 | } | |
1815 | (ProjectionElem::Downcast(_, v1), ProjectionElem::Downcast(_, v2)) => { | |
1816 | // different variants are treated as having disjoint fields, | |
1817 | // even if they occupy the same "space", because it's | |
1818 | // impossible for 2 variants of the same enum to exist | |
1819 | // (and therefore, to be borrowed) at the same time. | |
1820 | // | |
1821 | // Note that this is different from unions - we *do* allow | |
1822 | // this code to compile: | |
1823 | // | |
1824 | // ``` | |
1825 | // fn foo(x: &mut Result<i32, i32>) { | |
1826 | // let mut v = None; | |
1827 | // if let Ok(ref mut a) = *x { | |
1828 | // v = Some(a); | |
1829 | // } | |
1830 | // // here, you would *think* that the | |
1831 | // // *entirety* of `x` would be borrowed, | |
1832 | // // but in fact only the `Ok` variant is, | |
1833 | // // so the `Err` variant is *entirely free*: | |
1834 | // if let Err(ref mut a) = *x { | |
1835 | // v = Some(a); | |
1836 | // } | |
1837 | // drop(v); | |
1838 | // } | |
1839 | // ``` | |
1840 | if v1 == v2 { | |
1841 | debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD"); | |
1842 | Overlap::EqualOrDisjoint | |
1843 | } else { | |
1844 | debug!("place_element_conflict: DISJOINT-FIELD"); | |
1845 | Overlap::Disjoint | |
1846 | } | |
1847 | } | |
1848 | (ProjectionElem::Index(..), ProjectionElem::Index(..)) | |
1849 | | (ProjectionElem::Index(..), ProjectionElem::ConstantIndex { .. }) | |
1850 | | (ProjectionElem::Index(..), ProjectionElem::Subslice { .. }) | |
1851 | | (ProjectionElem::ConstantIndex { .. }, ProjectionElem::Index(..)) | |
1852 | | ( | |
1853 | ProjectionElem::ConstantIndex { .. }, | |
1854 | ProjectionElem::ConstantIndex { .. }, | |
1855 | ) | |
1856 | | (ProjectionElem::ConstantIndex { .. }, ProjectionElem::Subslice { .. }) | |
1857 | | (ProjectionElem::Subslice { .. }, ProjectionElem::Index(..)) | |
1858 | | (ProjectionElem::Subslice { .. }, ProjectionElem::ConstantIndex { .. }) | |
1859 | | (ProjectionElem::Subslice { .. }, ProjectionElem::Subslice { .. }) => { | |
1860 | // Array indexes (`a[0]` vs. `a[i]`). These can either be disjoint | |
1861 | // (if the indexes differ) or equal (if they are the same), so this | |
1862 | // is the recursive case that gives "equal *or* disjoint" its meaning. | |
1863 | // | |
1864 | // Note that by construction, MIR at borrowck can't subdivide | |
1865 | // `Subslice` accesses (e.g. `a[2..3][i]` will never be present) - they | |
1866 | // are only present in slice patterns, and we "merge together" nested | |
1867 | // slice patterns. That means we don't have to think about these. It's | |
1868 | // probably a good idea to assert this somewhere, but I'm too lazy. | |
1869 | // | |
1870 | // FIXME(#8636) we might want to return Disjoint if | |
1871 | // both projections are constant and disjoint. | |
1872 | debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY"); | |
1873 | Overlap::EqualOrDisjoint | |
1874 | } | |
1875 | ||
1876 | (ProjectionElem::Deref, _) | |
1877 | | (ProjectionElem::Field(..), _) | |
1878 | | (ProjectionElem::Index(..), _) | |
1879 | | (ProjectionElem::ConstantIndex { .. }, _) | |
1880 | | (ProjectionElem::Subslice { .. }, _) | |
1881 | | (ProjectionElem::Downcast(..), _) => bug!( | |
1882 | "mismatched projections in place_element_conflict: {:?} and {:?}", | |
1883 | elem1, | |
1884 | elem2 | |
1885 | ), | |
1886 | } | |
1887 | } | |
1888 | (Place::Projection(_), _) | (_, Place::Projection(_)) => bug!( | |
1889 | "unexpected elements in place_element_conflict: {:?} and {:?}", | |
1890 | elem1, | |
1891 | elem2 | |
1892 | ), | |
1893 | } | |
1894 | } | |
1895 | ||
1896 | /// Returns whether an access of kind `access` to `access_place` conflicts with | |
1897 | /// a borrow/full access to `borrow_place` (for deep accesses to mutable | |
1898 | /// locations, this function is symmetric between `borrow_place` & `access_place`). | |
1899 | fn places_conflict( | |
1900 | &mut self, | |
1901 | borrow_place: &Place<'tcx>, | |
1902 | access_place: &Place<'tcx>, | |
1903 | access: ShallowOrDeep, | |
1904 | ) -> bool { | |
1905 | debug!( | |
1906 | "places_conflict({:?},{:?},{:?})", | |
0531ce1d | 1907 | borrow_place, access_place, access |
ff7c6d11 XL |
1908 | ); |
1909 | ||
1910 | // Return all the prefixes of `place` in reverse order, including | |
1911 | // downcasts. | |
1912 | fn place_elements<'a, 'tcx>(place: &'a Place<'tcx>) -> Vec<&'a Place<'tcx>> { | |
1913 | let mut result = vec![]; | |
1914 | let mut place = place; | |
1915 | loop { | |
1916 | result.push(place); | |
1917 | match place { | |
1918 | Place::Projection(interior) => { | |
1919 | place = &interior.base; | |
1920 | } | |
1921 | Place::Local(_) | Place::Static(_) => { | |
1922 | result.reverse(); | |
1923 | return result; | |
1924 | } | |
1925 | } | |
1926 | } | |
1927 | } | |
1928 | ||
1929 | let borrow_components = place_elements(borrow_place); | |
1930 | let access_components = place_elements(access_place); | |
1931 | debug!( | |
1932 | "places_conflict: components {:?} / {:?}", | |
0531ce1d | 1933 | borrow_components, access_components |
ff7c6d11 XL |
1934 | ); |
1935 | ||
1936 | let borrow_components = borrow_components | |
1937 | .into_iter() | |
1938 | .map(Some) | |
1939 | .chain(iter::repeat(None)); | |
1940 | let access_components = access_components | |
1941 | .into_iter() | |
1942 | .map(Some) | |
1943 | .chain(iter::repeat(None)); | |
1944 | // The borrowck rules for proving disjointness are applied from the "root" of the | |
1945 | // borrow forwards, iterating over "similar" projections in lockstep until | |
1946 | // we can prove overlap one way or another. Essentially, we treat `Overlap` as | |
1947 | // a monoid and report a conflict if the product ends up not being `Disjoint`. | |
1948 | // | |
1949 | // At each step, if we didn't run out of borrow or place, we know that our elements | |
1950 | // have the same type, and that they only overlap if they are the identical. | |
1951 | // | |
1952 | // For example, if we are comparing these: | |
1953 | // BORROW: (*x1[2].y).z.a | |
1954 | // ACCESS: (*x1[i].y).w.b | |
1955 | // | |
1956 | // Then our steps are: | |
1957 | // x1 | x1 -- places are the same | |
1958 | // x1[2] | x1[i] -- equal or disjoint (disjoint if indexes differ) | |
1959 | // x1[2].y | x1[i].y -- equal or disjoint | |
1960 | // *x1[2].y | *x1[i].y -- equal or disjoint | |
1961 | // (*x1[2].y).z | (*x1[i].y).w -- we are disjoint and don't need to check more! | |
1962 | // | |
1963 | // Because `zip` does potentially bad things to the iterator inside, this loop | |
1964 | // also handles the case where the access might be a *prefix* of the borrow, e.g. | |
1965 | // | |
1966 | // BORROW: (*x1[2].y).z.a | |
1967 | // ACCESS: x1[i].y | |
1968 | // | |
1969 | // Then our steps are: | |
1970 | // x1 | x1 -- places are the same | |
1971 | // x1[2] | x1[i] -- equal or disjoint (disjoint if indexes differ) | |
1972 | // x1[2].y | x1[i].y -- equal or disjoint | |
1973 | // | |
1974 | // -- here we run out of access - the borrow can access a part of it. If this | |
1975 | // is a full deep access, then we *know* the borrow conflicts with it. However, | |
1976 | // if the access is shallow, then we can proceed: | |
1977 | // | |
1978 | // x1[2].y | (*x1[i].y) -- a deref! the access can't get past this, so we | |
1979 | // are disjoint | |
1980 | // | |
1981 | // Our invariant is, that at each step of the iteration: | |
1982 | // - If we didn't run out of access to match, our borrow and access are comparable | |
1983 | // and either equal or disjoint. | |
1984 | // - If we did run out of accesss, the borrow can access a part of it. | |
1985 | for (borrow_c, access_c) in borrow_components.zip(access_components) { | |
1986 | // loop invariant: borrow_c is always either equal to access_c or disjoint from it. | |
1987 | debug!("places_conflict: {:?} vs. {:?}", borrow_c, access_c); | |
1988 | match (borrow_c, access_c) { | |
1989 | (None, _) => { | |
1990 | // If we didn't run out of access, the borrow can access all of our | |
1991 | // place (e.g. a borrow of `a.b` with an access to `a.b.c`), | |
1992 | // so we have a conflict. | |
1993 | // | |
1994 | // If we did, then we still know that the borrow can access a *part* | |
1995 | // of our place that our access cares about (a borrow of `a.b.c` | |
1996 | // with an access to `a.b`), so we still have a conflict. | |
1997 | // | |
1998 | // FIXME: Differs from AST-borrowck; includes drive-by fix | |
1999 | // to #38899. Will probably need back-compat mode flag. | |
2000 | debug!("places_conflict: full borrow, CONFLICT"); | |
2001 | return true; | |
2002 | } | |
2003 | (Some(borrow_c), None) => { | |
2004 | // We know that the borrow can access a part of our place. This | |
2005 | // is a conflict if that is a part our access cares about. | |
2006 | ||
2007 | let (base, elem) = match borrow_c { | |
2008 | Place::Projection(box Projection { base, elem }) => (base, elem), | |
2009 | _ => bug!("place has no base?"), | |
2010 | }; | |
2011 | let base_ty = base.ty(self.mir, self.tcx).to_ty(self.tcx); | |
2012 | ||
2013 | match (elem, &base_ty.sty, access) { | |
2014 | (_, _, Shallow(Some(ArtificialField::Discriminant))) | |
2015 | | (_, _, Shallow(Some(ArtificialField::ArrayLength))) => { | |
2016 | // The discriminant and array length are like | |
2017 | // additional fields on the type; they do not | |
2018 | // overlap any existing data there. Furthermore, | |
2019 | // they cannot actually be a prefix of any | |
2020 | // borrowed place (at least in MIR as it is | |
2021 | // currently.) | |
2022 | // | |
2023 | // e.g. a (mutable) borrow of `a[5]` while we read the | |
2024 | // array length of `a`. | |
2025 | debug!("places_conflict: implicit field"); | |
2026 | return false; | |
2027 | } | |
2028 | ||
2029 | (ProjectionElem::Deref, _, Shallow(None)) => { | |
2030 | // e.g. a borrow of `*x.y` while we shallowly access `x.y` or some | |
2031 | // prefix thereof - the shallow access can't touch anything behind | |
2032 | // the pointer. | |
2033 | debug!("places_conflict: shallow access behind ptr"); | |
2034 | return false; | |
2035 | } | |
2036 | ( | |
2037 | ProjectionElem::Deref, | |
2038 | ty::TyRef( | |
2039 | _, | |
2040 | ty::TypeAndMut { | |
2041 | ty: _, | |
2042 | mutbl: hir::MutImmutable, | |
2043 | }, | |
2044 | ), | |
2045 | _, | |
2046 | ) => { | |
2047 | // the borrow goes through a dereference of a shared reference. | |
2048 | // | |
2049 | // I'm not sure why we are tracking these borrows - shared | |
2050 | // references can *always* be aliased, which means the | |
2051 | // permission check already account for this borrow. | |
2052 | debug!("places_conflict: behind a shared ref"); | |
2053 | return false; | |
2054 | } | |
2055 | ||
2056 | (ProjectionElem::Deref, _, Deep) | |
2057 | | (ProjectionElem::Field { .. }, _, _) | |
2058 | | (ProjectionElem::Index { .. }, _, _) | |
2059 | | (ProjectionElem::ConstantIndex { .. }, _, _) | |
2060 | | (ProjectionElem::Subslice { .. }, _, _) | |
2061 | | (ProjectionElem::Downcast { .. }, _, _) => { | |
2062 | // Recursive case. This can still be disjoint on a | |
2063 | // further iteration if this a shallow access and | |
2064 | // there's a deref later on, e.g. a borrow | |
2065 | // of `*x.y` while accessing `x`. | |
2066 | } | |
2067 | } | |
2068 | } | |
2069 | (Some(borrow_c), Some(access_c)) => { | |
2070 | match self.place_element_conflict(&borrow_c, access_c) { | |
2071 | Overlap::Arbitrary => { | |
2072 | // We have encountered different fields of potentially | |
2073 | // the same union - the borrow now partially overlaps. | |
2074 | // | |
2075 | // There is no *easy* way of comparing the fields | |
2076 | // further on, because they might have different types | |
2077 | // (e.g. borrows of `u.a.0` and `u.b.y` where `.0` and | |
2078 | // `.y` come from different structs). | |
2079 | // | |
2080 | // We could try to do some things here - e.g. count | |
2081 | // dereferences - but that's probably not a good | |
2082 | // idea, at least for now, so just give up and | |
2083 | // report a conflict. This is unsafe code anyway so | |
2084 | // the user could always use raw pointers. | |
2085 | debug!("places_conflict: arbitrary -> conflict"); | |
2086 | return true; | |
2087 | } | |
2088 | Overlap::EqualOrDisjoint => { | |
2089 | // This is the recursive case - proceed to the next element. | |
2090 | } | |
2091 | Overlap::Disjoint => { | |
2092 | // We have proven the borrow disjoint - further | |
2093 | // projections will remain disjoint. | |
2094 | debug!("places_conflict: disjoint"); | |
2095 | return false; | |
2096 | } | |
2097 | } | |
2098 | } | |
2099 | } | |
2100 | } | |
2101 | unreachable!("iter::repeat returned None") | |
2102 | } | |
2103 | ||
2104 | /// This function iterates over all of the current borrows | |
2105 | /// (represented by 1-bits in `flow_state.borrows`) that conflict | |
2106 | /// with an access to a place, invoking the `op` callback for each | |
2107 | /// one. | |
2108 | /// | |
2109 | /// "Current borrow" here means a borrow that reaches the point in | |
2110 | /// the control-flow where the access occurs. | |
2111 | /// | |
2112 | /// The borrow's phase is represented by the ReserveOrActivateIndex | |
2113 | /// passed to the callback: one can call `is_reservation()` and | |
2114 | /// `is_activation()` to determine what phase the borrow is | |
2115 | /// currently in, when such distinction matters. | |
2116 | fn each_borrow_involving_path<F>( | |
2117 | &mut self, | |
2118 | _context: Context, | |
2119 | access_place: (ShallowOrDeep, &Place<'tcx>), | |
2120 | flow_state: &Flows<'cx, 'gcx, 'tcx>, | |
2121 | mut op: F, | |
2122 | ) where | |
2123 | F: FnMut(&mut Self, ReserveOrActivateIndex, &BorrowData<'tcx>) -> Control, | |
2124 | { | |
2125 | let (access, place) = access_place; | |
2126 | ||
2127 | // FIXME: analogous code in check_loans first maps `place` to | |
2128 | // its base_path. | |
2129 | ||
2130 | let data = flow_state.borrows.operator().borrows(); | |
2131 | ||
2132 | // check for loan restricting path P being used. Accounts for | |
2133 | // borrows of P, P.a.b, etc. | |
0531ce1d XL |
2134 | let mut iter_incoming = flow_state.borrows.iter_incoming(); |
2135 | while let Some(i) = iter_incoming.next() { | |
ff7c6d11 XL |
2136 | let borrowed = &data[i.borrow_index()]; |
2137 | ||
2138 | if self.places_conflict(&borrowed.borrowed_place, place, access) { | |
0531ce1d XL |
2139 | debug!( |
2140 | "each_borrow_involving_path: {:?} @ {:?} vs. {:?}/{:?}", | |
2141 | i, borrowed, place, access | |
2142 | ); | |
ff7c6d11 XL |
2143 | let ctrl = op(self, i, borrowed); |
2144 | if ctrl == Control::Break { | |
2145 | return; | |
2146 | } | |
2147 | } | |
2148 | } | |
2149 | } | |
2150 | } | |
2151 | ||
2152 | impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { | |
2153 | // FIXME (#16118): function intended to allow the borrow checker | |
2154 | // to be less precise in its handling of Box while still allowing | |
2155 | // moves out of a Box. They should be removed when/if we stop | |
2156 | // treating Box specially (e.g. when/if DerefMove is added...) | |
2157 | ||
2158 | fn base_path<'d>(&self, place: &'d Place<'tcx>) -> &'d Place<'tcx> { | |
2159 | //! Returns the base of the leftmost (deepest) dereference of an | |
2160 | //! Box in `place`. If there is no dereference of an Box | |
2161 | //! in `place`, then it just returns `place` itself. | |
2162 | ||
2163 | let mut cursor = place; | |
2164 | let mut deepest = place; | |
2165 | loop { | |
2166 | let proj = match *cursor { | |
2167 | Place::Local(..) | Place::Static(..) => return deepest, | |
2168 | Place::Projection(ref proj) => proj, | |
2169 | }; | |
2170 | if proj.elem == ProjectionElem::Deref | |
2171 | && place.ty(self.mir, self.tcx).to_ty(self.tcx).is_box() | |
2172 | { | |
2173 | deepest = &proj.base; | |
2174 | } | |
2175 | cursor = &proj.base; | |
2176 | } | |
2177 | } | |
2178 | } | |
2179 | ||
2180 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
2181 | struct Context { | |
2182 | kind: ContextKind, | |
2183 | loc: Location, | |
2184 | } | |
2185 | ||
2186 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
2187 | enum ContextKind { | |
2188 | Activation, | |
2189 | AssignLhs, | |
2190 | AssignRhs, | |
2191 | SetDiscrim, | |
2192 | InlineAsm, | |
2193 | SwitchInt, | |
2194 | Drop, | |
2195 | DropAndReplace, | |
2196 | CallOperator, | |
2197 | CallOperand, | |
2198 | CallDest, | |
2199 | Assert, | |
2200 | Yield, | |
2201 | StorageDead, | |
2202 | } | |
2203 | ||
2204 | impl ContextKind { | |
2205 | fn new(self, loc: Location) -> Context { | |
2206 | Context { | |
2207 | kind: self, | |
2208 | loc: loc, | |
2209 | } | |
2210 | } | |
2211 | } |