mod.rs 81.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

11
//! This query borrow-checks the MIR to (further) ensure it is not broken.
12

13
use rustc::hir;
N
Niko Matsakis 已提交
14
use rustc::hir::def_id::DefId;
A
Ariel Ben-Yehuda 已提交
15
use rustc::hir::map::definitions::DefPathData;
N
Niko Matsakis 已提交
16 17
use rustc::infer::InferCtxt;
use rustc::ty::{self, ParamEnv, TyCtxt};
18
use rustc::ty::maps::Providers;
N
Niko Matsakis 已提交
19
use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Local, Location, Place};
20
use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue};
21
use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind};
22
use rustc::mir::ClosureRegionRequirements;
23

D
David Wood 已提交
24
use rustc_data_structures::fx::FxHashSet;
25
use rustc_data_structures::indexed_set::IdxSetBuf;
N
Niko Matsakis 已提交
26
use rustc_data_structures::indexed_vec::Idx;
27

N
Niko Matsakis 已提交
28
use syntax::ast;
29
use syntax_pos::Span;
30

31
use dataflow::{do_dataflow, DebugFormatted};
32
use dataflow::FlowAtLocation;
N
Niko Matsakis 已提交
33
use dataflow::MoveDataParamEnv;
34
use dataflow::{DataflowAnalysis, DataflowResultsConsumer};
35
use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals};
N
Niko Matsakis 已提交
36
use dataflow::{EverInitializedLvals, MovingOutStatements};
37 38
use dataflow::{Borrows, BorrowData, ReserveOrActivateIndex};
use dataflow::{ActiveBorrows, Reservations};
39
use dataflow::indexes::{BorrowIndex};
N
Niko Matsakis 已提交
40
use dataflow::move_paths::{IllegalMoveOriginKind, MoveError};
41
use dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MovePathIndex};
42 43
use util::borrowck_errors::{BorrowckErrors, Origin};

44 45
use std::iter;

46
use self::flows::Flows;
47
use self::prefixes::PrefixSet;
48 49
use self::MutateMode::{JustWrite, WriteAndRead};

50
mod error_reporting;
51
mod flows;
52
mod prefixes;
53 54 55

use std::borrow::Cow;

56
pub(crate) mod nll;
57

58 59 60 61 62 63
pub fn provide(providers: &mut Providers) {
    *providers = Providers {
        mir_borrowck,
        ..*providers
    };
}
64

65 66 67
fn mir_borrowck<'a, 'tcx>(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    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

72
    if {
N
Niko Matsakis 已提交
73 74
        !tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.sess.opts.borrowck_mode.use_mir()
            && !tcx.sess.opts.debugging_opts.nll
75
    } {
76
        return None;
77 78
    }

79
    let opt_closure_req = tcx.infer_ctxt().enter(|infcx| {
80
        let input_mir: &Mir = &input_mir.borrow();
81
        do_mir_borrowck(&infcx, input_mir, def_id)
82 83
    });
    debug!("mir_borrowck done");
84 85

    opt_closure_req
86 87
}

N
Niko Matsakis 已提交
88 89 90 91
fn do_mir_borrowck<'a, 'gcx, 'tcx>(
    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
    input_mir: &Mir<'gcx>,
    def_id: DefId,
92
) -> Option<ClosureRegionRequirements<'gcx>> {
93
    let tcx = infcx.tcx;
94 95
    let attributes = tcx.get_attrs(def_id);
    let param_env = tcx.param_env(def_id);
N
Niko Matsakis 已提交
96 97
    let id = tcx.hir
        .as_local_node_id(def_id)
98
        .expect("do_mir_borrowck: non-local DefId");
99

100 101 102 103 104 105 106 107 108 109
    // Make our own copy of the MIR. This copy will be modified (in place) to
    // contain non-lexical lifetimes. It will have a lifetime tied
    // to the inference context.
    let mut mir: Mir<'tcx> = input_mir.clone();
    let free_regions = if !tcx.sess.opts.debugging_opts.nll {
        None
    } else {
        let mir = &mut mir;

        // Replace all regions with fresh inference variables.
110
        Some(nll::replace_regions_in_mir(infcx, def_id, param_env, mir))
111 112 113 114
    };
    let mir = &mir;

    let move_data: MoveData<'tcx> = match MoveData::gather_moves(mir, tcx) {
115 116 117 118
        Ok(move_data) => move_data,
        Err((move_data, move_errors)) => {
            for move_error in move_errors {
                let (span, kind): (Span, IllegalMoveOriginKind) = match move_error {
N
Niko Matsakis 已提交
119 120 121 122 123 124
                    MoveError::UnionMove { .. } => {
                        unimplemented!("dont know how to report union move errors yet.")
                    }
                    MoveError::IllegalMove {
                        cannot_move_out_of: o,
                    } => (o.span, o.kind),
125 126 127
                };
                let origin = Origin::Mir;
                let mut err = match kind {
N
Niko Matsakis 已提交
128 129 130 131 132 133 134 135 136 137 138 139
                    IllegalMoveOriginKind::Static => {
                        tcx.cannot_move_out_of(span, "static item", origin)
                    }
                    IllegalMoveOriginKind::BorrowedContent => {
                        tcx.cannot_move_out_of(span, "borrowed content", origin)
                    }
                    IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
                        tcx.cannot_move_out_of_interior_of_drop(span, ty, origin)
                    }
                    IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => {
                        tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin)
                    }
140 141
                };
                err.emit();
142
            }
143 144 145
            move_data
        }
    };
146

N
Niko Matsakis 已提交
147 148 149 150
    let mdpe = MoveDataParamEnv {
        move_data: move_data,
        param_env: param_env,
    };
A
Ariel Ben-Yehuda 已提交
151 152 153 154 155 156
    let body_id = match tcx.def_key(def_id).disambiguated_data.data {
        DefPathData::StructCtor |
        DefPathData::EnumVariant(_) => None,
        _ => Some(tcx.hir.body_owned_by(id))
    };

157
    let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
158
    let mut flow_inits = FlowAtLocation::new(do_dataflow(
N
Niko Matsakis 已提交
159 160 161 162 163 164
        tcx,
        mir,
        id,
        &attributes,
        &dead_unwinds,
        MaybeInitializedLvals::new(tcx, mir, &mdpe),
165
        |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]),
166
    ));
167
    let flow_uninits = FlowAtLocation::new(do_dataflow(
N
Niko Matsakis 已提交
168 169 170 171 172 173
        tcx,
        mir,
        id,
        &attributes,
        &dead_unwinds,
        MaybeUninitializedLvals::new(tcx, mir, &mdpe),
174
        |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]),
175
    ));
176
    let flow_move_outs = FlowAtLocation::new(do_dataflow(
N
Niko Matsakis 已提交
177 178 179 180 181 182
        tcx,
        mir,
        id,
        &attributes,
        &dead_unwinds,
        MovingOutStatements::new(tcx, mir, &mdpe),
183
        |bd, i| DebugFormatted::new(&bd.move_data().moves[i]),
184
    ));
185
    let flow_ever_inits = FlowAtLocation::new(do_dataflow(
N
Niko Matsakis 已提交
186 187 188 189 190 191
        tcx,
        mir,
        id,
        &attributes,
        &dead_unwinds,
        EverInitializedLvals::new(tcx, mir, &mdpe),
192
        |bd, i| DebugFormatted::new(&bd.move_data().inits[i]),
193 194 195
    ));

    // If we are in non-lexical mode, compute the non-lexical lifetimes.
196 197
    let (opt_regioncx, opt_closure_req) = if let Some(free_regions) = free_regions {
        let (regioncx, opt_closure_req) = nll::compute_regions(
198 199 200 201 202 203 204
            infcx,
            def_id,
            free_regions,
            mir,
            param_env,
            &mut flow_inits,
            &mdpe.move_data,
205 206
        );
        (Some(regioncx), opt_closure_req)
207 208
    } else {
        assert!(!tcx.sess.opts.debugging_opts.nll);
209
        (None, None)
210 211
    };
    let flow_inits = flow_inits; // remove mut
212

213 214 215 216 217 218 219 220 221 222 223
    let mut mbcx = MirBorrowckCtxt {
        tcx: tcx,
        mir: mir,
        node_id: id,
        move_data: &mdpe.move_data,
        param_env: param_env,
        locals_are_invalidated_at_exit: match tcx.hir.body_owner_kind(id) {
            hir::BodyOwnerKind::Const |
            hir::BodyOwnerKind::Static(_) => false,
            hir::BodyOwnerKind::Fn => true,
        },
224 225
        storage_dead_or_drop_error_reported_l: FxHashSet(),
        storage_dead_or_drop_error_reported_s: FxHashSet(),
226
        reservation_error_reported: FxHashSet(),
227 228
    };

229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
    let borrows = Borrows::new(tcx, mir, opt_regioncx, def_id, body_id);
    let flow_reservations = do_dataflow(
        tcx,
        mir,
        id,
        &attributes,
        &dead_unwinds,
        Reservations::new(borrows),
        |rs, i| {
            // In principle we could make the dataflow ensure that
            // only reservation bits show up, and assert so here.
            //
            // In practice it is easier to be looser; in particular,
            // it is okay for the kill-sets to hold activation bits.
            DebugFormatted::new(&(i.kind(), rs.location(i)))
        });
    let flow_active_borrows = {
        let reservations_on_entry = flow_reservations.0.sets.entry_set_state();
        let reservations = flow_reservations.0.operator;
        let a = DataflowAnalysis::new_with_entry_sets(mir,
                                                      &dead_unwinds,
                                                      Cow::Borrowed(reservations_on_entry),
                                                      ActiveBorrows::new(reservations));
        let results = a.run(tcx,
                            id,
                            &attributes,
                            |ab, i| DebugFormatted::new(&(i.kind(), ab.location(i))));
        FlowAtLocation::new(results)
    };

    let mut state = Flows::new(
        flow_active_borrows,
        flow_inits,
        flow_uninits,
        flow_move_outs,
        flow_ever_inits,
    );

267
    mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
268 269

    opt_closure_req
270 271 272
}

#[allow(dead_code)]
273 274 275
pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
    tcx: TyCtxt<'cx, 'gcx, 'tcx>,
    mir: &'cx Mir<'tcx>,
