]> git.lizzy.rs Git - rust.git/commitdiff
Merge remote-tracking branch 'origin/master' into gen
authorAlex Crichton <alex@alexcrichton.com>
Wed, 16 Aug 2017 18:33:10 +0000 (11:33 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Wed, 16 Aug 2017 18:33:10 +0000 (11:33 -0700)
22 files changed:
src/Cargo.lock
src/librustc/session/config.rs
src/librustc_borrowck/borrowck/check_loans.rs
src/librustc_borrowck/borrowck/mod.rs
src/librustc_borrowck/diagnostics.rs
src/librustc_data_structures/indexed_set.rs
src/librustc_driver/driver.rs
src/librustc_mir/Cargo.toml
src/librustc_mir/dataflow/drop_flag_effects.rs
src/librustc_mir/dataflow/graphviz.rs
src/librustc_mir/dataflow/impls/borrows.rs [new file with mode: 0644]
src/librustc_mir/dataflow/impls/mod.rs
src/librustc_mir/dataflow/mod.rs
src/librustc_mir/dataflow/move_paths/builder.rs [new file with mode: 0644]
src/librustc_mir/dataflow/move_paths/mod.rs
src/librustc_mir/diagnostics.rs
src/librustc_mir/lib.rs
src/librustc_mir/transform/borrow_check.rs [new file with mode: 0644]
src/librustc_mir/transform/mod.rs
src/librustc_mir/transform/rustc_peek.rs
src/librustc_mir/util/borrowck_errors.rs [new file with mode: 0644]
src/librustc_mir/util/mod.rs

index c175198c227a54adc24d0fc04abd27072402d0ab..b5db5ce36133f33978aad38301c783b63ff8bbab 100644 (file)
@@ -1452,6 +1452,7 @@ dependencies = [
  "rustc_const_eval 0.0.0",
  "rustc_const_math 0.0.0",
  "rustc_data_structures 0.0.0",
+ "rustc_errors 0.0.0",
  "syntax 0.0.0",
  "syntax_pos 0.0.0",
 ]
index 6995f0996774fbbb5dde1570a2d7e58e49e789b7..5985dcb97c739b170ccceed2c727824dceb10691 100644 (file)
@@ -918,6 +918,8 @@ fn parse_optimization_fuel(slot: &mut Option<(String, u64)>, v: Option<&str>) ->
         "when debug-printing compiler state, do not include spans"), // o/w tests have closure@path
     identify_regions: bool = (false, parse_bool, [UNTRACKED],
         "make unnamed regions display as '# (where # is some non-ident unique id)"),
+    borrowck_mir: bool = (false, parse_bool, [UNTRACKED],
+        "implicitly treat functions as if they have `#[rustc_mir_borrowck]` attribute"),
     time_passes: bool = (false, parse_bool, [UNTRACKED],
         "measure time of each rustc pass"),
     count_llvm_insns: bool = (false, parse_bool,
index c82377173252f6630c5d23fd2822450fbbb4e1f8..4058f3198afb4f52e8430c63fa07abf037324338 100644 (file)
@@ -29,6 +29,7 @@
 use syntax::ast;
 use syntax_pos::Span;
 use rustc::hir;
+use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin};
 
 use std::rc::Rc;
 
