diff --git a/src/Cargo.lock b/src/Cargo.lock index c175198c227..b5db5ce3613 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_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs index c8237717325..4058f3198af 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 9514b9b39fd..8c79534d209 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 fea9d0d6f13..1f1fc4cc65f 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_mir/Cargo.toml b/src/librustc_mir/Cargo.toml index 6e42e02d510..49e626c5400 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/diagnostics.rs b/src/librustc_mir/diagnostics.rs index 34170a6609c..83a8ce34c69 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 ea8624930e5..143ad784171 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/util/borrowck_errors.rs b/src/librustc_mir/util/borrowck_errors.rs new file mode 100644 index 00000000000..9de30726586 --- /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 4386bab38c0..f0d837e1362 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;