276
    node_id: ast::NodeId,
277 278
    move_data: &'cx MoveData<'tcx>,
    param_env: ParamEnv<'gcx>,
279
    /// This keeps track of whether local variables are free-ed when the function
A
Ariel Ben-Yehuda 已提交
280 281 282 283 284
    /// exits even without a `StorageDead`, which appears to be the case for
    /// constants.
    ///
    /// I'm not sure this is the right approach - @eddyb could you try and
    /// figure this out?
285
    locals_are_invalidated_at_exit: bool,
286
    /// This field keeps track of when storage dead or drop errors are reported
D
David Wood 已提交
287 288
    /// in order to stop duplicate error reporting and identify the conditions required
    /// for a "temporary value dropped here while still borrowed" error. See #45360.
289 290 291
    storage_dead_or_drop_error_reported_l: FxHashSet<Local>,
    /// Same as the above, but for statics (thread-locals)
    storage_dead_or_drop_error_reported_s: FxHashSet<DefId>,
292 293 294 295 296 297 298 299
    /// This field keeps track of when borrow conflict errors are reported
    /// for reservations, so that we don't report seemingly duplicate
    /// errors for corresponding activations
    ///
    /// FIXME: Ideally this would be a set of BorrowIndex, not Places,
    /// but it is currently inconvenient to track down the BorrowIndex
    /// at the time we detect and report a reservation error.
    reservation_error_reported: FxHashSet<Place<'tcx>>,
300 301 302 303 304 305 306
}

// Check that:
// 1. assignments are always made to mutable locations (FIXME: does that still really go here?)
// 2. loans made in overlapping scopes do not conflict
// 3. assignments do not affect things loaned out as immutable
// 4. moves do not affect things loaned out in any way
307
impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
308
    type FlowState = Flows<'cx, 'gcx, 'tcx>;
309

N
Niko Matsakis 已提交
310 311 312
    fn mir(&self) -> &'cx Mir<'tcx> {
        self.mir
    }
313

N
Niko Matsakis 已提交
314
    fn visit_block_entry(&mut self, bb: BasicBlock, flow_state: &Self::FlowState) {
315
        debug!("MirBorrowckCtxt::process_block({:?}): {}", bb, flow_state);
316 317
    }

N
Niko Matsakis 已提交
318 319 320 321 322 323 324 325 326 327
    fn visit_statement_entry(
        &mut self,
        location: Location,
        stmt: &Statement<'tcx>,
        flow_state: &Self::FlowState,
    ) {
        debug!(
            "MirBorrowckCtxt::process_statement({:?}, {:?}): {}",
            location,
            stmt,
328
            flow_state
N
Niko Matsakis 已提交
329
        );
330
        let span = stmt.source_info.span;
331 332 333

        self.check_activations(location, span, flow_state);

334 335
        match stmt.kind {
            StatementKind::Assign(ref lhs, ref rhs) => {
336 337 338
                // NOTE: NLL RFC calls for *shallow* write; using Deep
                // for short-term compat w/ AST-borrowck. Also, switch
                // to shallow requires to dataflow: "if this is an
339 340
                // assignment `place = <rvalue>`, then any loan for some
                // path P of which `place` is a prefix is killed."
N
Niko Matsakis 已提交
341 342 343 344 345 346 347 348 349 350 351 352 353 354
                self.mutate_place(
                    ContextKind::AssignLhs.new(location),
                    (lhs, span),
                    Deep,
                    JustWrite,
                    flow_state,
                );

                self.consume_rvalue(
                    ContextKind::AssignRhs.new(location),
                    (rhs, span),
                    location,
                    flow_state,
                );
355
            }
N
Niko Matsakis 已提交
356 357 358 359 360 361 362 363 364 365 366
            StatementKind::SetDiscriminant {
                ref place,
                variant_index: _,
            } => {
                self.mutate_place(
                    ContextKind::SetDiscrim.new(location),
                    (place, span),
                    Shallow(Some(ArtificialField::Discriminant)),
                    JustWrite,
                    flow_state,
                );
367
            }
N
Niko Matsakis 已提交
368 369 370 371 372
            StatementKind::InlineAsm {
                ref asm,
                ref outputs,
                ref inputs,
            } => {
373
                let context = ContextKind::InlineAsm.new(location);
374 375
                for (o, output) in asm.outputs.iter().zip(outputs) {
                    if o.is_indirect {
376
                        // FIXME(eddyb) indirect inline asm outputs should
377
                        // be encoeded through MIR place derefs instead.
N
Niko Matsakis 已提交
378 379 380 381 382 383 384 385 386 387 388 389 390
                        self.access_place(
                            context,
                            (output, span),
                            (Deep, Read(ReadKind::Copy)),
                            LocalMutationIsAllowed::No,
                            flow_state,
                        );
                        self.check_if_path_is_moved(
                            context,
                            InitializationRequiringAction::Use,
                            (output, span),
                            flow_state,
                        );
391
                    } else {
N
Niko Matsakis 已提交
392 393 394 395 396 397 398
                        self.mutate_place(
                            context,
                            (output, span),
                            Deep,
                            if o.is_rw { WriteAndRead } else { JustWrite },
                            flow_state,
                        );
399 400 401
                    }
                }
                for input in inputs {
402
                    self.consume_operand(context, (input, span), flow_state);
403 404 405 406 407 408
                }
            }
            StatementKind::EndRegion(ref _rgn) => {
                // ignored when consuming results (update to
                // flow_state already handled).
            }
N
Niko Matsakis 已提交
409
            StatementKind::Nop | StatementKind::Validate(..) | StatementKind::StorageLive(..) => {
410 411
                // `Nop`, `Validate`, and `StorageLive` are irrelevant
                // to borrow check.
412
            }
413

414
            StatementKind::StorageDead(local) => {
N
Niko Matsakis 已提交
415 416
                self.access_place(
                    ContextKind::StorageDead.new(location),
417
                    (&Place::Local(local), span),
418 419
                    (Shallow(None), Write(WriteKind::StorageDeadOrDrop)),
                    LocalMutationIsAllowed::Yes,
N
Niko Matsakis 已提交
420 421
                    flow_state,
                );
422
            }
423 424 425
        }
    }

