diff --git a/src/Cargo.lock b/src/Cargo.lock index c175198c227a5..b5db5ce36133f 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -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", ] diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 6995f0996774f..5985dcb97c739 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -918,6 +918,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "when debug-printing compiler state, do not include spans"), // o/w tests have closure@path identify_regions: bool = (false, parse_bool, [UNTRACKED], "make unnamed regions display as '# (where # is some non-ident unique id)"), + borrowck_mir: bool = (false, parse_bool, [UNTRACKED], + "implicitly treat functions as if they have `#[rustc_mir_borrowck]` attribute"), time_passes: bool = (false, parse_bool, [UNTRACKED], "measure time of each rustc pass"), count_llvm_insns: bool = (false, parse_bool, diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs index c82377173252f..4058f3198afb4 100644 --- a/src/librustc_borrowck/borrowck/check_loans.rs +++ b/src/librustc_borrowck/borrowck/check_loans.rs @@ -29,6 +29,7 @@ use rustc::ty::{self, TyCtxt}; use syntax::ast; use syntax_pos::Span; use rustc::hir; +use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin}; use std::rc::Rc; @@ -465,10 +466,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { let mut err = match (new_loan.kind, old_loan.kind) { (ty::MutBorrow, ty::MutBorrow) => { - let mut err = struct_span_err!(self.bccx, new_loan.span, E0499, - "cannot borrow `{}`{} as mutable \ - more than once at a time", - nl, new_loan_msg); + let mut err = self.bccx.cannot_mutably_borrow_multiply( + new_loan.span, &nl, &new_loan_msg, Origin::Ast); if new_loan.span == old_loan.span { // Both borrows are happening in the same place @@ -496,10 +495,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { } (ty::UniqueImmBorrow, ty::UniqueImmBorrow) => { - let mut err = struct_span_err!(self.bccx, new_loan.span, E0524, - "two closures require unique access to `{}` \ - at the same time", - nl); + let mut err = self.bccx.cannot_uniquely_borrow_by_two_closures( + new_loan.span, &nl, Origin::Ast); err.span_label( old_loan.span, "first closure is constructed here"); @@ -513,10 +510,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { } (ty::UniqueImmBorrow, _) => { - let mut err = struct_span_err!(self.bccx, new_loan.span, E0500, - "closure requires unique access to `{}` \ - but {} is already borrowed{}", - nl, ol_pronoun, old_loan_msg); + let mut err = self.bccx.cannot_uniquely_borrow_by_one_closure( + new_loan.span, &nl, &ol_pronoun, &old_loan_msg, Origin::Ast); err.span_label( new_loan.span, format!("closure construction occurs here{}", new_loan_msg)); @@ -530,10 +525,9 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { } (_, ty::UniqueImmBorrow) => { - let mut err = struct_span_err!(self.bccx, new_loan.span, E0501, - "cannot borrow `{}`{} as {} because \ - previous closure requires unique access", - nl, new_loan_msg, new_loan.kind.to_user_str()); + let new_loan_str = &new_loan.kind.to_user_str(); + let mut err = self.bccx.cannot_reborrow_already_uniquely_borrowed( + new_loan.span, &nl, &new_loan_msg, new_loan_str, Origin::Ast); err.span_label( new_loan.span, format!("borrow occurs here{}", new_loan_msg)); @@ -547,15 +541,10 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { } (..) => { - let mut err = struct_span_err!(self.bccx, new_loan.span, E0502, - "cannot borrow `{}`{} as {} because \ - {} is also borrowed as {}{}", - nl, - new_loan_msg, - new_loan.kind.to_user_str(), - ol_pronoun, - old_loan.kind.to_user_str(), - old_loan_msg); + let mut err = self.bccx.cannot_reborrow_already_borrowed( + new_loan.span, + &nl, &new_loan_msg, &new_loan.kind.to_user_str(), + &ol_pronoun, &old_loan.kind.to_user_str(), &old_loan_msg, Origin::Ast); err.span_label( new_loan.span, format!("{} borrow occurs here{}", @@ -645,9 +634,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { match self.analyze_restrictions_on_use(id, copy_path, ty::ImmBorrow) { UseOk => { } UseWhileBorrowed(loan_path, loan_span) => { - struct_span_err!(self.bccx, span, E0503, - "cannot use `{}` because it was mutably borrowed", - &self.bccx.loan_path_to_string(copy_path)) + let desc = self.bccx.loan_path_to_string(copy_path); + self.bccx.cannot_use_when_mutably_borrowed(span, &desc, Origin::Ast) .span_label(loan_span, format!("borrow of `{}` occurs here", &self.bccx.loan_path_to_string(&loan_path)) @@ -673,9 +661,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { UseWhileBorrowed(loan_path, loan_span) => { let mut err = match move_kind { move_data::Captured => { - let mut err = struct_span_err!(self.bccx, span, E0504, - "cannot move `{}` into closure because it is borrowed", - &self.bccx.loan_path_to_string(move_path)); + let mut err = self.bccx.cannot_move_into_closure( + span, &self.bccx.loan_path_to_string(move_path), Origin::Ast); err.span_label( loan_span, format!("borrow of `{}` occurs here", @@ -690,9 +677,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { move_data::Declared | move_data::MoveExpr | move_data::MovePat => { - let mut err = struct_span_err!(self.bccx, span, E0505, - "cannot move out of `{}` because it is borrowed", - &self.bccx.loan_path_to_string(move_path)); + let desc = self.bccx.loan_path_to_string(move_path); + let mut err = self.bccx.cannot_move_when_borrowed(span, &desc, Origin::Ast); err.span_label( loan_span, format!("borrow of `{}` occurs here", @@ -874,9 +860,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { span: Span, loan_path: &LoanPath<'tcx>, loan: &Loan) { - struct_span_err!(self.bccx, span, E0506, - "cannot assign to `{}` because it is borrowed", - self.bccx.loan_path_to_string(loan_path)) + self.bccx.cannot_assign_to_borrowed( + span, &self.bccx.loan_path_to_string(loan_path), Origin::Ast) .span_label(loan.span, format!("borrow of `{}` occurs here", self.bccx.loan_path_to_string(loan_path))) diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index 9514b9b39fd3b..8c79534d209ef 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -37,6 +37,8 @@ use rustc::middle::free_region::RegionRelations; use rustc::ty::{self, TyCtxt}; use rustc::ty::maps::Providers; +use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin}; + use std::fmt; use std::rc::Rc; use std::hash::{Hash, Hasher}; @@ -218,6 +220,25 @@ pub struct BorrowckCtxt<'a, 'tcx: 'a> { owner_def_id: DefId, } +impl<'b, 'tcx: 'b> BorrowckErrors for BorrowckCtxt<'b, 'tcx> { + fn struct_span_err_with_code<'a, S: Into>(&'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>(&'a self, + sp: S, + msg: &str) + -> DiagnosticBuilder<'a> + { + self.tcx.sess.struct_span_err(sp, msg) + } +} + /////////////////////////////////////////////////////////////////////////// // Loans and loan paths @@ -549,14 +570,13 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { move_data::Declared => { // If this is an uninitialized variable, just emit a simple warning // and return. - struct_span_err!( - self.tcx.sess, use_span, E0381, - "{} of possibly uninitialized variable: `{}`", - verb, - self.loan_path_to_string(lp)) - .span_label(use_span, format!("use of possibly uninitialized `{}`", - self.loan_path_to_string(lp))) - .emit(); + self.cannot_act_on_uninitialized_variable(use_span, + verb, + &self.loan_path_to_string(lp), + Origin::Ast) + .span_label(use_span, format!("use of possibly uninitialized `{}`", + self.loan_path_to_string(lp))) + .emit(); return; } _ => { @@ -683,10 +703,9 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { lp: &LoanPath<'tcx>, assign: &move_data::Assignment) { - let mut err = struct_span_err!( - self.tcx.sess, span, E0384, - "re-assignment of immutable variable `{}`", - self.loan_path_to_string(lp)); + let mut err = self.cannot_reassign_immutable(span, + &self.loan_path_to_string(lp), + Origin::Ast); err.span_label(span, "re-assignment of immutable variable"); if span != assign.span { err.span_label(assign.span, format!("first assignment to `{}`", diff --git a/src/librustc_borrowck/diagnostics.rs b/src/librustc_borrowck/diagnostics.rs index fea9d0d6f1326..1f1fc4cc65fbe 100644 --- a/src/librustc_borrowck/diagnostics.rs +++ b/src/librustc_borrowck/diagnostics.rs @@ -63,27 +63,6 @@ Now that the closure has its own copy of the data, there's no need to worry about safety. "##, -E0381: r##" -It is not allowed to use or capture an uninitialized variable. For example: - -```compile_fail,E0381 -fn main() { - let x: i32; - let y = x; // error, use of possibly uninitialized variable -} -``` - -To fix this, ensure that any declared variables are initialized before being -used. Example: - -``` -fn main() { - let x: i32 = 0; - let y = x; // ok! -} -``` -"##, - E0382: r##" This error occurs when an attempt is made to use a variable after its contents have been moved elsewhere. For example: @@ -182,28 +161,6 @@ x = Foo { a: 2 }; ``` "##, -E0384: r##" -This error occurs when an attempt is made to reassign an immutable variable. -For example: - -```compile_fail,E0384 -fn main() { - let x = 3; - x = 5; // error, reassignment of immutable variable -} -``` - -By default, variables in Rust are immutable. To fix this error, add the keyword -`mut` after the keyword `let` when declaring the variable. For example: - -``` -fn main() { - let mut x = 3; - x = 5; -} -``` -"##, - /*E0386: r##" This error occurs when an attempt is made to mutate the target of a mutable reference stored inside an immutable container. @@ -360,512 +317,6 @@ fn main() { ``` "##, -E0499: r##" -A variable was borrowed as mutable more than once. Erroneous code example: - -```compile_fail,E0499 -let mut i = 0; -let mut x = &mut i; -let mut a = &mut i; -// error: cannot borrow `i` as mutable more than once at a time -``` - -Please note that in rust, you can either have many immutable references, or one -mutable reference. Take a look at -https://doc.rust-lang.org/book/first-edition/references-and-borrowing.html -for more information. Example: - - -``` -let mut i = 0; -let mut x = &mut i; // ok! - -// or: -let mut i = 0; -let a = &i; // ok! -let b = &i; // still ok! -let c = &i; // still ok! -``` -"##, - -E0500: r##" -A borrowed variable was used in another closure. Example of erroneous code: - -```compile_fail -fn you_know_nothing(jon_snow: &mut i32) { - let nights_watch = || { - *jon_snow = 2; - }; - let starks = || { - *jon_snow = 3; // error: closure requires unique access to `jon_snow` - // but it is already borrowed - }; -} -``` - -In here, `jon_snow` is already borrowed by the `nights_watch` closure, so it -cannot be borrowed by the `starks` closure at the same time. To fix this issue, -you can put the closure in its own scope: - -``` -fn you_know_nothing(jon_snow: &mut i32) { - { - let nights_watch = || { - *jon_snow = 2; - }; - } // At this point, `jon_snow` is free. - let starks = || { - *jon_snow = 3; - }; -} -``` - -Or, if the type implements the `Clone` trait, you can clone it between -closures: - -``` -fn you_know_nothing(jon_snow: &mut i32) { - let mut jon_copy = jon_snow.clone(); - let nights_watch = || { - jon_copy = 2; - }; - let starks = || { - *jon_snow = 3; - }; -} -``` -"##, - -E0501: r##" -This error indicates that a mutable variable is being used while it is still -captured by a closure. Because the closure has borrowed the variable, it is not -available for use until the closure goes out of scope. - -Note that a capture will either move or borrow a variable, but in this -situation, the closure is borrowing the variable. Take a look at -http://rustbyexample.com/fn/closures/capture.html for more information about -capturing. - -Example of erroneous code: - -```compile_fail,E0501 -fn inside_closure(x: &mut i32) { - // Actions which require unique access -} - -fn outside_closure(x: &mut i32) { - // Actions which require unique access -} - -fn foo(a: &mut i32) { - let bar = || { - inside_closure(a) - }; - outside_closure(a); // error: cannot borrow `*a` as mutable because previous - // closure requires unique access. -} -``` - -To fix this error, you can place the closure in its own scope: - -``` -fn inside_closure(x: &mut i32) {} -fn outside_closure(x: &mut i32) {} - -fn foo(a: &mut i32) { - { - let bar = || { - inside_closure(a) - }; - } // borrow on `a` ends. - outside_closure(a); // ok! -} -``` - -Or you can pass the variable as a parameter to the closure: - -``` -fn inside_closure(x: &mut i32) {} -fn outside_closure(x: &mut i32) {} - -fn foo(a: &mut i32) { - let bar = |s: &mut i32| { - inside_closure(s) - }; - outside_closure(a); - bar(a); -} -``` - -It may be possible to define the closure later: - -``` -fn inside_closure(x: &mut i32) {} -fn outside_closure(x: &mut i32) {} - -fn foo(a: &mut i32) { - outside_closure(a); - let bar = || { - inside_closure(a) - }; -} -``` -"##, - -E0502: r##" -This error indicates that you are trying to borrow a variable as mutable when it -has already been borrowed as immutable. - -Example of erroneous code: - -```compile_fail,E0502 -fn bar(x: &mut i32) {} -fn foo(a: &mut i32) { - let ref y = a; // a is borrowed as immutable. - bar(a); // error: cannot borrow `*a` as mutable because `a` is also borrowed - // as immutable -} -``` - -To fix this error, ensure that you don't have any other references to the -variable before trying to access it mutably: - -``` -fn bar(x: &mut i32) {} -fn foo(a: &mut i32) { - bar(a); - let ref y = a; // ok! -} -``` - -For more information on the rust ownership system, take a look at -https://doc.rust-lang.org/book/first-edition/references-and-borrowing.html. -"##, - -E0503: r##" -A value was used after it was mutably borrowed. - -Example of erroneous code: - -```compile_fail,E0503 -fn main() { - let mut value = 3; - // Create a mutable borrow of `value`. This borrow - // lives until the end of this function. - let _borrow = &mut value; - let _sum = value + 1; // error: cannot use `value` because - // it was mutably borrowed -} -``` - -In this example, `value` is mutably borrowed by `borrow` and cannot be -used to calculate `sum`. This is not possible because this would violate -Rust's mutability rules. - -You can fix this error by limiting the scope of the borrow: - -``` -fn main() { - let mut value = 3; - // By creating a new block, you can limit the scope - // of the reference. - { - let _borrow = &mut value; // Use `_borrow` inside this block. - } - // The block has ended and with it the borrow. - // You can now use `value` again. - let _sum = value + 1; -} -``` - -Or by cloning `value` before borrowing it: - -``` -fn main() { - let mut value = 3; - // We clone `value`, creating a copy. - let value_cloned = value.clone(); - // The mutable borrow is a reference to `value` and - // not to `value_cloned`... - let _borrow = &mut value; - // ... which means we can still use `value_cloned`, - let _sum = value_cloned + 1; - // even though the borrow only ends here. -} -``` - -You can find more information about borrowing in the rust-book: -http://doc.rust-lang.org/book/first-edition/references-and-borrowing.html -"##, - -E0504: r##" -This error occurs when an attempt is made to move a borrowed variable into a -closure. - -Example of erroneous code: - -```compile_fail,E0504 -struct FancyNum { - num: u8, -} - -fn main() { - let fancy_num = FancyNum { num: 5 }; - let fancy_ref = &fancy_num; - - let x = move || { - println!("child function: {}", fancy_num.num); - // error: cannot move `fancy_num` into closure because it is borrowed - }; - - x(); - println!("main function: {}", fancy_ref.num); -} -``` - -Here, `fancy_num` is borrowed by `fancy_ref` and so cannot be moved into -the closure `x`. There is no way to move a value into a closure while it is -borrowed, as that would invalidate the borrow. - -If the closure can't outlive the value being moved, try using a reference -rather than moving: - -``` -struct FancyNum { - num: u8, -} - -fn main() { - let fancy_num = FancyNum { num: 5 }; - let fancy_ref = &fancy_num; - - let x = move || { - // fancy_ref is usable here because it doesn't move `fancy_num` - println!("child function: {}", fancy_ref.num); - }; - - x(); - - println!("main function: {}", fancy_num.num); -} -``` - -If the value has to be borrowed and then moved, try limiting the lifetime of -the borrow using a scoped block: - -``` -struct FancyNum { - num: u8, -} - -fn main() { - let fancy_num = FancyNum { num: 5 }; - - { - let fancy_ref = &fancy_num; - println!("main function: {}", fancy_ref.num); - // `fancy_ref` goes out of scope here - } - - let x = move || { - // `fancy_num` can be moved now (no more references exist) - println!("child function: {}", fancy_num.num); - }; - - x(); -} -``` - -If the lifetime of a reference isn't enough, such as in the case of threading, -consider using an `Arc` to create a reference-counted value: - -``` -use std::sync::Arc; -use std::thread; - -struct FancyNum { - num: u8, -} - -fn main() { - let fancy_ref1 = Arc::new(FancyNum { num: 5 }); - let fancy_ref2 = fancy_ref1.clone(); - - let x = thread::spawn(move || { - // `fancy_ref1` can be moved and has a `'static` lifetime - println!("child thread: {}", fancy_ref1.num); - }); - - x.join().expect("child thread should finish"); - println!("main thread: {}", fancy_ref2.num); -} -``` -"##, - -E0505: r##" -A value was moved out while it was still borrowed. - -Erroneous code example: - -```compile_fail,E0505 -struct Value {} - -fn eat(val: Value) {} - -fn main() { - let x = Value{}; - { - let _ref_to_val: &Value = &x; - eat(x); - } -} -``` - -Here, the function `eat` takes the ownership of `x`. However, -`x` cannot be moved because it was borrowed to `_ref_to_val`. -To fix that you can do few different things: - -* Try to avoid moving the variable. -* Release borrow before move. -* Implement the `Copy` trait on the type. - -Examples: - -``` -struct Value {} - -fn eat(val: &Value) {} - -fn main() { - let x = Value{}; - { - let _ref_to_val: &Value = &x; - eat(&x); // pass by reference, if it's possible - } -} -``` - -Or: - -``` -struct Value {} - -fn eat(val: Value) {} - -fn main() { - let x = Value{}; - { - let _ref_to_val: &Value = &x; - } - eat(x); // release borrow and then move it. -} -``` - -Or: - -``` -#[derive(Clone, Copy)] // implement Copy trait -struct Value {} - -fn eat(val: Value) {} - -fn main() { - let x = Value{}; - { - let _ref_to_val: &Value = &x; - eat(x); // it will be copied here. - } -} -``` - -You can find more information about borrowing in the rust-book: -http://doc.rust-lang.org/book/first-edition/references-and-borrowing.html -"##, - -E0506: r##" -This error occurs when an attempt is made to assign to a borrowed value. - -Example of erroneous code: - -```compile_fail,E0506 -struct FancyNum { - num: u8, -} - -fn main() { - let mut fancy_num = FancyNum { num: 5 }; - let fancy_ref = &fancy_num; - fancy_num = FancyNum { num: 6 }; - // error: cannot assign to `fancy_num` because it is borrowed - - println!("Num: {}, Ref: {}", fancy_num.num, fancy_ref.num); -} -``` - -Because `fancy_ref` still holds a reference to `fancy_num`, `fancy_num` can't -be assigned to a new value as it would invalidate the reference. - -Alternatively, we can move out of `fancy_num` into a second `fancy_num`: - -``` -struct FancyNum { - num: u8, -} - -fn main() { - let mut fancy_num = FancyNum { num: 5 }; - let moved_num = fancy_num; - fancy_num = FancyNum { num: 6 }; - - println!("Num: {}, Moved num: {}", fancy_num.num, moved_num.num); -} -``` - -If the value has to be borrowed, try limiting the lifetime of the borrow using -a scoped block: - -``` -struct FancyNum { - num: u8, -} - -fn main() { - let mut fancy_num = FancyNum { num: 5 }; - - { - let fancy_ref = &fancy_num; - println!("Ref: {}", fancy_ref.num); - } - - // Works because `fancy_ref` is no longer in scope - fancy_num = FancyNum { num: 6 }; - println!("Num: {}", fancy_num.num); -} -``` - -Or by moving the reference into a function: - -``` -struct FancyNum { - num: u8, -} - -fn main() { - let mut fancy_num = FancyNum { num: 5 }; - - print_fancy_ref(&fancy_num); - - // Works because function borrow has ended - fancy_num = FancyNum { num: 6 }; - println!("Num: {}", fancy_num.num); -} - -fn print_fancy_ref(fancy_ref: &FancyNum){ - println!("Ref: {}", fancy_ref.num); -} -``` -"##, - E0507: r##" You tried to move out of a value which was borrowed. Erroneous code example: @@ -1205,7 +656,6 @@ x.x = Some(&y); register_diagnostics! { // E0385, // {} in an aliasable location - E0524, // two closures require unique access to `..` at the same time E0594, // cannot assign to {} E0598, // lifetime of {} is too short to guarantee its contents can be... } diff --git a/src/librustc_data_structures/indexed_set.rs b/src/librustc_data_structures/indexed_set.rs index 572ce98d3ae8e..9cb6806e9ade5 100644 --- a/src/librustc_data_structures/indexed_set.rs +++ b/src/librustc_data_structures/indexed_set.rs @@ -153,4 +153,68 @@ impl IdxSet { pub fn subtract(&mut self, other: &IdxSet) -> 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(&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 { + Elems { i: 0, set: self, universe_size: universe_size } + } +} + +pub struct Elems<'a, T: Idx> { i: usize, set: &'a IdxSet, universe_size: usize } + +impl<'a, T: Idx> Iterator for Elems<'a, T> { + type Item = T; + fn next(&mut self) -> Option { + 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(words: &IdxSet, max_bits: usize, mut f: F) where F: FnMut(T) { + let usize_bits: usize = mem::size_of::() * 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)); + } + } + } + } + } } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 8a12aa1f7ae6a..0c729b5a3fc87 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -970,7 +970,12 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, // We compute "constant qualifications" between MIR_CONST and MIR_VALIDATED. // What we need to run borrowck etc. + passes.push_pass(MIR_VALIDATED, mir::transform::qualify_consts::QualifyAndPromoteConstants); + + // FIXME: ariel points SimplifyBranches should run after + // mir-borrowck; otherwise code within `if false { ... }` would + // not be checked. passes.push_pass(MIR_VALIDATED, mir::transform::simplify_branches::SimplifyBranches::new("initial")); passes.push_pass(MIR_VALIDATED, mir::transform::simplify::SimplifyCfg::new("qualify-consts")); @@ -978,6 +983,10 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, // borrowck runs between MIR_VALIDATED and MIR_OPTIMIZED. + // FIXME: niko says this should be a query (see rustc::ty::maps) + // instead of a pass. + passes.push_pass(MIR_VALIDATED, mir::transform::borrow_check::BorrowckMir); + // These next passes must be executed together passes.push_pass(MIR_OPTIMIZED, mir::transform::no_landing_pads::NoLandingPads); passes.push_pass(MIR_OPTIMIZED, mir::transform::add_call_guards::CriticalCallEdges); diff --git a/src/librustc_mir/Cargo.toml b/src/librustc_mir/Cargo.toml index 6e42e02d5109b..49e626c540082 100644 --- a/src/librustc_mir/Cargo.toml +++ b/src/librustc_mir/Cargo.toml @@ -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" } diff --git a/src/librustc_mir/dataflow/drop_flag_effects.rs b/src/librustc_mir/dataflow/drop_flag_effects.rs index a7894f0249972..bd41bce67da8e 100644 --- a/src/librustc_mir/dataflow/drop_flag_effects.rs +++ b/src/librustc_mir/dataflow/drop_flag_effects.rs @@ -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 { - 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, - bd: BD, - p: P) - -> DataflowResults - where BD: BitDenotation + DataflowOperator, - P: Fn(&BD, BD::Idx) -> &fmt::Debug -{ - let name_found = |sess: &Session, attrs: &[ast::Attribute], name| -> Option { - 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) diff --git a/src/librustc_mir/dataflow/graphviz.rs b/src/librustc_mir/dataflow/graphviz.rs index e6d77aa2686af..7ff4fbcf199e0 100644 --- a/src/librustc_mir/dataflow/graphviz.rs +++ b/src/librustc_mir/dataflow/graphviz.rs @@ -13,7 +13,6 @@ use syntax::ast::NodeId; use rustc::mir::{BasicBlock, Mir}; use rustc_data_structures::bitslice::bits_to_string; -use rustc_data_structures::indexed_set::{IdxSet}; use rustc_data_structures::indexed_vec::Idx; use dot; @@ -24,7 +23,6 @@ use std::fs::File; use std::io; use std::io::prelude::*; use std::marker::PhantomData; -use std::mem; use std::path::Path; use util; @@ -32,54 +30,6 @@ use util; use super::{BitDenotation, DataflowState}; use super::DataflowBuilder; -impl DataflowState { - fn each_bit(&self, words: &IdxSet, 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::() * 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, - render_idx: &P) - -> Vec<&'c Debug> - where P: Fn(&O, O::Idx) -> &Debug - { - let mut v = Vec::new(); - self.each_bit(words, |i| { - v.push(render_idx(o, i)); - }); - v - } -} - pub trait MirWithFlowState<'tcx> { type BD: BitDenotation; fn node_id(&self) -> NodeId; diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs new file mode 100644 index 0000000000000..ab62342e607dd --- /dev/null +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -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 or the MIT license +// , 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>, + location_map: FxHashMap, + region_map: FxHashMap, FxHashSet>, +} + +// 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>, + location_map: FxHashMap, + region_map: FxHashMap, FxHashSet>, + } + 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> { &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) { + // 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, + 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, + _location: Location) { + // no terminators start nor end code extents. + } + + fn propagate_call_return(&self, + _in_out: &mut IdxSet, + _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 + } +} diff --git a/src/librustc_mir/dataflow/impls/mod.rs b/src/librustc_mir/dataflow/impls/mod.rs index 41019799e41de..c7c66e1bb2b07 100644 --- a/src/librustc_mir/dataflow/impls/mod.rs +++ b/src/librustc_mir/dataflow/impls/mod.rs @@ -30,6 +30,9 @@ use super::drop_flag_effects_for_function_entry; use super::drop_flag_effects_for_location; use super::on_lookup_result_bits; +#[allow(dead_code)] +pub(super) mod borrows; + /// `MaybeInitializedLvals` tracks all l-values that might be /// initialized upon reaching a particular point in the control flow /// for a function. @@ -287,24 +290,22 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { fn statement_effect(&self, sets: &mut BlockSets, - 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, - bb: mir::BasicBlock, - statements_len: usize) + location: Location) { drop_flag_effects_for_location( self.tcx, self.mir, self.mdpe, - Location { block: bb, statement_index: statements_len }, + location, |path, s| Self::update_bits(sets, path, s) ) } @@ -344,24 +345,22 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { fn statement_effect(&self, sets: &mut BlockSets, - 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, - bb: mir::BasicBlock, - statements_len: usize) + location: Location) { drop_flag_effects_for_location( self.tcx, self.mir, self.mdpe, - Location { block: bb, statement_index: statements_len }, + location, |path, s| Self::update_bits(sets, path, s) ) } @@ -400,24 +399,22 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { fn statement_effect(&self, sets: &mut BlockSets, - 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, - bb: mir::BasicBlock, - statements_len: usize) + location: Location) { drop_flag_effects_for_location( self.tcx, self.mir, self.mdpe, - Location { block: bb, statement_index: statements_len }, + location, |path, s| Self::update_bits(sets, path, s) ) } @@ -448,18 +445,16 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { } fn statement_effect(&self, sets: &mut BlockSets, - bb: mir::BasicBlock, - idx: usize) { + location: Location) { let (tcx, mir, move_data) = (self.tcx, self.mir, self.move_data()); - let stmt = &mir[bb].statements[idx]; + let stmt = &mir[location.block].statements[location.statement_index]; let loc_map = &move_data.loc_map; let path_map = &move_data.path_map; let rev_lookup = &move_data.rev_lookup; - let loc = Location { block: bb, statement_index: idx }; debug!("stmt {:?} at loc {:?} moves out of move_indexes {:?}", - stmt, loc, &loc_map[loc]); - for move_index in &loc_map[loc] { + stmt, location, &loc_map[location]); + for move_index in &loc_map[location] { // Every path deinitialized by a *particular move* // has corresponding bit, "gen'ed" (i.e. set) // here, in dataflow vector @@ -506,17 +501,15 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { fn terminator_effect(&self, sets: &mut BlockSets, - 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); } diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs index ea855ee4d3e5f..e460848450e0b 100644 --- a/src/librustc_mir/dataflow/mod.rs +++ b/src/librustc_mir/dataflow/mod.rs @@ -8,16 +8,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use syntax::ast; +use syntax::ast::{self, MetaItem}; use rustc_data_structures::indexed_set::{IdxSet, IdxSetBuf}; use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::bitslice::{bitwise, BitwiseOperator}; -use rustc::ty::{TyCtxt}; -use rustc::mir::{self, Mir}; +use rustc::ty::{self, TyCtxt}; +use rustc::mir::{self, Mir, BasicBlock, BasicBlockData, Location, Statement, Terminator}; +use rustc::session::Session; -use std::fmt::Debug; +use std::fmt::{self, Debug}; use std::io; use std::mem; use std::path::PathBuf; @@ -25,9 +26,11 @@ use std::usize; pub use self::impls::{MaybeInitializedLvals, MaybeUninitializedLvals}; pub use self::impls::{DefinitelyInitializedLvals, MovingOutStatements}; - +pub use self::impls::borrows::{Borrows, BorrowData, BorrowIndex}; pub(crate) use self::drop_flag_effects::*; +use self::move_paths::MoveData; + mod drop_flag_effects; mod graphviz; mod impls; @@ -44,11 +47,22 @@ pub(crate) struct DataflowBuilder<'a, 'tcx: 'a, BD> where BD: BitDenotation } pub trait Dataflow { - fn dataflow

(&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

(&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 for DataflowBuilder<'a, 'tcx, BD> - where BD: BitDenotation + DataflowOperator +impl<'a, 'tcx: 'a, BD> Dataflow for DataflowBuilder<'a, 'tcx, BD> where BD: BitDenotation { fn dataflow

(&mut self, p: P) where P: Fn(&BD, BD::Idx) -> &Debug { self.flow_state.build_sets(); @@ -56,17 +70,79 @@ impl<'a, 'tcx: 'a, BD> Dataflow for DataflowBuilder<'a, 'tcx, BD> self.flow_state.propagate(); self.post_dataflow_instrumentation(|c,i| p(c,i)).unwrap(); } + + fn build_sets(&mut self) { self.flow_state.build_sets(); } + fn propagate(&mut self) { self.flow_state.propagate(); } +} + +pub(crate) fn has_rustc_mir_with(attrs: &[ast::Attribute], name: &str) -> Option { + 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, + bd: BD, + p: P) + -> DataflowResults + where BD: BitDenotation, + P: Fn(&BD, BD::Idx) -> &fmt::Debug +{ + let name_found = |sess: &Session, attrs: &[ast::Attribute], name| -> Option { + if let Some(item) = has_rustc_mir_with(attrs, name) { + if let Some(s) = item.value_str() { + return Some(s.to_string()) + } else { + sess.span_err( + item.span, + &format!("{} attribute requires a path", item.name())); + return None; + } + } + return None; + }; + + let print_preflow_to = + name_found(tcx.sess, attributes, "borrowck_graphviz_preflow"); + let print_postflow_to = + name_found(tcx.sess, attributes, "borrowck_graphviz_postflow"); + + let mut mbcx = DataflowBuilder { + node_id, + print_preflow_to, + print_postflow_to, + flow_state: DataflowAnalysis::new(tcx, mir, dead_unwinds, bd), + }; + + mbcx.dataflow(p); + mbcx.flow_state.results() } -struct PropagationContext<'b, 'a: 'b, 'tcx: 'a, O> - where O: 'b + BitDenotation +struct PropagationContext<'b, 'a: 'b, 'tcx: 'a, O> where O: 'b + BitDenotation { builder: &'b mut DataflowAnalysis<'a, 'tcx, O>, changed: bool, } -impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> - where BD: BitDenotation + DataflowOperator +impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> where BD: BitDenotation { fn propagate(&mut self) { let mut temp = IdxSetBuf::new_empty(self.flow_state.sets.bits_per_block); @@ -98,19 +174,19 @@ impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> let sets = &mut self.flow_state.sets.for_block(bb.index()); for j_stmt in 0..statements.len() { - self.flow_state.operator.statement_effect(sets, bb, j_stmt); + let location = Location { block: bb, statement_index: j_stmt }; + self.flow_state.operator.statement_effect(sets, location); } if terminator.is_some() { - let stmts_len = statements.len(); - self.flow_state.operator.terminator_effect(sets, bb, stmts_len); + let location = Location { block: bb, statement_index: statements.len() }; + self.flow_state.operator.terminator_effect(sets, location); } } } } -impl<'b, 'a: 'b, 'tcx: 'a, BD> PropagationContext<'b, 'a, 'tcx, BD> - where BD: BitDenotation + DataflowOperator +impl<'b, 'a: 'b, 'tcx: 'a, BD> PropagationContext<'b, 'a, 'tcx, BD> where BD: BitDenotation { fn reset(&mut self, bits: &mut IdxSet) { 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

(&self, p: P) -> io::Result<()> where P: Fn(&BD, BD::Idx) -> &Debug @@ -189,21 +264,108 @@ impl Bits { } } -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, dead_unwinds: &'a IdxSet, 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 { DataflowResults(self.flow_state) } + pub fn flow_state(&self) -> &DataflowState { &self.flow_state } + pub fn mir(&self) -> &'a Mir<'tcx> { self.mir } } @@ -213,10 +375,14 @@ impl DataflowResults { pub fn sets(&self) -> &AllSets { &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 { /// All the sets for the analysis. (Factored into its @@ -228,6 +394,28 @@ pub struct DataflowState pub(crate) operator: O, } +impl DataflowState { + pub fn each_bit(&self, words: &IdxSet, 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, + 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 { /// Analysis bitwidth for each block. @@ -251,9 +439,28 @@ pub struct AllSets { on_entry_sets: Bits, } +/// 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, + + /// Bits that are set to 1 by the time we exit the given block. pub(crate) gen_set: &'a mut IdxSet, + + /// Bits that are set to 0 by the time we exit the given block. pub(crate) kill_set: &'a mut IdxSet, } @@ -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, - bb: mir::BasicBlock, - idx_stmt: usize); + location: Location); /// Mutates the block-sets (the flow sets for the given /// basic block) according to the effects of evaluating @@ -356,8 +562,7 @@ pub trait BitDenotation { /// terminator took. fn terminator_effect(&self, sets: &mut BlockSets, - bb: mir::BasicBlock, - idx_term: usize); + location: Location); /// Mutates the block-sets according to the (flow-dependent) /// effect of a successful return from a Call terminator. @@ -385,8 +590,7 @@ pub trait BitDenotation { dest_lval: &mir::Lvalue); } -impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> - where D: BitDenotation + DataflowOperator +impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation { pub fn new(_tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>, @@ -427,8 +631,7 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> } } -impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> - where D: BitDenotation + DataflowOperator +impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation { /// Propagates the bits of `in_out` into all the successors of `bb`, /// using bitwise operator denoted by `self.operator`. diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs new file mode 100644 index 0000000000000..c45c91011d9f4 --- /dev/null +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -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 or the MIT license +// , 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>, + path_map: &mut IndexVec>, + parent: Option, + 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 + { + 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 + { + 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); + } +} diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs index eff6883f170e7..d2d8064984682 100644 --- a/src/librustc_mir/dataflow/move_paths/mod.rs +++ b/src/librustc_mir/dataflow/move_paths/mod.rs @@ -11,15 +11,10 @@ use rustc::ty::{self, TyCtxt}; use rustc::mir::*; -use rustc::mir::tcx::RvalueInitializationState; use rustc::util::nodemap::FxHashMap; use rustc_data_structures::indexed_vec::{IndexVec}; -use syntax::codemap::DUMMY_SP; - -use std::collections::hash_map::Entry; use std::fmt; -use std::mem; use std::ops::{Index, IndexMut}; use self::abs_domain::{AbstractElem, Lift}; @@ -63,6 +58,9 @@ pub(crate) mod indexes { /// Index into MoveData.moves. new_index!(MoveOutIndex, "mo"); + + /// Index into Borrows.locations + new_index!(BorrowIndex, "bw"); } pub use self::indexes::MovePathIndex; @@ -110,6 +108,12 @@ impl<'tcx> fmt::Debug for MovePath<'tcx> { } } +impl<'tcx> fmt::Display for MovePath<'tcx> { + fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { + write!(w, "{:?}", self.lvalue) + } +} + #[derive(Debug)] pub struct MoveData<'tcx> { pub move_paths: IndexVec>, @@ -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>, - path_map: &mut IndexVec>, - parent: Option, - 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 - { - 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 - { - let base = try!(self.move_path_for(&proj.base)); - let lv_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); - match lv_ty.sty { - // error: can't move out of borrowed content - ty::TyRef(..) | ty::TyRawPtr(..) => return Err(MovePathError::IllegalMove), - // error: can't move out of struct with destructor - ty::TyAdt(adt, _) if adt.has_dtor(self.tcx) && !adt.is_box() => - return Err(MovePathError::IllegalMove), - // move out of union - always move the entire union - ty::TyAdt(adt, _) if adt.is_union() => - return Err(MovePathError::UnionMove { path: base }), - // error: can't move out of a slice - ty::TySlice(..) => - return Err(MovePathError::IllegalMove), - ty::TyArray(..) => match proj.elem { - // error: can't move out of an array - ProjectionElem::Index(..) => return Err(MovePathError::IllegalMove), - _ => { - // FIXME: still badly broken - } - }, - _ => {} - }; - match self.data.rev_lookup.projections.entry((base, proj.elem.lift())) { - Entry::Occupied(ent) => Ok(*ent.get()), - Entry::Vacant(ent) => { - let path = Self::new_move_path( - &mut self.data.move_paths, - &mut self.data.path_map, - Some(base), - lval.clone() - ); - ent.insert(path); - Ok(path) - } - } - } - - fn finalize(self) -> MoveData<'tcx> { - debug!("{}", { - debug!("moves for {:?}:", self.mir.span); - for (j, mo) in self.data.moves.iter_enumerated() { - debug!(" {:?} = {:?}", j, mo); - } - debug!("move paths for {:?}:", self.mir.span); - for (j, path) in self.data.move_paths.iter_enumerated() { - debug!(" {:?} = {:?}", j, path); - } - "done dumping moves" - }); - self.data - } -} +mod builder; #[derive(Copy, Clone, Debug)] pub enum LookupResult { @@ -375,165 +232,6 @@ impl<'a, 'tcx> MoveData<'tcx> { tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self { - gather_moves(mir, tcx, param_env) - } -} - -fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>) - -> MoveData<'tcx> { - let mut builder = MoveDataBuilder::new(mir, tcx, param_env); - - for (bb, block) in mir.basic_blocks().iter_enumerated() { - for (i, stmt) in block.statements.iter().enumerate() { - let source = Location { block: bb, statement_index: i }; - builder.gather_statement(source, stmt); - } - - let terminator_loc = Location { - block: bb, - statement_index: block.statements.len() - }; - builder.gather_terminator(terminator_loc, block.terminator()); - } - - builder.finalize() -} - -impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { - fn gather_statement(&mut self, loc: Location, stmt: &Statement<'tcx>) { - debug!("gather_statement({:?}, {:?})", loc, stmt); - match stmt.kind { - StatementKind::Assign(ref lval, ref rval) => { - self.create_move_path(lval); - if let RvalueInitializationState::Shallow = rval.initialization_state() { - // Box starts out uninitialized - need to create a separate - // move-path for the interior so it will be separate from - // the exterior. - self.create_move_path(&lval.clone().deref()); - } - self.gather_rvalue(loc, rval); - } - StatementKind::StorageLive(_) | - StatementKind::StorageDead(_) => {} - StatementKind::SetDiscriminant{ .. } => { - span_bug!(stmt.source_info.span, - "SetDiscriminant should not exist during borrowck"); - } - StatementKind::InlineAsm { .. } | - StatementKind::EndRegion(_) | - StatementKind::Validate(..) | - StatementKind::Nop => {} - } - } - - fn gather_rvalue(&mut self, loc: Location, rvalue: &Rvalue<'tcx>) { - match *rvalue { - Rvalue::Use(ref operand) | - Rvalue::Repeat(ref operand, _) | - Rvalue::Cast(_, ref operand, _) | - Rvalue::UnaryOp(_, ref operand) => { - self.gather_operand(loc, operand) - } - Rvalue::BinaryOp(ref _binop, ref lhs, ref rhs) | - Rvalue::CheckedBinaryOp(ref _binop, ref lhs, ref rhs) => { - self.gather_operand(loc, lhs); - self.gather_operand(loc, rhs); - } - Rvalue::Aggregate(ref _kind, ref operands) => { - for operand in operands { - self.gather_operand(loc, operand); - } - } - Rvalue::Ref(..) | - Rvalue::Discriminant(..) | - Rvalue::Len(..) | - Rvalue::NullaryOp(NullOp::SizeOf, _) | - Rvalue::NullaryOp(NullOp::Box, _) => { - // This returns an rvalue with uninitialized contents. We can't - // move out of it here because it is an rvalue - assignments always - // completely initialize their lvalue. - // - // However, this does not matter - MIR building is careful to - // only emit a shallow free for the partially-initialized - // temporary. - // - // In any case, if we want to fix this, we have to register a - // special move and change the `statement_effect` functions. - } - } - } - - fn gather_terminator(&mut self, loc: Location, term: &Terminator<'tcx>) { - debug!("gather_terminator({:?}, {:?})", loc, term); - match term.kind { - TerminatorKind::Goto { target: _ } | - TerminatorKind::Resume | - TerminatorKind::Unreachable => { } - - TerminatorKind::Return => { - self.gather_move(loc, &Lvalue::Local(RETURN_POINTER)); - } - - TerminatorKind::Assert { .. } | - TerminatorKind::SwitchInt { .. } => { - // branching terminators - these don't move anything - } - - TerminatorKind::Drop { ref location, target: _, unwind: _ } => { - self.gather_move(loc, location); - } - TerminatorKind::DropAndReplace { ref location, ref value, .. } => { - self.create_move_path(location); - self.gather_operand(loc, value); - } - TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => { - self.gather_operand(loc, func); - for arg in args { - self.gather_operand(loc, arg); - } - if let Some((ref destination, _bb)) = *destination { - self.create_move_path(destination); - } - } - } - } - - fn gather_operand(&mut self, loc: Location, operand: &Operand<'tcx>) { - match *operand { - Operand::Constant(..) => {} // not-a-move - Operand::Consume(ref lval) => { // a move - self.gather_move(loc, lval); - } - } - } - - fn gather_move(&mut self, loc: Location, lval: &Lvalue<'tcx>) { - debug!("gather_move({:?}, {:?})", loc, lval); - - let lv_ty = lval.ty(self.mir, self.tcx).to_ty(self.tcx); - if !lv_ty.moves_by_default(self.tcx, self.param_env, DUMMY_SP) { - debug!("gather_move({:?}, {:?}) - {:?} is Copy. skipping", loc, lval, lv_ty); - return - } - - let path = match self.move_path_for(lval) { - Ok(path) | Err(MovePathError::UnionMove { path }) => path, - Err(MovePathError::IllegalMove) => { - // Moving out of a bad path. Eventually, this should be a MIR - // borrowck error instead of a bug. - span_bug!(self.mir.span, - "Broken MIR: moving out of lvalue {:?}: {:?} at {:?}", - lval, lv_ty, loc); - } - }; - let move_out = self.data.moves.push(MoveOut { path: path, source: loc }); - - debug!("gather_move({:?}, {:?}): adding move {:?} of {:?}", - loc, lval, move_out, path); - - self.data.path_map[path].push(move_out); - self.data.loc_map[loc].push(move_out); + builder::gather_moves(mir, tcx, param_env) } } diff --git a/src/librustc_mir/diagnostics.rs b/src/librustc_mir/diagnostics.rs index 34170a6609c46..83a8ce34c6928 100644 --- a/src/librustc_mir/diagnostics.rs +++ b/src/librustc_mir/diagnostics.rs @@ -195,6 +195,50 @@ instead of using a `const fn`, or refactoring the code to a functional style to avoid mutation if possible. "##, +E0381: r##" +It is not allowed to use or capture an uninitialized variable. For example: + +```compile_fail,E0381 +fn main() { + let x: i32; + let y = x; // error, use of possibly uninitialized variable +} +``` + +To fix this, ensure that any declared variables are initialized before being +used. Example: + +``` +fn main() { + let x: i32 = 0; + let y = x; // ok! +} +``` +"##, + +E0384: r##" +This error occurs when an attempt is made to reassign an immutable variable. +For example: + +```compile_fail,E0384 +fn main() { + let x = 3; + x = 5; // error, reassignment of immutable variable +} +``` + +By default, variables in Rust are immutable. To fix this error, add the keyword +`mut` after the keyword `let` when declaring the variable. For example: + +``` +fn main() { + let mut x = 3; + x = 5; +} +``` +"##, + + E0394: r##" A static was referred to by value by another static. @@ -438,9 +482,516 @@ static A : &'static u32 = &S.a; // ok! ``` "##, +E0499: r##" +A variable was borrowed as mutable more than once. Erroneous code example: + +```compile_fail,E0499 +let mut i = 0; +let mut x = &mut i; +let mut a = &mut i; +// error: cannot borrow `i` as mutable more than once at a time +``` + +Please note that in rust, you can either have many immutable references, or one +mutable reference. Take a look at +https://doc.rust-lang.org/stable/book/references-and-borrowing.html for more +information. Example: + + +``` +let mut i = 0; +let mut x = &mut i; // ok! + +// or: +let mut i = 0; +let a = &i; // ok! +let b = &i; // still ok! +let c = &i; // still ok! +``` +"##, + +E0500: r##" +A borrowed variable was used in another closure. Example of erroneous code: + +```compile_fail +fn you_know_nothing(jon_snow: &mut i32) { + let nights_watch = || { + *jon_snow = 2; + }; + let starks = || { + *jon_snow = 3; // error: closure requires unique access to `jon_snow` + // but it is already borrowed + }; +} +``` + +In here, `jon_snow` is already borrowed by the `nights_watch` closure, so it +cannot be borrowed by the `starks` closure at the same time. To fix this issue, +you can put the closure in its own scope: + +``` +fn you_know_nothing(jon_snow: &mut i32) { + { + let nights_watch = || { + *jon_snow = 2; + }; + } // At this point, `jon_snow` is free. + let starks = || { + *jon_snow = 3; + }; +} +``` + +Or, if the type implements the `Clone` trait, you can clone it between +closures: + +``` +fn you_know_nothing(jon_snow: &mut i32) { + let mut jon_copy = jon_snow.clone(); + let nights_watch = || { + jon_copy = 2; + }; + let starks = || { + *jon_snow = 3; + }; +} +``` +"##, + +E0501: r##" +This error indicates that a mutable variable is being used while it is still +captured by a closure. Because the closure has borrowed the variable, it is not +available for use until the closure goes out of scope. + +Note that a capture will either move or borrow a variable, but in this +situation, the closure is borrowing the variable. Take a look at +http://rustbyexample.com/fn/closures/capture.html for more information about +capturing. + +Example of erroneous code: + +```compile_fail,E0501 +fn inside_closure(x: &mut i32) { + // Actions which require unique access +} + +fn outside_closure(x: &mut i32) { + // Actions which require unique access +} + +fn foo(a: &mut i32) { + let bar = || { + inside_closure(a) + }; + outside_closure(a); // error: cannot borrow `*a` as mutable because previous + // closure requires unique access. +} +``` + +To fix this error, you can place the closure in its own scope: + +``` +fn inside_closure(x: &mut i32) {} +fn outside_closure(x: &mut i32) {} + +fn foo(a: &mut i32) { + { + let bar = || { + inside_closure(a) + }; + } // borrow on `a` ends. + outside_closure(a); // ok! +} +``` + +Or you can pass the variable as a parameter to the closure: + +``` +fn inside_closure(x: &mut i32) {} +fn outside_closure(x: &mut i32) {} + +fn foo(a: &mut i32) { + let bar = |s: &mut i32| { + inside_closure(s) + }; + outside_closure(a); + bar(a); +} +``` + +It may be possible to define the closure later: + +``` +fn inside_closure(x: &mut i32) {} +fn outside_closure(x: &mut i32) {} + +fn foo(a: &mut i32) { + outside_closure(a); + let bar = || { + inside_closure(a) + }; +} +``` +"##, + +E0502: r##" +This error indicates that you are trying to borrow a variable as mutable when it +has already been borrowed as immutable. + +Example of erroneous code: + +```compile_fail,E0502 +fn bar(x: &mut i32) {} +fn foo(a: &mut i32) { + let ref y = a; // a is borrowed as immutable. + bar(a); // error: cannot borrow `*a` as mutable because `a` is also borrowed + // as immutable +} +``` + +To fix this error, ensure that you don't have any other references to the +variable before trying to access it mutably: + +``` +fn bar(x: &mut i32) {} +fn foo(a: &mut i32) { + bar(a); + let ref y = a; // ok! +} +``` + +For more information on the rust ownership system, take a look at +https://doc.rust-lang.org/stable/book/references-and-borrowing.html. +"##, + +E0503: r##" +A value was used after it was mutably borrowed. + +Example of erroneous code: + +```compile_fail,E0503 +fn main() { + let mut value = 3; + // Create a mutable borrow of `value`. This borrow + // lives until the end of this function. + let _borrow = &mut value; + let _sum = value + 1; // error: cannot use `value` because + // it was mutably borrowed +} +``` + +In this example, `value` is mutably borrowed by `borrow` and cannot be +used to calculate `sum`. This is not possible because this would violate +Rust's mutability rules. + +You can fix this error by limiting the scope of the borrow: + +``` +fn main() { + let mut value = 3; + // By creating a new block, you can limit the scope + // of the reference. + { + let _borrow = &mut value; // Use `_borrow` inside this block. + } + // The block has ended and with it the borrow. + // You can now use `value` again. + let _sum = value + 1; +} +``` + +Or by cloning `value` before borrowing it: + +``` +fn main() { + let mut value = 3; + // We clone `value`, creating a copy. + let value_cloned = value.clone(); + // The mutable borrow is a reference to `value` and + // not to `value_cloned`... + let _borrow = &mut value; + // ... which means we can still use `value_cloned`, + let _sum = value_cloned + 1; + // even though the borrow only ends here. +} +``` + +You can find more information about borrowing in the rust-book: +http://doc.rust-lang.org/stable/book/references-and-borrowing.html +"##, + +E0504: r##" +This error occurs when an attempt is made to move a borrowed variable into a +closure. + +Example of erroneous code: + +```compile_fail,E0504 +struct FancyNum { + num: u8, +} + +fn main() { + let fancy_num = FancyNum { num: 5 }; + let fancy_ref = &fancy_num; + + let x = move || { + println!("child function: {}", fancy_num.num); + // error: cannot move `fancy_num` into closure because it is borrowed + }; + + x(); + println!("main function: {}", fancy_ref.num); +} +``` + +Here, `fancy_num` is borrowed by `fancy_ref` and so cannot be moved into +the closure `x`. There is no way to move a value into a closure while it is +borrowed, as that would invalidate the borrow. + +If the closure can't outlive the value being moved, try using a reference +rather than moving: + +``` +struct FancyNum { + num: u8, +} + +fn main() { + let fancy_num = FancyNum { num: 5 }; + let fancy_ref = &fancy_num; + + let x = move || { + // fancy_ref is usable here because it doesn't move `fancy_num` + println!("child function: {}", fancy_ref.num); + }; + + x(); + + println!("main function: {}", fancy_num.num); +} +``` + +If the value has to be borrowed and then moved, try limiting the lifetime of +the borrow using a scoped block: + +``` +struct FancyNum { + num: u8, +} + +fn main() { + let fancy_num = FancyNum { num: 5 }; + + { + let fancy_ref = &fancy_num; + println!("main function: {}", fancy_ref.num); + // `fancy_ref` goes out of scope here + } + + let x = move || { + // `fancy_num` can be moved now (no more references exist) + println!("child function: {}", fancy_num.num); + }; + + x(); +} +``` + +If the lifetime of a reference isn't enough, such as in the case of threading, +consider using an `Arc` to create a reference-counted value: + +``` +use std::sync::Arc; +use std::thread; + +struct FancyNum { + num: u8, +} + +fn main() { + let fancy_ref1 = Arc::new(FancyNum { num: 5 }); + let fancy_ref2 = fancy_ref1.clone(); + + let x = thread::spawn(move || { + // `fancy_ref1` can be moved and has a `'static` lifetime + println!("child thread: {}", fancy_ref1.num); + }); + + x.join().expect("child thread should finish"); + println!("main thread: {}", fancy_ref2.num); +} +``` +"##, + +E0505: r##" +A value was moved out while it was still borrowed. + +Erroneous code example: + +```compile_fail,E0505 +struct Value {} + +fn eat(val: Value) {} + +fn main() { + let x = Value{}; + { + let _ref_to_val: &Value = &x; + eat(x); + } +} +``` + +Here, the function `eat` takes the ownership of `x`. However, +`x` cannot be moved because it was borrowed to `_ref_to_val`. +To fix that you can do few different things: + +* Try to avoid moving the variable. +* Release borrow before move. +* Implement the `Copy` trait on the type. + +Examples: + +``` +struct Value {} + +fn eat(val: &Value) {} + +fn main() { + let x = Value{}; + { + let _ref_to_val: &Value = &x; + eat(&x); // pass by reference, if it's possible + } +} +``` + +Or: + +``` +struct Value {} + +fn eat(val: Value) {} + +fn main() { + let x = Value{}; + { + let _ref_to_val: &Value = &x; + } + eat(x); // release borrow and then move it. +} +``` + +Or: + +``` +#[derive(Clone, Copy)] // implement Copy trait +struct Value {} + +fn eat(val: Value) {} + +fn main() { + let x = Value{}; + { + let _ref_to_val: &Value = &x; + eat(x); // it will be copied here. + } +} +``` + +You can find more information about borrowing in the rust-book: +http://doc.rust-lang.org/stable/book/references-and-borrowing.html +"##, + +E0506: r##" +This error occurs when an attempt is made to assign to a borrowed value. + +Example of erroneous code: + +```compile_fail,E0506 +struct FancyNum { + num: u8, +} + +fn main() { + let mut fancy_num = FancyNum { num: 5 }; + let fancy_ref = &fancy_num; + fancy_num = FancyNum { num: 6 }; + // error: cannot assign to `fancy_num` because it is borrowed + + println!("Num: {}, Ref: {}", fancy_num.num, fancy_ref.num); +} +``` + +Because `fancy_ref` still holds a reference to `fancy_num`, `fancy_num` can't +be assigned to a new value as it would invalidate the reference. + +Alternatively, we can move out of `fancy_num` into a second `fancy_num`: + +``` +struct FancyNum { + num: u8, +} + +fn main() { + let mut fancy_num = FancyNum { num: 5 }; + let moved_num = fancy_num; + fancy_num = FancyNum { num: 6 }; + + println!("Num: {}, Moved num: {}", fancy_num.num, moved_num.num); +} +``` + +If the value has to be borrowed, try limiting the lifetime of the borrow using +a scoped block: + +``` +struct FancyNum { + num: u8, +} + +fn main() { + let mut fancy_num = FancyNum { num: 5 }; + + { + let fancy_ref = &fancy_num; + println!("Ref: {}", fancy_ref.num); + } + + // Works because `fancy_ref` is no longer in scope + fancy_num = FancyNum { num: 6 }; + println!("Num: {}", fancy_num.num); +} +``` + +Or by moving the reference into a function: + +``` +struct FancyNum { + num: u8, +} + +fn main() { + let mut fancy_num = FancyNum { num: 5 }; + + print_fancy_ref(&fancy_num); + + // Works because function borrow has ended + fancy_num = FancyNum { num: 6 }; + println!("Num: {}", fancy_num.num); +} + +fn print_fancy_ref(fancy_ref: &FancyNum){ + println!("Ref: {}", fancy_ref.num); +} +``` +"##, + } register_diagnostics! { + E0524, // two closures require unique access to `..` at the same time E0526, // shuffle indices are not constant E0625, // thread-local statics cannot be accessed at compile-time } diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index ea8624930e5f5..143ad784171a0 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -32,6 +32,7 @@ extern crate graphviz as dot; #[macro_use] extern crate rustc; extern crate rustc_data_structures; +extern crate rustc_errors; #[macro_use] #[no_link] extern crate rustc_bitflags; diff --git a/src/librustc_mir/transform/borrow_check.rs b/src/librustc_mir/transform/borrow_check.rs new file mode 100644 index 0000000000000..b56d5d31f55fa --- /dev/null +++ b/src/librustc_mir/transform/borrow_check.rs @@ -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 or the MIT license +// , 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>, + inits: FlowInProgress>, + uninits: FlowInProgress>, + move_outs: FlowInProgress>, +} + +struct FlowInProgress where BD: BitDenotation { + base_results: DataflowResults, + curr_state: IdxSetBuf, + stmt_gen: IdxSetBuf, + stmt_kill: IdxSetBuf, +} + +// 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 + { + // 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::>()), + (i, issued, self.base_path(&issued.lvalue), + self.restrictions(&issued.lvalue).collect::>()), + 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(&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 { + '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>, + inits: DataflowResults>, + uninits: DataflowResults>, + move_outs: DataflowResults>) -> Self { + InProgress { + borrows: FlowInProgress::new(borrows), + inits: FlowInProgress::new(inits), + uninits: FlowInProgress::new(uninits), + move_outs: FlowInProgress::new(move_outs), + } + } + + fn each_flow(&mut self, + mut xform_borrows: XB, + mut xform_inits: XI, + mut xform_uninits: XU) where + XB: FnMut(&mut FlowInProgress>), + XI: FnMut(&mut FlowInProgress>), + XU: FnMut(&mut FlowInProgress>), + { + 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 FlowInProgress where BD: BitDenotation { + fn each_state_bit(&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(&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) -> 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 { + let univ = self.base_results.sets().bits_per_block(); + self.stmt_gen.elems(univ) + } + + fn elems_incoming(&self) -> indexed_set::Elems { + 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, + } + } +} diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index e3e4265a7dbc6..38070a4b4858a 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -31,6 +31,7 @@ pub mod simplify; pub mod erase_regions; pub mod no_landing_pads; pub mod type_check; +pub mod borrow_check; pub mod rustc_peek; pub mod elaborate_drops; pub mod add_call_guards; diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs index 268e7a4c185b0..ceff52409b2f0 100644 --- a/src/librustc_mir/transform/rustc_peek.rs +++ b/src/librustc_mir/transform/rustc_peek.rs @@ -13,7 +13,7 @@ use syntax::ast; use syntax_pos::Span; use rustc::ty::{self, TyCtxt}; -use rustc::mir::{self, Mir}; +use rustc::mir::{self, Mir, Location}; use rustc::mir::transform::{MirPass, MirSource}; use rustc_data_structures::indexed_set::IdxSetBuf; use rustc_data_structures::indexed_vec::Idx; @@ -202,7 +202,7 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // reset GEN and KILL sets before emulating their effect. for e in sets.gen_set.words_mut() { *e = 0; } for e in sets.kill_set.words_mut() { *e = 0; } - results.0.operator.statement_effect(&mut sets, bb, j); + results.0.operator.statement_effect(&mut sets, Location { block: bb, statement_index: j }); sets.on_entry.union(sets.gen_set); sets.on_entry.subtract(sets.kill_set); } diff --git a/src/librustc_mir/util/borrowck_errors.rs b/src/librustc_mir/util/borrowck_errors.rs new file mode 100644 index 0000000000000..9de3072658660 --- /dev/null +++ b/src/librustc_mir/util/borrowck_errors.rs @@ -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 or the MIT license +// , 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>(&'a self, + sp: S, + msg: &str, + code: &str) + -> DiagnosticBuilder<'a>; + + fn struct_span_err<'a, S: Into>(&'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>(&'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>(&'a self, + sp: S, + msg: &str) + -> DiagnosticBuilder<'a> + { + self.sess.struct_span_err(sp, msg) + } +} diff --git a/src/librustc_mir/util/mod.rs b/src/librustc_mir/util/mod.rs index 4386bab38c039..f0d837e1362d1 100644 --- a/src/librustc_mir/util/mod.rs +++ b/src/librustc_mir/util/mod.rs @@ -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;