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