N
Niko Matsakis 已提交
426 427 428 429 430 431
    fn visit_terminator_entry(
        &mut self,
        location: Location,
        term: &Terminator<'tcx>,
        flow_state: &Self::FlowState,
    ) {
432
        let loc = location;
N
Niko Matsakis 已提交
433 434 435 436
        debug!(
            "MirBorrowckCtxt::process_terminator({:?}, {:?}): {}",
            location,
            term,
437
            flow_state
N
Niko Matsakis 已提交
438
        );
439
        let span = term.source_info.span;
440 441 442

        self.check_activations(location, span, flow_state);

443
        match term.kind {
N
Niko Matsakis 已提交
444 445 446 447 448 449 450
            TerminatorKind::SwitchInt {
                ref discr,
                switch_ty: _,
                values: _,
                targets: _,
            } => {
                self.consume_operand(ContextKind::SwitchInt.new(loc), (discr, span), flow_state);
451
            }
N
Niko Matsakis 已提交
452 453 454 455 456 457 458 459 460 461 462 463
            TerminatorKind::Drop {
                location: ref drop_place,
                target: _,
                unwind: _,
            } => {
                self.access_place(
                    ContextKind::Drop.new(loc),
                    (drop_place, span),
                    (Deep, Write(WriteKind::StorageDeadOrDrop)),
                    LocalMutationIsAllowed::Yes,
                    flow_state,
                );
464
            }
N
Niko Matsakis 已提交
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
            TerminatorKind::DropAndReplace {
                location: ref drop_place,
                value: ref new_value,
                target: _,
                unwind: _,
            } => {
                self.mutate_place(
                    ContextKind::DropAndReplace.new(loc),
                    (drop_place, span),
                    Deep,
                    JustWrite,
                    flow_state,
                );
                self.consume_operand(
                    ContextKind::DropAndReplace.new(loc),
                    (new_value, span),
                    flow_state,
                );
483
            }
N
Niko Matsakis 已提交
484 485 486 487 488 489 490
            TerminatorKind::Call {
                ref func,
                ref args,
                ref destination,
                cleanup: _,
            } => {
                self.consume_operand(ContextKind::CallOperator.new(loc), (func, span), flow_state);
491
                for arg in args {
N
Niko Matsakis 已提交
492 493 494 495 496
                    self.consume_operand(
                        ContextKind::CallOperand.new(loc),
                        (arg, span),
                        flow_state,
                    );
497
                }
N
Niko Matsakis 已提交
498 499 500 501 502 503 504 505
                if let Some((ref dest, _ /*bb*/)) = *destination {
                    self.mutate_place(
                        ContextKind::CallDest.new(loc),
                        (dest, span),
                        Deep,
                        JustWrite,
                        flow_state,
                    );
506 507
                }
            }
N
Niko Matsakis 已提交
508 509 510 511 512 513 514 515
            TerminatorKind::Assert {
                ref cond,
                expected: _,
                ref msg,
                target: _,
                cleanup: _,
            } => {
                self.consume_operand(ContextKind::Assert.new(loc), (cond, span), flow_state);
516 517
                match *msg {
                    AssertMessage::BoundsCheck { ref len, ref index } => {
N
Niko Matsakis 已提交
518 519 520 521 522 523
                        self.consume_operand(ContextKind::Assert.new(loc), (len, span), flow_state);
                        self.consume_operand(
                            ContextKind::Assert.new(loc),
                            (index, span),
                            flow_state,
                        );
524
                    }
N
Niko Matsakis 已提交
525
                    AssertMessage::Math(_ /*const_math_err*/) => {}
A
Alex Crichton 已提交
526 527
                    AssertMessage::GeneratorResumedAfterReturn => {}
                    AssertMessage::GeneratorResumedAfterPanic => {}
528 529 530
                }
            }

N
Niko Matsakis 已提交
531 532 533 534 535 536
            TerminatorKind::Yield {
                ref value,
                resume: _,
                drop: _,
            } => {
                self.consume_operand(ContextKind::Yield.new(loc), (value, span), flow_state);
A
Alex Crichton 已提交
537 538
            }

N
Niko Matsakis 已提交
539
            TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => {
540 541 542 543
                // Returning from the function implicitly kills storage for all locals and statics.
                // Often, the storage will already have been killed by an explicit
                // StorageDead, but we don't always emit those (notably on unwind paths),
                // so this "extra check" serves as a kind of backup.
544
                let domain = flow_state.borrows.operator();
545
                let data = domain.borrows();
N
Niko Matsakis 已提交
546 547
                flow_state.borrows.with_elems_outgoing(|borrows| {
                    for i in borrows {
548
                        let borrow = &data[i.borrow_index()];
549 550
                        let context = ContextKind::StorageDead.new(loc);
                        self.check_for_invalidation_at_exit(context, borrow, span, flow_state);
551
                    }
552
                });
553 554
            }
            TerminatorKind::Goto { target: _ } |
555 556
            TerminatorKind::Unreachable |
            TerminatorKind::FalseEdges { .. } => {
557 558 559 560 561 562 563
                // no data used, thus irrelevant to borrowck
            }
        }
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
N
Niko Matsakis 已提交
564 565 566 567
enum MutateMode {
    JustWrite,
    WriteAndRead,
}
568 569

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
N
Niko Matsakis 已提交
570 571 572 573
enum Control {
    Continue,
    Break,
}
574

N
Niko Matsakis 已提交
575
use self::ShallowOrDeep::{Deep, Shallow};
576
use self::ReadOrWrite::{Activation, Read, Reservation, Write};
577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum ArtificialField {
    Discriminant,
    ArrayLength,
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum ShallowOrDeep {
    /// From the RFC: "A *shallow* access means that the immediate
    /// fields reached at LV are accessed, but references or pointers
    /// found within are not dereferenced. Right now, the only access
    /// that is shallow is an assignment like `x = ...;`, which would
    /// be a *shallow write* of `x`."
    Shallow(Option<ArtificialField>),

    /// From the RFC: "A *deep* access means that all data reachable
594
    /// through the given place may be invalidated or accesses by
595 596 597 598
    /// this action."
    Deep,
}

599 600
/// Kind of access to a value: read or write
/// (For informational purposes only)
601 602 603 604 605 606 607 608 609 610
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum ReadOrWrite {
    /// From the RFC: "A *read* means that the existing data may be
    /// read, but will not be changed."
    Read(ReadKind),

    /// From the RFC: "A *write* means that the data may be mutated to
    /// new values or otherwise invalidated (for example, it could be
    /// de-initialized, as in a move operation).
    Write(WriteKind),
611 612 613 614 615 616

    /// For two-phase borrows, we distinguish a reservation (which is treated
    /// like a Read) from an activation (which is treated like a write), and
    /// each of those is furthermore distinguished from Reads/Writes above.
    Reservation(WriteKind),
    Activation(WriteKind, BorrowIndex),
617 618
}

619 620
/// Kind of read access to a value
/// (For informational purposes only)
621 622 623 624 625 626
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum ReadKind {
    Borrow(BorrowKind),
    Copy,
}

627 628
/// Kind of write access to a value
/// (For informational purposes only)
629 630
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum WriteKind {
D
David Wood 已提交
631
    StorageDeadOrDrop,
632 633 634 635 636
    MutableBorrow(BorrowKind),
    Mutate,
    Move,
}

637 638
/// When checking permissions for a place access, this flag is used to indicate that an immutable
/// local place can be mutated.
639 640 641 642 643 644 645 646 647
///
/// FIXME: @nikomatsakis suggested that this flag could be removed with the following modifications:
/// - Merge `check_access_permissions()` and `check_if_reassignment_to_immutable_state()`
/// - Split `is_mutable()` into `is_assignable()` (can be directly assigned) and
///   `is_declared_mutable()`
/// - Take flow state into consideration in `is_assignable()` for local variables
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum LocalMutationIsAllowed {
    Yes,
648 649 650
    /// We want use of immutable upvars to cause a "write to immutable upvar"
    /// error, not an "reassignment" error.
    ExceptUpvars,
651
    No
652 653
}

654 655 656 657 658 659
struct AccessErrorsReported {
    mutability_error: bool,
    #[allow(dead_code)]
    conflict_error: bool
}

660 661 662 663 664 665 666 667 668 669 670
#[derive(Copy, Clone)]
enum InitializationRequiringAction {
    Update,
    Borrow,
    Use,
    Assignment,
}

impl InitializationRequiringAction {
    fn as_noun(self) -> &'static str {
        match self {
N
Niko Matsakis 已提交
671 672 673 674
            InitializationRequiringAction::Update => "update",
            InitializationRequiringAction::Borrow => "borrow",
            InitializationRequiringAction::Use => "use",
            InitializationRequiringAction::Assignment => "assign",
675 676 677 678 679
        }
    }

    fn as_verb_in_past_tense(self) -> &'static str {
        match self {
N
Niko Matsakis 已提交
680 681 682 683
            InitializationRequiringAction::Update => "updated",
            InitializationRequiringAction::Borrow => "borrowed",
            InitializationRequiringAction::Use => "used",
            InitializationRequiringAction::Assignment => "assigned",
684 685 686 687
        }
    }
}

688
impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
689
    /// Checks an access to the given place to see if it is allowed. Examines the set of borrows
D
David Wood 已提交
690
    /// that are in scope, as well as which paths have been initialized, to ensure that (a) the
691
    /// place is initialized and (b) it is not borrowed in some way that would prevent this
D
David Wood 已提交
692 693 694
    /// access.
    ///
    /// Returns true if an error is reported, false otherwise.
