Auto merge of #43108 - pnkfelix:mir-borrowck3c, r=arielb1

MIR borrow check (under debug flag)

Here is the current state of MIR borrow check.

It consists of (1.) some refactoring, (2.) a dataflow analysis to identify the borrows themselves, and (3.) a mir "transform" that does the borrow check itself based on the aforementioned dataflow results.

(There's also a drive-by fix to dataflow that I can factor into a separate PR if necessary. Interestingly I could not find a way to observe the bug outside of MIR borrowck.)

To be clear, this branch is not ready to be used as the default borrow check. Thus the code is guarded: To get mir-borrowck to run, you need to either supply an attribute `#[rustc_mir_borrowck]` or a debug flag `-Z borrowck-mir`.

Here are the main issues with the current MIR borrowck as it stands in this PR:

 * No Notes emitted yet, just errors. (So the feedback is definitely inferior compared to AST borrowck today)
 * Lvalue rendering differs between Ast and Mir. (Mostly minor, but replacement of field names with indices is very bad; big priority for me to fix ASAP.)
 * Lots of ICEs (presumably because some MIR operations used here have well-formedness assumptions that are violated in borrowck-broken code)
 * Conflates lots of cases that are distinguished by AST-borrowck
 * Conflates "uninitialized" with "moved" (special case of previous bullet, one that I think should be fixed ASAP)

 (I am hoping to fix as many of the above issues as I can in the near term, but I also would like to land this even if they are *not* all fixed, because the rebasing effort is getting to be a real drag.)
This commit is contained in:
bors 2017-08-16 15:36:00 +00:00
commit 00a6797f05
22 changed files with 2940 additions and 1098 deletions

1
src/Cargo.lock generated
View file

@ -1452,6 +1452,7 @@ dependencies = [
"rustc_const_eval 0.0.0",
"rustc_const_math 0.0.0",
"rustc_data_structures 0.0.0",
"rustc_errors 0.0.0",
"syntax 0.0.0",
"syntax_pos 0.0.0",
]

View file

@ -918,6 +918,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
"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,

View file

@ -29,6 +29,7 @@ use rustc::ty::{self, TyCtxt};
use syntax::ast;
use syntax_pos::Span;
use rustc::hir;
use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin};
use std::rc::Rc;
@ -465,10 +466,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
let mut err = match (new_loan.kind, old_loan.kind) {
(ty::MutBorrow, ty::MutBorrow) => {
let mut err = struct_span_err!(self.bccx, new_loan.span, E0499,
"cannot borrow `{}`{} as mutable \
more than once at a time",
nl, new_loan_msg);
let mut err = self.bccx.cannot_mutably_borrow_multiply(
new_loan.span, &nl, &new_loan_msg, Origin::Ast);
if new_loan.span == old_loan.span {
// Both borrows are happening in the same place
@ -496,10 +495,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
}
(ty::UniqueImmBorrow, ty::UniqueImmBorrow) => {
let mut err = struct_span_err!(self.bccx, new_loan.span, E0524,
"two closures require unique access to `{}` \
at the same time",
nl);
let mut err = self.bccx.cannot_uniquely_borrow_by_two_closures(
new_loan.span, &nl, Origin::Ast);
err.span_label(
old_loan.span,
"first closure is constructed here");
@ -513,10 +510,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
}
(ty::UniqueImmBorrow, _) => {
let mut err = struct_span_err!(self.bccx, new_loan.span, E0500,
"closure requires unique access to `{}` \
but {} is already borrowed{}",
nl, ol_pronoun, old_loan_msg);
let mut err = self.bccx.cannot_uniquely_borrow_by_one_closure(
new_loan.span, &nl, &ol_pronoun, &old_loan_msg, Origin::Ast);
err.span_label(
new_loan.span,
format!("closure construction occurs here{}", new_loan_msg));
@ -530,10 +525,9 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
}
(_, ty::UniqueImmBorrow) => {
let mut err = struct_span_err!(self.bccx, new_loan.span, E0501,
"cannot borrow `{}`{} as {} because \
previous closure requires unique access",
nl, new_loan_msg, new_loan.kind.to_user_str());
let new_loan_str = &new_loan.kind.to_user_str();
let mut err = self.bccx.cannot_reborrow_already_uniquely_borrowed(
new_loan.span, &nl, &new_loan_msg, new_loan_str, Origin::Ast);
err.span_label(
new_loan.span,
format!("borrow occurs here{}", new_loan_msg));
@ -547,15 +541,10 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
}
(..) => {
let mut err = struct_span_err!(self.bccx, new_loan.span, E0502,
"cannot borrow `{}`{} as {} because \
{} is also borrowed as {}{}",
nl,
new_loan_msg,
new_loan.kind.to_user_str(),
ol_pronoun,
old_loan.kind.to_user_str(),
old_loan_msg);
let mut err = self.bccx.cannot_reborrow_already_borrowed(
new_loan.span,
&nl, &new_loan_msg, &new_loan.kind.to_user_str(),
&ol_pronoun, &old_loan.kind.to_user_str(), &old_loan_msg, Origin::Ast);
err.span_label(
new_loan.span,
format!("{} borrow occurs here{}",
@ -645,9 +634,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
match self.analyze_restrictions_on_use(id, copy_path, ty::ImmBorrow) {
UseOk => { }
UseWhileBorrowed(loan_path, loan_span) => {
struct_span_err!(self.bccx, span, E0503,
"cannot use `{}` because it was mutably borrowed",
&self.bccx.loan_path_to_string(copy_path))
let desc = self.bccx.loan_path_to_string(copy_path);
self.bccx.cannot_use_when_mutably_borrowed(span, &desc, Origin::Ast)
.span_label(loan_span,
format!("borrow of `{}` occurs here",
&self.bccx.loan_path_to_string(&loan_path))
@ -673,9 +661,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
UseWhileBorrowed(loan_path, loan_span) => {
let mut err = match move_kind {
move_data::Captured => {
let mut err = struct_span_err!(self.bccx, span, E0504,
"cannot move `{}` into closure because it is borrowed",
&self.bccx.loan_path_to_string(move_path));
let mut err = self.bccx.cannot_move_into_closure(
span, &self.bccx.loan_path_to_string(move_path), Origin::Ast);
err.span_label(
loan_span,
format!("borrow of `{}` occurs here",
@ -690,9 +677,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
move_data::Declared |
move_data::MoveExpr |
move_data::MovePat => {
let mut err = struct_span_err!(self.bccx, span, E0505,
"cannot move out of `{}` because it is borrowed",
&self.bccx.loan_path_to_string(move_path));
let desc = self.bccx.loan_path_to_string(move_path);
let mut err = self.bccx.cannot_move_when_borrowed(span, &desc, Origin::Ast);
err.span_label(
loan_span,
format!("borrow of `{}` occurs here",
@ -874,9 +860,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
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)))

View file

@ -37,6 +37,8 @@ use rustc::middle::free_region::RegionRelations;
use rustc::ty::{self, TyCtxt};
use rustc::ty::maps::Providers;
use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin};
use std::fmt;
use std::rc::Rc;
use std::hash::{Hash, Hasher};
@ -218,6 +220,25 @@ pub struct BorrowckCtxt<'a, 'tcx: 'a> {
owner_def_id: DefId,
}
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
@ -549,14 +570,13 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
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;
}
_ => {
@ -683,10 +703,9 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
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 `{}`",

View file

@ -63,27 +63,6 @@ Now that the closure has its own copy of the data, there's no need to worry
about safety.
"##,
E0381: r##"
It is not allowed to use or capture an uninitialized variable. For example:
```compile_fail,E0381
fn main() {
let x: i32;
let y = x; // error, use of possibly uninitialized variable
}
```
To fix this, ensure that any declared variables are initialized before being
used. Example:
```
fn main() {
let x: i32 = 0;
let y = x; // ok!
}
```
"##,
E0382: r##"
This error occurs when an attempt is made to use a variable after its contents
have been moved elsewhere. For example:
@ -182,28 +161,6 @@ x = Foo { a: 2 };
```
"##,
E0384: r##"
This error occurs when an attempt is made to reassign an immutable variable.
For example:
```compile_fail,E0384
fn main() {
let x = 3;
x = 5; // error, reassignment of immutable variable
}
```
By default, variables in Rust are immutable. To fix this error, add the keyword
`mut` after the keyword `let` when declaring the variable. For example:
```
fn main() {
let mut x = 3;
x = 5;
}
```
"##,
/*E0386: r##"
This error occurs when an attempt is made to mutate the target of a mutable
reference stored inside an immutable container.
@ -360,512 +317,6 @@ fn main() {
```
"##,
E0499: r##"
A variable was borrowed as mutable more than once. Erroneous code example:
```compile_fail,E0499
let mut i = 0;
let mut x = &mut i;
let mut a = &mut i;
// error: cannot borrow `i` as mutable more than once at a time
```
Please note that in rust, you can either have many immutable references, or one
mutable reference. Take a look at
https://doc.rust-lang.org/book/first-edition/references-and-borrowing.html
for more information. Example:
```
let mut i = 0;
let mut x = &mut i; // ok!
// or:
let mut i = 0;
let a = &i; // ok!
let b = &i; // still ok!
let c = &i; // still ok!
```
"##,
E0500: r##"
A borrowed variable was used in another closure. Example of erroneous code:
```compile_fail
fn you_know_nothing(jon_snow: &mut i32) {
let nights_watch = || {
*jon_snow = 2;
};
let starks = || {
*jon_snow = 3; // error: closure requires unique access to `jon_snow`
// but it is already borrowed
};
}
```
In here, `jon_snow` is already borrowed by the `nights_watch` closure, so it
cannot be borrowed by the `starks` closure at the same time. To fix this issue,
you can put the closure in its own scope:
```
fn you_know_nothing(jon_snow: &mut i32) {
{
let nights_watch = || {
*jon_snow = 2;
};
} // At this point, `jon_snow` is free.
let starks = || {
*jon_snow = 3;
};
}
```
Or, if the type implements the `Clone` trait, you can clone it between
closures:
```
fn you_know_nothing(jon_snow: &mut i32) {
let mut jon_copy = jon_snow.clone();
let nights_watch = || {
jon_copy = 2;
};
let starks = || {
*jon_snow = 3;
};
}
```
"##,
E0501: r##"
This error indicates that a mutable variable is being used while it is still
captured by a closure. Because the closure has borrowed the variable, it is not
available for use until the closure goes out of scope.
Note that a capture will either move or borrow a variable, but in this
situation, the closure is borrowing the variable. Take a look at
http://rustbyexample.com/fn/closures/capture.html for more information about
capturing.
Example of erroneous code:
```compile_fail,E0501
fn inside_closure(x: &mut i32) {
// Actions which require unique access
}
fn outside_closure(x: &mut i32) {
// Actions which require unique access
}
fn foo(a: &mut i32) {
let bar = || {
inside_closure(a)
};
outside_closure(a); // error: cannot borrow `*a` as mutable because previous
// closure requires unique access.
}
```
To fix this error, you can place the closure in its own scope:
```
fn inside_closure(x: &mut i32) {}
fn outside_closure(x: &mut i32) {}
fn foo(a: &mut i32) {
{
let bar = || {
inside_closure(a)
};
} // borrow on `a` ends.
outside_closure(a); // ok!
}
```
Or you can pass the variable as a parameter to the closure:
```
fn inside_closure(x: &mut i32) {}
fn outside_closure(x: &mut i32) {}
fn foo(a: &mut i32) {
let bar = |s: &mut i32| {
inside_closure(s)
};
outside_closure(a);
bar(a);
}
```
It may be possible to define the closure later:
```
fn inside_closure(x: &mut i32) {}
fn outside_closure(x: &mut i32) {}
fn foo(a: &mut i32) {
outside_closure(a);
let bar = || {
inside_closure(a)
};
}
```
"##,
E0502: r##"
This error indicates that you are trying to borrow a variable as mutable when it
has already been borrowed as immutable.
Example of erroneous code:
```compile_fail,E0502
fn bar(x: &mut i32) {}
fn foo(a: &mut i32) {
let ref y = a; // a is borrowed as immutable.
bar(a); // error: cannot borrow `*a` as mutable because `a` is also borrowed
// as immutable
}
```
To fix this error, ensure that you don't have any other references to the
variable before trying to access it mutably:
```
fn bar(x: &mut i32) {}
fn foo(a: &mut i32) {
bar(a);
let ref y = a; // ok!
}
```
For more information on the rust ownership system, take a look at
https://doc.rust-lang.org/book/first-edition/references-and-borrowing.html.
"##,
E0503: r##"
A value was used after it was mutably borrowed.
Example of erroneous code:
```compile_fail,E0503
fn main() {
let mut value = 3;
// Create a mutable borrow of `value`. This borrow
// lives until the end of this function.
let _borrow = &mut value;
let _sum = value + 1; // error: cannot use `value` because
// it was mutably borrowed
}
```
In this example, `value` is mutably borrowed by `borrow` and cannot be
used to calculate `sum`. This is not possible because this would violate
Rust's mutability rules.
You can fix this error by limiting the scope of the borrow:
```
fn main() {
let mut value = 3;
// By creating a new block, you can limit the scope
// of the reference.
{
let _borrow = &mut value; // Use `_borrow` inside this block.
}
// The block has ended and with it the borrow.
// You can now use `value` again.
let _sum = value + 1;
}
```
Or by cloning `value` before borrowing it:
```
fn main() {
let mut value = 3;
// We clone `value`, creating a copy.
let value_cloned = value.clone();
// The mutable borrow is a reference to `value` and
// not to `value_cloned`...
let _borrow = &mut value;
// ... which means we can still use `value_cloned`,
let _sum = value_cloned + 1;
// even though the borrow only ends here.
}
```
You can find more information about borrowing in the rust-book:
http://doc.rust-lang.org/book/first-edition/references-and-borrowing.html
"##,
E0504: r##"
This error occurs when an attempt is made to move a borrowed variable into a
closure.
Example of erroneous code:
```compile_fail,E0504
struct FancyNum {
num: u8,
}
fn main() {
let fancy_num = FancyNum { num: 5 };
let fancy_ref = &fancy_num;
let x = move || {
println!("child function: {}", fancy_num.num);
// error: cannot move `fancy_num` into closure because it is borrowed
};
x();
println!("main function: {}", fancy_ref.num);
}
```
Here, `fancy_num` is borrowed by `fancy_ref` and so cannot be moved into
the closure `x`. There is no way to move a value into a closure while it is
borrowed, as that would invalidate the borrow.
If the closure can't outlive the value being moved, try using a reference
rather than moving:
```
struct FancyNum {
num: u8,
}
fn main() {
let fancy_num = FancyNum { num: 5 };
let fancy_ref = &fancy_num;
let x = move || {
// fancy_ref is usable here because it doesn't move `fancy_num`
println!("child function: {}", fancy_ref.num);
};
x();
println!("main function: {}", fancy_num.num);
}
```
If the value has to be borrowed and then moved, try limiting the lifetime of
the borrow using a scoped block:
```
struct FancyNum {
num: u8,
}
fn main() {
let fancy_num = FancyNum { num: 5 };
{
let fancy_ref = &fancy_num;
println!("main function: {}", fancy_ref.num);
// `fancy_ref` goes out of scope here
}
let x = move || {
// `fancy_num` can be moved now (no more references exist)
println!("child function: {}", fancy_num.num);
};
x();
}
```
If the lifetime of a reference isn't enough, such as in the case of threading,
consider using an `Arc` to create a reference-counted value:
```
use std::sync::Arc;
use std::thread;
struct FancyNum {
num: u8,
}
fn main() {
let fancy_ref1 = Arc::new(FancyNum { num: 5 });
let fancy_ref2 = fancy_ref1.clone();
let x = thread::spawn(move || {
// `fancy_ref1` can be moved and has a `'static` lifetime
println!("child thread: {}", fancy_ref1.num);
});
x.join().expect("child thread should finish");
println!("main thread: {}", fancy_ref2.num);
}
```
"##,
E0505: r##"
A value was moved out while it was still borrowed.
Erroneous code example:
```compile_fail,E0505
struct Value {}
fn eat(val: Value) {}
fn main() {
let x = Value{};
{
let _ref_to_val: &Value = &x;
eat(x);
}
}
```
Here, the function `eat` takes the ownership of `x`. However,
`x` cannot be moved because it was borrowed to `_ref_to_val`.
To fix that you can do few different things:
* Try to avoid moving the variable.
* Release borrow before move.
* Implement the `Copy` trait on the type.
Examples:
```
struct Value {}
fn eat(val: &Value) {}
fn main() {
let x = Value{};
{
let _ref_to_val: &Value = &x;
eat(&x); // pass by reference, if it's possible
}
}
```
Or:
```
struct Value {}
fn eat(val: Value) {}
fn main() {
let x = Value{};
{
let _ref_to_val: &Value = &x;
}
eat(x); // release borrow and then move it.
}
```
Or:
```
#[derive(Clone, Copy)] // implement Copy trait
struct Value {}
fn eat(val: Value) {}
fn main() {
let x = Value{};
{
let _ref_to_val: &Value = &x;
eat(x); // it will be copied here.
}
}
```
You can find more information about borrowing in the rust-book:
http://doc.rust-lang.org/book/first-edition/references-and-borrowing.html
"##,
E0506: r##"
This error occurs when an attempt is made to assign to a borrowed value.
Example of erroneous code:
```compile_fail,E0506
struct FancyNum {
num: u8,
}
fn main() {
let mut fancy_num = FancyNum { num: 5 };
let fancy_ref = &fancy_num;
fancy_num = FancyNum { num: 6 };
// error: cannot assign to `fancy_num` because it is borrowed
println!("Num: {}, Ref: {}", fancy_num.num, fancy_ref.num);
}
```
Because `fancy_ref` still holds a reference to `fancy_num`, `fancy_num` can't
be assigned to a new value as it would invalidate the reference.
Alternatively, we can move out of `fancy_num` into a second `fancy_num`:
```
struct FancyNum {
num: u8,
}
fn main() {
let mut fancy_num = FancyNum { num: 5 };
let moved_num = fancy_num;
fancy_num = FancyNum { num: 6 };
println!("Num: {}, Moved num: {}", fancy_num.num, moved_num.num);
}
```
If the value has to be borrowed, try limiting the lifetime of the borrow using
a scoped block:
```
struct FancyNum {
num: u8,
}
fn main() {
let mut fancy_num = FancyNum { num: 5 };
{
let fancy_ref = &fancy_num;
println!("Ref: {}", fancy_ref.num);
}
// Works because `fancy_ref` is no longer in scope
fancy_num = FancyNum { num: 6 };
println!("Num: {}", fancy_num.num);
}
```
Or by moving the reference into a function:
```
struct FancyNum {
num: u8,
}
fn main() {
let mut fancy_num = FancyNum { num: 5 };
print_fancy_ref(&fancy_num);
// Works because function borrow has ended
fancy_num = FancyNum { num: 6 };
println!("Num: {}", fancy_num.num);
}
fn print_fancy_ref(fancy_ref: &FancyNum){
println!("Ref: {}", fancy_ref.num);
}
```
"##,
E0507: r##"
You tried to move out of a value which was borrowed. Erroneous code example:
@ -1205,7 +656,6 @@ x.x = Some(&y);
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...
}

View file

@ -153,4 +153,68 @@ impl<T: Idx> IdxSet<T> {
pub fn subtract(&mut self, other: &IdxSet<T>) -> bool {
bitwise(self.words_mut(), other.words(), &Subtract)
}
/// 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));
}
}
}
}
}
}

View file

@ -970,7 +970,12 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
// We compute "constant qualifications" between MIR_CONST and MIR_VALIDATED.
// What we need to run borrowck etc.
passes.push_pass(MIR_VALIDATED, mir::transform::qualify_consts::QualifyAndPromoteConstants);
// FIXME: ariel points SimplifyBranches should run after
// mir-borrowck; otherwise code within `if false { ... }` would
// not be checked.
passes.push_pass(MIR_VALIDATED,
mir::transform::simplify_branches::SimplifyBranches::new("initial"));
passes.push_pass(MIR_VALIDATED, mir::transform::simplify::SimplifyCfg::new("qualify-consts"));
@ -978,6 +983,10 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
// 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);

View file

@ -15,6 +15,7 @@ rustc = { path = "../librustc" }
rustc_const_eval = { path = "../librustc_const_eval" }
rustc_const_math = { path = "../librustc_const_math" }
rustc_data_structures = { path = "../librustc_data_structures" }
rustc_errors = { path = "../librustc_errors" }
rustc_bitflags = { path = "../librustc_bitflags" }
syntax = { path = "../libsyntax" }
syntax_pos = { path = "../libsyntax_pos" }

View file

@ -8,84 +8,16 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use syntax::ast::{self, MetaItem};
use syntax_pos::DUMMY_SP;
use rustc::mir::{self, Mir, BasicBlock, Location};
use rustc::session::Session;
use rustc::mir::{self, Mir, Location};
use rustc::ty::{self, TyCtxt};
use util::elaborate_drops::DropFlagState;
use rustc_data_structures::indexed_set::{IdxSet};
use std::fmt;
use super::{Dataflow, DataflowBuilder, DataflowAnalysis};
use super::{BitDenotation, DataflowOperator, DataflowResults};
use super::{MoveDataParamEnv};
use super::indexes::MovePathIndex;
use super::move_paths::{MoveData, LookupResult};
pub(crate) fn has_rustc_mir_with(attrs: &[ast::Attribute], name: &str) -> Option<MetaItem> {
for attr in attrs {
if attr.check_name("rustc_mir") {
let items = attr.meta_item_list();
for item in items.iter().flat_map(|l| l.iter()) {
match item.meta_item() {
Some(mi) if mi.check_name(name) => return Some(mi.clone()),
_ => continue
}
}
}
}
return None;
}
pub struct MoveDataParamEnv<'tcx> {
pub(crate) move_data: MoveData<'tcx>,
pub(crate) param_env: ty::ParamEnv<'tcx>,
}
pub(crate) fn do_dataflow<'a, 'tcx, BD, P>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir: &Mir<'tcx>,
node_id: ast::NodeId,
attributes: &[ast::Attribute],
dead_unwinds: &IdxSet<BasicBlock>,
bd: BD,
p: P)
-> DataflowResults<BD>
where BD: BitDenotation<Idx=MovePathIndex> + DataflowOperator,
P: Fn(&BD, BD::Idx) -> &fmt::Debug
{
let name_found = |sess: &Session, attrs: &[ast::Attribute], name| -> Option<String> {
if let Some(item) = has_rustc_mir_with(attrs, name) {
if let Some(s) = item.value_str() {
return Some(s.to_string())
} else {
sess.span_err(
item.span,
&format!("{} attribute requires a path", item.name()));
return None;
}
}
return None;
};
let print_preflow_to =
name_found(tcx.sess, attributes, "borrowck_graphviz_preflow");
let print_postflow_to =
name_found(tcx.sess, attributes, "borrowck_graphviz_postflow");
let mut mbcx = DataflowBuilder {
node_id,
print_preflow_to,
print_postflow_to,
flow_state: DataflowAnalysis::new(tcx, mir, dead_unwinds, bd),
};
mbcx.dataflow(p);
mbcx.flow_state.results()
}
pub fn move_path_children_matching<'tcx, F>(move_data: &MoveData<'tcx>,
path: MovePathIndex,
mut cond: F)

View file

@ -13,7 +13,6 @@
use syntax::ast::NodeId;
use rustc::mir::{BasicBlock, Mir};
use rustc_data_structures::bitslice::bits_to_string;
use rustc_data_structures::indexed_set::{IdxSet};
use rustc_data_structures::indexed_vec::Idx;
use dot;
@ -24,7 +23,6 @@ use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::marker::PhantomData;
use std::mem;
use std::path::Path;
use util;
@ -32,54 +30,6 @@ 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;

View file

@ -0,0 +1,180 @@
// Copyright 2012-2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use rustc::mir::{self, Location, Mir};
use rustc::mir::visit::Visitor;
use rustc::ty::{Region, TyCtxt};
use rustc::ty::RegionKind::ReScope;
use rustc::util::nodemap::{FxHashMap, FxHashSet};
use rustc_data_structures::bitslice::{BitwiseOperator};
use rustc_data_structures::indexed_set::{IdxSet};
use rustc_data_structures::indexed_vec::{IndexVec};
use dataflow::{BitDenotation, BlockSets, DataflowOperator};
pub use dataflow::indexes::BorrowIndex;
use std::fmt;
// `Borrows` maps each dataflow bit to an `Rvalue::Ref`, which can be
// uniquely identified in the MIR by the `Location` of the assigment
// statement in which it appears on the right hand side.
pub struct Borrows<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir: &'a Mir<'tcx>,
borrows: IndexVec<BorrowIndex, BorrowData<'tcx>>,
location_map: FxHashMap<Location, BorrowIndex>,
region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
}
// temporarily allow some dead fields: `kind` and `region` will be
// needed by borrowck; `lvalue` will probably be a MovePathIndex when
// that is extended to include borrowed data paths.
#[allow(dead_code)]
#[derive(Debug)]
pub struct BorrowData<'tcx> {
pub(crate) location: Location,
pub(crate) kind: mir::BorrowKind,
pub(crate) region: Region<'tcx>,
pub(crate) lvalue: mir::Lvalue<'tcx>,
}
impl<'tcx> fmt::Display for BorrowData<'tcx> {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
let kind = match self.kind {
mir::BorrowKind::Shared => "",
mir::BorrowKind::Unique => "uniq ",
mir::BorrowKind::Mut => "mut ",
};
let region = format!("{}", self.region);
let region = if region.len() > 0 { format!("{} ", region) } else { region };
write!(w, "&{}{}{:?}", region, kind, self.lvalue)
}
}
impl<'a, 'tcx> Borrows<'a, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
let mut visitor = GatherBorrows { idx_vec: IndexVec::new(),
location_map: FxHashMap(),
region_map: FxHashMap(), };
visitor.visit_mir(mir);
return Borrows { tcx: tcx,
mir: mir,
borrows: visitor.idx_vec,
location_map: visitor.location_map,
region_map: visitor.region_map, };
struct GatherBorrows<'tcx> {
idx_vec: IndexVec<BorrowIndex, BorrowData<'tcx>>,
location_map: FxHashMap<Location, BorrowIndex>,
region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
}
impl<'tcx> Visitor<'tcx> for GatherBorrows<'tcx> {
fn visit_rvalue(&mut self,
rvalue: &mir::Rvalue<'tcx>,
location: mir::Location) {
if let mir::Rvalue::Ref(region, kind, ref lvalue) = *rvalue {
let borrow = BorrowData {
location: location, kind: kind, region: region, lvalue: lvalue.clone(),
};
let idx = self.idx_vec.push(borrow);
self.location_map.insert(location, idx);
let borrows = self.region_map.entry(region).or_insert(FxHashSet());
borrows.insert(idx);
}
}
}
}
pub fn borrows(&self) -> &IndexVec<BorrowIndex, BorrowData<'tcx>> { &self.borrows }
pub fn location(&self, idx: BorrowIndex) -> &Location {
&self.borrows[idx].location
}
}
impl<'a, 'tcx> BitDenotation for Borrows<'a, 'tcx> {
type Idx = BorrowIndex;
fn name() -> &'static str { "borrows" }
fn bits_per_block(&self) -> usize {
self.borrows.len()
}
fn start_block_effect(&self, _sets: &mut BlockSets<BorrowIndex>) {
// no borrows of code extents have been taken prior to
// function execution, so this method has no effect on
// `_sets`.
}
fn statement_effect(&self,
sets: &mut BlockSets<BorrowIndex>,
location: Location) {
let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| {
panic!("could not find block at location {:?}", location);
});
let stmt = block.statements.get(location.statement_index).unwrap_or_else(|| {
panic!("could not find statement at location {:?}");
});
match stmt.kind {
mir::StatementKind::EndRegion(extent) => {
let borrow_indexes = self.region_map.get(&ReScope(extent)).unwrap_or_else(|| {
panic!("could not find BorrowIndexs for code-extent {:?}", extent);
});
for idx in borrow_indexes { sets.kill(&idx); }
}
mir::StatementKind::Assign(_, ref rhs) => {
if let mir::Rvalue::Ref(region, _, _) = *rhs {
let index = self.location_map.get(&location).unwrap_or_else(|| {
panic!("could not find BorrowIndex for location {:?}", location);
});
assert!(self.region_map.get(region).unwrap_or_else(|| {
panic!("could not find BorrowIndexs for region {:?}", region);
}).contains(&index));
sets.gen(&index);
}
}
mir::StatementKind::InlineAsm { .. } |
mir::StatementKind::SetDiscriminant { .. } |
mir::StatementKind::StorageLive(..) |
mir::StatementKind::StorageDead(..) |
mir::StatementKind::Validate(..) |
mir::StatementKind::Nop => {}
}
}
fn terminator_effect(&self,
_sets: &mut BlockSets<BorrowIndex>,
_location: Location) {
// no terminators start nor end code extents.
}
fn propagate_call_return(&self,
_in_out: &mut IdxSet<BorrowIndex>,
_call_bb: mir::BasicBlock,
_dest_bb: mir::BasicBlock,
_dest_lval: &mir::Lvalue) {
// there are no effects on the extents from method calls.
}
}
impl<'a, 'tcx> BitwiseOperator for Borrows<'a, 'tcx> {
#[inline]
fn join(&self, pred1: usize, pred2: usize) -> usize {
pred1 | pred2 // union effects of preds when computing borrows
}
}
impl<'a, 'tcx> DataflowOperator for Borrows<'a, 'tcx> {
#[inline]
fn bottom_value() -> bool {
false // bottom = no Rvalue::Refs are active by default
}
}

View file

@ -30,6 +30,9 @@ use super::drop_flag_effects_for_function_entry;
use super::drop_flag_effects_for_location;
use super::on_lookup_result_bits;
#[allow(dead_code)]
pub(super) mod borrows;
/// `MaybeInitializedLvals` tracks all l-values that might be
/// initialized upon reaching a particular point in the control flow
/// for a function.
@ -287,24 +290,22 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> {
fn statement_effect(&self,
sets: &mut BlockSets<MovePathIndex>,
bb: mir::BasicBlock,
idx: usize)
location: Location)
{
drop_flag_effects_for_location(
self.tcx, self.mir, self.mdpe,
Location { block: bb, statement_index: idx },
location,
|path, s| Self::update_bits(sets, path, s)
)
}
fn terminator_effect(&self,
sets: &mut BlockSets<MovePathIndex>,
bb: mir::BasicBlock,
statements_len: usize)
location: Location)
{
drop_flag_effects_for_location(
self.tcx, self.mir, self.mdpe,
Location { block: bb, statement_index: statements_len },
location,
|path, s| Self::update_bits(sets, path, s)
)
}
@ -344,24 +345,22 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> {
fn statement_effect(&self,
sets: &mut BlockSets<MovePathIndex>,
bb: mir::BasicBlock,
idx: usize)
location: Location)
{
drop_flag_effects_for_location(
self.tcx, self.mir, self.mdpe,
Location { block: bb, statement_index: idx },
location,
|path, s| Self::update_bits(sets, path, s)
)
}
fn terminator_effect(&self,
sets: &mut BlockSets<MovePathIndex>,
bb: mir::BasicBlock,
statements_len: usize)
location: Location)
{
drop_flag_effects_for_location(
self.tcx, self.mir, self.mdpe,
Location { block: bb, statement_index: statements_len },
location,
|path, s| Self::update_bits(sets, path, s)
)
}
@ -400,24 +399,22 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> {
fn statement_effect(&self,
sets: &mut BlockSets<MovePathIndex>,
bb: mir::BasicBlock,
idx: usize)
location: Location)
{
drop_flag_effects_for_location(
self.tcx, self.mir, self.mdpe,
Location { block: bb, statement_index: idx },
location,
|path, s| Self::update_bits(sets, path, s)
)
}
fn terminator_effect(&self,
sets: &mut BlockSets<MovePathIndex>,
bb: mir::BasicBlock,
statements_len: usize)
location: Location)
{
drop_flag_effects_for_location(
self.tcx, self.mir, self.mdpe,
Location { block: bb, statement_index: statements_len },
location,
|path, s| Self::update_bits(sets, path, s)
)
}
@ -448,18 +445,16 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
}
fn statement_effect(&self,
sets: &mut BlockSets<MoveOutIndex>,
bb: mir::BasicBlock,
idx: usize) {
location: Location) {
let (tcx, mir, move_data) = (self.tcx, self.mir, self.move_data());
let stmt = &mir[bb].statements[idx];
let stmt = &mir[location.block].statements[location.statement_index];
let loc_map = &move_data.loc_map;
let path_map = &move_data.path_map;
let rev_lookup = &move_data.rev_lookup;
let loc = Location { block: bb, statement_index: idx };
debug!("stmt {:?} at loc {:?} moves out of move_indexes {:?}",
stmt, loc, &loc_map[loc]);
for move_index in &loc_map[loc] {
stmt, location, &loc_map[location]);
for move_index in &loc_map[location] {
// Every path deinitialized by a *particular move*
// has corresponding bit, "gen'ed" (i.e. set)
// here, in dataflow vector
@ -506,17 +501,15 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
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);
}

View file

@ -8,16 +8,17 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use syntax::ast;
use syntax::ast::{self, MetaItem};
use rustc_data_structures::indexed_set::{IdxSet, IdxSetBuf};
use rustc_data_structures::indexed_vec::Idx;
use rustc_data_structures::bitslice::{bitwise, BitwiseOperator};
use rustc::ty::{TyCtxt};
use rustc::mir::{self, Mir};
use rustc::ty::{self, TyCtxt};
use rustc::mir::{self, Mir, BasicBlock, BasicBlockData, Location, Statement, Terminator};
use rustc::session::Session;
use std::fmt::Debug;
use std::fmt::{self, Debug};
use std::io;
use std::mem;
use std::path::PathBuf;
@ -25,9 +26,11 @@ use std::usize;
pub use self::impls::{MaybeInitializedLvals, MaybeUninitializedLvals};
pub use self::impls::{DefinitelyInitializedLvals, MovingOutStatements};
pub use self::impls::borrows::{Borrows, BorrowData, BorrowIndex};
pub(crate) use self::drop_flag_effects::*;
use self::move_paths::MoveData;
mod drop_flag_effects;
mod graphviz;
mod impls;
@ -44,11 +47,22 @@ pub(crate) struct DataflowBuilder<'a, 'tcx: 'a, BD> where BD: BitDenotation
}
pub trait Dataflow<BD: BitDenotation> {
fn dataflow<P>(&mut self, p: P) where P: Fn(&BD, BD::Idx) -> &Debug;
/// Sets up and runs the dataflow problem, using `p` to render results if
/// implementation so chooses.
fn dataflow<P>(&mut self, p: P) where P: Fn(&BD, BD::Idx) -> &Debug {
let _ = p; // default implementation does not instrument process.
self.build_sets();
self.propagate();
}
/// Sets up the entry, gen, and kill sets for this instance of a dataflow problem.
fn build_sets(&mut self);
/// Finds a fixed-point solution to this instance of a dataflow problem.
fn propagate(&mut self);
}
impl<'a, 'tcx: 'a, BD> Dataflow<BD> for DataflowBuilder<'a, 'tcx, BD>
where BD: BitDenotation + DataflowOperator
impl<'a, 'tcx: 'a, BD> Dataflow<BD> for DataflowBuilder<'a, 'tcx, BD> where BD: BitDenotation
{
fn dataflow<P>(&mut self, p: P) where P: Fn(&BD, BD::Idx) -> &Debug {
self.flow_state.build_sets();
@ -56,17 +70,79 @@ impl<'a, 'tcx: 'a, BD> Dataflow<BD> for DataflowBuilder<'a, 'tcx, BD>
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(); }
}
struct PropagationContext<'b, 'a: 'b, 'tcx: 'a, O>
where O: 'b + BitDenotation
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
{
builder: &'b mut DataflowAnalysis<'a, 'tcx, O>,
changed: bool,
}
impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD>
where BD: BitDenotation + DataflowOperator
impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> where BD: BitDenotation
{
fn propagate(&mut self) {
let mut temp = IdxSetBuf::new_empty(self.flow_state.sets.bits_per_block);
@ -98,19 +174,19 @@ impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD>
let sets = &mut self.flow_state.sets.for_block(bb.index());
for j_stmt in 0..statements.len() {
self.flow_state.operator.statement_effect(sets, bb, j_stmt);
let location = Location { block: bb, statement_index: j_stmt };
self.flow_state.operator.statement_effect(sets, location);
}
if terminator.is_some() {
let stmts_len = statements.len();
self.flow_state.operator.terminator_effect(sets, bb, stmts_len);
let location = Location { block: bb, statement_index: statements.len() };
self.flow_state.operator.terminator_effect(sets, location);
}
}
}
}
impl<'b, 'a: 'b, 'tcx: 'a, BD> PropagationContext<'b, 'a, 'tcx, BD>
where BD: BitDenotation + DataflowOperator
impl<'b, 'a: 'b, 'tcx: 'a, BD> PropagationContext<'b, 'a, 'tcx, BD> where BD: BitDenotation
{
fn reset(&mut self, bits: &mut IdxSet<BD::Idx>) {
let e = if BD::bottom_value() {!0} else {0};
@ -147,8 +223,7 @@ fn dataflow_path(context: &str, prepost: &str, path: &str) -> PathBuf {
path
}
impl<'a, 'tcx: 'a, BD> DataflowBuilder<'a, 'tcx, BD>
where BD: BitDenotation
impl<'a, 'tcx: 'a, BD> DataflowBuilder<'a, 'tcx, BD> where BD: BitDenotation
{
fn pre_dataflow_instrumentation<P>(&self, p: P) -> io::Result<()>
where P: Fn(&BD, BD::Idx) -> &Debug
@ -189,21 +264,108 @@ impl<E:Idx> Bits<E> {
}
}
pub struct DataflowAnalysis<'a, 'tcx: 'a, O>
where O: BitDenotation
/// DataflowResultsConsumer abstracts over walking the MIR with some
/// already constructed dataflow results.
///
/// It abstracts over the FlowState and also completely hides the
/// underlying flow analysis results, because it needs to handle cases
/// where we are combining the results of *multiple* flow analyses
/// (e.g. borrows + inits + uninits).
pub trait DataflowResultsConsumer<'a, 'tcx: 'a> {
type FlowState;
// Observation Hooks: override (at least one of) these to get analysis feedback.
fn visit_block_entry(&mut self,
_bb: BasicBlock,
_flow_state: &Self::FlowState) {}
fn visit_statement_entry(&mut self,
_loc: Location,
_stmt: &Statement<'tcx>,
_flow_state: &Self::FlowState) {}
fn visit_terminator_entry(&mut self,
_loc: Location,
_term: &Terminator<'tcx>,
_flow_state: &Self::FlowState) {}
// Main entry point: this drives the processing of results.
fn analyze_results(&mut self, flow_uninit: &mut Self::FlowState) {
let flow = flow_uninit;
for bb in self.mir().basic_blocks().indices() {
self.reset_to_entry_of(bb, flow);
self.process_basic_block(bb, flow);
}
}
fn process_basic_block(&mut self, bb: BasicBlock, flow_state: &mut Self::FlowState) {
let BasicBlockData { ref statements, ref terminator, is_cleanup: _ } =
self.mir()[bb];
let mut location = Location { block: bb, statement_index: 0 };
for stmt in statements.iter() {
self.reconstruct_statement_effect(location, flow_state);
self.visit_statement_entry(location, stmt, flow_state);
self.apply_local_effect(location, flow_state);
location.statement_index += 1;
}
if let Some(ref term) = *terminator {
self.reconstruct_terminator_effect(location, flow_state);
self.visit_terminator_entry(location, term, flow_state);
// We don't need to apply the effect of the terminator,
// since we are only visiting dataflow state on control
// flow entry to the various nodes. (But we still need to
// reconstruct the effect, because the visit method might
// inspect it.)
}
}
// Delegated Hooks: Provide access to the MIR and process the flow state.
fn mir(&self) -> &'a Mir<'tcx>;
// reset the state bitvector to represent the entry to block `bb`.
fn reset_to_entry_of(&mut self,
bb: BasicBlock,
flow_state: &mut Self::FlowState);
// build gen + kill sets for statement at `loc`.
fn reconstruct_statement_effect(&mut self,
loc: Location,
flow_state: &mut Self::FlowState);
// build gen + kill sets for terminator for `loc`.
fn reconstruct_terminator_effect(&mut self,
loc: Location,
flow_state: &mut Self::FlowState);
// apply current gen + kill sets to `flow_state`.
//
// (`bb` and `stmt_idx` parameters can be ignored if desired by
// client. For the terminator, the `stmt_idx` will be the number
// of statements in the block.)
fn apply_local_effect(&mut self,
loc: Location,
flow_state: &mut Self::FlowState);
}
pub struct DataflowAnalysis<'a, 'tcx: 'a, O> where O: BitDenotation
{
flow_state: DataflowState<O>,
dead_unwinds: &'a IdxSet<mir::BasicBlock>,
mir: &'a Mir<'tcx>,
}
impl<'a, 'tcx: 'a, O> DataflowAnalysis<'a, 'tcx, O>
where O: BitDenotation
impl<'a, 'tcx: 'a, O> DataflowAnalysis<'a, 'tcx, O> where O: BitDenotation
{
pub fn results(self) -> DataflowResults<O> {
DataflowResults(self.flow_state)
}
pub fn flow_state(&self) -> &DataflowState<O> { &self.flow_state }
pub fn mir(&self) -> &'a Mir<'tcx> { self.mir }
}
@ -213,10 +375,14 @@ impl<O: BitDenotation> DataflowResults<O> {
pub fn sets(&self) -> &AllSets<O::Idx> {
&self.0.sets
}
pub fn operator(&self) -> &O {
&self.0.operator
}
}
// FIXME: This type shouldn't be public, but the graphviz::MirWithFlowState trait
// references it in a method signature. Look into using `pub(crate)` to address this.
/// State of a dataflow analysis; couples a collection of bit sets
/// with operator used to initialize and merge bits during analysis.
pub struct DataflowState<O: BitDenotation>
{
/// All the sets for the analysis. (Factored into its
@ -228,6 +394,28 @@ pub struct DataflowState<O: BitDenotation>
pub(crate) operator: O,
}
impl<O: BitDenotation> DataflowState<O> {
pub fn each_bit<F>(&self, words: &IdxSet<O::Idx>, f: F) where F: FnMut(O::Idx)
{
let bits_per_block = self.operator.bits_per_block();
words.each_bit(bits_per_block, f)
}
pub fn interpret_set<'c, P>(&self,
o: &'c O,
words: &IdxSet<O::Idx>,
render_idx: &P)
-> Vec<&'c Debug>
where P: Fn(&O, O::Idx) -> &Debug
{
let mut v = Vec::new();
self.each_bit(words, |i| {
v.push(render_idx(o, i));
});
v
}
}
#[derive(Debug)]
pub struct AllSets<E: Idx> {
/// Analysis bitwidth for each block.
@ -251,9 +439,28 @@ pub struct AllSets<E: Idx> {
on_entry_sets: Bits<E>,
}
/// Triple of sets associated with a given block.
///
/// Generally, one sets up `on_entry`, `gen_set`, and `kill_set` for
/// each block individually, and then runs the dataflow analysis which
/// iteratively modifies the various `on_entry` sets (but leaves the
/// other two sets unchanged, since they represent the effect of the
/// block, which should be invariant over the course of the analysis).
///
/// It is best to ensure that the intersection of `gen_set` and
/// `kill_set` is empty; otherwise the results of the dataflow will
/// have a hidden dependency on what order the bits are generated and
/// killed during the iteration. (This is such a good idea that the
/// `fn gen` and `fn kill` methods that set their state enforce this
/// for you.)
pub struct BlockSets<'a, E: Idx> {
/// Dataflow state immediately before control flow enters the given block.
pub(crate) on_entry: &'a mut IdxSet<E>,
/// Bits that are set to 1 by the time we exit the given block.
pub(crate) gen_set: &'a mut IdxSet<E>,
/// Bits that are set to 0 by the time we exit the given block.
pub(crate) kill_set: &'a mut IdxSet<E>,
}
@ -302,7 +509,7 @@ pub trait DataflowOperator: BitwiseOperator {
fn bottom_value() -> bool;
}
pub trait BitDenotation {
pub trait BitDenotation: DataflowOperator {
/// Specifies what index type is used to access the bitvector.
type Idx: Idx;
@ -341,8 +548,7 @@ pub trait BitDenotation {
/// the MIR.
fn statement_effect(&self,
sets: &mut BlockSets<Self::Idx>,
bb: mir::BasicBlock,
idx_stmt: usize);
location: Location);
/// Mutates the block-sets (the flow sets for the given
/// basic block) according to the effects of evaluating
@ -356,8 +562,7 @@ pub trait BitDenotation {
/// terminator took.
fn terminator_effect(&self,
sets: &mut BlockSets<Self::Idx>,
bb: mir::BasicBlock,
idx_term: usize);
location: Location);
/// Mutates the block-sets according to the (flow-dependent)
/// effect of a successful return from a Call terminator.
@ -385,8 +590,7 @@ pub trait BitDenotation {
dest_lval: &mir::Lvalue);
}
impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
where D: BitDenotation + DataflowOperator
impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation
{
pub fn new(_tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir: &'a Mir<'tcx>,
@ -427,8 +631,7 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
}
}
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`.

View file

@ -0,0 +1,332 @@
// 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::Unreachable => { }
TerminatorKind::Return => {
self.gather_move(loc, &Lvalue::Local(RETURN_POINTER));
}
TerminatorKind::Assert { .. } |
TerminatorKind::SwitchInt { .. } => {
// branching terminators - these don't move anything
}
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);
}
}

View file

@ -11,15 +11,10 @@
use rustc::ty::{self, TyCtxt};
use rustc::mir::*;
use rustc::mir::tcx::RvalueInitializationState;
use rustc::util::nodemap::FxHashMap;
use rustc_data_structures::indexed_vec::{IndexVec};
use syntax::codemap::DUMMY_SP;
use std::collections::hash_map::Entry;
use std::fmt;
use std::mem;
use std::ops::{Index, IndexMut};
use self::abs_domain::{AbstractElem, Lift};
@ -63,6 +58,9 @@ pub(crate) mod indexes {
/// Index into MoveData.moves.
new_index!(MoveOutIndex, "mo");
/// Index into Borrows.locations
new_index!(BorrowIndex, "bw");
}
pub use self::indexes::MovePathIndex;
@ -110,6 +108,12 @@ impl<'tcx> fmt::Debug for MovePath<'tcx> {
}
}
impl<'tcx> fmt::Display for MovePath<'tcx> {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
write!(w, "{:?}", self.lvalue)
}
}
#[derive(Debug)]
pub struct MoveData<'tcx> {
pub move_paths: IndexVec<MovePathIndex, MovePath<'tcx>>,
@ -191,154 +195,7 @@ pub struct MovePathLookup<'tcx> {
projections: FxHashMap<(MovePathIndex, AbstractElem<'tcx>), MovePathIndex>
}
pub(super) struct MoveDataBuilder<'a, 'tcx: 'a> {
mir: &'a Mir<'tcx>,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
data: MoveData<'tcx>,
}
pub enum MovePathError {
IllegalMove,
UnionMove { path: MovePathIndex },
}
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
fn new(mir: &'a Mir<'tcx>,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>)
-> Self {
let mut move_paths = IndexVec::new();
let mut path_map = IndexVec::new();
MoveDataBuilder {
mir,
tcx,
param_env,
data: MoveData {
moves: IndexVec::new(),
loc_map: LocationMap::new(mir),
rev_lookup: MovePathLookup {
locals: mir.local_decls.indices().map(Lvalue::Local).map(|v| {
Self::new_move_path(&mut move_paths, &mut path_map, None, v)
}).collect(),
projections: FxHashMap(),
},
move_paths,
path_map,
}
}
}
fn new_move_path(move_paths: &mut IndexVec<MovePathIndex, MovePath<'tcx>>,
path_map: &mut IndexVec<MovePathIndex, Vec<MoveOutIndex>>,
parent: Option<MovePathIndex>,
lvalue: Lvalue<'tcx>)
-> MovePathIndex
{
let move_path = move_paths.push(MovePath {
next_sibling: None,
first_child: None,
parent,
lvalue,
});
if let Some(parent) = parent {
let next_sibling =
mem::replace(&mut move_paths[parent].first_child, Some(move_path));
move_paths[move_path].next_sibling = next_sibling;
}
let path_map_ent = path_map.push(vec![]);
assert_eq!(path_map_ent, move_path);
move_path
}
/// This creates a MovePath for a given lvalue, returning an `MovePathError`
/// if that lvalue can't be moved from.
///
/// NOTE: lvalues behind references *do not* get a move path, which is
/// problematic for borrowck.
///
/// Maybe we should have separate "borrowck" and "moveck" modes.
fn move_path_for(&mut self, lval: &Lvalue<'tcx>)
-> Result<MovePathIndex, MovePathError>
{
debug!("lookup({:?})", lval);
match *lval {
Lvalue::Local(local) => Ok(self.data.rev_lookup.locals[local]),
// error: can't move out of a static
Lvalue::Static(..) => Err(MovePathError::IllegalMove),
Lvalue::Projection(ref proj) => {
self.move_path_for_projection(lval, proj)
}
}
}
fn create_move_path(&mut self, lval: &Lvalue<'tcx>) {
// This is an assignment, not a move, so this not being a valid
// move path is OK.
let _ = self.move_path_for(lval);
}
fn move_path_for_projection(&mut self,
lval: &Lvalue<'tcx>,
proj: &LvalueProjection<'tcx>)
-> Result<MovePathIndex, MovePathError>
{
let base = try!(self.move_path_for(&proj.base));
let lv_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
match lv_ty.sty {
// error: can't move out of borrowed content
ty::TyRef(..) | ty::TyRawPtr(..) => return Err(MovePathError::IllegalMove),
// error: can't move out of struct with destructor
ty::TyAdt(adt, _) if adt.has_dtor(self.tcx) && !adt.is_box() =>
return Err(MovePathError::IllegalMove),
// move out of union - always move the entire union
ty::TyAdt(adt, _) if adt.is_union() =>
return Err(MovePathError::UnionMove { path: base }),
// error: can't move out of a slice
ty::TySlice(..) =>
return Err(MovePathError::IllegalMove),
ty::TyArray(..) => match proj.elem {
// error: can't move out of an array
ProjectionElem::Index(..) => return Err(MovePathError::IllegalMove),
_ => {
// FIXME: still badly broken
}
},
_ => {}
};
match self.data.rev_lookup.projections.entry((base, proj.elem.lift())) {
Entry::Occupied(ent) => Ok(*ent.get()),
Entry::Vacant(ent) => {
let path = Self::new_move_path(
&mut self.data.move_paths,
&mut self.data.path_map,
Some(base),
lval.clone()
);
ent.insert(path);
Ok(path)
}
}
}
fn finalize(self) -> MoveData<'tcx> {
debug!("{}", {
debug!("moves for {:?}:", self.mir.span);
for (j, mo) in self.data.moves.iter_enumerated() {
debug!(" {:?} = {:?}", j, mo);
}
debug!("move paths for {:?}:", self.mir.span);
for (j, path) in self.data.move_paths.iter_enumerated() {
debug!(" {:?} = {:?}", j, path);
}
"done dumping moves"
});
self.data
}
}
mod builder;
#[derive(Copy, Clone, Debug)]
pub enum LookupResult {
@ -375,165 +232,6 @@ impl<'a, 'tcx> MoveData<'tcx> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>)
-> Self {
gather_moves(mir, tcx, param_env)
}
}
fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>)
-> MoveData<'tcx> {
let mut builder = MoveDataBuilder::new(mir, tcx, param_env);
for (bb, block) in mir.basic_blocks().iter_enumerated() {
for (i, stmt) in block.statements.iter().enumerate() {
let source = Location { block: bb, statement_index: i };
builder.gather_statement(source, stmt);
}
let terminator_loc = Location {
block: bb,
statement_index: block.statements.len()
};
builder.gather_terminator(terminator_loc, block.terminator());
}
builder.finalize()
}
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
fn gather_statement(&mut self, loc: Location, stmt: &Statement<'tcx>) {
debug!("gather_statement({:?}, {:?})", loc, stmt);
match stmt.kind {
StatementKind::Assign(ref lval, ref rval) => {
self.create_move_path(lval);
if let RvalueInitializationState::Shallow = rval.initialization_state() {
// Box starts out uninitialized - need to create a separate
// move-path for the interior so it will be separate from
// the exterior.
self.create_move_path(&lval.clone().deref());
}
self.gather_rvalue(loc, rval);
}
StatementKind::StorageLive(_) |
StatementKind::StorageDead(_) => {}
StatementKind::SetDiscriminant{ .. } => {
span_bug!(stmt.source_info.span,
"SetDiscriminant should not exist during borrowck");
}
StatementKind::InlineAsm { .. } |
StatementKind::EndRegion(_) |
StatementKind::Validate(..) |
StatementKind::Nop => {}
}
}
fn gather_rvalue(&mut self, loc: Location, rvalue: &Rvalue<'tcx>) {
match *rvalue {
Rvalue::Use(ref operand) |
Rvalue::Repeat(ref operand, _) |
Rvalue::Cast(_, ref operand, _) |
Rvalue::UnaryOp(_, ref operand) => {
self.gather_operand(loc, operand)
}
Rvalue::BinaryOp(ref _binop, ref lhs, ref rhs) |
Rvalue::CheckedBinaryOp(ref _binop, ref lhs, ref rhs) => {
self.gather_operand(loc, lhs);
self.gather_operand(loc, rhs);
}
Rvalue::Aggregate(ref _kind, ref operands) => {
for operand in operands {
self.gather_operand(loc, operand);
}
}
Rvalue::Ref(..) |
Rvalue::Discriminant(..) |
Rvalue::Len(..) |
Rvalue::NullaryOp(NullOp::SizeOf, _) |
Rvalue::NullaryOp(NullOp::Box, _) => {
// This returns an rvalue with uninitialized contents. We can't
// move out of it here because it is an rvalue - assignments always
// completely initialize their lvalue.
//
// However, this does not matter - MIR building is careful to
// only emit a shallow free for the partially-initialized
// temporary.
//
// In any case, if we want to fix this, we have to register a
// special move and change the `statement_effect` functions.
}
}
}
fn gather_terminator(&mut self, loc: Location, term: &Terminator<'tcx>) {
debug!("gather_terminator({:?}, {:?})", loc, term);
match term.kind {
TerminatorKind::Goto { target: _ } |
TerminatorKind::Resume |
TerminatorKind::Unreachable => { }
TerminatorKind::Return => {
self.gather_move(loc, &Lvalue::Local(RETURN_POINTER));
}
TerminatorKind::Assert { .. } |
TerminatorKind::SwitchInt { .. } => {
// branching terminators - these don't move anything
}
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)
}
}

