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