N
Niko Matsakis 已提交
695 696 697 698 699 700
    fn access_place(
        &mut self,
        context: Context,
        place_span: (&Place<'tcx>, Span),
        kind: (ShallowOrDeep, ReadOrWrite),
        is_local_mutation_allowed: LocalMutationIsAllowed,
701
        flow_state: &Flows<'cx, 'gcx, 'tcx>,
702
    ) -> AccessErrorsReported {
703
        let (sd, rw) = kind;
704

705 706 707 708
        if let Activation(_, borrow_index) = rw {
            if self.reservation_error_reported.contains(&place_span.0) {
                debug!("skipping access_place for activation of invalid reservation \
                        place: {:?} borrow_index: {:?}", place_span.0, borrow_index);
709
                return AccessErrorsReported { mutability_error: false, conflict_error: true };
710 711 712
            }
        }

713
        let mutability_error =
N
Niko Matsakis 已提交
714
            self.check_access_permissions(place_span, rw, is_local_mutation_allowed);
715 716 717 718 719 720 721 722 723 724 725 726 727 728 729
        let conflict_error =
            self.check_access_for_conflict(context, place_span, sd, rw, flow_state);

        AccessErrorsReported { mutability_error, conflict_error }
    }

    fn check_access_for_conflict(
        &mut self,
        context: Context,
        place_span: (&Place<'tcx>, Span),
        sd: ShallowOrDeep,
        rw: ReadOrWrite,
        flow_state: &Flows<'cx, 'gcx, 'tcx>,
    ) -> bool {
        let mut error_reported = false;
730
        self.each_borrow_involving_path(
N
Niko Matsakis 已提交
731 732 733
            context,
            (sd, place_span.0),
            flow_state,
734
            |this, index, borrow| match (rw, borrow.kind) {
735 736 737 738 739 740 741 742 743 744 745 746 747
                // Obviously an activation is compatible with its own
                // reservation (or even prior activating uses of same
                // borrow); so don't check if they interfere.
                //
                // NOTE: *reservations* do conflict with themselves;
                // thus aren't injecting unsoundenss w/ this check.)
                (Activation(_, activating), _) if activating == index.borrow_index() =>
                {
                    debug!("check_access_for_conflict place_span: {:?} sd: {:?} rw: {:?} \
                            skipping {:?} b/c activation of same borrow_index: {:?}",
                           place_span, sd, rw, (index, borrow), index.borrow_index());
                    Control::Continue
                }
748

749 750 751 752 753
                (Read(_), BorrowKind::Shared) |
                (Reservation(..), BorrowKind::Shared) => Control::Continue,

                (Read(kind), BorrowKind::Unique) |
                (Read(kind), BorrowKind::Mut) => {
754 755 756 757 758 759 760
                    // Reading from mere reservations of mutable-borrows is OK.
                    if this.tcx.sess.opts.debugging_opts.two_phase_borrows &&
                        index.is_reservation()
                    {
                        return Control::Continue;
                    }

N
Niko Matsakis 已提交
761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
                    match kind {
                        ReadKind::Copy => {
                            error_reported = true;
                            this.report_use_while_mutably_borrowed(context, place_span, borrow)
                        }
                        ReadKind::Borrow(bk) => {
                            let end_issued_loan_span = flow_state
                                .borrows
                                .operator()
                                .opt_region_end_span(&borrow.region);
                            error_reported = true;
                            this.report_conflicting_borrow(
                                context,
                                place_span,
                                bk,
                                &borrow,
                                end_issued_loan_span,
                            )
779 780
                        }
                    }
N
Niko Matsakis 已提交
781 782
                    Control::Break
                }
783 784 785 786

                (Reservation(kind), BorrowKind::Unique) |
                (Reservation(kind), BorrowKind::Mut) |
                (Activation(kind, _), _) |
N
Niko Matsakis 已提交
787
                (Write(kind), _) => {
788 789 790 791 792 793 794 795 796 797 798 799 800 801

                    match rw {
                        Reservation(_) => {
                            debug!("recording invalid reservation of \
                                    place: {:?}", place_span.0);
                            this.reservation_error_reported.insert(place_span.0.clone());
                        }
                        Activation(_, activating) => {
                            debug!("observing check_place for activation of \
                                    borrow_index: {:?}", activating);
                        }
                        Read(..) | Write(..) => {}
                    }

N
Niko Matsakis 已提交
802 803 804 805 806 807
                    match kind {
                        WriteKind::MutableBorrow(bk) => {
                            let end_issued_loan_span = flow_state
                                .borrows
                                .operator()
                                .opt_region_end_span(&borrow.region);
808

N
Niko Matsakis 已提交
809 810 811 812 813 814 815 816 817 818 819 820
                            error_reported = true;
                            this.report_conflicting_borrow(
                                context,
                                place_span,
                                bk,
                                &borrow,
                                end_issued_loan_span,
                            )
                        }
                        WriteKind::StorageDeadOrDrop => {
                            error_reported = true;
                            this.report_borrowed_value_does_not_live_long_enough(
821 822
                                context, borrow, place_span.1,
                                flow_state.borrows.operator());
N
Niko Matsakis 已提交
823 824 825 826 827 828 829 830
                        }
                        WriteKind::Mutate => {
                            error_reported = true;
                            this.report_illegal_mutation_of_borrowed(context, place_span, borrow)
                        }
                        WriteKind::Move => {
                            error_reported = true;
                            this.report_move_out_while_borrowed(context, place_span, &borrow)
831 832
                        }
                    }
N
Niko Matsakis 已提交
833
                    Control::Break
834
                }
N
Niko Matsakis 已提交
835 836
            },
        );
837

838
        error_reported
839 840
    }

N
Niko Matsakis 已提交
841 842 843 844 845 846
    fn mutate_place(
        &mut self,
        context: Context,
        place_span: (&Place<'tcx>, Span),
        kind: ShallowOrDeep,
        mode: MutateMode,
847
        flow_state: &Flows<'cx, 'gcx, 'tcx>,
N
Niko Matsakis 已提交
848
    ) {
849 850 851
        // Write of P[i] or *P, or WriteAndRead of any P, requires P init'd.
        match mode {
            MutateMode::WriteAndRead => {
N
Niko Matsakis 已提交
852 853 854 855 856 857
                self.check_if_path_is_moved(
                    context,
                    InitializationRequiringAction::Update,
                    place_span,
                    flow_state,
                );
858 859
            }
            MutateMode::JustWrite => {
860
                self.check_if_assigned_path_is_moved(context, place_span, flow_state);
861 862 863
            }
        }

864
        let errors_reported = self.access_place(
N
Niko Matsakis 已提交
865 866 867
            context,
            place_span,
            (kind, Write(WriteKind::Mutate)),
868 869 870 871 872 873
            // We want immutable upvars to cause an "assignment to immutable var"
            // error, not an "reassignment of immutable var" error, because the
            // latter can't find a good previous assignment span.
            //
            // There's probably a better way to do this.
            LocalMutationIsAllowed::ExceptUpvars,
N
Niko Matsakis 已提交
874 875
            flow_state,
        );
876

877 878 879 880
        if !errors_reported.mutability_error {
            // check for reassignments to immutable local variables
            self.check_if_reassignment_to_immutable_state(context, place_span, flow_state);
        }
881 882
    }

N
Niko Matsakis 已提交
883 884 885 886 887
    fn consume_rvalue(
        &mut self,
        context: Context,
        (rvalue, span): (&Rvalue<'tcx>, Span),
        _location: Location,
888
        flow_state: &Flows<'cx, 'gcx, 'tcx>,
N
Niko Matsakis 已提交
889
    ) {
890
        match *rvalue {
N
Niko Matsakis 已提交
891
            Rvalue::Ref(_ /*rgn*/, bk, ref place) => {
892 893
                let access_kind = match bk {
                    BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
N
Niko Matsakis 已提交
894
                    BorrowKind::Unique | BorrowKind::Mut => {
895 896 897 898 899 900
                        let wk = WriteKind::MutableBorrow(bk);
                        if self.tcx.sess.opts.debugging_opts.two_phase_borrows {
                            (Deep, Reservation(wk))
                        } else {
                            (Deep, Write(wk))
                        }
N
Niko Matsakis 已提交
901
                    }
902
                };
903

N
Niko Matsakis 已提交
904 905 906 907 908 909 910
                self.access_place(
                    context,
                    (place, span),
                    access_kind,
                    LocalMutationIsAllowed::No,
                    flow_state,
                );
911

N
Niko Matsakis 已提交
912 913 914 915 916 917
                self.check_if_path_is_moved(
                    context,
                    InitializationRequiringAction::Borrow,
                    (place, span),
                    flow_state,
                );
918 919 920 921
            }

            Rvalue::Use(ref operand) |
            Rvalue::Repeat(ref operand, _) |
N
Niko Matsakis 已提交
922 923
            Rvalue::UnaryOp(_ /*un_op*/, ref operand) |
            Rvalue::Cast(_ /*cast_kind*/, ref operand, _ /*ty*/) => {
924
                self.consume_operand(context, (operand, span), flow_state)
925 926
            }

N
Niko Matsakis 已提交
927
            Rvalue::Len(ref place) | Rvalue::Discriminant(ref place) => {
928 929 930 931 932
                let af = match *rvalue {
                    Rvalue::Len(..) => ArtificialField::ArrayLength,
                    Rvalue::Discriminant(..) => ArtificialField::Discriminant,
                    _ => unreachable!(),
                };
N
Niko Matsakis 已提交
933 934 935 936 937 938 939 940 941 942 943 944 945
                self.access_place(
                    context,
                    (place, span),
                    (Shallow(Some(af)), Read(ReadKind::Copy)),
                    LocalMutationIsAllowed::No,
                    flow_state,
                );
                self.check_if_path_is_moved(
                    context,
                    InitializationRequiringAction::Use,
                    (place, span),
                    flow_state,
                );
946 947 948 949
            }

            Rvalue::BinaryOp(_bin_op, ref operand1, ref operand2) |
            Rvalue::CheckedBinaryOp(_bin_op, ref operand1, ref operand2) => {
950 951
                self.consume_operand(context, (operand1, span), flow_state);
                self.consume_operand(context, (operand2, span), flow_state);
952 953 954 955 956 957 958 959 960 961
            }

            Rvalue::NullaryOp(_op, _ty) => {
                // nullary ops take no dynamic input; no borrowck effect.
                //
                // FIXME: is above actually true? Do we want to track
                // the fact that uninitialized data can be created via
                // `NullOp::Box`?
            }

N
Niko Matsakis 已提交
962 963 964
            Rvalue::Aggregate(ref _aggregate_kind, ref operands) => for operand in operands {
                self.consume_operand(context, (operand, span), flow_state);
            },
965 966 967
        }
    }

N
Niko Matsakis 已提交
968 969 970 971
    fn consume_operand(
        &mut self,
        context: Context,
        (operand, span): (&Operand<'tcx>, Span),
972
        flow_state: &Flows<'cx, 'gcx, 'tcx>,
N
Niko Matsakis 已提交
973
    ) {
974
        match *operand {
975 976
            Operand::Copy(ref place) => {
                // copy of place: check if this is "copy of frozen path"
977
                // (FIXME: see check_loans.rs)
N
Niko Matsakis 已提交
978 979 980 981 982 983 984
                self.access_place(
                    context,
                    (place, span),
                    (Deep, Read(ReadKind::Copy)),
                    LocalMutationIsAllowed::No,
                    flow_state,
                );
985

986
                // Finally, check if path was already moved.
N
Niko Matsakis 已提交
987 988 989 990 991 992
                self.check_if_path_is_moved(
                    context,
                    InitializationRequiringAction::Use,
                    (place, span),
                    flow_state,
                );
993
            }
994 995
            Operand::Move(ref place) => {
                // move of place: check if this is move of already borrowed path
N
Niko Matsakis 已提交
996 997 998 999
                self.access_place(
                    context,
                    (place, span),
                    (Deep, Write(WriteKind::Move)),
1000
                    LocalMutationIsAllowed::Yes,
N
Niko Matsakis 已提交
1001 1002
                    flow_state,
                );
1003 1004

                // Finally, check if path was already moved.
N
Niko Matsakis 已提交
1005 1006 1007 1008 1009 1010
                self.check_if_path_is_moved(
                    context,
                    InitializationRequiringAction::Use,
                    (place, span),
                    flow_state,
                );
1011
            }
1012
            Operand::Constant(_) => {}
1013 1014
        }
    }
1015 1016 1017

    /// Returns whether a borrow of this place is invalidated when the function
    /// exits
1018 1019 1020 1021 1022 1023 1024
    fn check_for_invalidation_at_exit(&mut self,
                                      context: Context,
                                      borrow: &BorrowData<'tcx>,
                                      span: Span,
                                      flow_state: &Flows<'cx, 'gcx, 'tcx>)
    {
        debug!("check_for_invalidation_at_exit({:?})", borrow);
1025
        let place = &borrow.borrowed_place;
1026 1027 1028 1029 1030
        let root_place = self.prefixes(place, PrefixSet::All).last().unwrap();

        // FIXME(nll-rfc#40): do more precise destructor tracking here. For now
        // we just know that all locals are dropped at function exit (otherwise
        // we'll have a memory leak) and assume that all statics have a destructor.
1031
        //
A
Ariel Ben-Yehuda 已提交
1032
        // FIXME: allow thread-locals to borrow other thread locals?
1033
        let (might_be_alive, will_be_dropped) = match root_place {
1034 1035 1036
            Place::Static(statik) => {
                // Thread-locals might be dropped after the function exits, but
                // "true" statics will never be.
N
Niko Matsakis 已提交
1037 1038 1039 1040
                let is_thread_local = self.tcx
                    .get_attrs(statik.def_id)
                    .iter()
                    .any(|attr| attr.check_name("thread_local"));
1041

1042
                (true, is_thread_local)
1043
            }
1044
            Place::Local(_) => {
1045 1046
                // Locals are always dropped at function exit, and if they
                // have a destructor it would've been called already.
1047
                (false, self.locals_are_invalidated_at_exit)
1048
            }
N
Niko Matsakis 已提交
1049 1050 1051
            Place::Projection(..) => {
                bug!("root of {:?} is a projection ({:?})?", place, root_place)
            }
1052 1053 1054
        };

        if !will_be_dropped {
N
Niko Matsakis 已提交
1055 1056 1057 1058
            debug!(
                "place_is_invalidated_at_exit({:?}) - won't be dropped",
                place
            );
1059
            return;
1060 1061 1062 1063
        }

        // FIXME: replace this with a proper borrow_conflicts_with_place when
        // that is merged.
1064 1065
        let sd = if might_be_alive {
            Deep
1066
        } else {
1067
            Shallow(None)
1068 1069
        };

1070 1071 1072 1073 1074 1075 1076 1077 1078 1079
        if self.places_conflict(place, root_place, sd) {
            debug!("check_for_invalidation_at_exit({:?}): INVALID", place);
            // FIXME: should be talking about the region lifetime instead
            // of just a span here.
            self.report_borrowed_value_does_not_live_long_enough(
                context,
                borrow,
                span.end_point(),
                flow_state.borrows.operator()
            )
1080
        }
1081
    }
1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097

    fn check_activations(&mut self,
                         location: Location,
                         span: Span,
                         flow_state: &Flows<'cx, 'gcx, 'tcx>)
    {
        if !self.tcx.sess.opts.debugging_opts.two_phase_borrows {
            return;
        }

        // Two-phase borrow support: For each activation that is newly
        // generated at this statement, check if it interferes with
        // another borrow.
        let domain = flow_state.borrows.operator();
        let data = domain.borrows();
        flow_state.borrows.each_gen_bit(|gen| {
1098
            if gen.is_activation()
1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123
            {
                let borrow_index = gen.borrow_index();
                let borrow = &data[borrow_index];
                // currently the flow analysis registers
                // activations for both mutable and immutable
                // borrows. So make sure we are talking about a
                // mutable borrow before we check it.
                match borrow.kind {
                    BorrowKind::Shared => return,
                    BorrowKind::Unique |
                    BorrowKind::Mut => {}
                }

                self.access_place(ContextKind::Activation.new(location),
                                  (&borrow.borrowed_place, span),
                                  (Deep, Activation(WriteKind::MutableBorrow(borrow.kind),
                                                    borrow_index)),
                                  LocalMutationIsAllowed::No,
                                  flow_state);
                // We do not need to call `check_if_path_is_moved`
                // again, as we already called it when we made the
                // initial reservation.
            }
        });
    }
1124 1125
}

1126
impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
N
Niko Matsakis 已提交
1127 1128 1129 1130
    fn check_if_reassignment_to_immutable_state(
        &mut self,
        context: Context,
        (place, span): (&Place<'tcx>, Span),
1131
        flow_state: &Flows<'cx, 'gcx, 'tcx>,
N
Niko Matsakis 已提交
1132
    ) {
1133
        debug!("check_if_reassignment_to_immutable_state({:?})", place);
1134
        // determine if this path has a non-mut owner (and thus needs checking).
1135
        if let Ok(()) = self.is_mutable(place, LocalMutationIsAllowed::No) {
1136
            return;
1137
        }
1138 1139 1140 1141 1142 1143 1144 1145
        debug!("check_if_reassignment_to_immutable_state({:?}) - is an imm local", place);

        for i in flow_state.ever_inits.elems_incoming() {
            let init = self.move_data.inits[i];
            let init_place = &self.move_data.move_paths[init.path].place;
            if self.places_conflict(&init_place, place, Deep) {
                self.report_illegal_reassignment(context, (place, span), init.span);
                break;
N
Niko Matsakis 已提交
1146
            }
1147 1148 1149
        }
    }

N
Niko Matsakis 已提交
1150 1151 1152 1153 1154
    fn check_if_path_is_moved(
        &mut self,
        context: Context,
        desired_action: InitializationRequiringAction,
        place_span: (&Place<'tcx>, Span),
1155
        flow_state: &Flows<'cx, 'gcx, 'tcx>,
N
Niko Matsakis 已提交
1156
    ) {
1157
        // FIXME: analogous code in check_loans first maps `place` to
1158
        // its base_path ... but is that what we want here?
1159
        let place = self.base_path(place_span.0);
1160 1161

        let maybe_uninits = &flow_state.uninits;
1162
        let curr_move_outs = &flow_state.move_outs;
1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195

        // Bad scenarios:
        //
        // 1. Move of `a.b.c`, use of `a.b.c`
        // 2. Move of `a.b.c`, use of `a.b.c.d` (without first reinitializing `a.b.c.d`)
        // 3. Move of `a.b.c`, use of `a` or `a.b`
        // 4. Uninitialized `(a.b.c: &_)`, use of `*a.b.c`; note that with
        //    partial initialization support, one might have `a.x`
        //    initialized but not `a.b`.
        //
        // OK scenarios:
        //
        // 5. Move of `a.b.c`, use of `a.b.d`
        // 6. Uninitialized `a.x`, initialized `a.b`, use of `a.b`
        // 7. Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b`
        //    must have been initialized for the use to be sound.
        // 8. Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d`

        // The dataflow tracks shallow prefixes distinctly (that is,
        // field-accesses on P distinctly from P itself), in order to
        // track substructure initialization separately from the whole
        // structure.
        //
        // E.g., when looking at (*a.b.c).d, if the closest prefix for
        // which we have a MovePath is `a.b`, then that means that the
        // initialization state of `a.b` is all we need to inspect to
        // know if `a.b.c` is valid (and from that we infer that the
        // dereference and `.d` access is also valid, since we assume
        // `a.b.c` is assigned a reference to a initialized and
        // well-formed record structure.)

        // Therefore, if we seek out the *closest* prefix for which we
        // have a MovePath, that should capture the initialization
1196
        // state for the place scenario.
1197 1198 1199
        //
        // This code covers scenarios 1, 2, and 4.

1200 1201
        debug!("check_if_path_is_moved part1 place: {:?}", place);
        match self.move_path_closest_to(place) {
1202
            Ok(mpi) => {
1203
                if maybe_uninits.contains(&mpi) {
N
Niko Matsakis 已提交
1204 1205 1206 1207 1208 1209 1210
                    self.report_use_of_moved_or_uninitialized(
                        context,
                        desired_action,
                        place_span,
                        mpi,
                        curr_move_outs,
                    );
1211 1212 1213 1214 1215
                    return; // don't bother finding other problems.
                }
            }
            Err(NoMovePathFound::ReachedStatic) => {
                // Okay: we do not build MoveData for static variables
N
Niko Matsakis 已提交
1216 1217 1218 1219 1220 1221
            } // Only query longest prefix with a MovePath, not further
              // ancestors; dataflow recurs on children when parents
              // move (to support partial (re)inits).
              //
              // (I.e. querying parents breaks scenario 8; but may want
              // to do such a query based on partial-init feature-gate.)
1222 1223
        }

1224 1225
        // A move of any shallow suffix of `place` also interferes
        // with an attempt to use `place`. This is scenario 3 above.
1226 1227
        //
        // (Distinct from handling of scenarios 1+2+4 above because
1228
        // `place` does not interfere with suffixes of its prefixes,
1229 1230
        // e.g. `a.b.c` does not interfere with `a.b.d`)

1231 1232
        debug!("check_if_path_is_moved part2 place: {:?}", place);
        if let Some(mpi) = self.move_path_for_place(place) {
1233
            if let Some(child_mpi) = maybe_uninits.has_any_child_of(mpi) {
N
Niko Matsakis 已提交
1234 1235 1236 1237 1238 1239 1240
                self.report_use_of_moved_or_uninitialized(
                    context,
                    desired_action,
                    place_span,
                    child_mpi,
                    curr_move_outs,
                );
1241
                return; // don't bother finding other problems.
1242 1243 1244 1245
            }
        }
    }

1246
    /// Currently MoveData does not store entries for all places in
1247
    /// the input MIR. For example it will currently filter out
1248 1249 1250
    /// places that are Copy; thus we do not track places of shared
    /// reference type. This routine will walk up a place along its
    /// prefixes, searching for a foundational place that *is*
1251 1252 1253
    /// tracked in the MoveData.
    ///
    /// An Err result includes a tag indicated why the search failed.
1254
    /// Currenly this can only occur if the place is built off of a
1255
    /// static variable, as we do not track those in the MoveData.
N
Niko Matsakis 已提交
1256 1257 1258 1259
    fn move_path_closest_to(
        &mut self,
        place: &Place<'tcx>,
    ) -> Result<MovePathIndex, NoMovePathFound> {
1260 1261 1262
        let mut last_prefix = place;
        for prefix in self.prefixes(place, PrefixSet::All) {
            if let Some(mpi) = self.move_path_for_place(prefix) {
1263 1264 1265 1266 1267
                return Ok(mpi);
            }
            last_prefix = prefix;
        }
        match *last_prefix {
1268 1269 1270
            Place::Local(_) => panic!("should have move path for every Local"),
            Place::Projection(_) => panic!("PrefixSet::All meant dont stop for Projection"),
            Place::Static(_) => return Err(NoMovePathFound::ReachedStatic),
1271 1272 1273
        }
    }

N
Niko Matsakis 已提交
1274
    fn move_path_for_place(&mut self, place: &Place<'tcx>) -> Option<MovePathIndex> {
1275
        // If returns None, then there is no move path corresponding
1276
        // to a direct owner of `place` (which means there is nothing
1277 1278
        // that borrowck tracks for its analysis).

1279
        match self.move_data.rev_lookup.find(place) {
1280 1281 1282 1283 1284
            LookupResult::Parent(_) => None,
            LookupResult::Exact(mpi) => Some(mpi),
        }
    }

N
Niko Matsakis 已提交
1285 1286 1287 1288
    fn check_if_assigned_path_is_moved(
        &mut self,
        context: Context,
        (place, span): (&Place<'tcx>, Span),
1289
        flow_state: &Flows<'cx, 'gcx, 'tcx>,
N
Niko Matsakis 已提交
1290
    ) {
1291 1292
        // recur down place; dispatch to check_if_path_is_moved when necessary
        let mut place = place;
1293
        loop {
1294
            match *place {
1295
                Place::Local(_) | Place::Static(_) => {
1296 1297 1298
                    // assigning to `x` does not require `x` be initialized.
                    break;
                }
1299
                Place::Projection(ref proj) => {
1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329
                    let Projection { ref base, ref elem } = **proj;
                    match *elem {
                        ProjectionElem::Deref |
                        // assigning to *P requires `P` initialized.
                        ProjectionElem::Index(_/*operand*/) |
                        ProjectionElem::ConstantIndex { .. } |
                        // assigning to P[i] requires `P` initialized.
                        ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) =>
                        // assigning to (P->variant) is okay if assigning to `P` is okay
                        //
                        // FIXME: is this true even if P is a adt with a dtor?
                        { }

                        ProjectionElem::Subslice { .. } => {
                            panic!("we dont allow assignments to subslices, context: {:?}",
                                   context);
                        }

                        ProjectionElem::Field(..) => {
                            // if type of `P` has a dtor, then
                            // assigning to `P.f` requires `P` itself
                            // be already initialized
                            let tcx = self.tcx;
                            match base.ty(self.mir, tcx).to_ty(tcx).sty {
                                ty::TyAdt(def, _) if def.has_dtor(tcx) => {

                                    // FIXME: analogous code in
                                    // check_loans.rs first maps
                                    // `base` to its base_path.

1330
                                    self.check_if_path_is_moved(
1331 1332
                                        context, InitializationRequiringAction::Assignment,
                                        (base, span), flow_state);
1333 1334 1335 1336 1337 1338 1339 1340 1341 1342

                                    // (base initialized; no need to
                                    // recur further)
                                    break;
                                }
                                _ => {}
                            }
                        }
                    }

1343
                    place = base;
1344 1345 1346 1347 1348
                    continue;
                }
            }
        }
    }
1349

1350
    /// Check the permissions for the given place and read or write kind
1351 1352
    ///
    /// Returns true if an error is reported, false otherwise.
N
Niko Matsakis 已提交
1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364
    fn check_access_permissions(
        &self,
        (place, span): (&Place<'tcx>, Span),
        kind: ReadOrWrite,
        is_local_mutation_allowed: LocalMutationIsAllowed,
    ) -> bool {
        debug!(
            "check_access_permissions({:?}, {:?}, {:?})",
            place,
            kind,
            is_local_mutation_allowed
        );
1365
        let mut error_reported = false;
1366
        match kind {
1367
            Reservation(WriteKind::MutableBorrow(BorrowKind::Unique)) |
1368
            Write(WriteKind::MutableBorrow(BorrowKind::Unique)) => {
A
Ariel Ben-Yehuda 已提交
1369
                if let Err(_place_err) = self.is_mutable(place, LocalMutationIsAllowed::Yes) {
1370
                    span_bug!(span, "&unique borrow for {:?} should not fail", place);
1371
                }
N
Niko Matsakis 已提交
1372
            }
1373
            Reservation(WriteKind::MutableBorrow(BorrowKind::Mut)) |
N
Niko Matsakis 已提交
1374 1375 1376 1377
            Write(WriteKind::MutableBorrow(BorrowKind::Mut)) => if let Err(place_err) =
                self.is_mutable(place, is_local_mutation_allowed)
            {
                error_reported = true;
1378

N
Niko Matsakis 已提交
1379 1380 1381 1382
                let item_msg = match self.describe_place(place) {
                    Some(name) => format!("immutable item `{}`", name),
                    None => "immutable item".to_owned(),
                };
1383

N
Niko Matsakis 已提交
1384 1385 1386
                let mut err = self.tcx
                    .cannot_borrow_path_as_mutable(span, &item_msg, Origin::Mir);
                err.span_label(span, "cannot borrow as mutable");
1387

N
Niko Matsakis 已提交
1388 1389 1390
                if place != place_err {
                    if let Some(name) = self.describe_place(place_err) {
                        err.note(&format!("Value not mutable causing this error: `{}`", name));
1391 1392
                    }
                }
N
Niko Matsakis 已提交
1393 1394

                err.emit();
1395
            },
1396
            Reservation(WriteKind::Mutate) |
1397
            Write(WriteKind::Mutate) => {
1398
                if let Err(place_err) = self.is_mutable(place, is_local_mutation_allowed) {
1399 1400
                    error_reported = true;

1401
                    let item_msg = match self.describe_place(place) {
1402
                        Some(name) => format!("immutable item `{}`", name),
N
Niko Matsakis 已提交
1403
                        None => "immutable item".to_owned(),
1404 1405
                    };

N
Niko Matsakis 已提交
1406
                    let mut err = self.tcx.cannot_assign(span, &item_msg, Origin::Mir);
1407 1408
                    err.span_label(span, "cannot mutate");

1409 1410
                    if place != place_err {
                        if let Some(name) = self.describe_place(place_err) {
1411 1412
                            err.note(&format!("Value not mutable causing this error: `{}`", name));
                        }
1413 1414 1415 1416
                    }

                    err.emit();
                }
N
Niko Matsakis 已提交
1417
            }
1418 1419 1420
            Reservation(WriteKind::Move) |
            Reservation(WriteKind::StorageDeadOrDrop) |
            Reservation(WriteKind::MutableBorrow(BorrowKind::Shared)) |
1421 1422 1423
            Write(WriteKind::Move) |
            Write(WriteKind::StorageDeadOrDrop) |
            Write(WriteKind::MutableBorrow(BorrowKind::Shared)) => {
1424
                if let Err(_place_err) = self.is_mutable(place, is_local_mutation_allowed) {
N
Niko Matsakis 已提交
1425 1426 1427 1428
                    self.tcx.sess.delay_span_bug(
                        span,
                        &format!(
                            "Accessing `{:?}` with the kind `{:?}` shouldn't be possible",
1429
                            place,
N
Niko Matsakis 已提交
1430 1431 1432
                            kind
                        ),
                    );
1433
                }
N
Niko Matsakis 已提交
1434
            }
1435 1436 1437

            Activation(..) => {} // permission checks are done at Reservation point.

1438 1439 1440 1441
            Read(ReadKind::Borrow(BorrowKind::Unique)) |
            Read(ReadKind::Borrow(BorrowKind::Mut)) |
            Read(ReadKind::Borrow(BorrowKind::Shared)) |
            Read(ReadKind::Copy) => {} // Access authorized
1442
        }
1443 1444

        error_reported
1445 1446 1447
    }

    /// Can this value be written or borrowed mutably
N
Niko Matsakis 已提交
1448 1449 1450 1451 1452
    fn is_mutable<'d>(
        &self,
        place: &'d Place<'tcx>,
        is_local_mutation_allowed: LocalMutationIsAllowed,
    ) -> Result<(), &'d Place<'tcx>> {
1453
        match *place {
1454
            Place::Local(local) => {
1455 1456
                let local = &self.mir.local_decls[local];
                match local.mutability {
N
Niko Matsakis 已提交
1457
                    Mutability::Not => match is_local_mutation_allowed {
1458
                        LocalMutationIsAllowed::Yes |
1459
                        LocalMutationIsAllowed::ExceptUpvars => Ok(()),
N
Niko Matsakis 已提交
1460 1461 1462
                        LocalMutationIsAllowed::No => Err(place),
                    },
                    Mutability::Mut => Ok(()),
1463
                }
N
Niko Matsakis 已提交
1464 1465 1466 1467 1468
            }
            Place::Static(ref static_) => if !self.tcx.is_static_mut(static_.def_id) {
                Err(place)
            } else {
                Ok(())
1469
            },
1470
            Place::Projection(ref proj) => {
1471 1472 1473 1474
                match proj.elem {
                    ProjectionElem::Deref => {
                        let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);

1475
                        // Check the kind of deref to decide
1476 1477 1478 1479
                        match base_ty.sty {
                            ty::TyRef(_, tnm) => {
                                match tnm.mutbl {
                                    // Shared borrowed data is never mutable
1480
                                    hir::MutImmutable => Err(place),
1481 1482
                                    // Mutably borrowed data is mutable, but only if we have a
                                    // unique path to the `&mut`
1483
                                    hir::MutMutable => {
1484 1485 1486
                                        let mode = match
                                            self.is_upvar_field_projection(&proj.base)
                                        {
1487 1488
                                            Some(field) if {
                                                self.mir.upvar_decls[field.index()].by_ref
A
Ariel Ben-Yehuda 已提交
1489 1490 1491 1492 1493
                                            } => is_local_mutation_allowed,
                                            _ => LocalMutationIsAllowed::Yes
                                        };

                                        self.is_mutable(&proj.base, mode)
N
Niko Matsakis 已提交
1494
                                    }
1495
                                }
N
Niko Matsakis 已提交
1496
                            }
1497 1498 1499
                            ty::TyRawPtr(tnm) => {
                                match tnm.mutbl {
                                    // `*const` raw pointers are not mutable
A
Ariel Ben-Yehuda 已提交
1500
                                    hir::MutImmutable => return Err(place),
1501 1502
                                    // `*mut` raw pointers are always mutable, regardless of context
                                    // The users have to check by themselve.
A
Ariel Ben-Yehuda 已提交
1503
                                    hir::MutMutable => return Ok(()),
1504
                                }
N
Niko Matsakis 已提交
1505
                            }
1506
                            // `Box<T>` owns its content, so mutable if its location is mutable
N
Niko Matsakis 已提交
1507
                            _ if base_ty.is_box() => {
1508
                                self.is_mutable(&proj.base, is_local_mutation_allowed)
N
Niko Matsakis 已提交
1509
                            }
1510
                            // Deref should only be for reference, pointers or boxes
1511
                            _ => bug!("Deref of unexpected type: {:?}", base_ty),
1512
                        }
N
Niko Matsakis 已提交
1513
                    }
1514 1515 1516 1517
                    // All other projections are owned by their base path, so mutable if
                    // base path is mutable
                    ProjectionElem::Field(..) |
                    ProjectionElem::Index(..) |
N
Niko Matsakis 已提交
1518 1519
                    ProjectionElem::ConstantIndex { .. } |
                    ProjectionElem::Subslice { .. } |
1520
                    ProjectionElem::Downcast(..) => {
A
Ariel Ben-Yehuda 已提交
1521
                        if let Some(field) = self.is_upvar_field_projection(place) {
1522
                            let decl = &self.mir.upvar_decls[field.index()];
1523 1524
                            debug!("decl.mutability={:?} local_mutation_is_allowed={:?} place={:?}",
                                   decl, is_local_mutation_allowed, place);
A
Ariel Ben-Yehuda 已提交
1525
                            match (decl.mutability, is_local_mutation_allowed) {
1526
                                (Mutability::Not, LocalMutationIsAllowed::No) |
1527 1528 1529
                                (Mutability::Not, LocalMutationIsAllowed::ExceptUpvars)
                                    => Err(place),
                                (Mutability::Not, LocalMutationIsAllowed::Yes) |
A
Ariel Ben-Yehuda 已提交
1530 1531
                                (Mutability::Mut, _) =>
                                    self.is_mutable(&proj.base, is_local_mutation_allowed)
N
Niko Matsakis 已提交
1532
                            }
A
Ariel Ben-Yehuda 已提交
1533 1534
                        } else {
                            self.is_mutable(&proj.base, is_local_mutation_allowed)
1535
                        }
N
Niko Matsakis 已提交
1536
                    }
1537 1538 1539 1540
                }
            }
        }
    }
1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566


    /// If this is a field projection, and the field is being projected from a closure type,
    /// then returns the index of the field being projected. Note that this closure will always
    /// be `self` in the current MIR, because that is the only time we directly access the fields
    /// of a closure type.
    fn is_upvar_field_projection(&self, place: &Place<'tcx>) -> Option<Field> {
        match *place {
            Place::Projection(ref proj) => match proj.elem {
                ProjectionElem::Field(field, _ty) => {
                    let is_projection_from_ty_closure = proj.base
                        .ty(self.mir, self.tcx)
                        .to_ty(self.tcx)
                        .is_closure();

                    if is_projection_from_ty_closure {
                        Some(field)
                    } else {
                        None
                    }
                }
                _ => None,
            },
            _ => None,
        }
    }
F
Felix S. Klock II 已提交
1567
}
1568

1569 1570 1571 1572 1573
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum NoMovePathFound {
    ReachedStatic,
}

1574 1575 1576 1577 1578 1579 1580 1581
/// The degree of overlap between 2 places for borrow-checking.
enum Overlap {
    /// The places might partially overlap - in this case, we give
    /// up and say that they might conflict. This occurs when
    /// different fields of a union are borrowed. For example,
    /// if `u` is a union, we have no way of telling how disjoint
    /// `u.a.x` and `a.b.y` are.
    Arbitrary,
A
Ariel Ben-Yehuda 已提交
1582 1583 1584 1585
    /// The places have the same type, and are either completely disjoint
    /// or equal - i.e. they can't "partially" overlap as can occur with
    /// unions. This is the "base case" on which we recur for extensions
    /// of the place.
1586 1587 1588 1589 1590 1591
    EqualOrDisjoint,
    /// The places are disjoint, so we know all extensions of them
    /// will also be disjoint.
    Disjoint,
}

1592
impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612
    // Given that the bases of `elem1` and `elem2` are always either equal
    // or disjoint (and have the same type!), return the overlap situation
    // between `elem1` and `elem2`.
    fn place_element_conflict(&self,
                               elem1: &Place<'tcx>,
                               elem2: &Place<'tcx>)
                               -> Overlap
    {
        match (elem1, elem2) {
            (Place::Local(l1), Place::Local(l2)) => {
                if l1 == l2 {
                    // the same local - base case, equal
                    debug!("place_element_conflict: DISJOINT-OR-EQ-LOCAL");
                    Overlap::EqualOrDisjoint
                } else {
                    // different locals - base case, disjoint
                    debug!("place_element_conflict: DISJOINT-LOCAL");
                    Overlap::Disjoint
                }
            }
1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624
            (Place::Static(static1), Place::Static(static2)) => {
                if static1.def_id != static2.def_id {
                    debug!("place_element_conflict: DISJOINT-STATIC");
                    Overlap::Disjoint
                } else if self.tcx.is_static_mut(static1.def_id) {
                    // We ignore mutable statics - they can only be unsafe code.
                    debug!("place_element_conflict: IGNORE-STATIC-MUT");
                    Overlap::Disjoint
                } else {
                    debug!("place_element_conflict: DISJOINT-OR-EQ-STATIC");
                    Overlap::EqualOrDisjoint
                }
1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735
            }
            (Place::Local(_), Place::Static(_)) |
            (Place::Static(_), Place::Local(_)) => {
                debug!("place_element_conflict: DISJOINT-STATIC-LOCAL");
                Overlap::Disjoint
            }
            (Place::Projection(pi1), Place::Projection(pi2)) => {
                match (&pi1.elem, &pi2.elem) {
                    (ProjectionElem::Deref, ProjectionElem::Deref) => {
                        // derefs (e.g. `*x` vs. `*x`) - recur.
                        debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF");
                        Overlap::EqualOrDisjoint
                    }
                    (ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => {
                        if f1 == f2 {
                            // same field (e.g. `a.y` vs. `a.y`) - recur.
                            debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
                            Overlap::EqualOrDisjoint
                        } else {
                            let ty = pi1.base.ty(self.mir, self.tcx).to_ty(self.tcx);
                            match ty.sty {
                                ty::TyAdt(def, _) if def.is_union() => {
                                    // Different fields of a union, we are basically stuck.
                                    debug!("place_element_conflict: STUCK-UNION");
                                    Overlap::Arbitrary
                                }
                                _ => {
                                    // Different fields of a struct (`a.x` vs. `a.y`). Disjoint!
                                    debug!("place_element_conflict: DISJOINT-FIELD");
                                    Overlap::Disjoint
                                }
                            }
                        }
                    }
                    (ProjectionElem::Downcast(_, v1), ProjectionElem::Downcast(_, v2)) => {
                        // different variants are treated as having disjoint fields,
                        // even if they occupy the same "space", because it's
                        // impossible for 2 variants of the same enum to exist
                        // (and therefore, to be borrowed) at the same time.
                        //
                        // Note that this is different from unions - we *do* allow
                        // this code to compile:
                        //
                        // ```
                        // fn foo(x: &mut Result<i32, i32>) {
                        //     let mut v = None;
                        //     if let Ok(ref mut a) = *x {
                        //         v = Some(a);
                        //     }
                        //     // here, you would *think* that the
                        //     // *entirety* of `x` would be borrowed,
                        //     // but in fact only the `Ok` variant is,
                        //     // so the `Err` variant is *entirely free*:
                        //     if let Err(ref mut a) = *x {
                        //         v = Some(a);
                        //     }
                        //     drop(v);
                        // }
                        // ```
                        if v1 == v2 {
                            debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
                            Overlap::EqualOrDisjoint
                        } else {
                            debug!("place_element_conflict: DISJOINT-FIELD");
                            Overlap::Disjoint
                        }
                    }
                    (ProjectionElem::Index(..), ProjectionElem::Index(..)) |
                    (ProjectionElem::Index(..), ProjectionElem::ConstantIndex { .. }) |
                    (ProjectionElem::Index(..), ProjectionElem::Subslice { .. }) |
                    (ProjectionElem::ConstantIndex { .. }, ProjectionElem::Index(..)) |
                    (ProjectionElem::ConstantIndex { .. }, ProjectionElem::ConstantIndex { .. }) |
                    (ProjectionElem::ConstantIndex { .. }, ProjectionElem::Subslice { .. }) |
                    (ProjectionElem::Subslice { .. }, ProjectionElem::Index(..)) |
                    (ProjectionElem::Subslice { .. }, ProjectionElem::ConstantIndex { .. }) |
                    (ProjectionElem::Subslice { .. }, ProjectionElem::Subslice { .. }) => {
                        // Array indexes (`a[0]` vs. `a[i]`). These can either be disjoint
                        // (if the indexes differ) or equal (if they are the same), so this
                        // is the recursive case that gives "equal *or* disjoint" its meaning.
                        //
                        // Note that by construction, MIR at borrowck can't subdivide
                        // `Subslice` accesses (e.g. `a[2..3][i]` will never be present) - they
                        // are only present in slice patterns, and we "merge together" nested
                        // slice patterns. That means we don't have to think about these. It's
                        // probably a good idea to assert this somewhere, but I'm too lazy.
                        //
                        // FIXME(#8636) we might want to return Disjoint if
                        // both projections are constant and disjoint.
                        debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY");
                        Overlap::EqualOrDisjoint
                    }

                    (ProjectionElem::Deref, _) |
                    (ProjectionElem::Field(..), _) |
                    (ProjectionElem::Index(..), _) |
                    (ProjectionElem::ConstantIndex { .. }, _) |
                    (ProjectionElem::Subslice { .. }, _) |
                    (ProjectionElem::Downcast(..), _) => {
                        bug!("mismatched projections in place_element_conflict: {:?} and {:?}",

                             elem1, elem2)
                    }
                }
            }
            (Place::Projection(_), _) |
            (_, Place::Projection(_)) => {
                bug!("unexpected elements in place_element_conflict: {:?} and {:?}",
                     elem1, elem2)
            }
        }
    }
1736 1737 1738 1739 1740 1741 1742 1743 1744

    /// Returns whether an access of kind `access` to `access_place` conflicts with
    /// a borrow/full access to `borrow_place` (for deep accesses to mutable
    /// locations, this function is symmetric between `borrow_place` & `access_place`).
    fn places_conflict(&mut self,
                       borrow_place: &Place<'tcx>,
                       access_place: &Place<'tcx>,
                       access: ShallowOrDeep)
                       -> bool
1745
    {
1746
        debug!("places_conflict({:?},{:?},{:?})", borrow_place, access_place, access);
1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759

        // Return all the prefixes of `place` in reverse order, including
        // downcasts.
        fn place_elements<'a, 'tcx>(place: &'a Place<'tcx>) -> Vec<&'a Place<'tcx>>
        {
            let mut result = vec![];
            let mut place = place;
            loop {
                result.push(place);
                match place {
                    Place::Projection(interior) => {
                        place = &interior.base;
                    }
A
Ariel Ben-Yehuda 已提交
1760
                    Place::Local(_) | Place::Static(_) => {
1761 1762 1763 1764 1765 1766 1767
                        result.reverse();
                        return result;
                    }
                }
            }
        }

1768 1769 1770
        let borrow_components = place_elements(borrow_place);
        let access_components = place_elements(access_place);
        debug!("places_conflict: components {:?} / {:?}",
1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819
               borrow_components, access_components);

        let borrow_components = borrow_components.into_iter()
             .map(Some).chain(iter::repeat(None));
        let access_components = access_components.into_iter()
             .map(Some).chain(iter::repeat(None));
        // The borrowck rules for proving disjointness are applied from the "root" of the
        // borrow forwards, iterating over "similar" projections in lockstep until
        // we can prove overlap one way or another. Essentially, we treat `Overlap` as
        // a monoid and report a conflict if the product ends up not being `Disjoint`.
        //
        // At each step, if we didn't run out of borrow or place, we know that our elements
        // have the same type, and that they only overlap if they are the identical.
        //
        // For example, if we are comparing these:
        // BORROW:  (*x1[2].y).z.a
        // ACCESS:  (*x1[i].y).w.b
        //
        // Then our steps are:
        //       x1         |   x1          -- places are the same
        //       x1[2]      |   x1[i]       -- equal or disjoint (disjoint if indexes differ)
        //       x1[2].y    |   x1[i].y     -- equal or disjoint
        //      *x1[2].y    |  *x1[i].y     -- equal or disjoint
        //     (*x1[2].y).z | (*x1[i].y).w  -- we are disjoint and don't need to check more!
        //
        // Because `zip` does potentially bad things to the iterator inside, this loop
        // also handles the case where the access might be a *prefix* of the borrow, e.g.
        //
        // BORROW:  (*x1[2].y).z.a
        // ACCESS:  x1[i].y
        //
        // Then our steps are:
        //       x1         |   x1          -- places are the same
        //       x1[2]      |   x1[i]       -- equal or disjoint (disjoint if indexes differ)
        //       x1[2].y    |   x1[i].y     -- equal or disjoint
        //
        // -- here we run out of access - the borrow can access a part of it. If this
        // is a full deep access, then we *know* the borrow conflicts with it. However,
        // if the access is shallow, then we can proceed:
        //
        //       x1[2].y    | (*x1[i].y)    -- a deref! the access can't get past this, so we
        //                                     are disjoint
        //
        // Our invariant is, that at each step of the iteration:
        //  - If we didn't run out of access to match, our borrow and access are comparable
        //    and either equal or disjoint.
        //  - If we did run out of accesss, the borrow can access a part of it.
        for (borrow_c, access_c) in borrow_components.zip(access_components) {
            // loop invariant: borrow_c is always either equal to access_c or disjoint from it.
1820
            debug!("places_conflict: {:?} vs. {:?}", borrow_c, access_c);
1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832
            match (borrow_c, access_c) {
                (None, _) => {
                    // If we didn't run out of access, the borrow can access all of our
                    // place (e.g. a borrow of `a.b` with an access to `a.b.c`),
                    // so we have a conflict.
                    //
                    // If we did, then we still know that the borrow can access a *part*
                    // of our place that our access cares about (a borrow of `a.b.c`
                    // with an access to `a.b`), so we still have a conflict.
                    //
                    // FIXME: Differs from AST-borrowck; includes drive-by fix
                    // to #38899. Will probably need back-compat mode flag.
1833
                    debug!("places_conflict: full borrow, CONFLICT");
1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857
                    return true;
                }
                (Some(borrow_c), None) => {
                    // We know that the borrow can access a part of our place. This
                    // is a conflict if that is a part our access cares about.

                    let (base, elem) = match borrow_c {
                        Place::Projection(box Projection { base, elem }) => (base, elem),
                        _ => bug!("place has no base?")
                    };
                    let base_ty = base.ty(self.mir, self.tcx).to_ty(self.tcx);

                    match (elem, &base_ty.sty, access) {
                        (_, _, Shallow(Some(ArtificialField::Discriminant))) |
                        (_, _, Shallow(Some(ArtificialField::ArrayLength))) => {
                            // The discriminant and array length are like
                            // additional fields on the type; they do not
                            // overlap any existing data there. Furthermore,
                            // they cannot actually be a prefix of any
                            // borrowed place (at least in MIR as it is
                            // currently.)
                            //
                            // e.g. a (mutable) borrow of `a[5]` while we read the
                            // array length of `a`.
1858
                            debug!("places_conflict: implicit field");
1859 1860 1861 1862 1863 1864 1865
                            return false;
                        }

                        (ProjectionElem::Deref, _, Shallow(None)) => {
                            // e.g. a borrow of `*x.y` while we shallowly access `x.y` or some
                            // prefix thereof - the shallow access can't touch anything behind
                            // the pointer.
1866
                            debug!("places_conflict: shallow access behind ptr");
1867 1868 1869 1870 1871 1872 1873 1874 1875 1876
                            return false;
                        }
                        (ProjectionElem::Deref, ty::TyRef(_, ty::TypeAndMut {
                            ty: _, mutbl: hir::MutImmutable
                        }), _) => {
                            // the borrow goes through a dereference of a shared reference.
                            //
                            // I'm not sure why we are tracking these borrows - shared
                            // references can *always* be aliased, which means the
                            // permission check already account for this borrow.
1877
                            debug!("places_conflict: behind a shared ref");
1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909
                            return false;
                        }

                        (ProjectionElem::Deref, _, Deep) |
                        (ProjectionElem::Field { .. }, _, _) |
                        (ProjectionElem::Index { ..}, _, _) |
                        (ProjectionElem::ConstantIndex { .. }, _, _) |
                        (ProjectionElem::Subslice { .. }, _, _) |
                        (ProjectionElem::Downcast { .. }, _, _) => {
                            // Recursive case. This can still be disjoint on a
                            // further iteration if this a shallow access and
                            // there's a deref later on, e.g. a borrow
                            // of `*x.y` while accessing `x`.
                        }
                    }
                }
                (Some(borrow_c), Some(access_c)) => {
                    match self.place_element_conflict(&borrow_c, access_c) {
                        Overlap::Arbitrary => {
                            // We have encountered different fields of potentially
                            // the same union - the borrow now partially overlaps.
                            //
                            // There is no *easy* way of comparing the fields
                            // further on, because they might have different types
                            // (e.g. borrows of `u.a.0` and `u.b.y` where `.0` and
                            // `.y` come from different structs).
                            //
                            // We could try to do some things here - e.g. count
                            // dereferences - but that's probably not a good
                            // idea, at least for now, so just give up and
                            // report a conflict. This is unsafe code anyway so
                            // the user could always use raw pointers.
1910
                            debug!("places_conflict: arbitrary -> conflict");
1911 1912 1913 1914 1915 1916 1917 1918
                            return true;
                        }
                        Overlap::EqualOrDisjoint => {
                            // This is the recursive case - proceed to the next element.
                        }
                        Overlap::Disjoint => {
                            // We have proven the borrow disjoint - further
                            // projections will remain disjoint.
1919
                            debug!("places_conflict: disjoint");
1920 1921 1922 1923 1924 1925 1926 1927 1928 1929
                            return false;
                        }
                    }

                }
            }
        }
        unreachable!("iter::repeat returned None")
    }

1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941
    /// This function iterates over all of the current borrows
    /// (represented by 1-bits in `flow_state.borrows`) that conflict
    /// with an access to a place, invoking the `op` callback for each
    /// one.
    ///
    /// "Current borrow" here means a borrow that reaches the point in
    /// the control-flow where the access occurs.
    ///
    /// The borrow's phase is represented by the ReserveOrActivateIndex
    /// passed to the callback: one can call `is_reservation()` and
    /// `is_activation()` to determine what phase the borrow is
    /// currently in, when such distinction matters.
N
Niko Matsakis 已提交
1942 1943 1944 1945
    fn each_borrow_involving_path<F>(
        &mut self,
        _context: Context,
        access_place: (ShallowOrDeep, &Place<'tcx>),
1946
        flow_state: &Flows<'cx, 'gcx, 'tcx>,
N
Niko Matsakis 已提交
1947 1948
        mut op: F,
    ) where
1949
        F: FnMut(&mut Self, ReserveOrActivateIndex, &BorrowData<'tcx>) -> Control,
1950
    {
1951
        let (access, place) = access_place;
1952

1953
        // FIXME: analogous code in check_loans first maps `place` to
1954 1955
        // its base_path.

1956
        let data = flow_state.borrows.operator().borrows();
1957 1958 1959

        // check for loan restricting path P being used. Accounts for
        // borrows of P, P.a.b, etc.
1960 1961
        let mut elems_incoming = flow_state.borrows.elems_incoming();
        while let Some(i) = elems_incoming.next() {
1962
            let borrowed = &data[i.borrow_index()];
1963

1964
            if self.places_conflict(&borrowed.borrowed_place, place, access) {
1965 1966
                let ctrl = op(self, i, borrowed);
                if ctrl == Control::Break { return; }
1967
            }
1968 1969 1970 1971
        }
    }
}

1972
impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1973 1974 1975 1976 1977
    // FIXME (#16118): function intended to allow the borrow checker
    // to be less precise in its handling of Box while still allowing
    // moves out of a Box. They should be removed when/if we stop
    // treating Box specially (e.g. when/if DerefMove is added...)

1978
    fn base_path<'d>(&self, place: &'d Place<'tcx>) -> &'d Place<'tcx> {
1979
        //! Returns the base of the leftmost (deepest) dereference of an
1980 1981
        //! Box in `place`. If there is no dereference of an Box
        //! in `place`, then it just returns `place` itself.
1982

1983 1984
        let mut cursor = place;
        let mut deepest = place;
1985 1986
        loop {
            let proj = match *cursor {
1987 1988
                Place::Local(..) | Place::Static(..) => return deepest,
                Place::Projection(ref proj) => proj,
1989
            };
N
Niko Matsakis 已提交
1990 1991
            if proj.elem == ProjectionElem::Deref
                && place.ty(self.mir, self.tcx).to_ty(self.tcx).is_box()
1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007
            {
                deepest = &proj.base;
            }
            cursor = &proj.base;
        }
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
struct Context {
    kind: ContextKind,
    loc: Location,
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum ContextKind {
2008
    Activation,
2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019
    AssignLhs,
    AssignRhs,
    SetDiscrim,
    InlineAsm,
    SwitchInt,
    Drop,
    DropAndReplace,
    CallOperator,
    CallOperand,
    CallDest,
    Assert,
A
Alex Crichton 已提交
2020
    Yield,
2021
    StorageDead,
2022 2023 2024
}

impl ContextKind {
N
Niko Matsakis 已提交
2025 2026 2027 2028 2029 2030
    fn new(self, loc: Location) -> Context {
        Context {
            kind: self,
            loc: loc,
        }
    }
2031
}