View file

@ -195,6 +195,50 @@ instead of using a `const fn`, or refactoring the code to a functional style to
avoid mutation if possible.
"##,
E0381: r##"
It is not allowed to use or capture an uninitialized variable. For example:
```compile_fail,E0381
fn main() {
let x: i32;
let y = x; // error, use of possibly uninitialized variable
}
```
To fix this, ensure that any declared variables are initialized before being
used. Example:
```
fn main() {
let x: i32 = 0;
let y = x; // ok!
}
```
"##,
E0384: r##"
This error occurs when an attempt is made to reassign an immutable variable.
For example:
```compile_fail,E0384
fn main() {
let x = 3;
x = 5; // error, reassignment of immutable variable
}
```
By default, variables in Rust are immutable. To fix this error, add the keyword
`mut` after the keyword `let` when declaring the variable. For example:
```
fn main() {
let mut x = 3;
x = 5;
}
```
"##,
E0394: r##"
A static was referred to by value by another static.
@ -438,9 +482,516 @@ static A : &'static u32 = &S.a; // ok!
```
"##,
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
}

View file

@ -32,6 +32,7 @@ extern crate graphviz as dot;
#[macro_use]
extern crate rustc;
extern crate rustc_data_structures;
extern crate rustc_errors;
#[macro_use]
#[no_link]
extern crate rustc_bitflags;

File diff suppressed because it is too large Load diff

View file

@ -31,6 +31,7 @@ pub mod simplify;
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;

View file

@ -13,7 +13,7 @@ use syntax::ast;
use syntax_pos::Span;
use rustc::ty::{self, TyCtxt};
use rustc::mir::{self, Mir};
use rustc::mir::{self, Mir, Location};
use rustc::mir::transform::{MirPass, MirSource};
use rustc_data_structures::indexed_set::IdxSetBuf;
use rustc_data_structures::indexed_vec::Idx;
@ -202,7 +202,7 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
// reset GEN and KILL sets before emulating their effect.
for e in sets.gen_set.words_mut() { *e = 0; }
for e in sets.kill_set.words_mut() { *e = 0; }
results.0.operator.statement_effect(&mut sets, bb, j);
results.0.operator.statement_effect(&mut sets, Location { block: bb, statement_index: j });
sets.on_entry.union(sets.gen_set);
sets.on_entry.subtract(sets.kill_set);
}

View file

@ -0,0 +1,192 @@
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use rustc::ty::{self, TyCtxt};
use rustc_errors::DiagnosticBuilder;
use syntax_pos::{MultiSpan, Span};
use std::fmt;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Origin { Ast, Mir }
impl fmt::Display for Origin {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
match *self {
Origin::Mir => write!(w, " (Mir)"),
Origin::Ast => ty::tls::with_opt(|opt_tcx| {
// If user passed `-Z borrowck-mir`, then include an
// AST origin as part of the error report
if let Some(tcx) = opt_tcx {
if tcx.sess.opts.debugging_opts.borrowck_mir {
return write!(w, " (Ast)");
}
}
// otherwise, do not include the origin (i.e., print
// nothing at all)
Ok(())
}),
}
}
}
pub trait BorrowckErrors {
fn struct_span_err_with_code<'a, S: Into<MultiSpan>>(&'a self,
sp: S,
msg: &str,
code: &str)
-> DiagnosticBuilder<'a>;
fn struct_span_err<'a, S: Into<MultiSpan>>(&'a self,
sp: S,
msg: &str)
-> DiagnosticBuilder<'a>;
fn cannot_move_when_borrowed(&self, span: Span, desc: &str, o: Origin)
-> DiagnosticBuilder
{
struct_span_err!(self, span, E0505,
"cannot move out of `{}` because it is borrowed{OGN}",
desc, OGN=o)
}
fn cannot_use_when_mutably_borrowed(&self, span: Span, desc: &str, o: Origin)
-> DiagnosticBuilder
{
struct_span_err!(self, span, E0503,
"cannot use `{}` because it was mutably borrowed{OGN}",
desc, OGN=o)
}
fn cannot_act_on_uninitialized_variable(&self,
span: Span,
verb: &str,
desc: &str,
o: Origin)
-> DiagnosticBuilder
{
struct_span_err!(self, span, E0381,
"{} of possibly uninitialized variable: `{}`{OGN}",
verb, desc, OGN=o)
}
fn cannot_mutably_borrow_multiply(&self,
span: Span,
desc: &str,
opt_via: &str,
o: Origin)
-> DiagnosticBuilder
{
struct_span_err!(self, span, E0499,
"cannot borrow `{}`{} as mutable more than once at a time{OGN}",
desc, opt_via, OGN=o)
}
fn cannot_uniquely_borrow_by_two_closures(&self, span: Span, desc: &str, o: Origin)
-> DiagnosticBuilder
{
struct_span_err!(self, span, E0524,
"two closures require unique access to `{}` at the same time{OGN}",
desc, OGN=o)
}
fn cannot_uniquely_borrow_by_one_closure(&self,
span: Span,
desc_new: &str,
noun_old: &str,
msg_old: &str,
o: Origin)
-> DiagnosticBuilder
{
struct_span_err!(self, span, E0500,
"closure requires unique access to `{}` but {} is already borrowed{}{OGN}",
desc_new, noun_old, msg_old, OGN=o)
}
fn cannot_reborrow_already_uniquely_borrowed(&self,
span: Span,
desc_new: &str,
msg_new: &str,
kind_new: &str,
o: Origin)
-> DiagnosticBuilder
{
struct_span_err!(self, span, E0501,
"cannot borrow `{}`{} as {} because previous closure \
requires unique access{OGN}",
desc_new, msg_new, kind_new, OGN=o)
}
fn cannot_reborrow_already_borrowed(&self,
span: Span,
desc_new: &str,
msg_new: &str,
kind_new: &str,
noun_old: &str,
kind_old: &str,
msg_old: &str,
o: Origin)
-> DiagnosticBuilder
{
struct_span_err!(self, span, E0502,
"cannot borrow `{}`{} as {} because {} is also borrowed as {}{}{OGN}",
desc_new, msg_new, kind_new, noun_old, kind_old, msg_old, OGN=o)
}
fn cannot_assign_to_borrowed(&self, span: Span, desc: &str, o: Origin)
-> DiagnosticBuilder
{
struct_span_err!(self, span, E0506,
"cannot assign to `{}` because it is borrowed{OGN}",
desc, OGN=o)
}
fn cannot_move_into_closure(&self, span: Span, desc: &str, o: Origin)
-> DiagnosticBuilder
{
struct_span_err!(self, span, E0504,
"cannot move `{}` into closure because it is borrowed{OGN}",
desc, OGN=o)
}
fn cannot_reassign_immutable(&self, span: Span, desc: &str, o: Origin)
-> DiagnosticBuilder
{
struct_span_err!(self, span, E0384,
"re-assignment of immutable variable `{}`{OGN}",
desc, OGN=o)
}
fn cannot_assign_static(&self, span: Span, desc: &str, o: Origin)
-> DiagnosticBuilder
{
self.struct_span_err(span, &format!("cannot assign to immutable static item {}{OGN}",
desc, OGN=o))
}
}
impl<'b, 'tcx, 'gcx> BorrowckErrors for TyCtxt<'b, 'tcx, 'gcx> {
fn struct_span_err_with_code<'a, S: Into<MultiSpan>>(&'a self,
sp: S,
msg: &str,
code: &str)
-> DiagnosticBuilder<'a>
{
self.sess.struct_span_err_with_code(sp, msg, code)
}
fn struct_span_err<'a, S: Into<MultiSpan>>(&'a self,
sp: S,
msg: &str)
-> DiagnosticBuilder<'a>
{
self.sess.struct_span_err(sp, msg)
}
}

View file

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub mod borrowck_errors;
pub mod elaborate_drops;
pub mod def_use;
pub mod patch;