@@ -465,10 +466,8 @@ pub fn report_error_if_loan_conflicts_with_restriction(&self,
 
             let mut err = match (new_loan.kind, old_loan.kind) {
                 (ty::MutBorrow, ty::MutBorrow) => {
-                    let mut err = struct_span_err!(self.bccx, new_loan.span, E0499,
-                                                      "cannot borrow `{}`{} as mutable \
-                                                      more than once at a time",
-                                                      nl, new_loan_msg);
+                    let mut err = self.bccx.cannot_mutably_borrow_multiply(
+                        new_loan.span, &nl, &new_loan_msg, Origin::Ast);
 
                     if new_loan.span == old_loan.span {
                         // Both borrows are happening in the same place
@@ -496,10 +495,8 @@ pub fn report_error_if_loan_conflicts_with_restriction(&self,
                 }
 
                 (ty::UniqueImmBorrow, ty::UniqueImmBorrow) => {
-                    let mut err = struct_span_err!(self.bccx, new_loan.span, E0524,
-                                     "two closures require unique access to `{}` \
-                                      at the same time",
-                                     nl);
+                    let mut err = self.bccx.cannot_uniquely_borrow_by_two_closures(
+                        new_loan.span, &nl, Origin::Ast);
                     err.span_label(
                             old_loan.span,
                             "first closure is constructed here");
@@ -513,10 +510,8 @@ pub fn report_error_if_loan_conflicts_with_restriction(&self,
                 }
 
                 (ty::UniqueImmBorrow, _) => {
-                    let mut err = struct_span_err!(self.bccx, new_loan.span, E0500,
-                                                   "closure requires unique access to `{}` \
-                                                   but {} is already borrowed{}",
-                                                   nl, ol_pronoun, old_loan_msg);
+                    let mut err = self.bccx.cannot_uniquely_borrow_by_one_closure(
+                        new_loan.span, &nl, &ol_pronoun, &old_loan_msg, Origin::Ast);
                     err.span_label(
                             new_loan.span,
                             format!("closure construction occurs here{}", new_loan_msg));
@@ -530,10 +525,9 @@ pub fn report_error_if_loan_conflicts_with_restriction(&self,
                 }
 
                 (_, ty::UniqueImmBorrow) => {
-                    let mut err = struct_span_err!(self.bccx, new_loan.span, E0501,
-                                                   "cannot borrow `{}`{} as {} because \
-                                                   previous closure requires unique access",
-                                                   nl, new_loan_msg, new_loan.kind.to_user_str());
+                    let new_loan_str = &new_loan.kind.to_user_str();
+                    let mut err = self.bccx.cannot_reborrow_already_uniquely_borrowed(
+                        new_loan.span, &nl, &new_loan_msg, new_loan_str, Origin::Ast);
                     err.span_label(
                             new_loan.span,
                             format!("borrow occurs here{}", new_loan_msg));
@@ -547,15 +541,10 @@ pub fn report_error_if_loan_conflicts_with_restriction(&self,
                 }
 
                 (..) => {
-                    let mut err = struct_span_err!(self.bccx, new_loan.span, E0502,
-                                                   "cannot borrow `{}`{} as {} because \
-                                                   {} is also borrowed as {}{}",
-                                                   nl,
-                                                   new_loan_msg,
-                                                   new_loan.kind.to_user_str(),
-                                                   ol_pronoun,
-                                                   old_loan.kind.to_user_str(),
-                                                   old_loan_msg);
+                    let mut err = self.bccx.cannot_reborrow_already_borrowed(
+                        new_loan.span,
+                        &nl, &new_loan_msg, &new_loan.kind.to_user_str(),
+                        &ol_pronoun, &old_loan.kind.to_user_str(), &old_loan_msg, Origin::Ast);
                     err.span_label(
                             new_loan.span,
                             format!("{} borrow occurs here{}",
@@ -645,9 +634,8 @@ fn check_for_copy_of_frozen_path(&self,
         match self.analyze_restrictions_on_use(id, copy_path, ty::ImmBorrow) {
             UseOk => { }
             UseWhileBorrowed(loan_path, loan_span) => {
-                struct_span_err!(self.bccx, span, E0503,
-                                 "cannot use `{}` because it was mutably borrowed",
-                                 &self.bccx.loan_path_to_string(copy_path))
+                let desc = self.bccx.loan_path_to_string(copy_path);
+                self.bccx.cannot_use_when_mutably_borrowed(span, &desc, Origin::Ast)
                     .span_label(loan_span,
                                format!("borrow of `{}` occurs here",
                                        &self.bccx.loan_path_to_string(&loan_path))
@@ -673,9 +661,8 @@ fn check_for_move_of_borrowed_path(&self,
             UseWhileBorrowed(loan_path, loan_span) => {
                 let mut err = match move_kind {
                     move_data::Captured => {
-                        let mut err = struct_span_err!(self.bccx, span, E0504,
-                                         "cannot move `{}` into closure because it is borrowed",
-                                         &self.bccx.loan_path_to_string(move_path));
+                        let mut err = self.bccx.cannot_move_into_closure(
+                            span, &self.bccx.loan_path_to_string(move_path), Origin::Ast);
                         err.span_label(
                             loan_span,
                             format!("borrow of `{}` occurs here",
@@ -690,9 +677,8 @@ fn check_for_move_of_borrowed_path(&self,
                     move_data::Declared |
                     move_data::MoveExpr |
                     move_data::MovePat => {
-                        let mut err = struct_span_err!(self.bccx, span, E0505,
-                                         "cannot move out of `{}` because it is borrowed",
-                                         &self.bccx.loan_path_to_string(move_path));
+                        let desc = self.bccx.loan_path_to_string(move_path);
+                        let mut err = self.bccx.cannot_move_when_borrowed(span, &desc, Origin::Ast);
                         err.span_label(
                             loan_span,
                             format!("borrow of `{}` occurs here",
@@ -874,9 +860,8 @@ pub fn report_illegal_mutation(&self,
                                    span: Span,
                                    loan_path: &LoanPath<'tcx>,
                                    loan: &Loan) {
-        struct_span_err!(self.bccx, span, E0506,
-                         "cannot assign to `{}` because it is borrowed",
-                         self.bccx.loan_path_to_string(loan_path))
+        self.bccx.cannot_assign_to_borrowed(
+            span, &self.bccx.loan_path_to_string(loan_path), Origin::Ast)
             .span_label(loan.span,
                        format!("borrow of `{}` occurs here",
                                self.bccx.loan_path_to_string(loan_path)))
index c1c35be540cab6bd10d816c4a66f1f6ca347995d..80ca16d23f52209c3a0a58cc3dfa88a4a2b7cfcf 100644 (file)
@@ -37,6 +37,8 @@
 use rustc::ty::{self, TyCtxt};
 use rustc::ty::maps::Providers;
 use rustc::util::nodemap::FxHashMap;
+use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin};
+
 use std::fmt;
 use std::rc::Rc;
 use std::hash::{Hash, Hasher};
@@ -219,6 +221,25 @@ pub struct BorrowckCtxt<'a, 'tcx: 'a> {
     body: &'tcx hir::Body,
 }
 
+impl<'b, 'tcx: 'b> BorrowckErrors for BorrowckCtxt<'b, 'tcx> {
+    fn struct_span_err_with_code<'a, S: Into<MultiSpan>>(&'a self,
+                                                         sp: S,
+                                                         msg: &str,
+                                                         code: &str)
+                                                         -> DiagnosticBuilder<'a>
+    {
+        self.tcx.sess.struct_span_err_with_code(sp, msg, code)
+    }
+
+    fn struct_span_err<'a, S: Into<MultiSpan>>(&'a self,
+                                               sp: S,
+                                               msg: &str)
+                                               -> DiagnosticBuilder<'a>
+    {
+        self.tcx.sess.struct_span_err(sp, msg)
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // Loans and loan paths
 
@@ -548,14 +569,13 @@ pub fn report_use_of_moved_value(&self,
             move_data::Declared => {
                 // If this is an uninitialized variable, just emit a simple warning
                 // and return.
-                struct_span_err!(
-                    self.tcx.sess, use_span, E0381,
-                    "{} of possibly uninitialized variable: `{}`",
-                    verb,
-                    self.loan_path_to_string(lp))
-                .span_label(use_span, format!("use of possibly uninitialized `{}`",
-                    self.loan_path_to_string(lp)))
-                .emit();
+                self.cannot_act_on_uninitialized_variable(use_span,
+                                                          verb,
+                                                          &self.loan_path_to_string(lp),
+                                                          Origin::Ast)
+                    .span_label(use_span, format!("use of possibly uninitialized `{}`",
+                                                  self.loan_path_to_string(lp)))
+                    .emit();
                 return;
             }
             _ => {
@@ -682,10 +702,9 @@ pub fn report_reassigned_immutable_variable(&self,
                                                 lp: &LoanPath<'tcx>,
                                                 assign:
                                                 &move_data::Assignment) {
-        let mut err = struct_span_err!(
-            self.tcx.sess, span, E0384,
-            "re-assignment of immutable variable `{}`",
-            self.loan_path_to_string(lp));
+        let mut err = self.cannot_reassign_immutable(span,
+                                                     &self.loan_path_to_string(lp),
+                                                     Origin::Ast);
         err.span_label(span, "re-assignment of immutable variable");
         if span != assign.span {
             err.span_label(assign.span, format!("first assignment to `{}`",
index 517b4e7f99b4d06d82ca06ebe14c703e3ac88bb4..50a9dccaa2bb8fa7aff8eb10ac8602f41f62048d 100644 (file)
@@ -63,27 +63,6 @@ fn foo() -> Box<Fn(u32) -> u32> {
 about safety.
 "##,
 
-E0381: r##"
-It is not allowed to use or capture an uninitialized variable. For example:
-
-```compile_fail,E0381
-fn main() {
-    let x: i32;
-    let y = x; // error, use of possibly uninitialized variable
-}
-```
-
-To fix this, ensure that any declared variables are initialized before being
-used. Example:
-
-```
-fn main() {
-    let x: i32 = 0;
-    let y = x; // ok!
-}
-```
-"##,
-
 E0382: r##"
 This error occurs when an attempt is made to use a variable after its contents
 have been moved elsewhere. For example:
@@ -182,28 +161,6 @@ fn drop(&mut self) { /* ... */ }
 ```
 "##,
 
-E0384: r##"
-This error occurs when an attempt is made to reassign an immutable variable.
-For example:
-
-```compile_fail,E0384
-fn main() {
-    let x = 3;
-    x = 5; // error, reassignment of immutable variable
-}
-```
-
-By default, variables in Rust are immutable. To fix this error, add the keyword
-`mut` after the keyword `let` when declaring the variable. For example:
-
-```
-fn main() {
-    let mut x = 3;
-    x = 5;
-}
-```
-"##,
-
 /*E0386: r##"
 This error occurs when an attempt is made to mutate the target of a mutable
 reference stored inside an immutable container.
@@ -360,512 +317,6 @@ fn main() {
 ```
 "##,
 
-E0499: r##"
-A variable was borrowed as mutable more than once. Erroneous code example:
-
-```compile_fail,E0499
-let mut i = 0;
-let mut x = &mut i;
-let mut a = &mut i;
-// error: cannot borrow `i` as mutable more than once at a time
-```
-
-Please note that in rust, you can either have many immutable references, or one
-mutable reference. Take a look at
-https://doc.rust-lang.org/book/first-edition/references-and-borrowing.html
-for more information. Example:
-
-
-```
-let mut i = 0;
-let mut x = &mut i; // ok!
-
-// or:
-let mut i = 0;
-let a = &i; // ok!
-let b = &i; // still ok!
-let c = &i; // still ok!
-```
-"##,
-
-E0500: r##"
-A borrowed variable was used in another closure. Example of erroneous code:
-
-```compile_fail
-fn you_know_nothing(jon_snow: &mut i32) {
-    let nights_watch = || {
-        *jon_snow = 2;
-    };
-    let starks = || {
-        *jon_snow = 3; // error: closure requires unique access to `jon_snow`
-                       //        but it is already borrowed
-    };
-}
-```
-
-In here, `jon_snow` is already borrowed by the `nights_watch` closure, so it
-cannot be borrowed by the `starks` closure at the same time. To fix this issue,
-you can put the closure in its own scope:
-
-```
-fn you_know_nothing(jon_snow: &mut i32) {
-    {
-        let nights_watch = || {
-            *jon_snow = 2;
-        };
-    } // At this point, `jon_snow` is free.
-    let starks = || {
-        *jon_snow = 3;
-    };
-}
-```
-
-Or, if the type implements the `Clone` trait, you can clone it between
-closures:
-
-```
-fn you_know_nothing(jon_snow: &mut i32) {
-    let mut jon_copy = jon_snow.clone();
-    let nights_watch = || {
-        jon_copy = 2;
-    };
-    let starks = || {
-        *jon_snow = 3;
-    };
-}
-```
-"##,
-
-E0501: r##"
-This error indicates that a mutable variable is being used while it is still
-captured by a closure. Because the closure has borrowed the variable, it is not
-available for use until the closure goes out of scope.
-
-Note that a capture will either move or borrow a variable, but in this
-situation, the closure is borrowing the variable. Take a look at
-http://rustbyexample.com/fn/closures/capture.html for more information about
-capturing.
-
-Example of erroneous code:
-
-```compile_fail,E0501
-fn inside_closure(x: &mut i32) {
-    // Actions which require unique access
-}
-
-fn outside_closure(x: &mut i32) {
-    // Actions which require unique access
-}
-
-fn foo(a: &mut i32) {
-    let bar = || {
-        inside_closure(a)
-    };
-    outside_closure(a); // error: cannot borrow `*a` as mutable because previous
-                        //        closure requires unique access.
-}
-```
-
-To fix this error, you can place the closure in its own scope:
-
-```
-fn inside_closure(x: &mut i32) {}
-fn outside_closure(x: &mut i32) {}
-
-fn foo(a: &mut i32) {
-    {
-        let bar = || {
-            inside_closure(a)
-        };
-    } // borrow on `a` ends.
-    outside_closure(a); // ok!
-}
-```
-
-Or you can pass the variable as a parameter to the closure:
-
-```
-fn inside_closure(x: &mut i32) {}
-fn outside_closure(x: &mut i32) {}
-
-fn foo(a: &mut i32) {
-    let bar = |s: &mut i32| {
-        inside_closure(s)
-    };
-    outside_closure(a);
-    bar(a);
-}
-```
-
-It may be possible to define the closure later:
-
-```
-fn inside_closure(x: &mut i32) {}
-fn outside_closure(x: &mut i32) {}
-
-fn foo(a: &mut i32) {
-    outside_closure(a);
-    let bar = || {
-        inside_closure(a)
-    };
-}
-```
-"##,
-
-E0502: r##"
-This error indicates that you are trying to borrow a variable as mutable when it
-has already been borrowed as immutable.
-
-Example of erroneous code:
-
-```compile_fail,E0502
-fn bar(x: &mut i32) {}
-fn foo(a: &mut i32) {
-    let ref y = a; // a is borrowed as immutable.
-    bar(a); // error: cannot borrow `*a` as mutable because `a` is also borrowed
-            //        as immutable
-}
-```
-
-To fix this error, ensure that you don't have any other references to the
-variable before trying to access it mutably:
-
-```
-fn bar(x: &mut i32) {}
-fn foo(a: &mut i32) {
-    bar(a);
-    let ref y = a; // ok!
-}
-```
-
-For more information on the rust ownership system, take a look at
-https://doc.rust-lang.org/book/first-edition/references-and-borrowing.html.
-"##,
-
-E0503: r##"
-A value was used after it was mutably borrowed.
-
-Example of erroneous code:
-
-```compile_fail,E0503
-fn main() {
-    let mut value = 3;
-    // Create a mutable borrow of `value`. This borrow
-    // lives until the end of this function.
-    let _borrow = &mut value;
-    let _sum = value + 1; // error: cannot use `value` because
-                          //        it was mutably borrowed
-}
-```
-
-In this example, `value` is mutably borrowed by `borrow` and cannot be
-used to calculate `sum`. This is not possible because this would violate
-Rust's mutability rules.
-
-You can fix this error by limiting the scope of the borrow:
-
-```
-fn main() {
-    let mut value = 3;
-    // By creating a new block, you can limit the scope
-    // of the reference.
-    {
-        let _borrow = &mut value; // Use `_borrow` inside this block.
-    }
-    // The block has ended and with it the borrow.
-    // You can now use `value` again.
-    let _sum = value + 1;
-}
-```
-
-Or by cloning `value` before borrowing it:
-
-```
-fn main() {
-    let mut value = 3;
-    // We clone `value`, creating a copy.
-    let value_cloned = value.clone();
-    // The mutable borrow is a reference to `value` and
-    // not to `value_cloned`...
-    let _borrow = &mut value;
-    // ... which means we can still use `value_cloned`,
-    let _sum = value_cloned + 1;
-    // even though the borrow only ends here.
-}
-```
-
-You can find more information about borrowing in the rust-book:
-http://doc.rust-lang.org/book/first-edition/references-and-borrowing.html
-"##,
-
-E0504: r##"
-This error occurs when an attempt is made to move a borrowed variable into a
-closure.
-
-Example of erroneous code:
-
-```compile_fail,E0504
-struct FancyNum {
-    num: u8,
-}
-
-fn main() {
-    let fancy_num = FancyNum { num: 5 };
-    let fancy_ref = &fancy_num;
-
-    let x = move || {
-        println!("child function: {}", fancy_num.num);
-        // error: cannot move `fancy_num` into closure because it is borrowed
-    };
-
-    x();
-    println!("main function: {}", fancy_ref.num);
-}
-```
-
-Here, `fancy_num` is borrowed by `fancy_ref` and so cannot be moved into
-the closure `x`. There is no way to move a value into a closure while it is
-borrowed, as that would invalidate the borrow.
-
-If the closure can't outlive the value being moved, try using a reference
-rather than moving:
-
-```
-struct FancyNum {
-    num: u8,
-}
-
-fn main() {
-    let fancy_num = FancyNum { num: 5 };
-    let fancy_ref = &fancy_num;
-
-    let x = move || {
-        // fancy_ref is usable here because it doesn't move `fancy_num`
-        println!("child function: {}", fancy_ref.num);
-    };
-
-    x();
-
-    println!("main function: {}", fancy_num.num);
-}
-```
-
-If the value has to be borrowed and then moved, try limiting the lifetime of
-the borrow using a scoped block:
-
-```
-struct FancyNum {
-    num: u8,
-}
-
-fn main() {
-    let fancy_num = FancyNum { num: 5 };
-
-    {
-        let fancy_ref = &fancy_num;
-        println!("main function: {}", fancy_ref.num);
-        // `fancy_ref` goes out of scope here
-    }
-
-    let x = move || {
-        // `fancy_num` can be moved now (no more references exist)
-        println!("child function: {}", fancy_num.num);
-    };
-
-    x();
-}
-```
-
-If the lifetime of a reference isn't enough, such as in the case of threading,
-consider using an `Arc` to create a reference-counted value:
-
-```
-use std::sync::Arc;
-use std::thread;
-
-struct FancyNum {
-    num: u8,
-}
-
-fn main() {
-    let fancy_ref1 = Arc::new(FancyNum { num: 5 });
-    let fancy_ref2 = fancy_ref1.clone();
-
-    let x = thread::spawn(move || {
-        // `fancy_ref1` can be moved and has a `'static` lifetime
-        println!("child thread: {}", fancy_ref1.num);
-    });
-
-    x.join().expect("child thread should finish");
-    println!("main thread: {}", fancy_ref2.num);
-}
-```
-"##,
-
-E0505: r##"
-A value was moved out while it was still borrowed.
-
-Erroneous code example:
-
-```compile_fail,E0505
-struct Value {}
-
-fn eat(val: Value) {}
-
-fn main() {
-    let x = Value{};
-    {
-        let _ref_to_val: &Value = &x;
-        eat(x);
-    }
-}
-```
-
-Here, the function `eat` takes the ownership of `x`. However,
-`x` cannot be moved because it was borrowed to `_ref_to_val`.
-To fix that you can do few different things:
-
-* Try to avoid moving the variable.
-* Release borrow before move.
-* Implement the `Copy` trait on the type.
-
-Examples:
-
-```
-struct Value {}
-
-fn eat(val: &Value) {}
-
-fn main() {
-    let x = Value{};
-    {
-        let _ref_to_val: &Value = &x;
-        eat(&x); // pass by reference, if it's possible
-    }
-}
-```
-
-Or:
-
-```
-struct Value {}
-
-fn eat(val: Value) {}
-
-fn main() {
-    let x = Value{};
-    {
-        let _ref_to_val: &Value = &x;
-    }
-    eat(x); // release borrow and then move it.
-}
-```
-
-Or:
-
-```
-#[derive(Clone, Copy)] // implement Copy trait
-struct Value {}
-
-fn eat(val: Value) {}
-
-fn main() {
-    let x = Value{};
-    {
-        let _ref_to_val: &Value = &x;
-        eat(x); // it will be copied here.
-    }
-}
-```
-
-You can find more information about borrowing in the rust-book:
-http://doc.rust-lang.org/book/first-edition/references-and-borrowing.html
-"##,
-
-E0506: r##"
-This error occurs when an attempt is made to assign to a borrowed value.
-
-Example of erroneous code:
-
-```compile_fail,E0506
-struct FancyNum {
-    num: u8,
-}
-
-fn main() {
-    let mut fancy_num = FancyNum { num: 5 };
-    let fancy_ref = &fancy_num;
-    fancy_num = FancyNum { num: 6 };
-    // error: cannot assign to `fancy_num` because it is borrowed
-
-    println!("Num: {}, Ref: {}", fancy_num.num, fancy_ref.num);
-}
-```
-
-Because `fancy_ref` still holds a reference to `fancy_num`, `fancy_num` can't
-be assigned to a new value as it would invalidate the reference.
-
-Alternatively, we can move out of `fancy_num` into a second `fancy_num`:
-
-```
-struct FancyNum {
-    num: u8,
-}
-
-fn main() {
-    let mut fancy_num = FancyNum { num: 5 };
-    let moved_num = fancy_num;
-    fancy_num = FancyNum { num: 6 };
-
-    println!("Num: {}, Moved num: {}", fancy_num.num, moved_num.num);
-}
-```
-
-If the value has to be borrowed, try limiting the lifetime of the borrow using
-a scoped block:
-
-```
-struct FancyNum {
-    num: u8,
-}
-
-fn main() {
-    let mut fancy_num = FancyNum { num: 5 };
-
-    {
-        let fancy_ref = &fancy_num;
-        println!("Ref: {}", fancy_ref.num);
-    }
-
-    // Works because `fancy_ref` is no longer in scope
-    fancy_num = FancyNum { num: 6 };
-    println!("Num: {}", fancy_num.num);
-}
-```
-
-Or by moving the reference into a function:
-
-```
-struct FancyNum {
-    num: u8,
-}
-
-fn main() {
-    let mut fancy_num = FancyNum { num: 5 };
-
-    print_fancy_ref(&fancy_num);
-
-    // Works because function borrow has ended
-    fancy_num = FancyNum { num: 6 };
-    println!("Num: {}", fancy_num.num);
-}
-
-fn print_fancy_ref(fancy_ref: &FancyNum){
-    println!("Ref: {}", fancy_ref.num);
-}
-```
-"##,
-
 E0507: r##"
 You tried to move out of a value which was borrowed. Erroneous code example:
 
@@ -1291,7 +742,6 @@ struct Foo<'a> {
 
 register_diagnostics! {
 //    E0385, // {} in an aliasable location
-    E0524, // two closures require unique access to `..` at the same time
     E0594, // cannot assign to {}
     E0598, // lifetime of {} is too short to guarantee its contents can be...
 }
index cc56289a5633467998551c93a0a27facc6ee7b99..47fa21e3bf0b2bf6a1e47f4d6c78b94c1aca9246 100644 (file)
@@ -171,6 +171,70 @@ pub fn iter(&self) -> Iter<T> {
             _pd: PhantomData,
         }
     }
+
+    /// Calls `f` on each index value held in this set, up to the
+    /// bound `max_bits` on the size of universe of indexes.
+    pub fn each_bit<F>(&self, max_bits: usize, f: F) where F: FnMut(T) {
+        each_bit(self, max_bits, f)
+    }
+
+    /// Removes all elements from this set.
+    pub fn reset_to_empty(&mut self) {
+        for word in self.words_mut() { *word = 0; }
+    }
+
+    pub fn elems(&self, universe_size: usize) -> Elems<T> {
+        Elems { i: 0, set: self, universe_size: universe_size }
+    }
+}
+
+pub struct Elems<'a, T: Idx> { i: usize, set: &'a IdxSet<T>, universe_size: usize }
+
+impl<'a, T: Idx> Iterator for Elems<'a, T> {
+    type Item = T;
+    fn next(&mut self) -> Option<T> {
+        if self.i >= self.universe_size { return None; }
+        let mut i = self.i;
+        loop {
+            if i >= self.universe_size {
+                self.i = i; // (mark iteration as complete.)
+                return None;
+            }
+            if self.set.contains(&T::new(i)) {
+                self.i = i + 1; // (next element to start at.)
+                return Some(T::new(i));
+            }
+            i = i + 1;
+        }
+    }
+}
+
+fn each_bit<T: Idx, F>(words: &IdxSet<T>, max_bits: usize, mut f: F) where F: FnMut(T) {
+    let usize_bits: usize = mem::size_of::<usize>() * 8;
+
+    for (word_index, &word) in words.words().iter().enumerate() {
+        if word != 0 {
+            let base_index = word_index * usize_bits;
+            for offset in 0..usize_bits {
+                let bit = 1 << offset;
+                if (word & bit) != 0 {
+                    // NB: we round up the total number of bits
+                    // that we store in any given bit set so that
+                    // it is an even multiple of usize::BITS. This
+                    // means that there may be some stray bits at
+                    // the end that do not correspond to any
+                    // actual value; that's why we first check
+                    // that we are in range of bits_per_block.
+                    let bit_index = base_index + offset as usize;
+                    if bit_index >= max_bits {
+                        return;
+                    } else {
+                        f(Idx::new(bit_index));
+                    }
+                }
+            }
+        }
+    }
 }
 
 pub struct Iter<'a, T: Idx> {
index 749b983e453fcd12e79b963e4ac8b6483259e15f..19d237b7761d355cf0be02dc0ee48d454a40fad1 100644 (file)
@@ -970,7 +970,12 @@ macro_rules! try_with_f {
     // We compute "constant qualifications" between MIR_CONST and MIR_VALIDATED.
 
     // What we need to run borrowck etc.
+
     passes.push_pass(MIR_VALIDATED, mir::transform::qualify_consts::QualifyAndPromoteConstants);
+
+    // FIXME: ariel points SimplifyBranches should run after
+    // mir-borrowck; otherwise code within `if false { ... }` would
+    // not be checked.
     passes.push_pass(MIR_VALIDATED,
                      mir::transform::simplify_branches::SimplifyBranches::new("initial"));
     passes.push_pass(MIR_VALIDATED, mir::transform::simplify::SimplifyCfg::new("qualify-consts"));
@@ -978,6 +983,10 @@ macro_rules! try_with_f {
 
     // borrowck runs between MIR_VALIDATED and MIR_OPTIMIZED.
 
+    // FIXME: niko says this should be a query (see rustc::ty::maps)
+    // instead of a pass.
+    passes.push_pass(MIR_VALIDATED, mir::transform::borrow_check::BorrowckMir);
+
     // These next passes must be executed together
     passes.push_pass(MIR_OPTIMIZED, mir::transform::no_landing_pads::NoLandingPads);
     passes.push_pass(MIR_OPTIMIZED, mir::transform::add_call_guards::CriticalCallEdges);
index 6e42e02d5109b6c5e1fd1ea83ec8d55c244ea379..49e626c540082869d2b45f330c3badb0227adb4a 100644 (file)
@@ -15,6 +15,7 @@ rustc = { path = "../librustc" }
 rustc_const_eval = { path = "../librustc_const_eval" }
 rustc_const_math = { path = "../librustc_const_math" }
 rustc_data_structures = { path = "../librustc_data_structures" }
+rustc_errors = { path = "../librustc_errors" }
 rustc_bitflags = { path = "../librustc_bitflags" }
 syntax = { path = "../libsyntax" }
 syntax_pos = { path = "../libsyntax_pos" }
index a7894f0249972cbba2ad8382429ed051e6cc710a..bd41bce67da8ead21670beefeba21af6ba57b5a3 100644 (file)
@@ -8,84 +8,16 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use syntax::ast::{self, MetaItem};
 use syntax_pos::DUMMY_SP;
 
-
-use rustc::mir::{self, Mir, BasicBlock, Location};
-use rustc::session::Session;
+use rustc::mir::{self, Mir, Location};
 use rustc::ty::{self, TyCtxt};
 use util::elaborate_drops::DropFlagState;
-use rustc_data_structures::indexed_set::{IdxSet};
-
-use std::fmt;
 
-use super::{Dataflow, DataflowBuilder, DataflowAnalysis};
-use super::{BitDenotation, DataflowOperator, DataflowResults};
+use super::{MoveDataParamEnv};
 use super::indexes::MovePathIndex;
 use super::move_paths::{MoveData, LookupResult};
 
-pub(crate) fn has_rustc_mir_with(attrs: &[ast::Attribute], name: &str) -> Option<MetaItem> {
-    for attr in attrs {
-        if attr.check_name("rustc_mir") {
-            let items = attr.meta_item_list();
-            for item in items.iter().flat_map(|l| l.iter()) {
-                match item.meta_item() {
-                    Some(mi) if mi.check_name(name) => return Some(mi.clone()),
-                    _ => continue
-                }
-            }
-        }
-    }
-    return None;
-}
-
-pub struct MoveDataParamEnv<'tcx> {
-    pub(crate) move_data: MoveData<'tcx>,
-    pub(crate) param_env: ty::ParamEnv<'tcx>,
-}
-
-pub(crate) fn do_dataflow<'a, 'tcx, BD, P>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                mir: &Mir<'tcx>,
-                                node_id: ast::NodeId,
-                                attributes: &[ast::Attribute],
-                                dead_unwinds: &IdxSet<BasicBlock>,
-                                bd: BD,
-                                p: P)
-                                -> DataflowResults<BD>
-    where BD: BitDenotation<Idx=MovePathIndex> + DataflowOperator,
-          P: Fn(&BD, BD::Idx) -> &fmt::Debug
-{
-    let name_found = |sess: &Session, attrs: &[ast::Attribute], name| -> Option<String> {
-        if let Some(item) = has_rustc_mir_with(attrs, name) {
-            if let Some(s) = item.value_str() {
-                return Some(s.to_string())
-            } else {
-                sess.span_err(
-                    item.span,
-                    &format!("{} attribute requires a path", item.name()));
-                return None;
-            }
-        }
-        return None;
-    };
-
-    let print_preflow_to =
-        name_found(tcx.sess, attributes, "borrowck_graphviz_preflow");
-    let print_postflow_to =
-        name_found(tcx.sess, attributes, "borrowck_graphviz_postflow");
-
-    let mut mbcx = DataflowBuilder {
-        node_id,
-        print_preflow_to,
-        print_postflow_to,
-        flow_state: DataflowAnalysis::new(tcx, mir, dead_unwinds, bd),
-    };
-
-    mbcx.dataflow(p);
-    mbcx.flow_state.results()
-}
-
 pub fn move_path_children_matching<'tcx, F>(move_data: &MoveData<'tcx>,
                                         path: MovePathIndex,
                                         mut cond: F)
index e6d77aa2686af345839169226078bdc7e9f3d0f5..7ff4fbcf199e0fc19381942050e7597c7af0e5fe 100644 (file)
@@ -13,7 +13,6 @@
 use syntax::ast::NodeId;
 use rustc::mir::{BasicBlock, Mir};
 use rustc_data_structures::bitslice::bits_to_string;
-use rustc_data_structures::indexed_set::{IdxSet};
 use rustc_data_structures::indexed_vec::Idx;
 
 use dot;
@@ -24,7 +23,6 @@
 use std::io;
 use std::io::prelude::*;
 use std::marker::PhantomData;
-use std::mem;
 use std::path::Path;
 
 use util;
 use super::{BitDenotation, DataflowState};
 use super::DataflowBuilder;
 
-impl<O: BitDenotation> DataflowState<O> {
-    fn each_bit<F>(&self, words: &IdxSet<O::Idx>, mut f: F)
-        where F: FnMut(O::Idx) {
-        //! Helper for iterating over the bits in a bitvector.
-
-        let bits_per_block = self.operator.bits_per_block();
-        let usize_bits: usize = mem::size_of::<usize>() * 8;
-
-        for (word_index, &word) in words.words().iter().enumerate() {
-            if word != 0 {
-                let base_index = word_index * usize_bits;
-                for offset in 0..usize_bits {
-                    let bit = 1 << offset;
-                    if (word & bit) != 0 {
-                        // NB: we round up the total number of bits
-                        // that we store in any given bit set so that
-                        // it is an even multiple of usize::BITS. This
-                        // means that there may be some stray bits at
-                        // the end that do not correspond to any
-                        // actual value; that's why we first check
-                        // that we are in range of bits_per_block.
-                        let bit_index = base_index + offset as usize;
-                        if bit_index >= bits_per_block {
-                            return;
-                        } else {
-                            f(O::Idx::new(bit_index));
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    pub fn interpret_set<'c, P>(&self,
-                                o: &'c O,
-                                words: &IdxSet<O::Idx>,
-                                render_idx: &P)
-                                -> Vec<&'c Debug>
-        where P: Fn(&O, O::Idx) -> &Debug
-    {
-        let mut v = Vec::new();
-        self.each_bit(words, |i| {
-            v.push(render_idx(o, i));
-        });
-        v
-    }
-}
-
 pub trait MirWithFlowState<'tcx> {
     type BD: BitDenotation;
     fn node_id(&self) -> NodeId;
diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs
new file mode 100644 (file)
index 0000000..ab62342
--- /dev/null
@@ -0,0 +1,180 @@
+// Copyright 2012-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.
+
+use rustc::mir::{self, Location, Mir};
+use rustc::mir::visit::Visitor;
+use rustc::ty::{Region, TyCtxt};
+use rustc::ty::RegionKind::ReScope;
+use rustc::util::nodemap::{FxHashMap, FxHashSet};
+
+use rustc_data_structures::bitslice::{BitwiseOperator};
+use rustc_data_structures::indexed_set::{IdxSet};
+use rustc_data_structures::indexed_vec::{IndexVec};
+
+use dataflow::{BitDenotation, BlockSets, DataflowOperator};
+pub use dataflow::indexes::BorrowIndex;
+
+use std::fmt;
+
+// `Borrows` maps each dataflow bit to an `Rvalue::Ref`, which can be
+// uniquely identified in the MIR by the `Location` of the assigment
+// statement in which it appears on the right hand side.
+pub struct Borrows<'a, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    mir: &'a Mir<'tcx>,
+    borrows: IndexVec<BorrowIndex, BorrowData<'tcx>>,
+    location_map: FxHashMap<Location, BorrowIndex>,
+    region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
+}
+
+// temporarily allow some dead fields: `kind` and `region` will be
+// needed by borrowck; `lvalue` will probably be a MovePathIndex when
+// that is extended to include borrowed data paths.
+#[allow(dead_code)]
+#[derive(Debug)]
+pub struct BorrowData<'tcx> {
+    pub(crate) location: Location,
+    pub(crate) kind: mir::BorrowKind,
+    pub(crate) region: Region<'tcx>,
+    pub(crate) lvalue: mir::Lvalue<'tcx>,
+}
+
+impl<'tcx> fmt::Display for BorrowData<'tcx> {
+    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
+        let kind = match self.kind {
+            mir::BorrowKind::Shared => "",
+            mir::BorrowKind::Unique => "uniq ",
+            mir::BorrowKind::Mut => "mut ",
+        };
+        let region = format!("{}", self.region);
+        let region = if region.len() > 0 { format!("{} ", region) } else { region };
+        write!(w, "&{}{}{:?}", region, kind, self.lvalue)
+    }
+}
+
+impl<'a, 'tcx> Borrows<'a, 'tcx> {
+    pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
+        let mut visitor = GatherBorrows { idx_vec: IndexVec::new(),
+                                          location_map: FxHashMap(),
+                                          region_map: FxHashMap(), };
+        visitor.visit_mir(mir);
+        return Borrows { tcx: tcx,
+                         mir: mir,
+                         borrows: visitor.idx_vec,
+                         location_map: visitor.location_map,
+                         region_map: visitor.region_map, };
+
+        struct GatherBorrows<'tcx> {
+            idx_vec: IndexVec<BorrowIndex, BorrowData<'tcx>>,
+            location_map: FxHashMap<Location, BorrowIndex>,
+            region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
+        }
+        impl<'tcx> Visitor<'tcx> for GatherBorrows<'tcx> {
+            fn visit_rvalue(&mut self,
+                            rvalue: &mir::Rvalue<'tcx>,
+                            location: mir::Location) {
+                if let mir::Rvalue::Ref(region, kind, ref lvalue) = *rvalue {
+                    let borrow = BorrowData {
+                        location: location, kind: kind, region: region, lvalue: lvalue.clone(),
+                    };
+                    let idx = self.idx_vec.push(borrow);
+                    self.location_map.insert(location, idx);
+                    let borrows = self.region_map.entry(region).or_insert(FxHashSet());
+                    borrows.insert(idx);
+                }
+            }
+        }
+    }
+
+    pub fn borrows(&self) -> &IndexVec<BorrowIndex, BorrowData<'tcx>> { &self.borrows }
+
+    pub fn location(&self, idx: BorrowIndex) -> &Location {
+        &self.borrows[idx].location
+    }
+}
+
+impl<'a, 'tcx> BitDenotation for Borrows<'a, 'tcx> {
+    type Idx = BorrowIndex;
+    fn name() -> &'static str { "borrows" }
+    fn bits_per_block(&self) -> usize {
+        self.borrows.len()
+    }
+    fn start_block_effect(&self, _sets: &mut BlockSets<BorrowIndex>)  {
+        // no borrows of code extents have been taken prior to
+        // function execution, so this method has no effect on
+        // `_sets`.
+    }
+    fn statement_effect(&self,
+                        sets: &mut BlockSets<BorrowIndex>,
+                        location: Location) {
+        let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| {
+            panic!("could not find block at location {:?}", location);
+        });
+        let stmt = block.statements.get(location.statement_index).unwrap_or_else(|| {
+            panic!("could not find statement at location {:?}");
+        });
+        match stmt.kind {
+            mir::StatementKind::EndRegion(extent) => {
+                let borrow_indexes = self.region_map.get(&ReScope(extent)).unwrap_or_else(|| {
+                    panic!("could not find BorrowIndexs for code-extent {:?}", extent);
+                });
+
+                for idx in borrow_indexes { sets.kill(&idx); }
+            }
+
+            mir::StatementKind::Assign(_, ref rhs) => {
+                if let mir::Rvalue::Ref(region, _, _) = *rhs {
+                    let index = self.location_map.get(&location).unwrap_or_else(|| {
+                        panic!("could not find BorrowIndex for location {:?}", location);
+                    });
+                    assert!(self.region_map.get(region).unwrap_or_else(|| {
+                        panic!("could not find BorrowIndexs for region {:?}", region);
+                    }).contains(&index));
+                    sets.gen(&index);
+                }
+            }
+
+            mir::StatementKind::InlineAsm { .. } |
+            mir::StatementKind::SetDiscriminant { .. } |
+            mir::StatementKind::StorageLive(..) |
+            mir::StatementKind::StorageDead(..) |
+            mir::StatementKind::Validate(..) |
+            mir::StatementKind::Nop => {}
+
+        }
+    }
+    fn terminator_effect(&self,
+                         _sets: &mut BlockSets<BorrowIndex>,
+                         _location: Location) {
+        // no terminators start nor end code extents.
+    }
+
+    fn propagate_call_return(&self,
+                             _in_out: &mut IdxSet<BorrowIndex>,
+                             _call_bb: mir::BasicBlock,
+                             _dest_bb: mir::BasicBlock,
+                             _dest_lval: &mir::Lvalue) {
+        // there are no effects on the extents from method calls.
+    }
+}
+
+impl<'a, 'tcx> BitwiseOperator for Borrows<'a, 'tcx> {
+    #[inline]
+    fn join(&self, pred1: usize, pred2: usize) -> usize {
+        pred1 | pred2 // union effects of preds when computing borrows
+    }
+}
+
+impl<'a, 'tcx> DataflowOperator for Borrows<'a, 'tcx> {
+    #[inline]
+    fn bottom_value() -> bool {
+        false // bottom = no Rvalue::Refs are active by default
+    }
+}
index 41019799e41de76ef3cd376835d561fff9a289f4..c7c66e1bb2b070cff15a2c6fc315027d8494183d 100644 (file)
@@ -30,6 +30,9 @@
 use super::drop_flag_effects_for_location;
 use super::on_lookup_result_bits;
 
+#[allow(dead_code)]
+pub(super) mod borrows;
+
 /// `MaybeInitializedLvals` tracks all l-values that might be
 /// initialized upon reaching a particular point in the control flow
 /// for a function.
@@ -287,24 +290,22 @@ fn start_block_effect(&self, sets: &mut BlockSets<MovePathIndex>)
 
     fn statement_effect(&self,
                         sets: &mut BlockSets<MovePathIndex>,
-                        bb: mir::BasicBlock,
-                        idx: usize)
+                        location: Location)
     {
         drop_flag_effects_for_location(
             self.tcx, self.mir, self.mdpe,
-            Location { block: bb, statement_index: idx },
+            location,
             |path, s| Self::update_bits(sets, path, s)
         )
     }
 
     fn terminator_effect(&self,
                          sets: &mut BlockSets<MovePathIndex>,
-                         bb: mir::BasicBlock,
-                         statements_len: usize)
+                         location: Location)
     {
         drop_flag_effects_for_location(
             self.tcx, self.mir, self.mdpe,
-            Location { block: bb, statement_index: statements_len },
+            location,
             |path, s| Self::update_bits(sets, path, s)
         )
     }
@@ -344,24 +345,22 @@ fn start_block_effect(&self, sets: &mut BlockSets<MovePathIndex>) {
 
     fn statement_effect(&self,
                         sets: &mut BlockSets<MovePathIndex>,
-                        bb: mir::BasicBlock,
-                        idx: usize)
+                        location: Location)
     {
         drop_flag_effects_for_location(
             self.tcx, self.mir, self.mdpe,
-            Location { block: bb, statement_index: idx },
+            location,
             |path, s| Self::update_bits(sets, path, s)
         )
     }
 
     fn terminator_effect(&self,
                          sets: &mut BlockSets<MovePathIndex>,
-                         bb: mir::BasicBlock,
-                         statements_len: usize)
+                         location: Location)
     {
         drop_flag_effects_for_location(
             self.tcx, self.mir, self.mdpe,
-            Location { block: bb, statement_index: statements_len },
+            location,
             |path, s| Self::update_bits(sets, path, s)
         )
     }
@@ -400,24 +399,22 @@ fn start_block_effect(&self, sets: &mut BlockSets<MovePathIndex>) {
 
     fn statement_effect(&self,
                         sets: &mut BlockSets<MovePathIndex>,
-                        bb: mir::BasicBlock,
-                        idx: usize)
+                        location: Location)
     {
         drop_flag_effects_for_location(
             self.tcx, self.mir, self.mdpe,
-            Location { block: bb, statement_index: idx },
+            location,
             |path, s| Self::update_bits(sets, path, s)
         )
     }
 
     fn terminator_effect(&self,
                          sets: &mut BlockSets<MovePathIndex>,
-                         bb: mir::BasicBlock,
-                         statements_len: usize)
+                         location: Location)
     {
         drop_flag_effects_for_location(
             self.tcx, self.mir, self.mdpe,
-            Location { block: bb, statement_index: statements_len },
+            location,
             |path, s| Self::update_bits(sets, path, s)
         )
     }
@@ -448,18 +445,16 @@ fn start_block_effect(&self, _sets: &mut BlockSets<MoveOutIndex>) {
     }
     fn statement_effect(&self,
                         sets: &mut BlockSets<MoveOutIndex>,
-                        bb: mir::BasicBlock,
-                        idx: usize) {
+                        location: Location) {
         let (tcx, mir, move_data) = (self.tcx, self.mir, self.move_data());
-        let stmt = &mir[bb].statements[idx];
+        let stmt = &mir[location.block].statements[location.statement_index];
         let loc_map = &move_data.loc_map;
         let path_map = &move_data.path_map;
         let rev_lookup = &move_data.rev_lookup;
 
-        let loc = Location { block: bb, statement_index: idx };
         debug!("stmt {:?} at loc {:?} moves out of move_indexes {:?}",
-               stmt, loc, &loc_map[loc]);
-        for move_index in &loc_map[loc] {
+               stmt, location, &loc_map[location]);
+        for move_index in &loc_map[location] {
             // Every path deinitialized by a *particular move*
             // has corresponding bit, "gen'ed" (i.e. set)
             // here, in dataflow vector
@@ -506,17 +501,15 @@ fn statement_effect(&self,
 
     fn terminator_effect(&self,
                          sets: &mut BlockSets<MoveOutIndex>,
-                         bb: mir::BasicBlock,
-                         statements_len: usize)
+                         location: Location)
     {
         let (mir, move_data) = (self.mir, self.move_data());
-        let term = mir[bb].terminator();
+        let term = mir[location.block].terminator();
         let loc_map = &move_data.loc_map;
-        let loc = Location { block: bb, statement_index: statements_len };
         debug!("terminator {:?} at loc {:?} moves out of move_indexes {:?}",
-               term, loc, &loc_map[loc]);
+               term, location, &loc_map[location]);
         let bits_per_block = self.bits_per_block();
-        for move_index in &loc_map[loc] {
+        for move_index in &loc_map[location] {
             assert!(move_index.index() < bits_per_block);
             zero_to_one(sets.gen_set.words_mut(), *move_index);
         }
index e4fd2b0e8f7c6a3e9fae731d67290d2d584b5b0d..cdf991a0933f3b809fd354276278119c4a31434d 100644 (file)
@@ -8,16 +8,17 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use syntax::ast;
+use syntax::ast::{self, MetaItem};
 
 use rustc_data_structures::indexed_set::{IdxSet, IdxSetBuf};
 use rustc_data_structures::indexed_vec::Idx;
 use rustc_data_structures::bitslice::{bitwise, BitwiseOperator};
 
-use rustc::ty::{TyCtxt};
-use rustc::mir::{self, Mir};
+use rustc::ty::{self, TyCtxt};
+use rustc::mir::{self, Mir, BasicBlock, BasicBlockData, Location, Statement, Terminator};
+use rustc::session::Session;
 
-use std::fmt::Debug;
+use std::fmt::{self, Debug};
 use std::io;
 use std::mem;
 use std::path::PathBuf;
 
 pub use self::impls::{MaybeInitializedLvals, MaybeUninitializedLvals};
 pub use self::impls::{DefinitelyInitializedLvals, MovingOutStatements};
-
+pub use self::impls::borrows::{Borrows, BorrowData, BorrowIndex};
 pub(crate) use self::drop_flag_effects::*;
 
+use self::move_paths::MoveData;
+
 mod drop_flag_effects;
 mod graphviz;
 mod impls;
@@ -44,11 +47,22 @@ pub(crate) struct DataflowBuilder<'a, 'tcx: 'a, BD> where BD: BitDenotation
 }
 
 pub trait Dataflow<BD: BitDenotation> {
-    fn dataflow<P>(&mut self, p: P) where P: Fn(&BD, BD::Idx) -> &Debug;
+    /// Sets up and runs the dataflow problem, using `p` to render results if
+    /// implementation so chooses.
+    fn dataflow<P>(&mut self, p: P) where P: Fn(&BD, BD::Idx) -> &Debug {
+        let _ = p; // default implementation does not instrument process.
+        self.build_sets();
+        self.propagate();
+    }
+
+    /// Sets up the entry, gen, and kill sets for this instance of a dataflow problem.
+    fn build_sets(&mut self);
+
+    /// Finds a fixed-point solution to this instance of a dataflow problem.
+    fn propagate(&mut self);
 }
 
-impl<'a, 'tcx: 'a, BD> Dataflow<BD> for DataflowBuilder<'a, 'tcx, BD>
-    where BD: BitDenotation + DataflowOperator
+impl<'a, 'tcx: 'a, BD> Dataflow<BD> for DataflowBuilder<'a, 'tcx, BD> where BD: BitDenotation
 {
     fn dataflow<P>(&mut self, p: P) where P: Fn(&BD, BD::Idx) -> &Debug {
         self.flow_state.build_sets();
@@ -56,17 +70,79 @@ fn dataflow<P>(&mut self, p: P) where P: Fn(&BD, BD::Idx) -> &Debug {
         self.flow_state.propagate();
         self.post_dataflow_instrumentation(|c,i| p(c,i)).unwrap();
     }
+
+    fn build_sets(&mut self) { self.flow_state.build_sets(); }
+    fn propagate(&mut self) { self.flow_state.propagate(); }
+}
+
+pub(crate) fn has_rustc_mir_with(attrs: &[ast::Attribute], name: &str) -> Option<MetaItem> {
+    for attr in attrs {
+        if attr.check_name("rustc_mir") {
+            let items = attr.meta_item_list();
+            for item in items.iter().flat_map(|l| l.iter()) {
+                match item.meta_item() {
+                    Some(mi) if mi.check_name(name) => return Some(mi.clone()),
+                    _ => continue
+                }
+            }
+        }
+    }
+    return None;
+}
+
+pub struct MoveDataParamEnv<'tcx> {
+    pub(crate) move_data: MoveData<'tcx>,
+    pub(crate) param_env: ty::ParamEnv<'tcx>,
+}
+
+pub(crate) fn do_dataflow<'a, 'tcx, BD, P>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                mir: &Mir<'tcx>,
+                                node_id: ast::NodeId,
+                                attributes: &[ast::Attribute],
+                                dead_unwinds: &IdxSet<BasicBlock>,
+                                bd: BD,
+                                p: P)
+                                -> DataflowResults<BD>
+    where BD: BitDenotation,
+          P: Fn(&BD, BD::Idx) -> &fmt::Debug
+{
+    let name_found = |sess: &Session, attrs: &[ast::Attribute], name| -> Option<String> {
+        if let Some(item) = has_rustc_mir_with(attrs, name) {
+            if let Some(s) = item.value_str() {
+                return Some(s.to_string())
+            } else {
+                sess.span_err(
+                    item.span,
+                    &format!("{} attribute requires a path", item.name()));
+                return None;
+            }
+        }
+        return None;
+    };
+
+    let print_preflow_to =
+        name_found(tcx.sess, attributes, "borrowck_graphviz_preflow");
+    let print_postflow_to =
+        name_found(tcx.sess, attributes, "borrowck_graphviz_postflow");
+
+    let mut mbcx = DataflowBuilder {
+        node_id,
+        print_preflow_to,
+        print_postflow_to,
+        flow_state: DataflowAnalysis::new(tcx, mir, dead_unwinds, bd),
+    };
+
+    mbcx.dataflow(p);
+    mbcx.flow_state.results()
 }
 
-struct PropagationContext<'b, 'a: 'b, 'tcx: 'a, O>
-    where O: 'b + BitDenotation
+struct PropagationContext<'b, 'a: 'b, 'tcx: 'a, O> where O: 'b + BitDenotation
 {
     builder: &'b mut DataflowAnalysis<'a, 'tcx, O>,
     changed: bool,
 }
 
-impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD>
-    where BD: BitDenotation + DataflowOperator
+impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> where BD: BitDenotation
 {
     fn propagate(&mut self) {
         let mut temp = IdxSetBuf::new_empty(self.flow_state.sets.bits_per_block);
@@ -98,19 +174,19 @@ fn build_sets(&mut self) {
 
             let sets = &mut self.flow_state.sets.for_block(bb.index());
             for j_stmt in 0..statements.len() {
-                self.flow_state.operator.statement_effect(sets, bb, j_stmt);
+                let location = Location { block: bb, statement_index: j_stmt };
+                self.flow_state.operator.statement_effect(sets, location);
             }
 
             if terminator.is_some() {
-                let stmts_len = statements.len();
-                self.flow_state.operator.terminator_effect(sets, bb, stmts_len);
+                let location = Location { block: bb, statement_index: statements.len() };
+                self.flow_state.operator.terminator_effect(sets, location);
             }
         }
     }
 }
 
-impl<'b, 'a: 'b, 'tcx: 'a, BD> PropagationContext<'b, 'a, 'tcx, BD>
-    where BD: BitDenotation + DataflowOperator
+impl<'b, 'a: 'b, 'tcx: 'a, BD> PropagationContext<'b, 'a, 'tcx, BD> where BD: BitDenotation
 {
     fn reset(&mut self, bits: &mut IdxSet<BD::Idx>) {
         let e = if BD::bottom_value() {!0} else {0};
@@ -147,8 +223,7 @@ fn dataflow_path(context: &str, prepost: &str, path: &str) -> PathBuf {
     path
 }
 
-impl<'a, 'tcx: 'a, BD> DataflowBuilder<'a, 'tcx, BD>
-    where BD: BitDenotation
+impl<'a, 'tcx: 'a, BD> DataflowBuilder<'a, 'tcx, BD> where BD: BitDenotation
 {
     fn pre_dataflow_instrumentation<P>(&self, p: P) -> io::Result<()>
         where P: Fn(&BD, BD::Idx) -> &Debug
@@ -189,21 +264,108 @@ fn new(bits: IdxSetBuf<E>) -> Self {
     }
 }
 
-pub struct DataflowAnalysis<'a, 'tcx: 'a, O>
-    where O: BitDenotation
+/// DataflowResultsConsumer abstracts over walking the MIR with some
+/// already constructed dataflow results.
+///
+/// It abstracts over the FlowState and also completely hides the
+/// underlying flow analysis results, because it needs to handle cases
+/// where we are combining the results of *multiple* flow analyses
+/// (e.g. borrows + inits + uninits).
+pub trait DataflowResultsConsumer<'a, 'tcx: 'a> {
+    type FlowState;
+
+    // Observation Hooks: override (at least one of) these to get analysis feedback.
+    fn visit_block_entry(&mut self,
+                         _bb: BasicBlock,
+                         _flow_state: &Self::FlowState) {}
+
+    fn visit_statement_entry(&mut self,
+                             _loc: Location,
+                             _stmt: &Statement<'tcx>,
+                             _flow_state: &Self::FlowState) {}
+
+    fn visit_terminator_entry(&mut self,
+                              _loc: Location,
+                              _term: &Terminator<'tcx>,
+                              _flow_state: &Self::FlowState) {}
+
+    // Main entry point: this drives the processing of results.
+
+    fn analyze_results(&mut self, flow_uninit: &mut Self::FlowState) {
+        let flow = flow_uninit;
+        for bb in self.mir().basic_blocks().indices() {
+            self.reset_to_entry_of(bb, flow);
+            self.process_basic_block(bb, flow);
+        }
+    }
+
+    fn process_basic_block(&mut self, bb: BasicBlock, flow_state: &mut Self::FlowState) {
+        let BasicBlockData { ref statements, ref terminator, is_cleanup: _ } =
+            self.mir()[bb];
+        let mut location = Location { block: bb, statement_index: 0 };
+        for stmt in statements.iter() {
+            self.reconstruct_statement_effect(location, flow_state);
+            self.visit_statement_entry(location, stmt, flow_state);
+            self.apply_local_effect(location, flow_state);
+            location.statement_index += 1;
+        }
+
+        if let Some(ref term) = *terminator {
+            self.reconstruct_terminator_effect(location, flow_state);
+            self.visit_terminator_entry(location, term, flow_state);
+
+            // We don't need to apply the effect of the terminator,
+            // since we are only visiting dataflow state on control
+            // flow entry to the various nodes. (But we still need to
+            // reconstruct the effect, because the visit method might
+            // inspect it.)
+        }
+    }
+
+    // Delegated Hooks: Provide access to the MIR and process the flow state.
+
+    fn mir(&self) -> &'a Mir<'tcx>;
+
+    // reset the state bitvector to represent the entry to block `bb`.
+    fn reset_to_entry_of(&mut self,
+                         bb: BasicBlock,
+                         flow_state: &mut Self::FlowState);
+
+    // build gen + kill sets for statement at `loc`.
+    fn reconstruct_statement_effect(&mut self,
+                                    loc: Location,
+                                    flow_state: &mut Self::FlowState);
+
+    // build gen + kill sets for terminator for `loc`.
+    fn reconstruct_terminator_effect(&mut self,
+                                     loc: Location,
+                                     flow_state: &mut Self::FlowState);
+
+    // apply current gen + kill sets to `flow_state`.
+    //
+    // (`bb` and `stmt_idx` parameters can be ignored if desired by
+    // client. For the terminator, the `stmt_idx` will be the number
+    // of statements in the block.)
+    fn apply_local_effect(&mut self,
+                          loc: Location,
+                          flow_state: &mut Self::FlowState);
+}
+
+pub struct DataflowAnalysis<'a, 'tcx: 'a, O> where O: BitDenotation
 {
     flow_state: DataflowState<O>,
     dead_unwinds: &'a IdxSet<mir::BasicBlock>,
     mir: &'a Mir<'tcx>,
 }
 
-impl<'a, 'tcx: 'a, O> DataflowAnalysis<'a, 'tcx, O>
-    where O: BitDenotation
+impl<'a, 'tcx: 'a, O> DataflowAnalysis<'a, 'tcx, O> where O: BitDenotation
 {
     pub fn results(self) -> DataflowResults<O> {
         DataflowResults(self.flow_state)
     }
 
+    pub fn flow_state(&self) -> &DataflowState<O> { &self.flow_state }
+
     pub fn mir(&self) -> &'a Mir<'tcx> { self.mir }
 }
 
@@ -213,10 +375,14 @@ impl<O: BitDenotation> DataflowResults<O> {
     pub fn sets(&self) -> &AllSets<O::Idx> {
         &self.0.sets
     }
+
+    pub fn operator(&self) -> &O {
+        &self.0.operator
+    }
 }
 
-// FIXME: This type shouldn't be public, but the graphviz::MirWithFlowState trait
-// references it in a method signature. Look into using `pub(crate)` to address this.
+/// State of a dataflow analysis; couples a collection of bit sets
+/// with operator used to initialize and merge bits during analysis.
 pub struct DataflowState<O: BitDenotation>
 {
     /// All the sets for the analysis. (Factored into its
@@ -228,6 +394,28 @@ pub struct DataflowState<O: BitDenotation>
     pub(crate) operator: O,
 }
 
+impl<O: BitDenotation> DataflowState<O> {
+    pub fn each_bit<F>(&self, words: &IdxSet<O::Idx>, f: F) where F: FnMut(O::Idx)
+    {
+        let bits_per_block = self.operator.bits_per_block();
+        words.each_bit(bits_per_block, f)
+    }
+
+    pub fn interpret_set<'c, P>(&self,
+                                o: &'c O,
+                                words: &IdxSet<O::Idx>,
+                                render_idx: &P)
+                                -> Vec<&'c Debug>
+        where P: Fn(&O, O::Idx) -> &Debug
+    {
+        let mut v = Vec::new();
+        self.each_bit(words, |i| {
+            v.push(render_idx(o, i));
+        });
+        v
+    }
+}
+
 #[derive(Debug)]
 pub struct AllSets<E: Idx> {
     /// Analysis bitwidth for each block.
@@ -251,9 +439,28 @@ pub struct AllSets<E: Idx> {
     on_entry_sets: Bits<E>,
 }
 
+/// Triple of sets associated with a given block.
+///
+/// Generally, one sets up `on_entry`, `gen_set`, and `kill_set` for
+/// each block individually, and then runs the dataflow analysis which
+/// iteratively modifies the various `on_entry` sets (but leaves the
+/// other two sets unchanged, since they represent the effect of the
+/// block, which should be invariant over the course of the analysis).
+///
+/// It is best to ensure that the intersection of `gen_set` and
+/// `kill_set` is empty; otherwise the results of the dataflow will
+/// have a hidden dependency on what order the bits are generated and
+/// killed during the iteration. (This is such a good idea that the
+/// `fn gen` and `fn kill` methods that set their state enforce this
+/// for you.)
 pub struct BlockSets<'a, E: Idx> {
+    /// Dataflow state immediately before control flow enters the given block.
     pub(crate) on_entry: &'a mut IdxSet<E>,
+
+    /// Bits that are set to 1 by the time we exit the given block.
     pub(crate) gen_set: &'a mut IdxSet<E>,
+
+    /// Bits that are set to 0 by the time we exit the given block.
     pub(crate) kill_set: &'a mut IdxSet<E>,
 }
 
@@ -302,7 +509,7 @@ pub trait DataflowOperator: BitwiseOperator {
     fn bottom_value() -> bool;
 }
 
-pub trait BitDenotation {
+pub trait BitDenotation: DataflowOperator {
     /// Specifies what index type is used to access the bitvector.
     type Idx: Idx;
 
@@ -341,8 +548,7 @@ pub trait BitDenotation {
     /// the MIR.
     fn statement_effect(&self,
                         sets: &mut BlockSets<Self::Idx>,
-                        bb: mir::BasicBlock,
-                        idx_stmt: usize);
+                        location: Location);
 
     /// Mutates the block-sets (the flow sets for the given
     /// basic block) according to the effects of evaluating
@@ -356,8 +562,7 @@ fn statement_effect(&self,
     /// terminator took.
     fn terminator_effect(&self,
                          sets: &mut BlockSets<Self::Idx>,
-                         bb: mir::BasicBlock,
-                         idx_term: usize);
+                         location: Location);
 
     /// Mutates the block-sets according to the (flow-dependent)
     /// effect of a successful return from a Call terminator.
@@ -385,8 +590,7 @@ fn propagate_call_return(&self,
                              dest_lval: &mir::Lvalue);
 }
 
-impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
-    where D: BitDenotation + DataflowOperator
+impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation
 {
     pub fn new(_tcx: TyCtxt<'a, 'tcx, 'tcx>,
                mir: &'a Mir<'tcx>,
@@ -427,8 +631,7 @@ pub fn new(_tcx: TyCtxt<'a, 'tcx, 'tcx>,
     }
 }
 
-impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
-    where D: BitDenotation + DataflowOperator
+impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation
 {
     /// Propagates the bits of `in_out` into all the successors of `bb`,
     /// using bitwise operator denoted by `self.operator`.
diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs
new file mode 100644 (file)
index 0000000..86298c3
--- /dev/null
@@ -0,0 +1,337 @@
+// 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.
+
+use rustc::ty::{self, TyCtxt};
+use rustc::mir::*;
+use rustc::mir::tcx::RvalueInitializationState;
+use rustc::util::nodemap::FxHashMap;
+use rustc_data_structures::indexed_vec::{IndexVec};
+
+use syntax::codemap::DUMMY_SP;
+
+use std::collections::hash_map::Entry;
+use std::mem;
+
+use super::abs_domain::Lift;
+
+use super::{LocationMap, MoveData, MovePath, MovePathLookup, MovePathIndex, MoveOut, MoveOutIndex};
+
+pub(super) struct MoveDataBuilder<'a, 'tcx: 'a> {
+    mir: &'a Mir<'tcx>,
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    data: MoveData<'tcx>,
+}
+
+pub enum MovePathError {
+    IllegalMove,
+    UnionMove { path: MovePathIndex },
+}
+
+impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
+    fn new(mir: &'a Mir<'tcx>,
+           tcx: TyCtxt<'a, 'tcx, 'tcx>,
+           param_env: ty::ParamEnv<'tcx>)
+           -> Self {
+        let mut move_paths = IndexVec::new();
+        let mut path_map = IndexVec::new();
+
+        MoveDataBuilder {
+            mir,
+            tcx,
+            param_env,
+            data: MoveData {
+                moves: IndexVec::new(),
+                loc_map: LocationMap::new(mir),
+                rev_lookup: MovePathLookup {
+                    locals: mir.local_decls.indices().map(Lvalue::Local).map(|v| {
+                        Self::new_move_path(&mut move_paths, &mut path_map, None, v)
+                    }).collect(),
+                    projections: FxHashMap(),
+                },
+                move_paths,
+                path_map,
+            }
+        }
+    }
+
+    fn new_move_path(move_paths: &mut IndexVec<MovePathIndex, MovePath<'tcx>>,
+                     path_map: &mut IndexVec<MovePathIndex, Vec<MoveOutIndex>>,
+                     parent: Option<MovePathIndex>,
+                     lvalue: Lvalue<'tcx>)
+                     -> MovePathIndex
+    {
+        let move_path = move_paths.push(MovePath {
+            next_sibling: None,
+            first_child: None,
+            parent,
+            lvalue,
+        });
+
+        if let Some(parent) = parent {
+            let next_sibling =
+                mem::replace(&mut move_paths[parent].first_child, Some(move_path));
+            move_paths[move_path].next_sibling = next_sibling;
+        }
+
+        let path_map_ent = path_map.push(vec![]);
+        assert_eq!(path_map_ent, move_path);
+        move_path
+    }
+
+    /// This creates a MovePath for a given lvalue, returning an `MovePathError`
+    /// if that lvalue can't be moved from.
+    ///
+    /// NOTE: lvalues behind references *do not* get a move path, which is
+    /// problematic for borrowck.
+    ///
+    /// Maybe we should have separate "borrowck" and "moveck" modes.
+    fn move_path_for(&mut self, lval: &Lvalue<'tcx>)
+                     -> Result<MovePathIndex, MovePathError>
+    {
+        debug!("lookup({:?})", lval);
+        match *lval {
+            Lvalue::Local(local) => Ok(self.data.rev_lookup.locals[local]),
+            // error: can't move out of a static
+            Lvalue::Static(..) => Err(MovePathError::IllegalMove),
+            Lvalue::Projection(ref proj) => {
+                self.move_path_for_projection(lval, proj)
+            }
+        }
+    }
+
+    fn create_move_path(&mut self, lval: &Lvalue<'tcx>) {
+        // This is an assignment, not a move, so this not being a valid
+        // move path is OK.
+        let _ = self.move_path_for(lval);
+    }
+
+    fn move_path_for_projection(&mut self,
+                                lval: &Lvalue<'tcx>,
+                                proj: &LvalueProjection<'tcx>)
+                                -> Result<MovePathIndex, MovePathError>
+    {
+        let base = try!(self.move_path_for(&proj.base));
+        let lv_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
+        match lv_ty.sty {
+            // error: can't move out of borrowed content
+            ty::TyRef(..) | ty::TyRawPtr(..) => return Err(MovePathError::IllegalMove),
+            // error: can't move out of struct with destructor
+            ty::TyAdt(adt, _) if adt.has_dtor(self.tcx) && !adt.is_box() =>
+                return Err(MovePathError::IllegalMove),
+            // move out of union - always move the entire union
+            ty::TyAdt(adt, _) if adt.is_union() =>
+                return Err(MovePathError::UnionMove { path: base }),
+            // error: can't move out of a slice
+            ty::TySlice(..) =>
+                return Err(MovePathError::IllegalMove),
+            ty::TyArray(..) => match proj.elem {
+                // error: can't move out of an array
+                ProjectionElem::Index(..) => return Err(MovePathError::IllegalMove),
+                _ => {
+                    // FIXME: still badly broken
+                }
+            },
+            _ => {}
+        };
+        match self.data.rev_lookup.projections.entry((base, proj.elem.lift())) {
+            Entry::Occupied(ent) => Ok(*ent.get()),
+            Entry::Vacant(ent) => {
+                let path = Self::new_move_path(
+                    &mut self.data.move_paths,
+                    &mut self.data.path_map,
+                    Some(base),
+                    lval.clone()
+                );
+                ent.insert(path);
+                Ok(path)
+            }
+        }
+    }
+
+    fn finalize(self) -> MoveData<'tcx> {
+        debug!("{}", {
+            debug!("moves for {:?}:", self.mir.span);
+            for (j, mo) in self.data.moves.iter_enumerated() {
+                debug!("    {:?} = {:?}", j, mo);
+            }
+            debug!("move paths for {:?}:", self.mir.span);
+            for (j, path) in self.data.move_paths.iter_enumerated() {
+                debug!("    {:?} = {:?}", j, path);
+            }
+            "done dumping moves"
+        });
+        self.data
+    }
+}
+
+pub(super) fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>,
+                                     tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                     param_env: ty::ParamEnv<'tcx>)
+                                     -> MoveData<'tcx> {
+    let mut builder = MoveDataBuilder::new(mir, tcx, param_env);
+
+    for (bb, block) in mir.basic_blocks().iter_enumerated() {
+        for (i, stmt) in block.statements.iter().enumerate() {
+            let source = Location { block: bb, statement_index: i };
+            builder.gather_statement(source, stmt);
+        }
+
+        let terminator_loc = Location {
+            block: bb,
+            statement_index: block.statements.len()
+        };
+        builder.gather_terminator(terminator_loc, block.terminator());
+    }
+
+    builder.finalize()
+}
+
+impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
+    fn gather_statement(&mut self, loc: Location, stmt: &Statement<'tcx>) {
+        debug!("gather_statement({:?}, {:?})", loc, stmt);
+        match stmt.kind {
+            StatementKind::Assign(ref lval, ref rval) => {
+                self.create_move_path(lval);
+                if let RvalueInitializationState::Shallow = rval.initialization_state() {
+                    // Box starts out uninitialized - need to create a separate
+                    // move-path for the interior so it will be separate from
+                    // the exterior.
+                    self.create_move_path(&lval.clone().deref());
+                }
+                self.gather_rvalue(loc, rval);
+            }
+            StatementKind::StorageLive(_) |
+            StatementKind::StorageDead(_) => {}
+            StatementKind::SetDiscriminant{ .. } => {
+                span_bug!(stmt.source_info.span,
+                          "SetDiscriminant should not exist during borrowck");
+            }
+            StatementKind::InlineAsm { .. } |
+            StatementKind::EndRegion(_) |
+            StatementKind::Validate(..) |
+            StatementKind::Nop => {}
+        }
+    }
+
+    fn gather_rvalue(&mut self, loc: Location, rvalue: &Rvalue<'tcx>) {
+        match *rvalue {
+            Rvalue::Use(ref operand) |
+            Rvalue::Repeat(ref operand, _) |
+            Rvalue::Cast(_, ref operand, _) |
+            Rvalue::UnaryOp(_, ref operand) => {
+                self.gather_operand(loc, operand)
+            }
+            Rvalue::BinaryOp(ref _binop, ref lhs, ref rhs) |
+            Rvalue::CheckedBinaryOp(ref _binop, ref lhs, ref rhs) => {
+                self.gather_operand(loc, lhs);
+                self.gather_operand(loc, rhs);
+            }
+            Rvalue::Aggregate(ref _kind, ref operands) => {
+                for operand in operands {
+                    self.gather_operand(loc, operand);
+                }
+            }
+            Rvalue::Ref(..) |
+            Rvalue::Discriminant(..) |
+            Rvalue::Len(..) |
+            Rvalue::NullaryOp(NullOp::SizeOf, _) |
+            Rvalue::NullaryOp(NullOp::Box, _) => {
+                // This returns an rvalue with uninitialized contents. We can't
+                // move out of it here because it is an rvalue - assignments always
+                // completely initialize their lvalue.
+                //
+                // However, this does not matter - MIR building is careful to
+                // only emit a shallow free for the partially-initialized
+                // temporary.
+                //
+                // In any case, if we want to fix this, we have to register a
+                // special move and change the `statement_effect` functions.
+            }
+        }
+    }
+
+    fn gather_terminator(&mut self, loc: Location, term: &Terminator<'tcx>) {
+        debug!("gather_terminator({:?}, {:?})", loc, term);
+        match term.kind {
+            TerminatorKind::Goto { target: _ } |
+            TerminatorKind::Resume |
+            TerminatorKind::GeneratorDrop |
+            TerminatorKind::Unreachable => { }
+
+            TerminatorKind::Return => {
+                self.gather_move(loc, &Lvalue::Local(RETURN_POINTER));
+            }
+
+            TerminatorKind::Assert { .. } |
+            TerminatorKind::SwitchInt { .. } => {
+                // branching terminators - these don't move anything
+            }
+
+            TerminatorKind::Yield { ref value, .. } => {
+                self.gather_operand(loc, value);
+            }
+
+            TerminatorKind::Drop { ref location, target: _, unwind: _ } => {
+                self.gather_move(loc, location);
+            }
+            TerminatorKind::DropAndReplace { ref location, ref value, .. } => {
+                self.create_move_path(location);
+                self.gather_operand(loc, value);
+            }
+            TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => {
+                self.gather_operand(loc, func);
+                for arg in args {
+                    self.gather_operand(loc, arg);
+                }
+                if let Some((ref destination, _bb)) = *destination {
+                    self.create_move_path(destination);
+                }
+            }
+        }
+    }
+
+    fn gather_operand(&mut self, loc: Location, operand: &Operand<'tcx>) {
+        match *operand {
+            Operand::Constant(..) => {} // not-a-move
+            Operand::Consume(ref lval) => { // a move
+                self.gather_move(loc, lval);
+            }
+        }
+    }
+
+    fn gather_move(&mut self, loc: Location, lval: &Lvalue<'tcx>) {
+        debug!("gather_move({:?}, {:?})", loc, lval);
+
+        let lv_ty = lval.ty(self.mir, self.tcx).to_ty(self.tcx);
+        if !lv_ty.moves_by_default(self.tcx, self.param_env, DUMMY_SP) {
+            debug!("gather_move({:?}, {:?}) - {:?} is Copy. skipping", loc, lval, lv_ty);
+            return
+        }
+
+        let path = match self.move_path_for(lval) {
+            Ok(path) | Err(MovePathError::UnionMove { path }) => path,
+            Err(MovePathError::IllegalMove) => {
+                // Moving out of a bad path. Eventually, this should be a MIR
+                // borrowck error instead of a bug.
+                span_bug!(self.mir.span,
+                          "Broken MIR: moving out of lvalue {:?}: {:?} at {:?}",
+                          lval, lv_ty, loc);
+            }
+        };
+        let move_out = self.data.moves.push(MoveOut { path: path, source: loc });
+
+        debug!("gather_move({:?}, {:?}): adding move {:?} of {:?}",
+               loc, lval, move_out, path);
+
+        self.data.path_map[path].push(move_out);
+        self.data.loc_map[loc].push(move_out);
+    }
+}
index 82eae6b50030a1d6a4a3170ceb5e0105e42c74a3..d2d80649846823c8390061c51ef0dd574f1c0d27 100644 (file)
 
 use rustc::ty::{self, TyCtxt};
 use rustc::mir::*;
-use rustc::mir::tcx::RvalueInitializationState;
 use rustc::util::nodemap::FxHashMap;
 use rustc_data_structures::indexed_vec::{IndexVec};
 
-use syntax::codemap::DUMMY_SP;
-
-use std::collections::hash_map::Entry;
 use std::fmt;
-use std::mem;
 use std::ops::{Index, IndexMut};
 
 use self::abs_domain::{AbstractElem, Lift};
@@ -63,6 +58,9 @@ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
 
     /// Index into MoveData.moves.
     new_index!(MoveOutIndex, "mo");
+
+    /// Index into Borrows.locations
+    new_index!(BorrowIndex, "bw");
 }
 
 pub use self::indexes::MovePathIndex;
@@ -110,6 +108,12 @@ fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
     }
 }
 
+impl<'tcx> fmt::Display for MovePath<'tcx> {
+    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
+        write!(w, "{:?}", self.lvalue)
+    }
+}
+
 #[derive(Debug)]
 pub struct MoveData<'tcx> {
     pub move_paths: IndexVec<MovePathIndex, MovePath<'tcx>>,
@@ -191,154 +195,7 @@ pub struct MovePathLookup<'tcx> {
     projections: FxHashMap<(MovePathIndex, AbstractElem<'tcx>), MovePathIndex>
 }
 
-pub(super) struct MoveDataBuilder<'a, 'tcx: 'a> {
-    mir: &'a Mir<'tcx>,
-    tcx: TyCtxt<'a, 'tcx, 'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    data: MoveData<'tcx>,
-}
-
-pub enum MovePathError {
-    IllegalMove,
-    UnionMove { path: MovePathIndex },
-}
-
-impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
-    fn new(mir: &'a Mir<'tcx>,
-           tcx: TyCtxt<'a, 'tcx, 'tcx>,
-           param_env: ty::ParamEnv<'tcx>)
-           -> Self {
-        let mut move_paths = IndexVec::new();
-        let mut path_map = IndexVec::new();
-
-        MoveDataBuilder {
-            mir,
-            tcx,
-            param_env,
-            data: MoveData {
-                moves: IndexVec::new(),
-                loc_map: LocationMap::new(mir),
-                rev_lookup: MovePathLookup {
-                    locals: mir.local_decls.indices().map(Lvalue::Local).map(|v| {
-                        Self::new_move_path(&mut move_paths, &mut path_map, None, v)
-                    }).collect(),
-                    projections: FxHashMap(),
-                },
-                move_paths,
-                path_map,
-            }
-        }
-    }
-
-    fn new_move_path(move_paths: &mut IndexVec<MovePathIndex, MovePath<'tcx>>,
-                     path_map: &mut IndexVec<MovePathIndex, Vec<MoveOutIndex>>,
-                     parent: Option<MovePathIndex>,
-                     lvalue: Lvalue<'tcx>)
-                     -> MovePathIndex
-    {
-        let move_path = move_paths.push(MovePath {
-            next_sibling: None,
-            first_child: None,
-            parent,
-            lvalue,
-        });
-
-        if let Some(parent) = parent {
-            let next_sibling =
-                mem::replace(&mut move_paths[parent].first_child, Some(move_path));
-            move_paths[move_path].next_sibling = next_sibling;
-        }
-
-        let path_map_ent = path_map.push(vec![]);
-        assert_eq!(path_map_ent, move_path);
-        move_path
-    }
-
-    /// This creates a MovePath for a given lvalue, returning an `MovePathError`
-    /// if that lvalue can't be moved from.
-    ///
-    /// NOTE: lvalues behind references *do not* get a move path, which is
-    /// problematic for borrowck.
-    ///
-    /// Maybe we should have separate "borrowck" and "moveck" modes.
-    fn move_path_for(&mut self, lval: &Lvalue<'tcx>)
-                     -> Result<MovePathIndex, MovePathError>
-    {
-        debug!("lookup({:?})", lval);
-        match *lval {
-            Lvalue::Local(local) => Ok(self.data.rev_lookup.locals[local]),
-            // error: can't move out of a static
-            Lvalue::Static(..) => Err(MovePathError::IllegalMove),
-            Lvalue::Projection(ref proj) => {
-                self.move_path_for_projection(lval, proj)
-            }
-        }
-    }
-
-    fn create_move_path(&mut self, lval: &Lvalue<'tcx>) {
-        // This is an assignment, not a move, so this not being a valid
-        // move path is OK.
-        let _ = self.move_path_for(lval);
-    }
-
-    fn move_path_for_projection(&mut self,
-                                lval: &Lvalue<'tcx>,
-                                proj: &LvalueProjection<'tcx>)
-                                -> Result<MovePathIndex, MovePathError>
-    {
-        let base = try!(self.move_path_for(&proj.base));
-        let lv_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
-        match lv_ty.sty {
-            // error: can't move out of borrowed content
-            ty::TyRef(..) | ty::TyRawPtr(..) => return Err(MovePathError::IllegalMove),
-            // error: can't move out of struct with destructor
-            ty::TyAdt(adt, _) if adt.has_dtor(self.tcx) && !adt.is_box() =>
-                return Err(MovePathError::IllegalMove),
-            // move out of union - always move the entire union
-            ty::TyAdt(adt, _) if adt.is_union() =>
-                return Err(MovePathError::UnionMove { path: base }),
-            // error: can't move out of a slice
-            ty::TySlice(..) =>
-                return Err(MovePathError::IllegalMove),
-            ty::TyArray(..) => match proj.elem {
-                // error: can't move out of an array
-                ProjectionElem::Index(..) => return Err(MovePathError::IllegalMove),
-                _ => {
-                    // FIXME: still badly broken
-                }
-            },
-            _ => {}
-        };
-        match self.data.rev_lookup.projections.entry((base, proj.elem.lift())) {
-            Entry::Occupied(ent) => Ok(*ent.get()),
-            Entry::Vacant(ent) => {
-                let path = Self::new_move_path(
-                    &mut self.data.move_paths,
-                    &mut self.data.path_map,
-                    Some(base),
-                    lval.clone()
-                );
-                ent.insert(path);
-                Ok(path)
-            }
-        }
-    }
-
-    fn finalize(self) -> MoveData<'tcx> {
-        debug!("{}", {
-            debug!("moves for {:?}:", self.mir.span);
-            for (j, mo) in self.data.moves.iter_enumerated() {
-                debug!("    {:?} = {:?}", j, mo);
-            }
-            debug!("move paths for {:?}:", self.mir.span);
-            for (j, path) in self.data.move_paths.iter_enumerated() {
-                debug!("    {:?} = {:?}", j, path);
-            }
-            "done dumping moves"
-        });
-        self.data
-    }
-}
+mod builder;
 
 #[derive(Copy, Clone, Debug)]
 pub enum LookupResult {
@@ -375,170 +232,6 @@ pub fn gather_moves(mir: &Mir<'tcx>,
                         tcx: TyCtxt<'a, 'tcx, 'tcx>,
                         param_env: ty::ParamEnv<'tcx>)
                         -> Self {
-        gather_moves(mir, tcx, param_env)
-    }
-}
-
-fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>,
-                          tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                          param_env: ty::ParamEnv<'tcx>)
-                          -> MoveData<'tcx> {
-    let mut builder = MoveDataBuilder::new(mir, tcx, param_env);
-
-    for (bb, block) in mir.basic_blocks().iter_enumerated() {
-        for (i, stmt) in block.statements.iter().enumerate() {
-            let source = Location { block: bb, statement_index: i };
-            builder.gather_statement(source, stmt);
-        }
-
-        let terminator_loc = Location {
-            block: bb,
-            statement_index: block.statements.len()
-        };
-        builder.gather_terminator(terminator_loc, block.terminator());
-    }
-
-    builder.finalize()
-}
-
-impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
-    fn gather_statement(&mut self, loc: Location, stmt: &Statement<'tcx>) {
-        debug!("gather_statement({:?}, {:?})", loc, stmt);
-        match stmt.kind {
-            StatementKind::Assign(ref lval, ref rval) => {
-                self.create_move_path(lval);
-                if let RvalueInitializationState::Shallow = rval.initialization_state() {
-                    // Box starts out uninitialized - need to create a separate
-                    // move-path for the interior so it will be separate from
-                    // the exterior.
-                    self.create_move_path(&lval.clone().deref());
-                }
-                self.gather_rvalue(loc, rval);
-            }
-            StatementKind::StorageLive(_) |
-            StatementKind::StorageDead(_) => {}
-            StatementKind::SetDiscriminant{ .. } => {
-                span_bug!(stmt.source_info.span,
-                          "SetDiscriminant should not exist during borrowck");
-            }
-            StatementKind::InlineAsm { .. } |
-            StatementKind::EndRegion(_) |
-            StatementKind::Validate(..) |
-            StatementKind::Nop => {}
-        }
-    }
-
-    fn gather_rvalue(&mut self, loc: Location, rvalue: &Rvalue<'tcx>) {
-        match *rvalue {
-            Rvalue::Use(ref operand) |
-            Rvalue::Repeat(ref operand, _) |
-            Rvalue::Cast(_, ref operand, _) |
-            Rvalue::UnaryOp(_, ref operand) => {
-                self.gather_operand(loc, operand)
-            }
-            Rvalue::BinaryOp(ref _binop, ref lhs, ref rhs) |
-            Rvalue::CheckedBinaryOp(ref _binop, ref lhs, ref rhs) => {
-                self.gather_operand(loc, lhs);
-                self.gather_operand(loc, rhs);
-            }
-            Rvalue::Aggregate(ref _kind, ref operands) => {
-                for operand in operands {
-                    self.gather_operand(loc, operand);
-                }
-            }
-            Rvalue::Ref(..) |
-            Rvalue::Discriminant(..) |
-            Rvalue::Len(..) |
-            Rvalue::NullaryOp(NullOp::SizeOf, _) |
-            Rvalue::NullaryOp(NullOp::Box, _) => {
-                // This returns an rvalue with uninitialized contents. We can't
-                // move out of it here because it is an rvalue - assignments always
-                // completely initialize their lvalue.
-                //
-                // However, this does not matter - MIR building is careful to
-                // only emit a shallow free for the partially-initialized
-                // temporary.
-                //
-                // In any case, if we want to fix this, we have to register a
-                // special move and change the `statement_effect` functions.
-            }
-        }
-    }
-
-    fn gather_terminator(&mut self, loc: Location, term: &Terminator<'tcx>) {
-        debug!("gather_terminator({:?}, {:?})", loc, term);
-        match term.kind {
-            TerminatorKind::Goto { target: _ } |
-            TerminatorKind::Resume |
-            TerminatorKind::GeneratorDrop |
-            TerminatorKind::Unreachable => { }
-
-            TerminatorKind::Return => {
-                self.gather_move(loc, &Lvalue::Local(RETURN_POINTER));
-            }
-
-            TerminatorKind::Assert { .. } |
-            TerminatorKind::SwitchInt { .. } => {
-                // branching terminators - these don't move anything
-            }
-
-            TerminatorKind::Yield { ref value,  .. } => {
-                self.gather_operand(loc, value);
-            }
-
-            TerminatorKind::Drop { ref location, target: _, unwind: _ } => {
-                self.gather_move(loc, location);
-            }
-            TerminatorKind::DropAndReplace { ref location, ref value, .. } => {
-                self.create_move_path(location);
-                self.gather_operand(loc, value);
-            }
-            TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => {
-                self.gather_operand(loc, func);
-                for arg in args {
-                    self.gather_operand(loc, arg);
-                }
-                if let Some((ref destination, _bb)) = *destination {
-                    self.create_move_path(destination);
-                }
-            }
-        }
-    }
-
-    fn gather_operand(&mut self, loc: Location, operand: &Operand<'tcx>) {
-        match *operand {
-            Operand::Constant(..) => {} // not-a-move
-            Operand::Consume(ref lval) => { // a move
-                self.gather_move(loc, lval);
-            }
-        }
-    }
-
-    fn gather_move(&mut self, loc: Location, lval: &Lvalue<'tcx>) {
-        debug!("gather_move({:?}, {:?})", loc, lval);
-
-        let lv_ty = lval.ty(self.mir, self.tcx).to_ty(self.tcx);
-        if !lv_ty.moves_by_default(self.tcx, self.param_env, DUMMY_SP) {
-            debug!("gather_move({:?}, {:?}) - {:?} is Copy. skipping", loc, lval, lv_ty);
-            return
-        }
-
-        let path = match self.move_path_for(lval) {
-            Ok(path) | Err(MovePathError::UnionMove { path }) => path,
-            Err(MovePathError::IllegalMove) => {
-                // Moving out of a bad path. Eventually, this should be a MIR
-                // borrowck error instead of a bug.
-                span_bug!(self.mir.span,
-                          "Broken MIR: moving out of lvalue {:?}: {:?} at {:?}",
-                          lval, lv_ty, loc);
-            }
-        };
-        let move_out = self.data.moves.push(MoveOut { path: path, source: loc });
-
-        debug!("gather_move({:?}, {:?}): adding move {:?} of {:?}",
-               loc, lval, move_out, path);
-
-        self.data.path_map[path].push(move_out);
-        self.data.loc_map[loc].push(move_out);
+        builder::gather_moves(mir, tcx, param_env)
     }
 }
index 34170a6609c4674d4163d594fb230d5e16aa5e75..83a8ce34c692884b7c3c10f4e1906693f405a5a6 100644 (file)
@@ -195,6 +195,50 @@ const fn foo(mut x: u8) {
 avoid mutation if possible.
 "##,
 
+E0381: r##"
+It is not allowed to use or capture an uninitialized variable. For example:
+
+```compile_fail,E0381
+fn main() {
+    let x: i32;
+    let y = x; // error, use of possibly uninitialized variable
+}
+```
+
+To fix this, ensure that any declared variables are initialized before being
+used. Example:
+
+```
+fn main() {
+    let x: i32 = 0;
+    let y = x; // ok!
+}
+```
+"##,
+
+E0384: r##"
+This error occurs when an attempt is made to reassign an immutable variable.
+For example:
+
+```compile_fail,E0384
+fn main() {
+    let x = 3;
+    x = 5; // error, reassignment of immutable variable
+}
+```
+
+By default, variables in Rust are immutable. To fix this error, add the keyword
+`mut` after the keyword `let` when declaring the variable. For example:
+
+```
+fn main() {
+    let mut x = 3;
+    x = 5;
+}
+```
+"##,
+
+
 E0394: r##"
 A static was referred to by value by another static.
 
@@ -438,9 +482,516 @@ struct Foo {
 ```
 "##,
 
+E0499: r##"
+A variable was borrowed as mutable more than once. Erroneous code example:
+
+```compile_fail,E0499
+let mut i = 0;
+let mut x = &mut i;
+let mut a = &mut i;
+// error: cannot borrow `i` as mutable more than once at a time
+```
+
+Please note that in rust, you can either have many immutable references, or one
+mutable reference. Take a look at
+https://doc.rust-lang.org/stable/book/references-and-borrowing.html for more
+information. Example:
+
+
+```
+let mut i = 0;
+let mut x = &mut i; // ok!
+
+// or:
+let mut i = 0;
+let a = &i; // ok!
+let b = &i; // still ok!
+let c = &i; // still ok!
+```
+"##,
+
+E0500: r##"
+A borrowed variable was used in another closure. Example of erroneous code:
+
+```compile_fail
+fn you_know_nothing(jon_snow: &mut i32) {
+    let nights_watch = || {
+        *jon_snow = 2;
+    };
+    let starks = || {
+        *jon_snow = 3; // error: closure requires unique access to `jon_snow`
+                       //        but it is already borrowed
+    };
+}
+```
+
+In here, `jon_snow` is already borrowed by the `nights_watch` closure, so it
+cannot be borrowed by the `starks` closure at the same time. To fix this issue,
+you can put the closure in its own scope:
+
+```
+fn you_know_nothing(jon_snow: &mut i32) {
+    {
+        let nights_watch = || {
+            *jon_snow = 2;
+        };
+    } // At this point, `jon_snow` is free.
+    let starks = || {
+        *jon_snow = 3;
+    };
+}
+```
+
+Or, if the type implements the `Clone` trait, you can clone it between
+closures:
+
+```
+fn you_know_nothing(jon_snow: &mut i32) {
+    let mut jon_copy = jon_snow.clone();
+    let nights_watch = || {
+        jon_copy = 2;
+    };
+    let starks = || {
+        *jon_snow = 3;
+    };
+}
+```
+"##,
+
+E0501: r##"
+This error indicates that a mutable variable is being used while it is still
+captured by a closure. Because the closure has borrowed the variable, it is not
+available for use until the closure goes out of scope.
+
+Note that a capture will either move or borrow a variable, but in this
+situation, the closure is borrowing the variable. Take a look at
+http://rustbyexample.com/fn/closures/capture.html for more information about
+capturing.
+
+Example of erroneous code:
+
+```compile_fail,E0501
+fn inside_closure(x: &mut i32) {
+    // Actions which require unique access
+}
+
+fn outside_closure(x: &mut i32) {
+    // Actions which require unique access
+}
+
+fn foo(a: &mut i32) {
+    let bar = || {
+        inside_closure(a)
+    };
+    outside_closure(a); // error: cannot borrow `*a` as mutable because previous
+                        //        closure requires unique access.
+}
+```
+
+To fix this error, you can place the closure in its own scope:
+
+```
+fn inside_closure(x: &mut i32) {}
+fn outside_closure(x: &mut i32) {}
+
+fn foo(a: &mut i32) {
+    {
+        let bar = || {
+            inside_closure(a)
+        };
+    } // borrow on `a` ends.
+    outside_closure(a); // ok!
+}
+```
+
+Or you can pass the variable as a parameter to the closure:
+
+```
+fn inside_closure(x: &mut i32) {}
+fn outside_closure(x: &mut i32) {}
+
+fn foo(a: &mut i32) {
+    let bar = |s: &mut i32| {
+        inside_closure(s)
+    };
+    outside_closure(a);
+    bar(a);
+}
+```
+
+It may be possible to define the closure later:
+
+```
+fn inside_closure(x: &mut i32) {}
+fn outside_closure(x: &mut i32) {}
+
+fn foo(a: &mut i32) {
+    outside_closure(a);
+    let bar = || {
+        inside_closure(a)
+    };
+}
+```
+"##,
+
+E0502: r##"
+This error indicates that you are trying to borrow a variable as mutable when it
+has already been borrowed as immutable.
+
+Example of erroneous code:
+
+```compile_fail,E0502
+fn bar(x: &mut i32) {}
+fn foo(a: &mut i32) {
+    let ref y = a; // a is borrowed as immutable.
+    bar(a); // error: cannot borrow `*a` as mutable because `a` is also borrowed
+            //        as immutable
+}
+```
+
+To fix this error, ensure that you don't have any other references to the
+variable before trying to access it mutably:
+
+```
+fn bar(x: &mut i32) {}
+fn foo(a: &mut i32) {
+    bar(a);
+    let ref y = a; // ok!
+}
+```
+
+For more information on the rust ownership system, take a look at
+https://doc.rust-lang.org/stable/book/references-and-borrowing.html.
+"##,
+
+E0503: r##"
+A value was used after it was mutably borrowed.
+
+Example of erroneous code:
+
+```compile_fail,E0503
+fn main() {
+    let mut value = 3;
+    // Create a mutable borrow of `value`. This borrow
+    // lives until the end of this function.
+    let _borrow = &mut value;
+    let _sum = value + 1; // error: cannot use `value` because
+                          //        it was mutably borrowed
+}
+```
+
+In this example, `value` is mutably borrowed by `borrow` and cannot be
+used to calculate `sum`. This is not possible because this would violate
+Rust's mutability rules.
+
+You can fix this error by limiting the scope of the borrow:
+
+```
+fn main() {
+    let mut value = 3;
+    // By creating a new block, you can limit the scope
+    // of the reference.
+    {
+        let _borrow = &mut value; // Use `_borrow` inside this block.
+    }
+    // The block has ended and with it the borrow.
+    // You can now use `value` again.
+    let _sum = value + 1;
+}
+```
+
+Or by cloning `value` before borrowing it:
+
+```
+fn main() {
+    let mut value = 3;
+    // We clone `value`, creating a copy.
+    let value_cloned = value.clone();
+    // The mutable borrow is a reference to `value` and
+    // not to `value_cloned`...
+    let _borrow = &mut value;
+    // ... which means we can still use `value_cloned`,
+    let _sum = value_cloned + 1;
+    // even though the borrow only ends here.
+}
+```
+
+You can find more information about borrowing in the rust-book:
+http://doc.rust-lang.org/stable/book/references-and-borrowing.html
+"##,
+
+E0504: r##"
+This error occurs when an attempt is made to move a borrowed variable into a
+closure.
+
+Example of erroneous code:
+
+```compile_fail,E0504
+struct FancyNum {
+    num: u8,
+}
+
+fn main() {
+    let fancy_num = FancyNum { num: 5 };
+    let fancy_ref = &fancy_num;
+
+    let x = move || {
+        println!("child function: {}", fancy_num.num);
+        // error: cannot move `fancy_num` into closure because it is borrowed
+    };
+
+    x();
+    println!("main function: {}", fancy_ref.num);
+}
+```
+
+Here, `fancy_num` is borrowed by `fancy_ref` and so cannot be moved into
+the closure `x`. There is no way to move a value into a closure while it is
+borrowed, as that would invalidate the borrow.
+
+If the closure can't outlive the value being moved, try using a reference
+rather than moving:
+
+```
+struct FancyNum {
+    num: u8,
+}
+
+fn main() {
+    let fancy_num = FancyNum { num: 5 };
+    let fancy_ref = &fancy_num;
+
+    let x = move || {
+        // fancy_ref is usable here because it doesn't move `fancy_num`
+        println!("child function: {}", fancy_ref.num);
+    };
+
+    x();
+
+    println!("main function: {}", fancy_num.num);
+}
+```
+
+If the value has to be borrowed and then moved, try limiting the lifetime of
+the borrow using a scoped block:
+
+```
+struct FancyNum {
+    num: u8,
+}
+
+fn main() {
+    let fancy_num = FancyNum { num: 5 };
+
+    {
+        let fancy_ref = &fancy_num;
+        println!("main function: {}", fancy_ref.num);
+        // `fancy_ref` goes out of scope here
+    }
+
+    let x = move || {
+        // `fancy_num` can be moved now (no more references exist)
+        println!("child function: {}", fancy_num.num);
+    };
+
+    x();
+}
+```
+
+If the lifetime of a reference isn't enough, such as in the case of threading,
+consider using an `Arc` to create a reference-counted value:
+
+```
+use std::sync::Arc;
+use std::thread;
+
+struct FancyNum {
+    num: u8,
+}
+
+fn main() {
+    let fancy_ref1 = Arc::new(FancyNum { num: 5 });
+    let fancy_ref2 = fancy_ref1.clone();
+
+    let x = thread::spawn(move || {
+        // `fancy_ref1` can be moved and has a `'static` lifetime
+        println!("child thread: {}", fancy_ref1.num);
+    });
+
+    x.join().expect("child thread should finish");
+    println!("main thread: {}", fancy_ref2.num);
+}
+```
+"##,
+
+E0505: r##"
+A value was moved out while it was still borrowed.
+
+Erroneous code example:
+
+```compile_fail,E0505
+struct Value {}
+
+fn eat(val: Value) {}
+
+fn main() {
+    let x = Value{};
+    {
+        let _ref_to_val: &Value = &x;
+        eat(x);
+    }
+}
+```
+
+Here, the function `eat` takes the ownership of `x`. However,
+`x` cannot be moved because it was borrowed to `_ref_to_val`.
+To fix that you can do few different things:
+
+* Try to avoid moving the variable.
+* Release borrow before move.
+* Implement the `Copy` trait on the type.
+
+Examples:
+
+```
+struct Value {}
+
+fn eat(val: &Value) {}
+
+fn main() {
+    let x = Value{};
+    {
+        let _ref_to_val: &Value = &x;
+        eat(&x); // pass by reference, if it's possible
+    }
+}
+```
+
+Or:
+
+```
+struct Value {}
+
+fn eat(val: Value) {}
+
+fn main() {
+    let x = Value{};
+    {
+        let _ref_to_val: &Value = &x;
+    }
+    eat(x); // release borrow and then move it.
+}
+```
+
+Or:
+
+```
+#[derive(Clone, Copy)] // implement Copy trait
+struct Value {}
+
+fn eat(val: Value) {}
+
+fn main() {
+    let x = Value{};
+    {
+        let _ref_to_val: &Value = &x;
+        eat(x); // it will be copied here.
+    }
+}
+```
+
+You can find more information about borrowing in the rust-book:
+http://doc.rust-lang.org/stable/book/references-and-borrowing.html
+"##,
+
+E0506: r##"
+This error occurs when an attempt is made to assign to a borrowed value.
+
+Example of erroneous code:
+
+```compile_fail,E0506
+struct FancyNum {
+    num: u8,
+}
+
+fn main() {
+    let mut fancy_num = FancyNum { num: 5 };
+    let fancy_ref = &fancy_num;
+    fancy_num = FancyNum { num: 6 };
+    // error: cannot assign to `fancy_num` because it is borrowed
+
+    println!("Num: {}, Ref: {}", fancy_num.num, fancy_ref.num);
+}
+```
+
+Because `fancy_ref` still holds a reference to `fancy_num`, `fancy_num` can't
+be assigned to a new value as it would invalidate the reference.
+
+Alternatively, we can move out of `fancy_num` into a second `fancy_num`:
+
+```
+struct FancyNum {
+    num: u8,
+}
+
+fn main() {
+    let mut fancy_num = FancyNum { num: 5 };
+    let moved_num = fancy_num;
+    fancy_num = FancyNum { num: 6 };
+
+    println!("Num: {}, Moved num: {}", fancy_num.num, moved_num.num);
+}
+```
+
+If the value has to be borrowed, try limiting the lifetime of the borrow using
+a scoped block:
+
+```
+struct FancyNum {
+    num: u8,
+}
+
+fn main() {
+    let mut fancy_num = FancyNum { num: 5 };
+
+    {
+        let fancy_ref = &fancy_num;
+        println!("Ref: {}", fancy_ref.num);
+    }
+
+    // Works because `fancy_ref` is no longer in scope
+    fancy_num = FancyNum { num: 6 };
+    println!("Num: {}", fancy_num.num);
+}
+```
+
+Or by moving the reference into a function:
+
+```
+struct FancyNum {
+    num: u8,
+}
+
+fn main() {
+    let mut fancy_num = FancyNum { num: 5 };
+
+    print_fancy_ref(&fancy_num);
+
+    // Works because function borrow has ended
+    fancy_num = FancyNum { num: 6 };
+    println!("Num: {}", fancy_num.num);
+}
+
+fn print_fancy_ref(fancy_ref: &FancyNum){
+    println!("Ref: {}", fancy_ref.num);
+}
+```
+"##,
+
 }
 
 register_diagnostics! {
+    E0524, // two closures require unique access to `..` at the same time
     E0526, // shuffle indices are not constant
     E0625, // thread-local statics cannot be accessed at compile-time
 }
index ea8624930e5f5dd05e4d308839062dac4fc29b69..143ad784171a098b5e3fcf738d53b0f6f441bd7a 100644 (file)
@@ -32,6 +32,7 @@
 #[macro_use]
 extern crate rustc;
 extern crate rustc_data_structures;
+extern crate rustc_errors;
 #[macro_use]
 #[no_link]
 extern crate rustc_bitflags;
diff --git a/src/librustc_mir/transform/borrow_check.rs b/src/librustc_mir/transform/borrow_check.rs
new file mode 100644 (file)
index 0000000..b56d5d3
--- /dev/null
@@ -0,0 +1,1277 @@
+// 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.
+
+//! This pass borrow-checks the MIR to (further) ensure it is not broken.
+
+use rustc::infer::{InferCtxt};
+use rustc::ty::{self, TyCtxt, ParamEnv};
+use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Location, Lvalue};
+use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue};
+use rustc::mir::{Statement, StatementKind, Terminator, TerminatorKind};
+use rustc::mir::transform::{MirPass, MirSource};
+
+use rustc_data_structures::indexed_set::{self, IdxSetBuf};
+use rustc_data_structures::indexed_vec::{Idx};
+
+use syntax::ast::{self};
+use syntax_pos::{DUMMY_SP, Span};
+
+use dataflow::{do_dataflow};
+use dataflow::{MoveDataParamEnv};
+use dataflow::{BitDenotation, BlockSets, DataflowResults, DataflowResultsConsumer};
+use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals};
+use dataflow::{MovingOutStatements};
+use dataflow::{Borrows, BorrowData, BorrowIndex};
+use dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex, LookupResult};
+use util::borrowck_errors::{BorrowckErrors, Origin};
+
+use self::MutateMode::{JustWrite, WriteAndRead};
+use self::ConsumeKind::{Consume};
+
+pub struct BorrowckMir;
+
+impl MirPass for BorrowckMir {
+    fn run_pass<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) {
+
+        // let err_count = tcx.sess.err_count();
+        // if err_count > 0 {
+        //     // compiling a broken program can obviously result in a
+        //     // broken MIR, so try not to report duplicate errors.
+        //     debug!("skipping BorrowckMir: {} due to {} previous errors",
+        //            tcx.node_path_str(src.item_id()), err_count);
+        //     return;
+        // }
+
+        debug!("run_pass BorrowckMir: {}", tcx.node_path_str(src.item_id()));
+
+        let def_id = tcx.hir.local_def_id(src.item_id());
+        if tcx.has_attr(def_id, "rustc_mir_borrowck") || tcx.sess.opts.debugging_opts.borrowck_mir {
+            borrowck_mir(tcx, src, mir);
+        }
+    }
+}
+
+fn borrowck_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &Mir<'tcx>)
+{
+    let id = src.item_id();
+    let def_id = tcx.hir.local_def_id(id);
+    debug!("borrowck_mir({}) UNIMPLEMENTED", tcx.item_path_str(def_id));
+
+    let attributes = tcx.get_attrs(def_id);
+    let param_env = tcx.param_env(def_id);
+    tcx.infer_ctxt().enter(|_infcx| {
+
+        let move_data = MoveData::gather_moves(mir, tcx, param_env);
+        let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
+        let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
+        let flow_borrows = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
+                                       Borrows::new(tcx, mir),
+                                       |bd, i| bd.location(i));
+        let flow_inits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
+                                     MaybeInitializedLvals::new(tcx, mir, &mdpe),
+                                     |bd, i| &bd.move_data().move_paths[i]);
+        let flow_uninits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
+                                       MaybeUninitializedLvals::new(tcx, mir, &mdpe),
+                                       |bd, i| &bd.move_data().move_paths[i]);
+        let flow_move_outs = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
+                                         MovingOutStatements::new(tcx, mir, &mdpe),
+                                         |bd, i| &bd.move_data().moves[i]);
+
+        let mut mbcx = MirBorrowckCtxt {
+            tcx: tcx,
+            mir: mir,
+            node_id: id,
+            move_data: &mdpe.move_data,
+            param_env: param_env,
+            fake_infer_ctxt: &_infcx,
+        };
+
+        let mut state = InProgress::new(flow_borrows,
+                                        flow_inits,
+                                        flow_uninits,
+                                        flow_move_outs);
+
+        mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
+    });
+
+    debug!("borrowck_mir done");
+}
+
+#[allow(dead_code)]
+pub struct MirBorrowckCtxt<'c, 'b, 'a: 'b+'c, 'gcx: 'a+'tcx, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'gcx, 'gcx>,
+    mir: &'b Mir<'gcx>,
+    node_id: ast::NodeId,
+    move_data: &'b MoveData<'gcx>,
+    param_env: ParamEnv<'tcx>,
+    fake_infer_ctxt: &'c InferCtxt<'c, 'gcx, 'tcx>,
+}
+
+// (forced to be `pub` due to its use as an associated type below.)
+pub struct InProgress<'b, 'tcx: 'b> {
+    borrows: FlowInProgress<Borrows<'b, 'tcx>>,
+    inits: FlowInProgress<MaybeInitializedLvals<'b, 'tcx>>,
+    uninits: FlowInProgress<MaybeUninitializedLvals<'b, 'tcx>>,
+    move_outs: FlowInProgress<MovingOutStatements<'b, 'tcx>>,
+}
+
+struct FlowInProgress<BD> where BD: BitDenotation {
+    base_results: DataflowResults<BD>,
+    curr_state: IdxSetBuf<BD::Idx>,
+    stmt_gen: IdxSetBuf<BD::Idx>,
+    stmt_kill: IdxSetBuf<BD::Idx>,
+}
+
+// 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
+impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'gcx>
+    for MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
+{
+    type FlowState = InProgress<'b, 'gcx>;
+
+    fn mir(&self) -> &'b Mir<'gcx> { self.mir }
+
+    fn reset_to_entry_of(&mut self, bb: BasicBlock, flow_state: &mut Self::FlowState) {
+        flow_state.each_flow(|b| b.reset_to_entry_of(bb),
+                             |i| i.reset_to_entry_of(bb),
+                             |u| u.reset_to_entry_of(bb));
+    }
+
+    fn reconstruct_statement_effect(&mut self,
+                                    location: Location,
+                                    flow_state: &mut Self::FlowState) {
+        flow_state.each_flow(|b| b.reconstruct_statement_effect(location),
+                             |i| i.reconstruct_statement_effect(location),
+                             |u| u.reconstruct_statement_effect(location));
+    }
+
+    fn apply_local_effect(&mut self,
+                          _location: Location,
+                          flow_state: &mut Self::FlowState) {
+        flow_state.each_flow(|b| b.apply_local_effect(),
+                             |i| i.apply_local_effect(),
+                             |u| u.apply_local_effect());
+    }
+
+    fn reconstruct_terminator_effect(&mut self,
+                                     location: Location,
+                                     flow_state: &mut Self::FlowState) {
+        flow_state.each_flow(|b| b.reconstruct_terminator_effect(location),
+                             |i| i.reconstruct_terminator_effect(location),
+                             |u| u.reconstruct_terminator_effect(location));
+    }
+
+    fn visit_block_entry(&mut self,
+                         bb: BasicBlock,
+                         flow_state: &Self::FlowState) {
+        let summary = flow_state.summary();
+        debug!("MirBorrowckCtxt::process_block({:?}): {}", bb, summary);
+    }
+
+    fn visit_statement_entry(&mut self,
+                             location: Location,
+                             stmt: &Statement<'gcx>,
+                             flow_state: &Self::FlowState) {
+        let summary = flow_state.summary();
+        debug!("MirBorrowckCtxt::process_statement({:?}, {:?}): {}", location, stmt, summary);
+        let span = stmt.source_info.span;
+        match stmt.kind {
+            StatementKind::Assign(ref lhs, ref rhs) => {
+                self.mutate_lvalue(ContextKind::AssignLhs.new(location),
+                                   (lhs, span), JustWrite, flow_state);
+                self.consume_rvalue(ContextKind::AssignRhs.new(location),
+                                    (rhs, span), location, flow_state);
+            }
+            StatementKind::SetDiscriminant { ref lvalue, variant_index: _ } => {
+                self.mutate_lvalue(ContextKind::SetDiscrim.new(location),
+                                   (lvalue, span), JustWrite, flow_state);
+            }
+            StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => {
+                for (o, output) in asm.outputs.iter().zip(outputs) {
+                    if o.is_indirect {
+                        self.consume_lvalue(ContextKind::InlineAsm.new(location),
+                                            Consume,
+                                            (output, span),
+                                            flow_state);
+                    } else {
+                        self.mutate_lvalue(ContextKind::InlineAsm.new(location),
+                                           (output, span),
+                                           if o.is_rw { WriteAndRead } else { JustWrite },
+                                           flow_state);
+                    }
+                }
+                for input in inputs {
+                    self.consume_operand(ContextKind::InlineAsm.new(location),
+                                         Consume,
+                                         (input, span), flow_state);
+                }
+            }
+            StatementKind::EndRegion(ref _rgn) => {
+                // ignored when consuming results (update to
+                // flow_state already handled).
+            }
+            StatementKind::Nop |
+            StatementKind::Validate(..) |
+            StatementKind::StorageLive(..) => {
+                // ignored by borrowck
+            }
+
+            StatementKind::StorageDead(ref lvalue) => {
+                // causes non-drop values to be dropped.
+                self.consume_lvalue(ContextKind::StorageDead.new(location),
+                                    ConsumeKind::Consume,
+                                    (lvalue, span),
+                                    flow_state)
+            }
+        }
+    }
+
+    fn visit_terminator_entry(&mut self,
+                              location: Location,
+                              term: &Terminator<'gcx>,
+                              flow_state: &Self::FlowState) {
+        let loc = location;
+        let summary = flow_state.summary();
+        debug!("MirBorrowckCtxt::process_terminator({:?}, {:?}): {}", location, term, summary);
+        let span = term.source_info.span;
+        match term.kind {
+            TerminatorKind::SwitchInt { ref discr, switch_ty: _, values: _, targets: _ } => {
+                self.consume_operand(ContextKind::SwitchInt.new(loc),
+                                     Consume,
+                                     (discr, span), flow_state);
+            }
+            TerminatorKind::Drop { location: ref drop_lvalue, target: _, unwind: _ } => {
+                self.consume_lvalue(ContextKind::Drop.new(loc),
+                                    ConsumeKind::Drop,
+                                    (drop_lvalue, span), flow_state);
+            }
+            TerminatorKind::DropAndReplace { location: ref drop_lvalue,
+                                             value: ref new_value,
+                                             target: _,
+                                             unwind: _ } => {
+                self.mutate_lvalue(ContextKind::DropAndReplace.new(loc),
+                                   (drop_lvalue, span), JustWrite, flow_state);
+                self.consume_operand(ContextKind::DropAndReplace.new(loc),
+                                     ConsumeKind::Drop,
+                                     (new_value, span), flow_state);
+            }
+            TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => {
+                self.consume_operand(ContextKind::CallOperator.new(loc),
+                                     Consume,
+                                     (func, span), flow_state);
+                for arg in args {
+                    self.consume_operand(ContextKind::CallOperand.new(loc),
+                                         Consume,
+                                         (arg, span), flow_state);
+                }
+                if let Some((ref dest, _/*bb*/)) = *destination {
+                    self.mutate_lvalue(ContextKind::CallDest.new(loc),
+                                       (dest, span), JustWrite, flow_state);
+                }
+            }
+            TerminatorKind::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ } => {
+                self.consume_operand(ContextKind::Assert.new(loc),
+                                     Consume,
+                                     (cond, span), flow_state);
+                match *msg {
+                    AssertMessage::BoundsCheck { ref len, ref index } => {
+                        self.consume_operand(ContextKind::Assert.new(loc),
+                                             Consume,
+                                             (len, span), flow_state);
+                        self.consume_operand(ContextKind::Assert.new(loc),
+                                             Consume,
+                                             (index, span), flow_state);
+                    }
+                    AssertMessage::Math(_/*const_math_err*/) => {}
+                }
+            }
+
+            TerminatorKind::Goto { target: _ } |
+            TerminatorKind::Resume |
+            TerminatorKind::Return |
+            TerminatorKind::Unreachable => {
+                // no data used, thus irrelevant to borrowck
+            }
+        }
+    }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+enum MutateMode { JustWrite, WriteAndRead }
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+enum ConsumeKind { Drop, Consume }
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+enum Control { Continue, Break }
+
+impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
+    fn mutate_lvalue(&mut self,
+                     context: Context,
+                     lvalue_span: (&Lvalue<'gcx>, Span),
+                     mode: MutateMode,
+                     flow_state: &InProgress<'b, 'gcx>) {
+        // Write of P[i] or *P, or WriteAndRead of any P, requires P init'd.
+        match mode {
+            MutateMode::WriteAndRead => {
+                self.check_if_path_is_moved(context, lvalue_span, flow_state);
+            }
+            MutateMode::JustWrite => {
+                self.check_if_assigned_path_is_moved(context, lvalue_span, flow_state);
+            }
+        }
+
+        // check we don't invalidate any outstanding loans
+        self.each_borrow_involving_path(context,
+                                        lvalue_span.0, flow_state, |this, _index, _data| {
+                                            this.report_illegal_mutation_of_borrowed(context,
+                                                                                     lvalue_span);
+                                            Control::Break
+                                        });
+
+        // check for reassignments to immutable local variables
+        self.check_if_reassignment_to_immutable_state(context, lvalue_span, flow_state);
+    }
+
+    fn consume_rvalue(&mut self,
+                      context: Context,
+                      (rvalue, span): (&Rvalue<'gcx>, Span),
+                      location: Location,
+                      flow_state: &InProgress<'b, 'gcx>) {
+        match *rvalue {
+            Rvalue::Ref(_/*rgn*/, bk, ref lvalue) => {
+                self.borrow(context, location, bk, (lvalue, span), flow_state)
+            }
+
+            Rvalue::Use(ref operand) |
+            Rvalue::Repeat(ref operand, _) |
+            Rvalue::UnaryOp(_/*un_op*/, ref operand) |
+            Rvalue::Cast(_/*cast_kind*/, ref operand, _/*ty*/) => {
+                self.consume_operand(context, Consume, (operand, span), flow_state)
+            }
+
+            Rvalue::Len(ref lvalue) |
+            Rvalue::Discriminant(ref lvalue) => {
+                // len(_)/discriminant(_) merely read, not consume.
+                self.check_if_path_is_moved(context, (lvalue, span), flow_state);
+            }
+
+            Rvalue::BinaryOp(_bin_op, ref operand1, ref operand2) |
+            Rvalue::CheckedBinaryOp(_bin_op, ref operand1, ref operand2) => {
+                self.consume_operand(context, Consume, (operand1, span), flow_state);
+                self.consume_operand(context, Consume, (operand2, span), flow_state);
+            }
+
+            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`?
+            }
+
+            Rvalue::Aggregate(ref _aggregate_kind, ref operands) => {
+                for operand in operands {
+                    self.consume_operand(context, Consume, (operand, span), flow_state);
+                }
+            }
+        }
+    }
+
+    fn consume_operand(&mut self,
+                       context: Context,
+                       consume_via_drop: ConsumeKind,
+                       (operand, span): (&Operand<'gcx>, Span),
+                       flow_state: &InProgress<'b, 'gcx>) {
+        match *operand {
+            Operand::Consume(ref lvalue) =>
+                self.consume_lvalue(context, consume_via_drop, (lvalue, span), flow_state),
+            Operand::Constant(_) => {}
+        }
+    }
+
+    fn consume_lvalue(&mut self,
+                      context: Context,
+                      consume_via_drop: ConsumeKind,
+                      lvalue_span: (&Lvalue<'gcx>, Span),
+                      flow_state: &InProgress<'b, 'gcx>) {
+        let lvalue = lvalue_span.0;
+        let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx);
+        let moves_by_default =
+            self.fake_infer_ctxt.type_moves_by_default(self.param_env, ty, DUMMY_SP);
+        if moves_by_default {
+            // move of lvalue: check if this is move of already borrowed path
+            self.each_borrow_involving_path(
+                context, lvalue_span.0, flow_state, |this, _idx, borrow| {
+                    if !borrow.compatible_with(BorrowKind::Mut) {
+                        this.report_move_out_while_borrowed(context, lvalue_span);
+                        Control::Break
+                    } else {
+                        Control::Continue
+                    }
+                });
+        } else {
+            // copy of lvalue: check if this is "copy of frozen path" (FIXME: see check_loans.rs)
+            self.each_borrow_involving_path(
+                context, lvalue_span.0, flow_state, |this, _idx, borrow| {
+                    if !borrow.compatible_with(BorrowKind::Shared) {
+                        this.report_use_while_mutably_borrowed(context, lvalue_span);
+                        Control::Break
+                    } else {
+                        Control::Continue
+                    }
+                });
+        }
+
+        // Finally, check if path was already moved.
+        match consume_via_drop {
+            ConsumeKind::Drop => {
+                // If path is merely being dropped, then we'll already
+                // check the drop flag to see if it is moved (thus we
+                // skip this check in that case).
+            }
+            ConsumeKind::Consume => {
+                self.check_if_path_is_moved(context, lvalue_span, flow_state);
+            }
+        }
+    }
+
+    fn borrow(&mut self,
+              context: Context,
+              location: Location,
+              bk: BorrowKind,
+              lvalue_span: (&Lvalue<'gcx>, Span),
+              flow_state: &InProgress<'b, 'gcx>) {
+        debug!("borrow location: {:?} lvalue: {:?} span: {:?}",
+               location, lvalue_span.0, lvalue_span.1);
+        self.check_if_path_is_moved(context, lvalue_span, flow_state);
+        self.check_for_conflicting_loans(context, location, bk, lvalue_span, flow_state);
+    }
+}
+
+impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
+    fn check_if_reassignment_to_immutable_state(&mut self,
+                                                context: Context,
+                                                (lvalue, span): (&Lvalue<'gcx>, Span),
+                                                flow_state: &InProgress<'b, 'gcx>) {
+        let move_data = flow_state.inits.base_results.operator().move_data();
+
+        // determine if this path has a non-mut owner (and thus needs checking).
+        let mut l = lvalue;
+        loop {
+            match *l {
+                Lvalue::Projection(ref proj) => {
+                    l = &proj.base;
+                    continue;
+                }
+                Lvalue::Local(local) => {
+                    match self.mir.local_decls[local].mutability {
+                        Mutability::Not => break, // needs check
+                        Mutability::Mut => return,
+                    }
+                }
+                Lvalue::Static(_) => {
+                    // mutation of non-mut static is always illegal,
+                    // independent of dataflow.
+                    self.report_assignment_to_static(context, (lvalue, span));
+                    return;
+                }
+            }
+        }
+
+        if let Some(mpi) = self.move_path_for_lvalue(context, move_data, lvalue) {
+            if flow_state.inits.curr_state.contains(&mpi) {
+                // may already be assigned before reaching this statement;
+                // report error.
+                self.report_illegal_reassignment(context, (lvalue, span));
+            }
+        }
+    }
+
+    fn check_if_path_is_moved(&mut self,
+                              context: Context,
+                              lvalue_span: (&Lvalue<'gcx>, Span),
+                              flow_state: &InProgress<'b, 'gcx>) {
+        // FIXME: analogous code in check_loans first maps `lvalue` to
+        // its base_path ... but is that what we want here?
+        let lvalue = self.base_path(lvalue_span.0);
+
+        let maybe_uninits = &flow_state.uninits;
+        let move_data = maybe_uninits.base_results.operator().move_data();
+        if let Some(mpi) = self.move_path_for_lvalue(context, move_data, lvalue) {
+            if maybe_uninits.curr_state.contains(&mpi) {
+                // find and report move(s) that could cause this to be uninitialized
+
+                // FIXME: for each move in flow_state.move_outs ...
+                &flow_state.move_outs;
+
+                self.report_use_of_moved(context, lvalue_span);
+            } else {
+                // sanity check: initialized on *some* path, right?
+                assert!(flow_state.inits.curr_state.contains(&mpi));
+            }
+        }
+    }
+
+    fn move_path_for_lvalue(&mut self,
+                            _context: Context,
+                            move_data: &MoveData<'gcx>,
+                            lvalue: &Lvalue<'gcx>)
+                            -> Option<MovePathIndex>
+    {
+        // If returns None, then there is no move path corresponding
+        // to a direct owner of `lvalue` (which means there is nothing
+        // that borrowck tracks for its analysis).
+
+        match move_data.rev_lookup.find(lvalue) {
+            LookupResult::Parent(_) => None,
+            LookupResult::Exact(mpi) => Some(mpi),
+        }
+    }
+
+    fn check_if_assigned_path_is_moved(&mut self,
+                                       context: Context,
+                                       (lvalue, span): (&Lvalue<'gcx>, Span),
+                                       flow_state: &InProgress<'b, 'gcx>) {
+        // recur down lvalue; dispatch to check_if_path_is_moved when necessary
+        let mut lvalue = lvalue;
+        loop {
+            match *lvalue {
+                Lvalue::Local(_) | Lvalue::Static(_) => {
+                    // assigning to `x` does not require `x` be initialized.
+                    break;
+                }
+                Lvalue::Projection(ref proj) => {
+                    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.
+
+                                    self.check_if_path_is_moved(context,
+                                                                (base, span), flow_state);
+
+                                    // (base initialized; no need to
+                                    // recur further)
+                                    break;
+                                }
+                                _ => {}
+                            }
+                        }
+                    }
+
+                    lvalue = base;
+                    continue;
+                }
+            }
+        }
+    }
+
+    fn check_for_conflicting_loans(&mut self,
+                                   context: Context,
+                                   _location: Location,
+                                   _bk: BorrowKind,
+                                   lvalue_span: (&Lvalue<'gcx>, Span),
+                                   flow_state: &InProgress<'b, 'gcx>) {
+        // NOTE FIXME: The analogous code in old borrowck
+        // check_loans.rs is careful to iterate over every *issued*
+        // loan, as opposed to just the in scope ones.
+        //
+        // (Or if you prefer, all the *other* iterations over loans
+        // only consider loans that are in scope of some given
+        // CodeExtent)
+        //
+        // The (currently skeletal) code here does not encode such a
+        // distinction, which means it is almost certainly over
+        // looking something.
+        //
+        // (It is probably going to reject code that should be
+        // accepted, I suspect, by treated issued-but-out-of-scope
+        // loans as issued-and-in-scope, and thus causing them to
+        // interfere with other loans.)
+        //
+        // However, I just want to get something running, especially
+        // since I am trying to move into new territory with NLL, so
+        // lets get this going first, and then address the issued vs
+        // in-scope distinction later.
+
+        let state = &flow_state.borrows;
+        let data = &state.base_results.operator().borrows();
+
+        debug!("check_for_conflicting_loans location: {:?}", _location);
+
+        // does any loan generated here conflict with a previously issued loan?
+        let mut loans_generated = 0;
+        for (g, gen) in state.elems_generated().map(|g| (g, &data[g])) {
+            loans_generated += 1;
+            for (i, issued) in state.elems_incoming().map(|i| (i, &data[i])) {
+                debug!("check_for_conflicting_loans gen: {:?} issued: {:?} conflicts: {}",
+                       (g, gen, self.base_path(&gen.lvalue),
+                        self.restrictions(&gen.lvalue).collect::<Vec<_>>()),
+                       (i, issued, self.base_path(&issued.lvalue),
+                        self.restrictions(&issued.lvalue).collect::<Vec<_>>()),
+                       self.conflicts_with(gen, issued));
+                if self.conflicts_with(gen, issued) {
+                    self.report_conflicting_borrow(context, lvalue_span, gen, issued);
+                }
+            }
+        }
+
+        // MIR statically ensures each statement gens *at most one*
+        // loan; mutual conflict (within a statement) can't arise.
+        //
+        // As safe-guard, assert that above property actually holds.
+        assert!(loans_generated <= 1);
+    } }
+
+impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
+    fn each_borrow_involving_path<F>(&mut self,
+                                     _context: Context,
+                                     lvalue: &Lvalue<'gcx>,
+                                     flow_state: &InProgress<'b, 'gcx>,
+                                     mut op: F)
+        where F: FnMut(&mut Self, BorrowIndex, &BorrowData<'gcx>) -> Control
+    {
+        // FIXME: analogous code in check_loans first maps `lvalue` to
+        // its base_path.
+
+        let domain = flow_state.borrows.base_results.operator();
+        let data = domain.borrows();
+
+        // check for loan restricting path P being used. Accounts for
+        // borrows of P, P.a.b, etc.
+        for i in flow_state.borrows.elems_incoming() {
+            // FIXME: check_loans.rs filtered this to "in scope"
+            // loans; i.e. it took a scope S and checked that each
+            // restriction's kill_scope was a superscope of S.
+            let borrowed = &data[i];
+            for restricted in self.restrictions(&borrowed.lvalue) {
+                if restricted == lvalue {
+                    let ctrl = op(self, i, borrowed);
+                    if ctrl == Control::Break { return; }
+                }
+            }
+        }
+
+        // check for loans (not restrictions) on any base path.
+        // e.g. Rejects `{ let x = &mut a.b; let y = a.b.c; }`,
+        // since that moves out of borrowed path `a.b`.
+        //
+        // Limiting to loans (not restrictions) keeps this one
+        // working: `{ let x = &mut a.b; let y = a.c; }`
+        let mut cursor = lvalue;
+        loop {
+            // FIXME: check_loans.rs invoked `op` *before* cursor
+            // shift here.  Might just work (and even avoid redundant
+            // errors?) given code above?  But for now, I want to try
+            // doing what I think is more "natural" check.
+            for i in flow_state.borrows.elems_incoming() {
+                let borrowed = &data[i];
+                if borrowed.lvalue == *cursor {
+                    let ctrl = op(self, i, borrowed);
+                    if ctrl == Control::Break { return; }
+                }
+            }
+
+            match *cursor {
+                Lvalue::Local(_) | Lvalue::Static(_) => break,
+                Lvalue::Projection(ref proj) => cursor = &proj.base,
+            }
+        }
+    }
+}
+
+mod restrictions {
+    use super::MirBorrowckCtxt;
+
+    use rustc::hir;
+    use rustc::ty::{self, TyCtxt};
+    use rustc::mir::{Lvalue, Mir, Operand, ProjectionElem};
+
+    pub(super) struct Restrictions<'c, 'tcx: 'c> {
+        mir: &'c Mir<'tcx>,
+        tcx: TyCtxt<'c, 'tcx, 'tcx>,
+        lvalue_stack: Vec<&'c Lvalue<'tcx>>,
+    }
+
+    impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
+        pub(super) fn restrictions<'d>(&self,
+                                       lvalue: &'d Lvalue<'gcx>)
+                                       -> Restrictions<'d, 'gcx> where 'b: 'd
+        {
+            let lvalue_stack = if self.has_restrictions(lvalue) { vec![lvalue] } else { vec![] };
+            Restrictions { lvalue_stack: lvalue_stack, mir: self.mir, tcx: self.tcx }
+        }
+
+        fn has_restrictions(&self, lvalue: &Lvalue<'gcx>) -> bool {
+            let mut cursor = lvalue;
+            loop {
+                let proj = match *cursor {
+                    Lvalue::Local(_) => return true,
+                    Lvalue::Static(_) => return false,
+                    Lvalue::Projection(ref proj) => proj,
+                };
+                match proj.elem {
+                    ProjectionElem::Index(..) |
+                    ProjectionElem::ConstantIndex { .. } |
+                    ProjectionElem::Downcast(..) |
+                    ProjectionElem::Subslice { .. } |
+                    ProjectionElem::Field(_/*field*/, _/*ty*/) => {
+                        cursor = &proj.base;
+                        continue;
+                    }
+                    ProjectionElem::Deref => {
+                        let ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
+                        match ty.sty {
+                            ty::TyRawPtr(_) => {
+                                return false;
+                            }
+                            ty::TyRef(_, ty::TypeAndMut { ty: _, mutbl: hir::MutImmutable }) => {
+                                // FIXME: do I need to check validity of
+                                // region here though? (I think the original
+                                // check_loans code did, like readme says)
+                                return false;
+                            }
+                            ty::TyRef(_, ty::TypeAndMut { ty: _, mutbl: hir::MutMutable }) => {
+                                cursor = &proj.base;
+                                continue;
+                            }
+                            ty::TyAdt(..) if ty.is_box() => {
+                                cursor = &proj.base;
+                                continue;
+                            }
+                            _ => {
+                                panic!("unknown type fed to Projection Deref.");
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    impl<'c, 'tcx> Iterator for Restrictions<'c, 'tcx> {
+        type Item = &'c Lvalue<'tcx>;
+        fn next(&mut self) -> Option<Self::Item> {
+            'pop: loop {
+                let lvalue = match self.lvalue_stack.pop() {
+                    None => return None,
+                    Some(lvalue) => lvalue,
+                };
+
+                // `lvalue` may not be a restriction itself, but may
+                // hold one further down (e.g. we never return
+                // downcasts here, but may return a base of a
+                // downcast).
+                //
+                // Also, we need to enqueue any additional
+                // subrestrictions that it implies, since we can only
+                // return from from this call alone.
+
+                let mut cursor = lvalue;
+                'cursor: loop {
+                    let proj = match *cursor {
+                        Lvalue::Local(_) => return Some(cursor), // search yielded this leaf
+                        Lvalue::Static(_) => continue 'pop, // fruitless leaf; try next on stack
+                        Lvalue::Projection(ref proj) => proj,
+                    };
+
+                    match proj.elem {
+                        ProjectionElem::Field(_/*field*/, _/*ty*/) => {
+                            // FIXME: add union handling
+                            self.lvalue_stack.push(&proj.base);
+                            return Some(cursor);
+                        }
+                        ProjectionElem::Downcast(..) |
+                        ProjectionElem::Subslice { .. } |
+                        ProjectionElem::ConstantIndex { .. } |
+                        ProjectionElem::Index(Operand::Constant(..)) => {
+                            cursor = &proj.base;
+                            continue 'cursor;
+                        }
+                        ProjectionElem::Index(Operand::Consume(ref index)) => {
+                            self.lvalue_stack.push(index); // FIXME: did old borrowck do this?
+                            cursor = &proj.base;
+                            continue 'cursor;
+                        }
+                        ProjectionElem::Deref => {
+                            // (handled below)
+                        }
+                    }
+
+                    assert_eq!(proj.elem, ProjectionElem::Deref);
+
+                    let ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
+                    match ty.sty {
+                        ty::TyRawPtr(_) => {
+                            // borrowck ignores raw ptrs; treat analogous to imm borrow
+                            continue 'pop;
+                        }
+                        // R-Deref-Imm-Borrowed
+                        ty::TyRef(_/*rgn*/, ty::TypeAndMut { ty: _, mutbl: hir::MutImmutable }) => {
+                            // immutably-borrowed referents do not
+                            // have recursively-implied restrictions
+                            // (because preventing actions on `*LV`
+                            // does nothing about aliases like `*LV1`)
+
+                            // FIXME: do I need to check validity of
+                            // `_r` here though? (I think the original
+                            // check_loans code did, like the readme
+                            // says)
+
+                            // (And do I *really* not have to
+                            // recursively process the `base` as a
+                            // further search here? Leaving this `if
+                            // false` here as a hint to look at this
+                            // again later.
+                            //
+                            // Ah, it might be because the
+                            // restrictions are distinct from the path
+                            // substructure. Note that there is a
+                            // separate loop over the path
+                            // substructure in fn
+                            // each_borrow_involving_path, for better
+                            // or for worse.
+
+                            if false {
+                                cursor = &proj.base;
+                                continue 'cursor;
+                            } else {
+                                continue 'pop;
+                            }
+                        }
+
+                        // R-Deref-Mut-Borrowed
+                        ty::TyRef(_/*rgn*/, ty::TypeAndMut { ty: _, mutbl: hir::MutMutable }) => {
+                            // mutably-borrowed referents are
+                            // themselves restricted.
+
+                            // FIXME: do I need to check validity of
+                            // `_r` here though? (I think the original
+                            // check_loans code did, like the readme
+                            // says)
+
+                            // schedule base for future iteration.
+                            self.lvalue_stack.push(&proj.base);
+                            return Some(cursor); // search yielded interior node
+                        }
+
+                        // R-Deref-Send-Pointer
+                        ty::TyAdt(..) if ty.is_box() => {
+                            // borrowing interior of a box implies that
+                            // its base can no longer be mutated (o/w box
+                            // storage would be freed)
+                            self.lvalue_stack.push(&proj.base);
+                            return Some(cursor); // search yielded interior node
+                        }
+
+                        _ => panic!("unknown type fed to Projection Deref."),
+                    }
+                }
+            }
+        }
+    }
+}
+
+impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
+    fn report_use_of_moved(&mut self,
+                           _context: Context,
+                           (lvalue, span): (&Lvalue, Span)) {
+        let mut err = self.tcx.cannot_act_on_uninitialized_variable(
+            span, "use", &self.describe_lvalue(lvalue), Origin::Mir);
+        // FIXME: add span_label for use of uninitialized variable
+        err.emit();
+    }
+
+    fn report_move_out_while_borrowed(&mut self,
+                                      _context: Context,
+                                      (lvalue, span): (&Lvalue, Span)) {
+        let mut err = self.tcx.cannot_move_when_borrowed(
+            span, &self.describe_lvalue(lvalue), Origin::Mir);
+        // FIXME 1: add span_label for "borrow of `()` occurs here"
+        // FIXME 2: add span_label for "move out of `{}` occurs here"
+        err.emit();
+    }
+
+    fn report_use_while_mutably_borrowed(&mut self,
+                                         _context: Context,
+                                         (lvalue, span): (&Lvalue, Span)) {
+        let mut err = self.tcx.cannot_use_when_mutably_borrowed(
+            span, &self.describe_lvalue(lvalue), Origin::Mir);
+        // FIXME 1: add span_label for "borrow of `()` occurs here"
+        // FIXME 2: add span_label for "use of `{}` occurs here"
+        err.emit();
+    }
+
+    fn report_conflicting_borrow(&mut self,
+                                 _context: Context,
+                                 (lvalue, span): (&Lvalue, Span),
+                                 loan1: &BorrowData,
+                                 loan2: &BorrowData) {
+        // FIXME: obviously falsifiable. Generalize for non-eq lvalues later.
+        assert_eq!(loan1.lvalue, loan2.lvalue);
+
+        // FIXME: supply non-"" `opt_via` when appropriate
+        let mut err = match (loan1.kind, "immutable", "mutable",
+                             loan2.kind, "immutable", "mutable") {
+            (BorrowKind::Shared, lft, _, BorrowKind::Mut, _, rgt) |
+            (BorrowKind::Mut, _, lft, BorrowKind::Shared, rgt, _) |
+            (BorrowKind::Mut, _, lft, BorrowKind::Mut, _, rgt) =>
+                self.tcx.cannot_reborrow_already_borrowed(
+                    span, &self.describe_lvalue(lvalue),
+                    "", lft, "it", rgt, "", Origin::Mir),
+
+            _ =>  self.tcx.cannot_mutably_borrow_multiply(
+                span, &self.describe_lvalue(lvalue), "", Origin::Mir),
+            // FIXME: add span labels for first and second mutable borrows, as well as
+            // end point for first.
+        };
+        err.emit();
+    }
+
+    fn report_illegal_mutation_of_borrowed(&mut self, _: Context, (lvalue, span): (&Lvalue, Span)) {
+        let mut err = self.tcx.cannot_assign_to_borrowed(
+            span, &self.describe_lvalue(lvalue), Origin::Mir);
+        // FIXME: add span labels for borrow and assignment points
+        err.emit();
+    }
+
+    fn report_illegal_reassignment(&mut self, _context: Context, (lvalue, span): (&Lvalue, Span)) {
+        let mut err = self.tcx.cannot_reassign_immutable(
+            span, &self.describe_lvalue(lvalue), Origin::Mir);
+        // FIXME: add span labels for borrow and assignment points
+        err.emit();
+    }
+
+    fn report_assignment_to_static(&mut self, _context: Context, (lvalue, span): (&Lvalue, Span)) {
+        let mut err = self.tcx.cannot_assign_static(
+            span, &self.describe_lvalue(lvalue), Origin::Mir);
+        // FIXME: add span labels for borrow and assignment points
+        err.emit();
+    }
+}
+
+impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
+    // End-user visible description of `lvalue`
+    fn describe_lvalue(&self, lvalue: &Lvalue) -> String {
+        let mut buf = String::new();
+        self.append_lvalue_to_string(lvalue, &mut buf);
+        buf
+    }
+
+    // Appends end-user visible description of `lvalue` to `buf`.
+    fn append_lvalue_to_string(&self, lvalue: &Lvalue, buf: &mut String) {
+        match *lvalue {
+            Lvalue::Local(local) => {
+                let local = &self.mir.local_decls[local];
+                match local.name {
+                    Some(name) => buf.push_str(&format!("{}", name)),
+                    None => buf.push_str("_"),
+                }
+            }
+            Lvalue::Static(ref static_) => {
+                buf.push_str(&format!("{}", &self.tcx.item_name(static_.def_id)));
+            }
+            Lvalue::Projection(ref proj) => {
+                let (prefix, suffix, index_operand) = match proj.elem {
+                    ProjectionElem::Deref =>
+                        ("(*", format!(")"), None),
+                    ProjectionElem::Downcast(..) =>
+                        ("",   format!(""), None), // (dont emit downcast info)
+                    ProjectionElem::Field(field, _ty) =>
+                        ("",   format!(".{}", field.index()), None),
+                    ProjectionElem::Index(ref index) =>
+                        ("",   format!(""), Some(index)),
+                    ProjectionElem::ConstantIndex { offset, min_length, from_end: true } =>
+                        ("",   format!("[{} of {}]", offset, min_length), None),
+                    ProjectionElem::ConstantIndex { offset, min_length, from_end: false } =>
+                        ("",   format!("[-{} of {}]", offset, min_length), None),
+                    ProjectionElem::Subslice { from, to: 0 } =>
+                        ("",   format!("[{}:]", from), None),
+                    ProjectionElem::Subslice { from: 0, to } =>
+                        ("",   format!("[:-{}]", to), None),
+                    ProjectionElem::Subslice { from, to } =>
+                        ("",   format!("[{}:-{}]", from, to), None),
+                };
+                buf.push_str(prefix);
+                self.append_lvalue_to_string(&proj.base, buf);
+                if let Some(index) = index_operand {
+                    buf.push_str("[");
+                    self.append_operand_to_string(index, buf);
+                    buf.push_str("]");
+                } else {
+                    buf.push_str(&suffix);
+                }
+
+            }
+        }
+    }
+
+    fn append_operand_to_string(&self, operand: &Operand, buf: &mut String) {
+        match *operand {
+            Operand::Consume(ref lvalue) => {
+                self.append_lvalue_to_string(lvalue, buf);
+            }
+            Operand::Constant(ref constant) => {
+                buf.push_str(&format!("{:?}", constant));
+            }
+        }
+    }
+}
+
+impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
+    // FIXME: needs to be able to express errors analogous to check_loans.rs
+    fn conflicts_with(&self, loan1: &BorrowData<'gcx>, loan2: &BorrowData<'gcx>) -> bool {
+        if loan1.compatible_with(loan2.kind) { return false; }
+
+        let loan2_base_path = self.base_path(&loan2.lvalue);
+        for restricted in self.restrictions(&loan1.lvalue) {
+            if restricted != loan2_base_path { continue; }
+            return true;
+        }
+
+        let loan1_base_path = self.base_path(&loan1.lvalue);
+        for restricted in self.restrictions(&loan2.lvalue) {
+            if restricted != loan1_base_path { continue; }
+            return true;
+        }
+
+        return false;
+    }
+
+    // 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...)
+
+    fn base_path<'d>(&self, lvalue: &'d Lvalue<'gcx>) -> &'d Lvalue<'gcx> {
+        //! Returns the base of the leftmost (deepest) dereference of an
+        //! Box in `lvalue`. If there is no dereference of an Box
+        //! in `lvalue`, then it just returns `lvalue` itself.
+
+        let mut cursor = lvalue;
+        let mut deepest = lvalue;
+        loop {
+            let proj = match *cursor {
+                Lvalue::Local(..) | Lvalue::Static(..) => return deepest,
+                Lvalue::Projection(ref proj) => proj,
+            };
+            if proj.elem == ProjectionElem::Deref &&
+                lvalue.ty(self.mir, self.tcx).to_ty(self.tcx).is_box()
+            {
+                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 {
+    AssignLhs,
+    AssignRhs,
+    SetDiscrim,
+    InlineAsm,
+    SwitchInt,
+    Drop,
+    DropAndReplace,
+    CallOperator,
+    CallOperand,
+    CallDest,
+    Assert,
+    StorageDead,
+}
+
+impl ContextKind {
+    fn new(self, loc: Location) -> Context { Context { kind: self, loc: loc } }
+}
+
+impl<'b, 'tcx: 'b> InProgress<'b, 'tcx> {
+    pub(super) fn new(borrows: DataflowResults<Borrows<'b, 'tcx>>,
+                      inits: DataflowResults<MaybeInitializedLvals<'b, 'tcx>>,
+                      uninits: DataflowResults<MaybeUninitializedLvals<'b, 'tcx>>,
+                      move_outs: DataflowResults<MovingOutStatements<'b, 'tcx>>) -> Self {
+        InProgress {
+            borrows: FlowInProgress::new(borrows),
+            inits: FlowInProgress::new(inits),
+            uninits: FlowInProgress::new(uninits),
+            move_outs: FlowInProgress::new(move_outs),
+        }
+    }
+
+    fn each_flow<XB, XI, XU>(&mut self,
+                             mut xform_borrows: XB,
+                             mut xform_inits: XI,
+                             mut xform_uninits: XU) where
+        XB: FnMut(&mut FlowInProgress<Borrows<'b, 'tcx>>),
+        XI: FnMut(&mut FlowInProgress<MaybeInitializedLvals<'b, 'tcx>>),
+        XU: FnMut(&mut FlowInProgress<MaybeUninitializedLvals<'b, 'tcx>>),
+    {
+        xform_borrows(&mut self.borrows);
+        xform_inits(&mut self.inits);
+        xform_uninits(&mut self.uninits);
+    }
+
+    fn summary(&self) -> String {
+        let mut s = String::new();
+
+        s.push_str("borrows in effect: [");
+        let mut saw_one = false;
+        self.borrows.each_state_bit(|borrow| {
+            if saw_one { s.push_str(", "); };
+            saw_one = true;
+            let borrow_data = &self.borrows.base_results.operator().borrows()[borrow];
+            s.push_str(&format!("{}", borrow_data));
+        });
+        s.push_str("] ");
+
+        s.push_str("borrows generated: [");
+        let mut saw_one = false;
+        self.borrows.each_gen_bit(|borrow| {
+            if saw_one { s.push_str(", "); };
+            saw_one = true;
+            let borrow_data = &self.borrows.base_results.operator().borrows()[borrow];
+            s.push_str(&format!("{}", borrow_data));
+        });
+        s.push_str("] ");
+
+        s.push_str("inits: [");
+        let mut saw_one = false;
+        self.inits.each_state_bit(|mpi_init| {
+            if saw_one { s.push_str(", "); };
+            saw_one = true;
+            let move_path =
+                &self.inits.base_results.operator().move_data().move_paths[mpi_init];
+            s.push_str(&format!("{}", move_path));
+        });
+        s.push_str("] ");
+
+        s.push_str("uninits: [");
+        let mut saw_one = false;
+        self.uninits.each_state_bit(|mpi_uninit| {
+            if saw_one { s.push_str(", "); };
+            saw_one = true;
+            let move_path =
+                &self.uninits.base_results.operator().move_data().move_paths[mpi_uninit];
+            s.push_str(&format!("{}", move_path));
+        });
+        s.push_str("]");
+
+        return s;
+    }
+}
+
+impl<BD> FlowInProgress<BD> where BD: BitDenotation {
+    fn each_state_bit<F>(&self, f: F) where F: FnMut(BD::Idx) {
+        self.curr_state.each_bit(self.base_results.operator().bits_per_block(), f)
+    }
+
+    fn each_gen_bit<F>(&self, f: F) where F: FnMut(BD::Idx) {
+        self.stmt_gen.each_bit(self.base_results.operator().bits_per_block(), f)
+    }
+
+    fn new(results: DataflowResults<BD>) -> Self {
+        let bits_per_block = results.sets().bits_per_block();
+        let curr_state = IdxSetBuf::new_empty(bits_per_block);
+        let stmt_gen = IdxSetBuf::new_empty(bits_per_block);
+        let stmt_kill = IdxSetBuf::new_empty(bits_per_block);
+        FlowInProgress {
+            base_results: results,
+            curr_state: curr_state,
+            stmt_gen: stmt_gen,
+            stmt_kill: stmt_kill,
+        }
+    }
+
+    fn reset_to_entry_of(&mut self, bb: BasicBlock) {
+        (*self.curr_state).clone_from(self.base_results.sets().on_entry_set_for(bb.index()));
+    }
+
+    fn reconstruct_statement_effect(&mut self, loc: Location) {
+        self.stmt_gen.reset_to_empty();
+        self.stmt_kill.reset_to_empty();
+        let mut ignored = IdxSetBuf::new_empty(0);
+        let mut sets = BlockSets {
+            on_entry: &mut ignored, gen_set: &mut self.stmt_gen, kill_set: &mut self.stmt_kill,
+        };
+        self.base_results.operator().statement_effect(&mut sets, loc);
+    }
+
+    fn reconstruct_terminator_effect(&mut self, loc: Location) {
+        self.stmt_gen.reset_to_empty();
+        self.stmt_kill.reset_to_empty();
+        let mut ignored = IdxSetBuf::new_empty(0);
+        let mut sets = BlockSets {
+            on_entry: &mut ignored, gen_set: &mut self.stmt_gen, kill_set: &mut self.stmt_kill,
+        };
+        self.base_results.operator().terminator_effect(&mut sets, loc);
+    }
+
+    fn apply_local_effect(&mut self) {
+        self.curr_state.union(&self.stmt_gen);
+        self.curr_state.subtract(&self.stmt_kill);
+    }
+
+    fn elems_generated(&self) -> indexed_set::Elems<BD::Idx> {
+        let univ = self.base_results.sets().bits_per_block();
+        self.stmt_gen.elems(univ)
+    }
+
+    fn elems_incoming(&self) -> indexed_set::Elems<BD::Idx> {
+        let univ = self.base_results.sets().bits_per_block();
+        self.curr_state.elems(univ)
+    }
+}
+
+impl<'tcx> BorrowData<'tcx> {
+    fn compatible_with(&self, bk: BorrowKind) -> bool {
+        match (self.kind, bk) {
+            (BorrowKind::Shared, BorrowKind::Shared) => true,
+
+            (BorrowKind::Mut, _) |
+            (BorrowKind::Unique, _) |
+            (_, BorrowKind::Mut) |
+            (_, BorrowKind::Unique) => false,
+        }
+    }
+}
index cc61f3c11d799a19fb140f07ce27e68ffcf96560..d8564f858e5683481f49ef46d8bde68018134f79 100644 (file)
@@ -31,6 +31,7 @@
 pub mod erase_regions;
 pub mod no_landing_pads;
 pub mod type_check;
+pub mod borrow_check;
 pub mod rustc_peek;
 pub mod elaborate_drops;
 pub mod add_call_guards;
index 268e7a4c185b00cd8eae1559064b164619d79e1d..ceff52409b2f0f53c35140d6399cf823b2bd764e 100644 (file)
@@ -13,7 +13,7 @@
 use syntax_pos::Span;
 
 use rustc::ty::{self, TyCtxt};
-use rustc::mir::{self, Mir};
+use rustc::mir::{self, Mir, Location};
 use rustc::mir::transform::{MirPass, MirSource};
 use rustc_data_structures::indexed_set::IdxSetBuf;
 use rustc_data_structures::indexed_vec::Idx;
@@ -202,7 +202,7 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         // reset GEN and KILL sets before emulating their effect.
         for e in sets.gen_set.words_mut() { *e = 0; }
         for e in sets.kill_set.words_mut() { *e = 0; }
-        results.0.operator.statement_effect(&mut sets, bb, j);
+        results.0.operator.statement_effect(&mut sets, Location { block: bb, statement_index: j });
         sets.on_entry.union(sets.gen_set);
         sets.on_entry.subtract(sets.kill_set);
     }
diff --git a/src/librustc_mir/util/borrowck_errors.rs b/src/librustc_mir/util/borrowck_errors.rs
new file mode 100644 (file)
index 0000000..9de3072
--- /dev/null
@@ -0,0 +1,192 @@
+// Copyright 2012-2015 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.
+
+use rustc::ty::{self, TyCtxt};
+use rustc_errors::DiagnosticBuilder;
+use syntax_pos::{MultiSpan, Span};
+
+use std::fmt;
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum Origin { Ast, Mir }
+
+impl fmt::Display for Origin {
+    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
+        match *self {
+            Origin::Mir => write!(w, " (Mir)"),
+            Origin::Ast => ty::tls::with_opt(|opt_tcx| {
+                // If user passed `-Z borrowck-mir`, then include an
+                // AST origin as part of the error report
+                if let Some(tcx) = opt_tcx {
+                    if tcx.sess.opts.debugging_opts.borrowck_mir {
+                        return write!(w, " (Ast)");
+                    }
+                }
+                // otherwise, do not include the origin (i.e., print
+                // nothing at all)
+                Ok(())
+            }),
+        }
+    }
+}
+
+pub trait BorrowckErrors {
+    fn struct_span_err_with_code<'a, S: Into<MultiSpan>>(&'a self,
+                                                         sp: S,
+                                                         msg: &str,
+                                                         code: &str)
+                                                         -> DiagnosticBuilder<'a>;
+
+    fn struct_span_err<'a, S: Into<MultiSpan>>(&'a self,
+                                               sp: S,
+                                               msg: &str)
+                                               -> DiagnosticBuilder<'a>;
+
+    fn cannot_move_when_borrowed(&self, span: Span, desc: &str, o: Origin)
+                                 -> DiagnosticBuilder
+    {
+        struct_span_err!(self, span, E0505,
+                         "cannot move out of `{}` because it is borrowed{OGN}",
+                         desc, OGN=o)
+    }
+
+    fn cannot_use_when_mutably_borrowed(&self, span: Span, desc: &str, o: Origin)
+                                        -> DiagnosticBuilder
+    {
+        struct_span_err!(self, span, E0503,
+                         "cannot use `{}` because it was mutably borrowed{OGN}",
+                         desc, OGN=o)
+    }
+
+    fn cannot_act_on_uninitialized_variable(&self,
+                                            span: Span,
+                                            verb: &str,
+                                            desc: &str,
+                                            o: Origin)
+                                            -> DiagnosticBuilder
+    {
+        struct_span_err!(self, span, E0381,
+                         "{} of possibly uninitialized variable: `{}`{OGN}",
+                         verb, desc, OGN=o)
+    }
+
+    fn cannot_mutably_borrow_multiply(&self,
+                                      span: Span,
+                                      desc: &str,
+                                      opt_via: &str,
+                                      o: Origin)
+                                      -> DiagnosticBuilder
+    {
+        struct_span_err!(self, span, E0499,
+                         "cannot borrow `{}`{} as mutable more than once at a time{OGN}",
+                         desc, opt_via, OGN=o)
+    }
+
+    fn cannot_uniquely_borrow_by_two_closures(&self, span: Span, desc: &str, o: Origin)
+                                              -> DiagnosticBuilder
+    {
+        struct_span_err!(self, span, E0524,
+                         "two closures require unique access to `{}` at the same time{OGN}",
+                         desc, OGN=o)
+    }
+
+    fn cannot_uniquely_borrow_by_one_closure(&self,
+                                             span: Span,
+                                             desc_new: &str,
+                                             noun_old: &str,
+                                             msg_old: &str,
+                                             o: Origin)
+                                             -> DiagnosticBuilder
+    {
+        struct_span_err!(self, span, E0500,
+                         "closure requires unique access to `{}` but {} is already borrowed{}{OGN}",
+                         desc_new, noun_old, msg_old, OGN=o)
+    }
+
+    fn cannot_reborrow_already_uniquely_borrowed(&self,
+                                                 span: Span,
+                                                 desc_new: &str,
+                                                 msg_new: &str,
+                                                 kind_new: &str,
+                                                 o: Origin)
+                                                 -> DiagnosticBuilder
+    {
+        struct_span_err!(self, span, E0501,
+                         "cannot borrow `{}`{} as {} because previous closure \
+                          requires unique access{OGN}",
+                         desc_new, msg_new, kind_new, OGN=o)
+    }
+
+    fn cannot_reborrow_already_borrowed(&self,
+                                        span: Span,
+                                        desc_new: &str,
+                                        msg_new: &str,
+                                        kind_new: &str,
+                                        noun_old: &str,
+                                        kind_old: &str,
+                                        msg_old: &str,
+                                        o: Origin)
+                                        -> DiagnosticBuilder
+    {
+        struct_span_err!(self, span, E0502,
+                         "cannot borrow `{}`{} as {} because {} is also borrowed as {}{}{OGN}",
+                         desc_new, msg_new, kind_new, noun_old, kind_old, msg_old, OGN=o)
+    }
+
+    fn cannot_assign_to_borrowed(&self, span: Span, desc: &str, o: Origin)
+                                 -> DiagnosticBuilder
+    {
+        struct_span_err!(self, span, E0506,
+                         "cannot assign to `{}` because it is borrowed{OGN}",
+                         desc, OGN=o)
+    }
+
+    fn cannot_move_into_closure(&self, span: Span, desc: &str, o: Origin)
+                                -> DiagnosticBuilder
+    {
+        struct_span_err!(self, span, E0504,
+                         "cannot move `{}` into closure because it is borrowed{OGN}",
+                         desc, OGN=o)
+    }
+
+    fn cannot_reassign_immutable(&self, span: Span, desc: &str, o: Origin)
+                                 -> DiagnosticBuilder
+    {
+        struct_span_err!(self, span, E0384,
+                         "re-assignment of immutable variable `{}`{OGN}",
+                         desc, OGN=o)
+    }
+
+    fn cannot_assign_static(&self, span: Span, desc: &str, o: Origin)
+                            -> DiagnosticBuilder
+    {
+        self.struct_span_err(span, &format!("cannot assign to immutable static item {}{OGN}",
+                                            desc, OGN=o))
+    }
+}
+
+impl<'b, 'tcx, 'gcx> BorrowckErrors for TyCtxt<'b, 'tcx, 'gcx> {
+    fn struct_span_err_with_code<'a, S: Into<MultiSpan>>(&'a self,
+                                                         sp: S,
+                                                         msg: &str,
+                                                         code: &str)
+                                                         -> DiagnosticBuilder<'a>
+    {
+        self.sess.struct_span_err_with_code(sp, msg, code)
+    }
+
+    fn struct_span_err<'a, S: Into<MultiSpan>>(&'a self,
+                                               sp: S,
+                                               msg: &str)
+                                               -> DiagnosticBuilder<'a>
+    {
+        self.sess.struct_span_err(sp, msg)
+    }
+}
index b03fd0196a369dc9fba5711e76139866f2dba91f..4b6da96824dcd9ac126247f25713b4e7fbb36205 100644 (file)
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+pub mod borrowck_errors;
 pub mod elaborate_drops;
 pub mod def_use;
 pub mod patch;