"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",
]
"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,
use syntax::ast;
use syntax_pos::Span;
use rustc::hir;
+use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin};
use std::rc::Rc;
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
}
(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");
}
(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));
}
(_, 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));
}
(..) => {
- 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{}",
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))
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",
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",
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)))
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};
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
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;
}
_ => {
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 `{}`",
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:
```
"##,
-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.
```
"##,
-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:
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...
}
_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> {
// 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"));
// 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);
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" }
// 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)
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;
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;
--- /dev/null
+// 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
+ }
+}
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.
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)
)
}
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)
)
}
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)
)
}
}
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
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);
}
// 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;
}
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();
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);
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};
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
}
}
-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 }
}
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
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.
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>,
}
fn bottom_value() -> bool;
}
-pub trait BitDenotation {
+pub trait BitDenotation: DataflowOperator {
/// Specifies what index type is used to access the bitvector.
type Idx: Idx;
/// 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
/// 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.
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>,
}
}
-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`.
--- /dev/null
+// 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);
+ }
+}
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};
/// Index into MoveData.moves.
new_index!(MoveOutIndex, "mo");
+
+ /// Index into Borrows.locations
+ new_index!(BorrowIndex, "bw");
}
pub use self::indexes::MovePathIndex;
}
}
+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>>,
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 {
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)
}
}
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.
```
"##,
+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
}
#[macro_use]
extern crate rustc;
extern crate rustc_data_structures;
+extern crate rustc_errors;
#[macro_use]
#[no_link]
extern crate rustc_bitflags;
--- /dev/null
+// 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,
+ }
+ }
+}
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;
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;
// 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);
}
--- /dev/null
+// 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)
+ }
+}
// 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;