diff options
34 files changed, 1265 insertions, 644 deletions
diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index d6f7c2aa887..fc3c2a3f68f 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -614,11 +614,12 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { let partial = moved_lp.depth() > lp.depth(); let msg = if !has_fork && partial { "partially " } else if has_fork && !has_common { "collaterally "} - else { "" }; - let mut err = struct_span_err!( - self.tcx.sess, use_span, E0382, - "{} of {}moved value: `{}`", - verb, msg, nl); + else { "" }; + let mut err = self.cannot_act_on_moved_value(use_span, + verb, + msg, + &format!("{}", nl), + Origin::Ast); let need_note = match lp.ty.sty { ty::TypeVariants::TyClosure(id, _) => { let node_id = self.tcx.hir.as_local_node_id(id).unwrap(); @@ -698,10 +699,10 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { &self, span: Span, lp: &LoanPath<'tcx>) { - span_err!( - self.tcx.sess, span, E0383, - "partial reinitialization of uninitialized structure `{}`", - self.loan_path_to_string(lp)); + self.cannot_partially_reinit_an_uninit_struct(span, + &self.loan_path_to_string(lp), + Origin::Ast) + .emit(); } pub fn report_reassigned_immutable_variable(&self, @@ -776,8 +777,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { db } BorrowViolation(euv::ClosureCapture(_)) => { - struct_span_err!(self.tcx.sess, error_span, E0595, - "closure cannot assign to {}", descr) + self.closure_cannot_assign_to_borrowed(error_span, &descr, Origin::Ast) } BorrowViolation(euv::OverloadedOperator) | BorrowViolation(euv::AddrOf) | @@ -786,8 +786,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { BorrowViolation(euv::AutoUnsafe) | BorrowViolation(euv::ForLoop) | BorrowViolation(euv::MatchDiscriminant) => { - struct_span_err!(self.tcx.sess, error_span, E0596, - "cannot borrow {} as mutable", descr) + self.cannot_borrow_path_as_mutable(error_span, &descr, Origin::Ast) } BorrowViolation(euv::ClosureInvocation) => { span_bug!(err.span, @@ -869,21 +868,12 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { if let Some((yield_span, _)) = maybe_borrow_across_yield { debug!("err_out_of_scope: opt_yield_span = {:?}", yield_span); - struct_span_err!(self.tcx.sess, - error_span, - E0626, - "borrow may still be in use when generator yields") - .span_label(yield_span, "possible yield occurs here") + self.cannot_borrow_across_generator_yield(error_span, yield_span, Origin::Ast) .emit(); return; } - let mut db = struct_span_err!(self.tcx.sess, - error_span, - E0597, - "{} does not live long enough", - msg); - + let mut db = self.path_does_not_live_long_enough(error_span, &msg, Origin::Ast); let (value_kind, value_msg) = match err.cmt.cat { mc::Categorization::Rvalue(..) => ("temporary value", "temporary value created here"), @@ -992,11 +982,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { } err_borrowed_pointer_too_short(loan_scope, ptr_scope) => { let descr = self.cmt_to_path_or_string(&err.cmt); - let mut db = struct_span_err!(self.tcx.sess, error_span, E0598, - "lifetime of {} is too short to guarantee \ - its contents can be safely reborrowed", - descr); - + let mut db = self.lifetime_too_short_for_reborrow(error_span, &descr, Origin::Ast); let descr = match opt_loan_path(&err.cmt) { Some(lp) => { format!("`{}`", self.loan_path_to_string(&lp)) @@ -1068,12 +1054,8 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { let blame = cmt.immutability_blame(); let mut err = match blame { Some(ImmutabilityBlame::ClosureEnv(id)) => { - let mut err = struct_span_err!( - self.tcx.sess, span, E0387, - "{} in a captured outer variable in an `Fn` closure", prefix); - // FIXME: the distinction between these 2 messages looks wrong. - let help = if let BorrowViolation(euv::ClosureCapture(_)) = kind { + let help_msg = if let BorrowViolation(euv::ClosureCapture(_)) = kind { // The aliasability violation with closure captures can // happen for nested closures, so we know the enclosing // closure incorrectly accepts an `Fn` while it needs to @@ -1084,15 +1066,15 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { "consider changing this closure to take self by mutable reference" }; let node_id = self.tcx.hir.def_index_to_node_id(id); - err.span_help(self.tcx.hir.span(node_id), help); - err + let help_span = self.tcx.hir.span(node_id); + self.cannot_act_on_capture_in_sharable_fn(span, + prefix, + (help_span, help_msg), + Origin::Ast) } _ => { - let mut err = struct_span_err!( - self.tcx.sess, span, E0389, - "{} in a `&` reference", prefix); - err.span_label(span, "assignment into an immutable reference"); - err + self.cannot_assign_into_immutable_reference(span, prefix, + Origin::Ast) } }; self.note_immutability_blame(&mut err, blame); @@ -1244,17 +1226,10 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { Err(_) => format!("move |<args>| <body>") }; - struct_span_err!(self.tcx.sess, err.span, E0373, - "closure may outlive the current function, \ - but it borrows {}, \ - which is owned by the current function", - cmt_path_or_string) - .span_label(capture_span, - format!("{} is borrowed here", - cmt_path_or_string)) - .span_label(err.span, - format!("may outlive borrowed value {}", - cmt_path_or_string)) + self.cannot_capture_in_long_lived_closure(err.span, + &cmt_path_or_string, + capture_span, + Origin::Ast) .span_suggestion(err.span, &format!("to force the closure to take ownership of {} \ (and any other referenced variables), \ diff --git a/src/librustc_borrowck/diagnostics.rs b/src/librustc_borrowck/diagnostics.rs index 031dbcb1ebb..3fea01443be 100644 --- a/src/librustc_borrowck/diagnostics.rs +++ b/src/librustc_borrowck/diagnostics.rs @@ -9,472 +9,3 @@ // except according to those terms. #![allow(non_snake_case)] - -register_long_diagnostics! { - -E0373: r##" -This error occurs when an attempt is made to use data captured by a closure, -when that data may no longer exist. It's most commonly seen when attempting to -return a closure: - -```compile_fail,E0373 -fn foo() -> Box<Fn(u32) -> u32> { - let x = 0u32; - Box::new(|y| x + y) -} -``` - -Notice that `x` is stack-allocated by `foo()`. By default, Rust captures -closed-over data by reference. This means that once `foo()` returns, `x` no -longer exists. An attempt to access `x` within the closure would thus be -unsafe. - -Another situation where this might be encountered is when spawning threads: - -```compile_fail,E0373 -fn foo() { - let x = 0u32; - let y = 1u32; - - let thr = std::thread::spawn(|| { - x + y - }); -} -``` - -Since our new thread runs in parallel, the stack frame containing `x` and `y` -may well have disappeared by the time we try to use them. Even if we call -`thr.join()` within foo (which blocks until `thr` has completed, ensuring the -stack frame won't disappear), we will not succeed: the compiler cannot prove -that this behaviour is safe, and so won't let us do it. - -The solution to this problem is usually to switch to using a `move` closure. -This approach moves (or copies, where possible) data into the closure, rather -than taking references to it. For example: - -``` -fn foo() -> Box<Fn(u32) -> u32> { - let x = 0u32; - Box::new(move |y| x + y) -} -``` - -Now that the closure has its own copy of the data, there's no need to worry -about safety. -"##, - -E0382: r##" -This error occurs when an attempt is made to use a variable after its contents -have been moved elsewhere. For example: - -```compile_fail,E0382 -struct MyStruct { s: u32 } - -fn main() { - let mut x = MyStruct{ s: 5u32 }; - let y = x; - x.s = 6; - println!("{}", x.s); -} -``` - -Since `MyStruct` is a type that is not marked `Copy`, the data gets moved out -of `x` when we set `y`. This is fundamental to Rust's ownership system: outside -of workarounds like `Rc`, a value cannot be owned by more than one variable. - -If we own the type, the easiest way to address this problem is to implement -`Copy` and `Clone` on it, as shown below. This allows `y` to copy the -information in `x`, while leaving the original version owned by `x`. Subsequent -changes to `x` will not be reflected when accessing `y`. - -``` -#[derive(Copy, Clone)] -struct MyStruct { s: u32 } - -fn main() { - let mut x = MyStruct{ s: 5u32 }; - let y = x; - x.s = 6; - println!("{}", x.s); -} -``` - -Alternatively, if we don't control the struct's definition, or mutable shared -ownership is truly required, we can use `Rc` and `RefCell`: - -``` -use std::cell::RefCell; -use std::rc::Rc; - -struct MyStruct { s: u32 } - -fn main() { - let mut x = Rc::new(RefCell::new(MyStruct{ s: 5u32 })); - let y = x.clone(); - x.borrow_mut().s = 6; - println!("{}", x.borrow().s); -} -``` - -With this approach, x and y share ownership of the data via the `Rc` (reference -count type). `RefCell` essentially performs runtime borrow checking: ensuring -that at most one writer or multiple readers can access the data at any one time. - -If you wish to learn more about ownership in Rust, start with the chapter in the -Book: - -https://doc.rust-lang.org/book/first-edition/ownership.html -"##, - -E0383: r##" -This error occurs when an attempt is made to partially reinitialize a -structure that is currently uninitialized. - -For example, this can happen when a drop has taken place: - -```compile_fail,E0383 -struct Foo { - a: u32, -} -impl Drop for Foo { - fn drop(&mut self) { /* ... */ } -} - -let mut x = Foo { a: 1 }; -drop(x); // `x` is now uninitialized -x.a = 2; // error, partial reinitialization of uninitialized structure `t` -``` - -This error can be fixed by fully reinitializing the structure in question: - -``` -struct Foo { - a: u32, -} -impl Drop for Foo { - fn drop(&mut self) { /* ... */ } -} - -let mut x = Foo { a: 1 }; -drop(x); -x = Foo { a: 2 }; -``` -"##, - -/*E0386: r##" -This error occurs when an attempt is made to mutate the target of a mutable -reference stored inside an immutable container. - -For example, this can happen when storing a `&mut` inside an immutable `Box`: - -```compile_fail,E0386 -let mut x: i64 = 1; -let y: Box<_> = Box::new(&mut x); -**y = 2; // error, cannot assign to data in an immutable container -``` - -This error can be fixed by making the container mutable: - -``` -let mut x: i64 = 1; -let mut y: Box<_> = Box::new(&mut x); -**y = 2; -``` - -It can also be fixed by using a type with interior mutability, such as `Cell` -or `RefCell`: - -``` -use std::cell::Cell; - -let x: i64 = 1; -let y: Box<Cell<_>> = Box::new(Cell::new(x)); -y.set(2); -``` -"##,*/ - -E0387: r##" -This error occurs when an attempt is made to mutate or mutably reference data -that a closure has captured immutably. Examples of this error are shown below: - -```compile_fail,E0387 -// Accepts a function or a closure that captures its environment immutably. -// Closures passed to foo will not be able to mutate their closed-over state. -fn foo<F: Fn()>(f: F) { } - -// Attempts to mutate closed-over data. Error message reads: -// `cannot assign to data in a captured outer variable...` -fn mutable() { - let mut x = 0u32; - foo(|| x = 2); -} - -// Attempts to take a mutable reference to closed-over data. Error message -// reads: `cannot borrow data mutably in a captured outer variable...` -fn mut_addr() { - let mut x = 0u32; - foo(|| { let y = &mut x; }); -} -``` - -The problem here is that foo is defined as accepting a parameter of type `Fn`. -Closures passed into foo will thus be inferred to be of type `Fn`, meaning that -they capture their context immutably. - -If the definition of `foo` is under your control, the simplest solution is to -capture the data mutably. This can be done by defining `foo` to take FnMut -rather than Fn: - -``` -fn foo<F: FnMut()>(f: F) { } -``` - -Alternatively, we can consider using the `Cell` and `RefCell` types to achieve -interior mutability through a shared reference. Our example's `mutable` -function could be redefined as below: - -``` -use std::cell::Cell; - -fn foo<F: Fn()>(f: F) { } - -fn mutable() { - let x = Cell::new(0u32); - foo(|| x.set(2)); -} -``` - -You can read more about cell types in the API documentation: - -https://doc.rust-lang.org/std/cell/ -"##, - -E0388: r##" -E0388 was removed and is no longer issued. -"##, - -E0389: r##" -An attempt was made to mutate data using a non-mutable reference. This -commonly occurs when attempting to assign to a non-mutable reference of a -mutable reference (`&(&mut T)`). - -Example of erroneous code: - -```compile_fail,E0389 -struct FancyNum { - num: u8, -} - -fn main() { - let mut fancy = FancyNum{ num: 5 }; - let fancy_ref = &(&mut fancy); - fancy_ref.num = 6; // error: cannot assign to data in a `&` reference - println!("{}", fancy_ref.num); -} -``` - -Here, `&mut fancy` is mutable, but `&(&mut fancy)` is not. Creating an -immutable reference to a value borrows it immutably. There can be multiple -references of type `&(&mut T)` that point to the same value, so they must be -immutable to prevent multiple mutable references to the same value. - -To fix this, either remove the outer reference: - -``` -struct FancyNum { - num: u8, -} - -fn main() { - let mut fancy = FancyNum{ num: 5 }; - - let fancy_ref = &mut fancy; - // `fancy_ref` is now &mut FancyNum, rather than &(&mut FancyNum) - - fancy_ref.num = 6; // No error! - - println!("{}", fancy_ref.num); -} -``` - -Or make the outer reference mutable: - -``` -struct FancyNum { - num: u8 -} - -fn main() { - let mut fancy = FancyNum{ num: 5 }; - - let fancy_ref = &mut (&mut fancy); - // `fancy_ref` is now &mut(&mut FancyNum), rather than &(&mut FancyNum) - - fancy_ref.num = 6; // No error! - - println!("{}", fancy_ref.num); -} -``` -"##, - -E0595: r##" -Closures cannot mutate immutable captured variables. - -Erroneous code example: - -```compile_fail,E0595 -let x = 3; // error: closure cannot assign to immutable local variable `x` -let mut c = || { x += 1 }; -``` - -Make the variable binding mutable: - -``` -let mut x = 3; // ok! -let mut c = || { x += 1 }; -``` -"##, - -E0596: r##" -This error occurs because you tried to mutably borrow a non-mutable variable. - -Example of erroneous code: - -```compile_fail,E0596 -let x = 1; -let y = &mut x; // error: cannot borrow mutably -``` - -In here, `x` isn't mutable, so when we try to mutably borrow it in `y`, it -fails. To fix this error, you need to make `x` mutable: - -``` -let mut x = 1; -let y = &mut x; // ok! -``` -"##, - -E0597: r##" -This error occurs because a borrow was made inside a variable which has a -greater lifetime than the borrowed one. - -Example of erroneous code: - -```compile_fail,E0597 -struct Foo<'a> { - x: Option<&'a u32>, -} - -let mut x = Foo { x: None }; -let y = 0; -x.x = Some(&y); // error: `y` does not live long enough -``` - -In here, `x` is created before `y` and therefore has a greater lifetime. Always -keep in mind that values in a scope are dropped in the opposite order they are -created. So to fix the previous example, just make the `y` lifetime greater than -the `x`'s one: - -``` -struct Foo<'a> { - x: Option<&'a u32>, -} - -let y = 0; -let mut x = Foo { x: None }; -x.x = Some(&y); -``` -"##, - -E0626: r##" -This error occurs because a borrow in a generator persists across a -yield point. - -```compile_fail,E0626 -# #![feature(generators, generator_trait)] -# use std::ops::Generator; -let mut b = || { - let a = &String::new(); // <-- This borrow... - yield (); // ...is still in scope here, when the yield occurs. - println!("{}", a); -}; -b.resume(); -``` - -At present, it is not permitted to have a yield that occurs while a -borrow is still in scope. To resolve this error, the borrow must -either be "contained" to a smaller scope that does not overlap the -yield or else eliminated in another way. So, for example, we might -resolve the previous example by removing the borrow and just storing -the integer by value: - -``` -# #![feature(generators, generator_trait)] -# use std::ops::Generator; -let mut b = || { - let a = 3; - yield (); - println!("{}", a); -}; -b.resume(); -``` - -This is a very simple case, of course. In more complex cases, we may -wish to have more than one reference to the value that was borrowed -- -in those cases, something like the `Rc` or `Arc` types may be useful. - -This error also frequently arises with iteration: - -```compile_fail,E0626 -# #![feature(generators, generator_trait)] -# use std::ops::Generator; -let mut b = || { - let v = vec![1,2,3]; - for &x in &v { // <-- borrow of `v` is still in scope... - yield x; // ...when this yield occurs. - } -}; -b.resume(); -``` - -Such cases can sometimes be resolved by iterating "by value" (or using -`into_iter()`) to avoid borrowing: - -``` -# #![feature(generators, generator_trait)] -# use std::ops::Generator; -let mut b = || { - let v = vec![1,2,3]; - for x in v { // <-- Take ownership of the values instead! - yield x; // <-- Now yield is OK. - } -}; -b.resume(); -``` - -If taking ownership is not an option, using indices can work too: - -``` -# #![feature(generators, generator_trait)] -# use std::ops::Generator; -let mut b = || { - let v = vec![1,2,3]; - let len = v.len(); // (*) - for i in 0..len { - let x = v[i]; // (*) - yield x; // <-- Now yield is OK. - } -}; -b.resume(); - -// (*) -- Unfortunately, these temporaries are currently required. -// See <https://github.com/rust-lang/rust/issues/43122>. -``` -"##, - -} - -register_diagnostics! { -// E0385, // {} in an aliasable location - E0598, // lifetime of {} is too short to guarantee its contents can be... -} diff --git a/src/librustc_borrowck/lib.rs b/src/librustc_borrowck/lib.rs index 9bedbfed5db..11120d2e46f 100644 --- a/src/librustc_borrowck/lib.rs +++ b/src/librustc_borrowck/lib.rs @@ -16,10 +16,9 @@ #![allow(non_camel_case_types)] #![feature(quote)] -#![feature(rustc_diagnostic_macros)] #[macro_use] extern crate log; -#[macro_use] extern crate syntax; +extern crate syntax; extern crate syntax_pos; extern crate rustc_errors as errors; @@ -33,14 +32,8 @@ extern crate rustc_mir; pub use borrowck::check_crate; pub use borrowck::build_borrowck_dataflow_data_for_fn; -// NB: This module needs to be declared first so diagnostics are -// registered before they are used. -mod diagnostics; - mod borrowck; pub mod graphviz; pub use borrowck::provide; - -__build_diagnostic_array! { librustc_borrowck, DIAGNOSTICS } diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index cd21060aff6..3514302c6c8 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -1259,7 +1259,6 @@ pub fn diagnostics_registry() -> errors::registry::Registry { let mut all_errors = Vec::new(); all_errors.extend_from_slice(&rustc::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_typeck::DIAGNOSTICS); - all_errors.extend_from_slice(&rustc_borrowck::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_resolve::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_privacy::DIAGNOSTICS); #[cfg(feature="llvm")] diff --git a/src/librustc_mir/diagnostics.rs b/src/librustc_mir/diagnostics.rs index 645af0bff64..98c5345c69d 100644 --- a/src/librustc_mir/diagnostics.rs +++ b/src/librustc_mir/diagnostics.rs @@ -229,6 +229,57 @@ fn main() { See also https://doc.rust-lang.org/book/first-edition/unsafe.html "##, +E0373: r##" +This error occurs when an attempt is made to use data captured by a closure, +when that data may no longer exist. It's most commonly seen when attempting to +return a closure: + +```compile_fail,E0373 +fn foo() -> Box<Fn(u32) -> u32> { + let x = 0u32; + Box::new(|y| x + y) +} +``` + +Notice that `x` is stack-allocated by `foo()`. By default, Rust captures +closed-over data by reference. This means that once `foo()` returns, `x` no +longer exists. An attempt to access `x` within the closure would thus be +unsafe. + +Another situation where this might be encountered is when spawning threads: + +```compile_fail,E0373 +fn foo() { + let x = 0u32; + let y = 1u32; + + let thr = std::thread::spawn(|| { + x + y + }); +} +``` + +Since our new thread runs in parallel, the stack frame containing `x` and `y` +may well have disappeared by the time we try to use them. Even if we call +`thr.join()` within foo (which blocks until `thr` has completed, ensuring the +stack frame won't disappear), we will not succeed: the compiler cannot prove +that this behaviour is safe, and so won't let us do it. + +The solution to this problem is usually to switch to using a `move` closure. +This approach moves (or copies, where possible) data into the closure, rather +than taking references to it. For example: + +``` +fn foo() -> Box<Fn(u32) -> u32> { + let x = 0u32; + Box::new(move |y| x + y) +} +``` + +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: @@ -250,6 +301,104 @@ fn main() { ``` "##, +E0382: r##" +This error occurs when an attempt is made to use a variable after its contents +have been moved elsewhere. For example: + +```compile_fail,E0382 +struct MyStruct { s: u32 } + +fn main() { + let mut x = MyStruct{ s: 5u32 }; + let y = x; + x.s = 6; + println!("{}", x.s); +} +``` + +Since `MyStruct` is a type that is not marked `Copy`, the data gets moved out +of `x` when we set `y`. This is fundamental to Rust's ownership system: outside +of workarounds like `Rc`, a value cannot be owned by more than one variable. + +If we own the type, the easiest way to address this problem is to implement +`Copy` and `Clone` on it, as shown below. This allows `y` to copy the +information in `x`, while leaving the original version owned by `x`. Subsequent +changes to `x` will not be reflected when accessing `y`. + +``` +#[derive(Copy, Clone)] +struct MyStruct { s: u32 } + +fn main() { + let mut x = MyStruct{ s: 5u32 }; + let y = x; + x.s = 6; + println!("{}", x.s); +} +``` + +Alternatively, if we don't control the struct's definition, or mutable shared +ownership is truly required, we can use `Rc` and `RefCell`: + +``` +use std::cell::RefCell; +use std::rc::Rc; + +struct MyStruct { s: u32 } + +fn main() { + let mut x = Rc::new(RefCell::new(MyStruct{ s: 5u32 })); + let y = x.clone(); + x.borrow_mut().s = 6; + println!("{}", x.borrow().s); +} +``` + +With this approach, x and y share ownership of the data via the `Rc` (reference +count type). `RefCell` essentially performs runtime borrow checking: ensuring +that at most one writer or multiple readers can access the data at any one time. + +If you wish to learn more about ownership in Rust, start with the chapter in the +Book: + +https://doc.rust-lang.org/book/first-edition/ownership.html +"##, + +E0383: r##" +This error occurs when an attempt is made to partially reinitialize a +structure that is currently uninitialized. + +For example, this can happen when a drop has taken place: + +```compile_fail,E0383 +struct Foo { + a: u32, +} +impl Drop for Foo { + fn drop(&mut self) { /* ... */ } +} + +let mut x = Foo { a: 1 }; +drop(x); // `x` is now uninitialized +x.a = 2; // error, partial reinitialization of uninitialized structure `t` +``` + +This error can be fixed by fully reinitializing the structure in question: + +``` +struct Foo { + a: u32, +} +impl Drop for Foo { + fn drop(&mut self) { /* ... */ } +} + +let mut x = Foo { a: 1 }; +drop(x); +x = Foo { a: 2 }; +``` +"##, + E0384: r##" This error occurs when an attempt is made to reassign an immutable variable. For example: @@ -272,6 +421,161 @@ fn main() { ``` "##, +/*E0386: r##" +This error occurs when an attempt is made to mutate the target of a mutable +reference stored inside an immutable container. + +For example, this can happen when storing a `&mut` inside an immutable `Box`: + +```compile_fail,E0386 +let mut x: i64 = 1; +let y: Box<_> = Box::new(&mut x); +**y = 2; // error, cannot assign to data in an immutable container +``` + +This error can be fixed by making the container mutable: + +``` +let mut x: i64 = 1; +let mut y: Box<_> = Box::new(&mut x); +**y = 2; +``` + +It can also be fixed by using a type with interior mutability, such as `Cell` +or `RefCell`: + +``` +use std::cell::Cell; + +let x: i64 = 1; +let y: Box<Cell<_>> = Box::new(Cell::new(x)); +y.set(2); +``` +"##,*/ + +E0387: r##" +This error occurs when an attempt is made to mutate or mutably reference data +that a closure has captured immutably. Examples of this error are shown below: + +```compile_fail,E0387 +// Accepts a function or a closure that captures its environment immutably. +// Closures passed to foo will not be able to mutate their closed-over state. +fn foo<F: Fn()>(f: F) { } + +// Attempts to mutate closed-over data. Error message reads: +// `cannot assign to data in a captured outer variable...` +fn mutable() { + let mut x = 0u32; + foo(|| x = 2); +} + +// Attempts to take a mutable reference to closed-over data. Error message +// reads: `cannot borrow data mutably in a captured outer variable...` +fn mut_addr() { + let mut x = 0u32; + foo(|| { let y = &mut x; }); +} +``` + +The problem here is that foo is defined as accepting a parameter of type `Fn`. +Closures passed into foo will thus be inferred to be of type `Fn`, meaning that +they capture their context immutably. + +If the definition of `foo` is under your control, the simplest solution is to +capture the data mutably. This can be done by defining `foo` to take FnMut +rather than Fn: + +``` +fn foo<F: FnMut()>(f: F) { } +``` + +Alternatively, we can consider using the `Cell` and `RefCell` types to achieve +interior mutability through a shared reference. Our example's `mutable` +function could be redefined as below: + +``` +use std::cell::Cell; + +fn foo<F: Fn()>(f: F) { } + +fn mutable() { + let x = Cell::new(0u32); + foo(|| x.set(2)); +} +``` + +You can read more about cell types in the API documentation: + +https://doc.rust-lang.org/std/cell/ +"##, + +E0388: r##" +E0388 was removed and is no longer issued. +"##, + +E0389: r##" +An attempt was made to mutate data using a non-mutable reference. This +commonly occurs when attempting to assign to a non-mutable reference of a +mutable reference (`&(&mut T)`). + +Example of erroneous code: + +```compile_fail,E0389 +struct FancyNum { + num: u8, +} + +fn main() { + let mut fancy = FancyNum{ num: 5 }; + let fancy_ref = &(&mut fancy); + fancy_ref.num = 6; // error: cannot assign to data in a `&` reference + println!("{}", fancy_ref.num); +} +``` + +Here, `&mut fancy` is mutable, but `&(&mut fancy)` is not. Creating an +immutable reference to a value borrows it immutably. There can be multiple +references of type `&(&mut T)` that point to the same value, so they must be +immutable to prevent multiple mutable references to the same value. + +To fix this, either remove the outer reference: + +``` +struct FancyNum { + num: u8, +} + +fn main() { + let mut fancy = FancyNum{ num: 5 }; + + let fancy_ref = &mut fancy; + // `fancy_ref` is now &mut FancyNum, rather than &(&mut FancyNum) + + fancy_ref.num = 6; // No error! + + println!("{}", fancy_ref.num); +} +``` + +Or make the outer reference mutable: + +``` +struct FancyNum { + num: u8 +} + +fn main() { + let mut fancy = FancyNum{ num: 5 }; + + let fancy_ref = &mut (&mut fancy); + // `fancy_ref` is now &mut(&mut FancyNum), rather than &(&mut FancyNum) + + fancy_ref.num = 6; // No error! + + println!("{}", fancy_ref.num); +} +``` +"##, E0394: r##" A static was referred to by value by another static. @@ -1265,12 +1569,169 @@ fn main() { ``` "##, +E0595: r##" +Closures cannot mutate immutable captured variables. + +Erroneous code example: + +```compile_fail,E0595 +let x = 3; // error: closure cannot assign to immutable local variable `x` +let mut c = || { x += 1 }; +``` + +Make the variable binding mutable: + +``` +let mut x = 3; // ok! +let mut c = || { x += 1 }; +``` +"##, + +E0596: r##" +This error occurs because you tried to mutably borrow a non-mutable variable. + +Example of erroneous code: + +```compile_fail,E0596 +let x = 1; +let y = &mut x; // error: cannot borrow mutably +``` + +In here, `x` isn't mutable, so when we try to mutably borrow it in `y`, it +fails. To fix this error, you need to make `x` mutable: + +``` +let mut x = 1; +let y = &mut x; // ok! +``` +"##, + +E0597: r##" +This error occurs because a borrow was made inside a variable which has a +greater lifetime than the borrowed one. + +Example of erroneous code: + +```compile_fail,E0597 +struct Foo<'a> { + x: Option<&'a u32>, +} + +let mut x = Foo { x: None }; +let y = 0; +x.x = Some(&y); // error: `y` does not live long enough +``` + +In here, `x` is created before `y` and therefore has a greater lifetime. Always +keep in mind that values in a scope are dropped in the opposite order they are +created. So to fix the previous example, just make the `y` lifetime greater than +the `x`'s one: + +``` +struct Foo<'a> { + x: Option<&'a u32>, +} + +let y = 0; +let mut x = Foo { x: None }; +x.x = Some(&y); +``` +"##, + +E0626: r##" +This error occurs because a borrow in a generator persists across a +yield point. + +```compile_fail,E0626 +# #![feature(generators, generator_trait)] +# use std::ops::Generator; +let mut b = || { + let a = &String::new(); // <-- This borrow... + yield (); // ...is still in scope here, when the yield occurs. + println!("{}", a); +}; +b.resume(); +``` + +At present, it is not permitted to have a yield that occurs while a +borrow is still in scope. To resolve this error, the borrow must +either be "contained" to a smaller scope that does not overlap the +yield or else eliminated in another way. So, for example, we might +resolve the previous example by removing the borrow and just storing +the integer by value: + +``` +# #![feature(generators, generator_trait)] +# use std::ops::Generator; +let mut b = || { + let a = 3; + yield (); + println!("{}", a); +}; +b.resume(); +``` + +This is a very simple case, of course. In more complex cases, we may +wish to have more than one reference to the value that was borrowed -- +in those cases, something like the `Rc` or `Arc` types may be useful. + +This error also frequently arises with iteration: + +```compile_fail,E0626 +# #![feature(generators, generator_trait)] +# use std::ops::Generator; +let mut b = || { + let v = vec![1,2,3]; + for &x in &v { // <-- borrow of `v` is still in scope... + yield x; // ...when this yield occurs. + } +}; +b.resume(); +``` + +Such cases can sometimes be resolved by iterating "by value" (or using +`into_iter()`) to avoid borrowing: + +``` +# #![feature(generators, generator_trait)] +# use std::ops::Generator; +let mut b = || { + let v = vec![1,2,3]; + for x in v { // <-- Take ownership of the values instead! + yield x; // <-- Now yield is OK. + } +}; +b.resume(); +``` + +If taking ownership is not an option, using indices can work too: + +``` +# #![feature(generators, generator_trait)] +# use std::ops::Generator; +let mut b = || { + let v = vec![1,2,3]; + let len = v.len(); // (*) + for i in 0..len { + let x = v[i]; // (*) + yield x; // <-- Now yield is OK. + } +}; +b.resume(); + +// (*) -- Unfortunately, these temporaries are currently required. +// See <https://github.com/rust-lang/rust/issues/43122>. +``` +"##, + } register_diagnostics! { +// E0385, // {} in an aliasable location E0493, // destructors cannot be evaluated at compile-time E0524, // two closures require unique access to `..` at the same time E0526, // shuffle indices are not constant E0594, // cannot assign to {} + E0598, // lifetime of {} is too short to guarantee its contents can be... E0625, // thread-local statics cannot be accessed at compile-time } diff --git a/src/librustc_mir/transform/nll/infer.rs b/src/librustc_mir/transform/nll/infer.rs new file mode 100644 index 00000000000..e6e00f295ca --- /dev/null +++ b/src/librustc_mir/transform/nll/infer.rs @@ -0,0 +1,222 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::{Region, RegionIndex}; +use std::mem; +use rustc::infer::InferCtxt; +use rustc::mir::{Location, Mir}; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc_data_structures::fx::FxHashSet; + +pub struct InferenceContext { + definitions: IndexVec<RegionIndex, VarDefinition>, + constraints: IndexVec<ConstraintIndex, Constraint>, + errors: IndexVec<InferenceErrorIndex, InferenceError>, +} + +pub struct InferenceError { + pub constraint_point: Location, + pub name: (), // FIXME(nashenas88) RegionName +} + +newtype_index!(InferenceErrorIndex); + +struct VarDefinition { + name: (), // FIXME(nashenas88) RegionName + value: Region, + capped: bool, +} + +impl VarDefinition { + pub fn new(value: Region) -> Self { + Self { + name: (), + value, + capped: false, + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct Constraint { + sub: RegionIndex, + sup: RegionIndex, + point: Location, +} + +newtype_index!(ConstraintIndex); + +impl InferenceContext { + pub fn new(values: IndexVec<RegionIndex, Region>) -> Self { + Self { + definitions: values.into_iter().map(VarDefinition::new).collect(), + constraints: IndexVec::new(), + errors: IndexVec::new(), + } + } + + #[allow(dead_code)] + pub fn cap_var(&mut self, v: RegionIndex) { + self.definitions[v].capped = true; + } + + #[allow(dead_code)] + pub fn add_live_point(&mut self, v: RegionIndex, point: Location) { + debug!("add_live_point({:?}, {:?})", v, point); + let definition = &mut self.definitions[v]; + if definition.value.add_point(point) { + if definition.capped { + self.errors.push(InferenceError { + constraint_point: point, + name: definition.name, + }); + } + } + } + + #[allow(dead_code)] + pub fn add_outlives(&mut self, sup: RegionIndex, sub: RegionIndex, point: Location) { + debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point); + self.constraints.push(Constraint { sup, sub, point }); + } + + #[allow(dead_code)] + pub fn region(&self, v: RegionIndex) -> &Region { + &self.definitions[v].value + } + + pub fn solve<'a, 'gcx, 'tcx>( + &mut self, + infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, + mir: &'a Mir<'tcx>, + ) -> IndexVec<InferenceErrorIndex, InferenceError> + where + 'gcx: 'tcx + 'a, + 'tcx: 'a, + { + let mut changed = true; + let mut dfs = Dfs::new(infcx, mir); + while changed { + changed = false; + for constraint in &self.constraints { + let sub = &self.definitions[constraint.sub].value.clone(); + let sup_def = &mut self.definitions[constraint.sup]; + debug!("constraint: {:?}", constraint); + debug!(" sub (before): {:?}", sub); + debug!(" sup (before): {:?}", sup_def.value); + + if dfs.copy(sub, &mut sup_def.value, constraint.point) { + changed = true; + if sup_def.capped { + // This is kind of a hack, but when we add a + // constraint, the "point" is always the point + // AFTER the action that induced the + // constraint. So report the error on the + // action BEFORE that. + assert!(constraint.point.statement_index > 0); + let p = Location { + block: constraint.point.block, + statement_index: constraint.point.statement_index - 1, + }; + + self.errors.push(InferenceError { + constraint_point: p, + name: sup_def.name, + }); + } + } + + debug!(" sup (after) : {:?}", sup_def.value); + debug!(" changed : {:?}", changed); + } + debug!("\n"); + } + + mem::replace(&mut self.errors, IndexVec::new()) + } +} + +struct Dfs<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> { + #[allow(dead_code)] + infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, + mir: &'a Mir<'tcx>, +} + +impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> { + fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self { + Self { infcx, mir } + } + + fn copy( + &mut self, + from_region: &Region, + to_region: &mut Region, + start_point: Location, + ) -> bool { + let mut changed = false; + + let mut stack = vec![]; + let mut visited = FxHashSet(); + + stack.push(start_point); + while let Some(p) = stack.pop() { + debug!(" dfs: p={:?}", p); + + if !from_region.may_contain(p) { + debug!(" not in from-region"); + continue; + } + + if !visited.insert(p) { + debug!(" already visited"); + continue; + } + + changed |= to_region.add_point(p); + + let block_data = &self.mir[p.block]; + let successor_points = if p.statement_index < block_data.statements.len() { + vec![Location { + statement_index: p.statement_index + 1, + ..p + }] + } else { + block_data.terminator() + .successors() + .iter() + .map(|&basic_block| Location { + statement_index: 0, + block: basic_block, + }) + .collect::<Vec<_>>() + }; + + if successor_points.is_empty() { + // FIXME handle free regions + // If we reach the END point in the graph, then copy + // over any skolemized end points in the `from_region` + // and make sure they are included in the `to_region`. + // for region_decl in self.infcx.tcx.tables.borrow().free_region_map() { + // // FIXME(nashenas88) figure out skolemized_end points + // let block = self.env.graph.skolemized_end(region_decl.name); + // let skolemized_end_point = Location { + // block, + // statement_index: 0, + // }; + // changed |= to_region.add_point(skolemized_end_point); + // } + } else { + stack.extend(successor_points); + } + } + + changed + } +} diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index 4925b1fcfed..805e9c976e4 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -8,13 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use self::infer::InferenceContext; use rustc::ty::TypeFoldable; use rustc::ty::subst::{Kind, Substs}; use rustc::ty::{Ty, TyCtxt, ClosureSubsts, RegionVid, RegionKind}; use rustc::mir::{Mir, Location, Rvalue, BasicBlock, Statement, StatementKind}; use rustc::mir::visit::{MutVisitor, Lookup}; use rustc::mir::transform::{MirPass, MirSource}; -use rustc::infer::{self, InferCtxt}; +use rustc::infer::{self as rustc_infer, InferCtxt}; use rustc::util::nodemap::FxHashSet; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use syntax_pos::DUMMY_SP; @@ -24,15 +25,18 @@ use std::fmt; use util as mir_util; use self::mir_util::PassWhere; +mod infer; + #[allow(dead_code)] struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { lookup_map: HashMap<RegionVid, Lookup>, regions: IndexVec<RegionIndex, Region>, - infcx: InferCtxt<'a, 'gcx, 'tcx>, + #[allow(dead_code)] + infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, } impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { - pub fn new(infcx: InferCtxt<'a, 'gcx, 'tcx>) -> Self { + pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self { NLLVisitor { infcx, lookup_map: HashMap::new(), @@ -40,14 +44,14 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { } } - pub fn into_results(self) -> HashMap<RegionVid, Lookup> { - self.lookup_map + pub fn into_results(self) -> (HashMap<RegionVid, Lookup>, IndexVec<RegionIndex, Region>) { + (self.lookup_map, self.regions) } fn renumber_regions<T>(&mut self, value: &T) -> T where T: TypeFoldable<'tcx> { self.infcx.tcx.fold_regions(value, &mut false, |_region, _depth| { self.regions.push(Region::default()); - self.infcx.next_region_var(infer::MiscVariable(DUMMY_SP)) + self.infcx.next_region_var(rustc_infer::MiscVariable(DUMMY_SP)) }) } @@ -147,7 +151,7 @@ impl MirPass for NLL { tcx.infer_ctxt().enter(|infcx| { // Clone mir so we can mutate it without disturbing the rest of the compiler let mut renumbered_mir = mir.clone(); - let mut visitor = NLLVisitor::new(infcx); + let mut visitor = NLLVisitor::new(&infcx); visitor.visit_mir(&mut renumbered_mir); mir_util::dump_mir(tcx, None, "nll", &0, source, mir, |pass_where, out| { if let PassWhere::BeforeCFG = pass_where { @@ -157,13 +161,15 @@ impl MirPass for NLL { } Ok(()) }); - let _results = visitor.into_results(); + let (_lookup_map, regions) = visitor.into_results(); + let mut inference_context = InferenceContext::new(regions); + inference_context.solve(&infcx, &renumbered_mir); }) } } #[derive(Clone, Default, PartialEq, Eq)] -struct Region { +pub struct Region { points: FxHashSet<Location>, } @@ -173,6 +179,14 @@ impl fmt::Debug for Region { } } +impl Region { + pub fn add_point(&mut self, point: Location) -> bool { + self.points.insert(point) + } + pub fn may_contain(&self, point: Location) -> bool { + self.points.contains(&point) + } +} newtype_index!(RegionIndex); diff --git a/src/librustc_mir/util/borrowck_errors.rs b/src/librustc_mir/util/borrowck_errors.rs index 37d53ca829e..216f6e44570 100644 --- a/src/librustc_mir/util/borrowck_errors.rs +++ b/src/librustc_mir/util/borrowck_errors.rs @@ -294,6 +294,139 @@ pub trait BorrowckErrors { err.span_label(move_from_span, "cannot move out of here"); err } + + fn cannot_act_on_moved_value(&self, + use_span: Span, + verb: &str, + optional_adverb_for_moved: &str, + moved_path: &str, + o: Origin) + -> DiagnosticBuilder + { + let err = struct_span_err!(self, use_span, E0382, + "{} of {}moved value: `{}`{OGN}", + verb, optional_adverb_for_moved, moved_path, OGN=o); + err + } + + fn cannot_partially_reinit_an_uninit_struct(&self, + span: Span, + uninit_path: &str, + o: Origin) + -> DiagnosticBuilder + { + let err = struct_span_err!(self, + span, + E0383, + "partial reinitialization of uninitialized structure `{}`{OGN}", + uninit_path, OGN=o); + err + } + + fn closure_cannot_assign_to_borrowed(&self, + span: Span, + descr: &str, + o: Origin) + -> DiagnosticBuilder + { + let err = struct_span_err!(self, span, E0595, "closure cannot assign to {}{OGN}", + descr, OGN=o); + err + } + + fn cannot_borrow_path_as_mutable(&self, + span: Span, + path: &str, + o: Origin) + -> DiagnosticBuilder + { + let err = struct_span_err!(self, span, E0596, "cannot borrow {} as mutable{OGN}", + path, OGN=o); + err + } + + fn cannot_borrow_across_generator_yield(&self, + span: Span, + yield_span: Span, + o: Origin) + -> DiagnosticBuilder + { + let mut err = struct_span_err!(self, + span, + E0626, + "borrow may still be in use when generator yields{OGN}", + OGN=o); + err.span_label(yield_span, "possible yield occurs here"); + err + } + + fn path_does_not_live_long_enough(&self, + span: Span, + path: &str, + o: Origin) + -> DiagnosticBuilder + { + let err = struct_span_err!(self, span, E0597, "{} does not live long enough{OGN}", + path, OGN=o); + err + } + + fn lifetime_too_short_for_reborrow(&self, + span: Span, + path: &str, + o: Origin) + -> DiagnosticBuilder + { + let err = struct_span_err!(self, span, E0598, + "lifetime of {} is too short to guarantee \ + its contents can be safely reborrowed{OGN}", + path, OGN=o); + err + } + + fn cannot_act_on_capture_in_sharable_fn(&self, + span: Span, + bad_thing: &str, + help: (Span, &str), + o: Origin) + -> DiagnosticBuilder + { + let (help_span, help_msg) = help; + let mut err = struct_span_err!(self, span, E0387, + "{} in a captured outer variable in an `Fn` closure{OGN}", + bad_thing, OGN=o); + err.span_help(help_span, help_msg); + err + } + + fn cannot_assign_into_immutable_reference(&self, + span: Span, + bad_thing: &str, + o: Origin) + -> DiagnosticBuilder + { + let mut err = struct_span_err!(self, span, E0389, "{} in a `&` reference{OGN}", + bad_thing, OGN=o); + err.span_label(span, "assignment into an immutable reference"); + err + } + + fn cannot_capture_in_long_lived_closure(&self, + closure_span: Span, + borrowed_path: &str, + capture_span: Span, + o: Origin) + -> DiagnosticBuilder + { + let mut err = struct_span_err!(self, closure_span, E0373, + "closure may outlive the current function, \ + but it borrows {}, \ + which is owned by the current function{OGN}", + borrowed_path, OGN=o); + err.span_label(capture_span, format!("{} is borrowed here", borrowed_path)) + .span_label(closure_span, format!("may outlive borrowed value {}", borrowed_path)); + err + } } impl<'b, 'tcx, 'gcx> BorrowckErrors for TyCtxt<'b, 'tcx, 'gcx> { diff --git a/src/test/mir-opt/README.md b/src/test/mir-opt/README.md index d999ff97551..b00b35aa29f 100644 --- a/src/test/mir-opt/README.md +++ b/src/test/mir-opt/README.md @@ -7,13 +7,13 @@ The test format is: // END RUST SOURCE // START $file_name_of_some_mir_dump_0 // $expected_line_0 -// ... +// (lines or elision) // $expected_line_N // END $file_name_of_some_mir_dump_0 -// ... +// (lines or elision) // START $file_name_of_some_mir_dump_N // $expected_line_0 -// ... +// (lines or elision) // $expected_line_N // END $file_name_of_some_mir_dump_N ``` @@ -22,10 +22,15 @@ All the test information is in comments so the test is runnable. For each $file_name, compiletest expects [$expected_line_0, ..., $expected_line_N] to appear in the dumped MIR in order. Currently it allows -other non-matched lines before, after and in-between. Note that this includes -lines that end basic blocks or begin new ones; it is good practice -in your tests to include the terminator for each of your basic blocks as an -internal sanity check guarding against a test like: +other non-matched lines before and after, but not between $expected_lines, +should you want to skip lines, you must include an elision comment, of the form +(as a regex) `//\s*...\s*`. The lines will be skipped lazily, that is, if there +are two identical lines in the output that match the line after the elision +comment, the first one wil be matched. + +Examples: + +The following blocks will not match the one after it. ``` bb0: { @@ -35,8 +40,6 @@ bb0: { } ``` -that will inadvertantly pattern-matching against: - ``` bb0: { StorageLive(_1); @@ -49,6 +52,18 @@ bb1: { } ``` +But this will match the one above, + +``` +bb0: { + StorageLive(_1); + _1 = const true; + ... + StorageDead(_1); + ... +} +``` + Lines match ignoring whitespace, and the prefix "//" is removed. It also currently strips trailing comments -- partly because the full file path diff --git a/src/test/mir-opt/box_expr.rs b/src/test/mir-opt/box_expr.rs index 4015930ef76..5c3b418e950 100644 --- a/src/test/mir-opt/box_expr.rs +++ b/src/test/mir-opt/box_expr.rs @@ -30,7 +30,10 @@ impl Drop for S { // END RUST SOURCE // START rustc.node4.ElaborateDrops.before.mir // let mut _0: (); -// let _1: std::boxed::Box<S>; +// scope 1 { +// let _1: std::boxed::Box<S>; +// } +// ... // let mut _2: std::boxed::Box<S>; // let mut _3: (); // let mut _4: std::boxed::Box<S>; diff --git a/src/test/mir-opt/copy_propagation.rs b/src/test/mir-opt/copy_propagation.rs index 26b042d0343..0b0d2f45f1c 100644 --- a/src/test/mir-opt/copy_propagation.rs +++ b/src/test/mir-opt/copy_propagation.rs @@ -18,17 +18,23 @@ fn main() { } // END RUST SOURCE // START rustc.node4.CopyPropagation.before.mir // bb0: { +// ... // _2 = _1; +// ... // _4 = _2; // _3 = _4; +// ... // _5 = _3; // _0 = _5; +// ... // return; // } // END rustc.node4.CopyPropagation.before.mir // START rustc.node4.CopyPropagation.after.mir // bb0: { +// ... // _0 = _1; +// ... // return; // } // END rustc.node4.CopyPropagation.after.mir diff --git a/src/test/mir-opt/deaggregator_test.rs b/src/test/mir-opt/deaggregator_test.rs index 81dd1932894..ce2b13ecda7 100644 --- a/src/test/mir-opt/deaggregator_test.rs +++ b/src/test/mir-opt/deaggregator_test.rs @@ -23,19 +23,25 @@ fn main() {} // END RUST SOURCE // START rustc.node13.Deaggregator.before.mir // bb0: { +// ... // _2 = _1; +// ... // _3 = _2; // _0 = Baz { x: _3, y: const 0f32, z: const false }; +// ... // return; // } // END rustc.node13.Deaggregator.before.mir // START rustc.node13.Deaggregator.after.mir // bb0: { +// ... // _2 = _1; +// ... // _3 = _2; // (_0.0: usize) = _3; // (_0.1: f32) = const 0f32; // (_0.2: bool) = const false; +// ... // return; // } // END rustc.node13.Deaggregator.after.mir diff --git a/src/test/mir-opt/deaggregator_test_enum.rs b/src/test/mir-opt/deaggregator_test_enum.rs index 25fa0e90835..d77dcb62781 100644 --- a/src/test/mir-opt/deaggregator_test_enum.rs +++ b/src/test/mir-opt/deaggregator_test_enum.rs @@ -28,18 +28,26 @@ fn main() { // END RUST SOURCE // START rustc.node10.Deaggregator.before.mir // bb0: { +// StorageLive(_2); // _2 = _1; +// StorageLive(_3); // _3 = _2; // _0 = Baz::Foo { x: _3 }; +// StorageDead(_3); +// StorageDead(_2); // return; // } // END rustc.node10.Deaggregator.before.mir // START rustc.node10.Deaggregator.after.mir // bb0: { +// StorageLive(_2); // _2 = _1; +// StorageLive(_3); // _3 = _2; // ((_0 as Foo).0: usize) = _3; // discriminant(_0) = 1; +// StorageDead(_3); +// StorageDead(_2); // return; // } // END rustc.node10.Deaggregator.after.mir diff --git a/src/test/mir-opt/deaggregator_test_enum_2.rs b/src/test/mir-opt/deaggregator_test_enum_2.rs index 02d496b2901..e65830bddc4 100644 --- a/src/test/mir-opt/deaggregator_test_enum_2.rs +++ b/src/test/mir-opt/deaggregator_test_enum_2.rs @@ -28,29 +28,35 @@ fn main() {} // END RUST SOURCE // START rustc.node12.Deaggregator.before.mir // bb1: { +// StorageLive(_6); // _6 = _4; // _0 = Foo::A(_6,); +// StorageDead(_6); // goto -> bb3; // } -// // bb2: { +// StorageLive(_7); // _7 = _4; // _0 = Foo::B(_7,); +// StorageDead(_7); // goto -> bb3; // } // END rustc.node12.Deaggregator.before.mir // START rustc.node12.Deaggregator.after.mir // bb1: { +// StorageLive(_6); // _6 = _4; // ((_0 as A).0: i32) = _6; // discriminant(_0) = 0; +// StorageDead(_6); // goto -> bb3; // } -// // bb2: { +// StorageLive(_7); // _7 = _4; // ((_0 as B).0: i32) = _7; // discriminant(_0) = 1; +// StorageDead(_7); // goto -> bb3; // } // END rustc.node12.Deaggregator.after.mir diff --git a/src/test/mir-opt/deaggregator_test_multiple.rs b/src/test/mir-opt/deaggregator_test_multiple.rs index a180a69be55..ed68d3bf5f7 100644 --- a/src/test/mir-opt/deaggregator_test_multiple.rs +++ b/src/test/mir-opt/deaggregator_test_multiple.rs @@ -24,25 +24,35 @@ fn main() { } // END RUST SOURCE // START rustc.node10.Deaggregator.before.mir // bb0: { +// ... // _2 = _1; +// ... // _4 = _2; // _3 = Foo::A(_4,); +// ... // _6 = _2; // _5 = Foo::A(_6,); +// ... // _0 = [_3, _5]; +// ... // return; // } // END rustc.node10.Deaggregator.before.mir // START rustc.node10.Deaggregator.after.mir // bb0: { +// ... // _2 = _1; +// ... // _4 = _2; // ((_3 as A).0: i32) = _4; // discriminant(_3) = 0; +// ... // _6 = _2; // ((_5 as A).0: i32) = _6; // discriminant(_5) = 0; +// ... // _0 = [_3, _5]; +// ... // return; // } // END rustc.node10.Deaggregator.after.mir diff --git a/src/test/mir-opt/end_region_1.rs b/src/test/mir-opt/end_region_1.rs index 1941d1bc7be..a0edcc82fe1 100644 --- a/src/test/mir-opt/end_region_1.rs +++ b/src/test/mir-opt/end_region_1.rs @@ -21,9 +21,11 @@ fn main() { // END RUST SOURCE // START rustc.node4.SimplifyCfg-qualify-consts.after.mir // let mut _0: (); +// ... // let _1: i32; +// ... // let _2: &'10_1rs i32; -// +// ... // bb0: { // StorageLive(_1); // _1 = const 3i32; diff --git a/src/test/mir-opt/end_region_2.rs b/src/test/mir-opt/end_region_2.rs index d8dd4aeadf4..69042fecc7d 100644 --- a/src/test/mir-opt/end_region_2.rs +++ b/src/test/mir-opt/end_region_2.rs @@ -26,11 +26,16 @@ fn main() { // END RUST SOURCE // START rustc.node4.SimplifyCfg-qualify-consts.after.mir // let mut _0: (); +// ... // let _2: bool; +// ... // let _3: &'23_1rs bool; +// ... // let _7: &'23_3rs bool; +// ... // let mut _4: (); // let mut _5: bool; +// ... // bb0: { // goto -> bb1; // } @@ -52,6 +57,7 @@ fn main() { // return; // } // bb3: { +// _4 = (); // StorageDead(_5); // StorageLive(_7); // _7 = &'23_3rs _2; diff --git a/src/test/mir-opt/end_region_3.rs b/src/test/mir-opt/end_region_3.rs index e404af838ce..da423163e84 100644 --- a/src/test/mir-opt/end_region_3.rs +++ b/src/test/mir-opt/end_region_3.rs @@ -27,13 +27,17 @@ fn main() { // END RUST SOURCE // START rustc.node4.SimplifyCfg-qualify-consts.after.mir // let mut _0: (); +// ... // let mut _1: bool; +// ... // let _3: &'26_1rs bool; +// ... // let _7: &'26_3rs bool; +// ... // let mut _2: (); // let mut _4: (); // let mut _5: bool; -// +// let mut _6: !; // bb0: { // StorageLive(_1); // goto -> bb1; diff --git a/src/test/mir-opt/end_region_4.rs b/src/test/mir-opt/end_region_4.rs index d51c627d14b..2087485b913 100644 --- a/src/test/mir-opt/end_region_4.rs +++ b/src/test/mir-opt/end_region_4.rs @@ -31,10 +31,15 @@ fn foo(i: i32) { // END RUST SOURCE // START rustc.node4.SimplifyCfg-qualify-consts.after.mir // let mut _0: (); +// ... // let _1: D; +// ... // let _2: i32; +// ... // let _3: &'26_2rs i32; +// ... // let _6: &'26_4rs i32; +// ... // let mut _4: (); // let mut _5: i32; // bb0: { diff --git a/src/test/mir-opt/end_region_5.rs b/src/test/mir-opt/end_region_5.rs index 6299ec3815c..4663b71bd7c 100644 --- a/src/test/mir-opt/end_region_5.rs +++ b/src/test/mir-opt/end_region_5.rs @@ -28,8 +28,11 @@ fn foo<F>(f: F) where F: FnOnce() -> i32 { // END RUST SOURCE // START rustc.node4.SimplifyCfg-qualify-consts.after.mir // fn main() -> () { +// ... // let mut _0: (); +// ... // let _1: D; +// ... // let mut _2: (); // let mut _3: [closure@NodeId(18) d:&'14s D]; // let mut _4: &'14s D; diff --git a/src/test/mir-opt/end_region_6.rs b/src/test/mir-opt/end_region_6.rs index 13ab3e4f2dd..7d2868ee4ba 100644 --- a/src/test/mir-opt/end_region_6.rs +++ b/src/test/mir-opt/end_region_6.rs @@ -29,7 +29,9 @@ fn foo<F>(f: F) where F: FnOnce() -> i32 { // START rustc.node4.SimplifyCfg-qualify-consts.after.mir // fn main() -> () { // let mut _0: (); +// ... // let _1: D; +// ... // let mut _2: (); // let mut _3: [closure@NodeId(22) d:&'19s D]; // let mut _4: &'19s D; @@ -65,9 +67,10 @@ fn foo<F>(f: F) where F: FnOnce() -> i32 { // START rustc.node22.SimplifyCfg-qualify-consts.after.mir // fn main::{{closure}}(_1: [closure@NodeId(22) d:&'19s D]) -> i32 { // let mut _0: i32; +// ... // let _2: &'15_0rs D; +// ... // let mut _3: i32; -// // bb0: { // StorageLive(_2); // _2 = &'15_0rs (*(_1.0: &'19s D)); diff --git a/src/test/mir-opt/end_region_7.rs b/src/test/mir-opt/end_region_7.rs index 826d3749167..0156c1be7ed 100644 --- a/src/test/mir-opt/end_region_7.rs +++ b/src/test/mir-opt/end_region_7.rs @@ -29,11 +29,12 @@ fn foo<F>(f: F) where F: FnOnce() -> i32 { // START rustc.node4.SimplifyCfg-qualify-consts.after.mir // fn main() -> () { // let mut _0: (); +// ... // let _1: D; +// ... // let mut _2: (); // let mut _3: [closure@NodeId(22) d:D]; // let mut _4: D; -// // bb0: { // StorageLive(_1); // _1 = D::{{constructor}}(const 0i32,); @@ -74,9 +75,10 @@ fn foo<F>(f: F) where F: FnOnce() -> i32 { // START rustc.node22.SimplifyCfg-qualify-consts.after.mir // fn main::{{closure}}(_1: [closure@NodeId(22) d:D]) -> i32 { // let mut _0: i32; +// ... // let _2: &'15_0rs D; +// ... // let mut _3: i32; -// // bb0: { // StorageLive(_2); // _2 = &'15_0rs (_1.0: D); diff --git a/src/test/mir-opt/end_region_8.rs b/src/test/mir-opt/end_region_8.rs index 6438484fcfa..6e8cf4204ee 100644 --- a/src/test/mir-opt/end_region_8.rs +++ b/src/test/mir-opt/end_region_8.rs @@ -30,8 +30,11 @@ fn foo<F>(f: F) where F: FnOnce() -> i32 { // START rustc.node4.SimplifyCfg-qualify-consts.after.mir // fn main() -> () { // let mut _0: (); +// ... // let _1: D; +// ... // let _2: &'21_1rs D; +// ... // let mut _3: (); // let mut _4: [closure@NodeId(22) r:&'21_1rs D]; // let mut _5: &'21_1rs D; diff --git a/src/test/mir-opt/end_region_9.rs b/src/test/mir-opt/end_region_9.rs index 59d5d934391..fd23d813452 100644 --- a/src/test/mir-opt/end_region_9.rs +++ b/src/test/mir-opt/end_region_9.rs @@ -40,15 +40,18 @@ fn main() { // START rustc.node4.SimplifyCfg-qualify-consts.after.mir // fn main() -> () { // let mut _0: (); +// ... // let mut _1: bool; +// ... // let _2: i32; +// ... // let mut _4: &'33_0rs i32; +// ... // let mut _3: (); // let mut _5: !; // let mut _6: (); // let mut _7: bool; // let mut _8: !; -// // bb0: { // StorageLive(_1); // _1 = const false; @@ -63,7 +66,6 @@ fn main() { // _7 = _1; // switchInt(_7) -> [0u8: bb3, otherwise: bb2]; // } -// // bb2: { // _0 = (); // StorageDead(_7); @@ -73,7 +75,6 @@ fn main() { // StorageDead(_1); // return; // } -// // bb3: { // _4 = &'33_0rs _2; // _6 = (); diff --git a/src/test/mir-opt/end_region_cyclic.rs b/src/test/mir-opt/end_region_cyclic.rs index 8f9dd79cd75..f70f6519275 100644 --- a/src/test/mir-opt/end_region_cyclic.rs +++ b/src/test/mir-opt/end_region_cyclic.rs @@ -45,6 +45,7 @@ fn query() -> bool { true } // scope 1 { // let _2: S<'35_0rs>; // } +// ... // let mut _1: (); // let mut _3: std::cell::Cell<std::option::Option<&'35_0rs S<'35_0rs>>>; // let mut _4: std::option::Option<&'35_0rs S<'35_0rs>>; diff --git a/src/test/mir-opt/issue-41110.rs b/src/test/mir-opt/issue-41110.rs index 3a8b5c449c2..384201b7c12 100644 --- a/src/test/mir-opt/issue-41110.rs +++ b/src/test/mir-opt/issue-41110.rs @@ -35,22 +35,26 @@ impl S { // END RUST SOURCE // START rustc.node4.ElaborateDrops.after.mir // let mut _0: (); -// let _1: (); +// scope 1 { +// let _1: (); +// } +// ... // let mut _2: S; // let mut _3: S; // let mut _4: S; // let mut _5: bool; -// // bb0: { // END rustc.node4.ElaborateDrops.after.mir // START rustc.node13.ElaborateDrops.after.mir // let mut _0: (); +// ... // let _1: S; +// ... // let mut _2: S; +// ... // let mut _3: (); // let mut _4: S; // let mut _5: S; // let mut _6: bool; -// // bb0: { // END rustc.node13.ElaborateDrops.after.mir diff --git a/src/test/mir-opt/storage_live_dead_in_statics.rs b/src/test/mir-opt/storage_live_dead_in_statics.rs index 9fb725a980e..370ab599eca 100644 --- a/src/test/mir-opt/storage_live_dead_in_statics.rs +++ b/src/test/mir-opt/storage_live_dead_in_statics.rs @@ -45,56 +45,156 @@ fn main() { // END RUST SOURCE // START rustc.node4.mir_map.0.mir +// let mut _0: &'static Foo; +// let mut _1: &'static Foo; +// let mut _2: Foo; +// let mut _3: &'static [(u32, u32)]; +// let mut _4: &'static [(u32, u32); 42]; +// let mut _5: &'static [(u32, u32); 42]; +// let mut _6: [(u32, u32); 42]; +// let mut _7: (u32, u32); +// let mut _8: (u32, u32); +// let mut _9: (u32, u32); +// let mut _10: (u32, u32); +// let mut _11: (u32, u32); +// let mut _12: (u32, u32); +// let mut _13: (u32, u32); +// let mut _14: (u32, u32); +// let mut _15: (u32, u32); +// let mut _16: (u32, u32); +// let mut _17: (u32, u32); +// let mut _18: (u32, u32); +// let mut _19: (u32, u32); +// let mut _20: (u32, u32); +// let mut _21: (u32, u32); +// let mut _22: (u32, u32); +// let mut _23: (u32, u32); +// let mut _24: (u32, u32); +// let mut _25: (u32, u32); +// let mut _26: (u32, u32); +// let mut _27: (u32, u32); +// let mut _28: (u32, u32); +// let mut _29: (u32, u32); +// let mut _30: (u32, u32); +// let mut _31: (u32, u32); +// let mut _32: (u32, u32); +// let mut _33: (u32, u32); +// let mut _34: (u32, u32); +// let mut _35: (u32, u32); +// let mut _36: (u32, u32); +// let mut _37: (u32, u32); +// let mut _38: (u32, u32); +// let mut _39: (u32, u32); +// let mut _40: (u32, u32); +// let mut _41: (u32, u32); +// let mut _42: (u32, u32); +// let mut _43: (u32, u32); +// let mut _44: (u32, u32); +// let mut _45: (u32, u32); +// let mut _46: (u32, u32); +// let mut _47: (u32, u32); +// let mut _48: (u32, u32); // bb0: { -// _7 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:29:9: 29:15 -// _8 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:29:17: 29:23 -// _9 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:29:25: 29:31 -// _10 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:30:9: 30:15 -// _11 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:30:17: 30:23 -// _12 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:30:25: 30:31 -// _13 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:31:9: 31:15 -// _14 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:31:17: 31:23 -// _15 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:31:25: 31:31 -// _16 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:32:9: 32:15 -// _17 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:32:17: 32:23 -// _18 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:32:25: 32:31 -// _19 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:33:9: 33:15 -// _20 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:33:17: 33:23 -// _21 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:33:25: 33:31 -// _22 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:34:9: 34:15 -// _23 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:34:17: 34:23 -// _24 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:34:25: 34:31 -// _25 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:35:9: 35:15 -// _26 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:35:17: 35:23 -// _27 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:35:25: 35:31 -// _28 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:36:9: 36:15 -// _29 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:36:17: 36:23 -// _30 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:36:25: 36:31 -// _31 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:37:9: 37:15 -// _32 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:37:17: 37:23 -// _33 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:37:25: 37:31 -// _34 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:38:9: 38:15 -// _35 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:38:17: 38:23 -// _36 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:38:25: 38:31 -// _37 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:39:9: 39:15 -// _38 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:39:17: 39:23 -// _39 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:39:25: 39:31 -// _40 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:40:9: 40:15 -// _41 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:40:17: 40:23 -// _42 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:40:25: 40:31 -// _43 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:41:9: 41:15 -// _44 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:41:17: 41:23 -// _45 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:41:25: 41:31 -// _46 = (const 0u32, const 1u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:42:9: 42:15 -// _47 = (const 0u32, const 2u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:42:17: 42:23 -// _48 = (const 0u32, const 3u32); // scope 0 at src/test/mir-opt/basic_assignment.rs:42:25: 42:31 -// _6 = [_7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48]; // scope 0 at src/test/mir-opt/basic_assignment.rs:28:12: 43:6 -// _5 = &_6; // scope 0 at src/test/mir-opt/basic_assignment.rs:28:11: 43:6 -// _4 = &(*_5); // scope 0 at src/test/mir-opt/basic_assignment.rs:28:11: 43:6 -// _3 = _4 as &'static [(u32, u32)] (Unsize); // scope 0 at src/test/mir-opt/basic_assignment.rs:28:11: 43:6 -// _2 = Foo { tup: const "hi", data: _3 }; // scope 0 at src/test/mir-opt/basic_assignment.rs:26:29: 44:2 -// _1 = &_2; // scope 0 at src/test/mir-opt/basic_assignment.rs:26:28: 44:2 -// _0 = &(*_1); // scope 0 at src/test/mir-opt/basic_assignment.rs:26:28: 44:2 -// return; // scope 0 at src/test/mir-opt/basic_assignment.rs:26:1: 44:3 +// StorageLive(_1); +// StorageLive(_2); +// StorageLive(_3); +// StorageLive(_4); +// StorageLive(_5); +// StorageLive(_6); +// StorageLive(_7); +// _7 = (const 0u32, const 1u32); +// StorageLive(_8); +// _8 = (const 0u32, const 2u32); +// StorageLive(_9); +// _9 = (const 0u32, const 3u32); +// StorageLive(_10); +// _10 = (const 0u32, const 1u32); +// StorageLive(_11); +// _11 = (const 0u32, const 2u32); +// StorageLive(_12); +// _12 = (const 0u32, const 3u32); +// StorageLive(_13); +// _13 = (const 0u32, const 1u32); +// StorageLive(_14); +// _14 = (const 0u32, const 2u32); +// StorageLive(_15); +// _15 = (const 0u32, const 3u32); +// StorageLive(_16); +// _16 = (const 0u32, const 1u32); +// StorageLive(_17); +// _17 = (const 0u32, const 2u32); +// StorageLive(_18); +// _18 = (const 0u32, const 3u32); +// StorageLive(_19); +// _19 = (const 0u32, const 1u32); +// StorageLive(_20); +// _20 = (const 0u32, const 2u32); +// StorageLive(_21); +// _21 = (const 0u32, const 3u32); +// StorageLive(_22); +// _22 = (const 0u32, const 1u32); +// StorageLive(_23); +// _23 = (const 0u32, const 2u32); +// StorageLive(_24); +// _24 = (const 0u32, const 3u32); +// StorageLive(_25); +// _25 = (const 0u32, const 1u32); +// StorageLive(_26); +// _26 = (const 0u32, const 2u32); +// StorageLive(_27); +// _27 = (const 0u32, const 3u32); +// StorageLive(_28); +// _28 = (const 0u32, const 1u32); +// StorageLive(_29); +// _29 = (const 0u32, const 2u32); +// StorageLive(_30); +// _30 = (const 0u32, const 3u32); +// StorageLive(_31); +// _31 = (const 0u32, const 1u32); +// StorageLive(_32); +// _32 = (const 0u32, const 2u32); +// StorageLive(_33); +// _33 = (const 0u32, const 3u32); +// StorageLive(_34); +// _34 = (const 0u32, const 1u32); +// StorageLive(_35); +// _35 = (const 0u32, const 2u32); +// StorageLive(_36); +// _36 = (const 0u32, const 3u32); +// StorageLive(_37); +// _37 = (const 0u32, const 1u32); +// StorageLive(_38); +// _38 = (const 0u32, const 2u32); +// StorageLive(_39); +// _39 = (const 0u32, const 3u32); +// StorageLive(_40); +// _40 = (const 0u32, const 1u32); +// StorageLive(_41); +// _41 = (const 0u32, const 2u32); +// StorageLive(_42); +// _42 = (const 0u32, const 3u32); +// StorageLive(_43); +// _43 = (const 0u32, const 1u32); +// StorageLive(_44); +// _44 = (const 0u32, const 2u32); +// StorageLive(_45); +// _45 = (const 0u32, const 3u32); +// StorageLive(_46); +// _46 = (const 0u32, const 1u32); +// StorageLive(_47); +// _47 = (const 0u32, const 2u32); +// StorageLive(_48); +// _48 = (const 0u32, const 3u32); +// _6 = [_7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48]; +// _5 = &_6; +// _4 = &(*_5); +// _3 = _4 as &'static [(u32, u32)] (Unsize); +// _2 = Foo { tup: const "hi", data: _3 }; +// _1 = &_2; +// _0 = &(*_1); +// StorageDead(_1); +// StorageDead(_5); +// return; // } +//} // END rustc.node4.mir_map.0.mir diff --git a/src/test/mir-opt/storage_ranges.rs b/src/test/mir-opt/storage_ranges.rs index 3fbd1a36f2f..7dbcf82af34 100644 --- a/src/test/mir-opt/storage_ranges.rs +++ b/src/test/mir-opt/storage_ranges.rs @@ -38,5 +38,6 @@ fn main() { // _0 = (); // StorageDead(_6); // StorageDead(_1); +// return; // } // END rustc.node4.TypeckMir.before.mir diff --git a/src/test/mir-opt/validate_1.rs b/src/test/mir-opt/validate_1.rs index 39f43e4e990..5a31be8bd50 100644 --- a/src/test/mir-opt/validate_1.rs +++ b/src/test/mir-opt/validate_1.rs @@ -31,12 +31,15 @@ fn main() { // START rustc.node12.EraseRegions.after.mir // bb0: { // Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(0:5) => validate_1[8cd8]::{{impl}}[0]::foo[0] }, BrAnon(0)) Test, _2: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(0:5) => validate_1[8cd8]::{{impl}}[0]::foo[0] }, BrAnon(1)) mut i32]); +// ... // return; // } // END rustc.node12.EraseRegions.after.mir // START rustc.node23.EraseRegions.after.mir // fn main() -> () { +// ... // bb0: { +// ... // Validate(Suspend(ReScope(Node(ItemLocalId(10)))), [_1: i32]); // _6 = &ReErased mut _1; // Validate(Acquire, [(*_6): i32/ReScope(Node(ItemLocalId(10)))]); @@ -50,12 +53,14 @@ fn main() { // bb1: { // Validate(Acquire, [_2: ()]); // EndRegion(ReScope(Node(ItemLocalId(10)))); +// ... // return; // } // } // END rustc.node23.EraseRegions.after.mir // START rustc.node50.EraseRegions.after.mir // fn main::{{closure}}(_1: &ReErased [closure@NodeId(50)], _2: &ReErased mut i32) -> i32 { +// ... // bb0: { // Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:11) => validate_1[8cd8]::main[0]::{{closure}}[0] }, BrEnv) [closure@NodeId(50)], _2: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:11) => validate_1[8cd8]::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]); // StorageLive(_3); diff --git a/src/test/mir-opt/validate_2.rs b/src/test/mir-opt/validate_2.rs index 37ebd720d52..aacf5a5ed0f 100644 --- a/src/test/mir-opt/validate_2.rs +++ b/src/test/mir-opt/validate_2.rs @@ -18,10 +18,18 @@ fn main() { // END RUST SOURCE // START rustc.node4.EraseRegions.after.mir // fn main() -> () { +// ... // bb1: { +// Validate(Acquire, [_2: std::boxed::Box<[i32; 3]>]); // Validate(Release, [_2: std::boxed::Box<[i32; 3]>]); // _1 = _2 as std::boxed::Box<[i32]> (Unsize); // Validate(Acquire, [_1: std::boxed::Box<[i32]>]); +// StorageDead(_2); +// StorageDead(_3); +// _0 = (); +// Validate(Release, [_1: std::boxed::Box<[i32]>]); +// drop(_1) -> bb2; // } +// ... // } // END rustc.node4.EraseRegions.after.mir diff --git a/src/test/mir-opt/validate_3.rs b/src/test/mir-opt/validate_3.rs index 116e35b2d6f..d7d3e023c9e 100644 --- a/src/test/mir-opt/validate_3.rs +++ b/src/test/mir-opt/validate_3.rs @@ -30,8 +30,17 @@ fn main() { // END RUST SOURCE // START rustc.node16.EraseRegions.after.mir // fn main() -> () { +// ... // let mut _5: &ReErased i32; // bb0: { +// StorageLive(_1); +// _1 = Test { x: const 0i32 }; +// StorageLive(_2); +// Validate(Suspend(ReScope(Remainder(BlockRemainder { block: ItemLocalId(19), first_statement_index: 3 }))), [_1: Test]); +// _2 = &ReErased _1; +// Validate(Acquire, [(*_2): Test/ReScope(Remainder(BlockRemainder { block: ItemLocalId(19), first_statement_index: 3 })) (imm)]); +// StorageLive(_4); +// StorageLive(_5); // Validate(Suspend(ReScope(Node(ItemLocalId(17)))), [((*_2).0: i32): i32/ReScope(Remainder(BlockRemainder { block: ItemLocalId(19), first_statement_index: 3 })) (imm)]); // _5 = &ReErased ((*_2).0: i32); // Validate(Acquire, [(*_5): i32/ReScope(Node(ItemLocalId(17))) (imm)]); @@ -42,8 +51,14 @@ fn main() { // _3 = const foo(_4) -> bb1; // } // bb1: { +// Validate(Acquire, [_3: ()]); // EndRegion(ReScope(Node(ItemLocalId(17)))); +// StorageDead(_4); +// StorageDead(_5); +// _0 = (); // EndRegion(ReScope(Remainder(BlockRemainder { block: ItemLocalId(19), first_statement_index: 3 }))); +// StorageDead(_2); +// StorageDead(_1); // return; // } // } diff --git a/src/test/mir-opt/validate_4.rs b/src/test/mir-opt/validate_4.rs index b670d8094dd..bcb21c60b26 100644 --- a/src/test/mir-opt/validate_4.rs +++ b/src/test/mir-opt/validate_4.rs @@ -38,15 +38,18 @@ fn main() { // END RUST SOURCE // START rustc.node4.EraseRegions.after.mir // fn write_42(_1: *mut i32) -> bool { +// ... // bb0: { // Validate(Acquire, [_1: *mut i32]); // Validate(Release, [_1: *mut i32]); +// ... // return; // } // } // END rustc.node4.EraseRegions.after.mir // START rustc.node22.EraseRegions.after.mir // fn write_42::{{closure}}(_1: &ReErased [closure@NodeId(22)], _2: *mut i32) -> () { +// ... // bb0: { // Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:9) => validate_4[8cd8]::write_42[0]::{{closure}}[0] }, BrEnv) [closure@NodeId(22)], _2: *mut i32]); // Validate(Release, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:9) => validate_4[8cd8]::write_42[0]::{{closure}}[0] }, BrEnv) [closure@NodeId(22)], _2: *mut i32]); @@ -60,24 +63,30 @@ fn main() { // END rustc.node22.EraseRegions.after.mir // START rustc.node31.EraseRegions.after.mir // fn test(_1: &ReErased mut i32) -> () { +// ... // bb0: { // Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(0:4) => validate_4[8cd8]::test[0] }, BrAnon(0)) mut i32]); // Validate(Release, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(0:4) => validate_4[8cd8]::test[0] }, BrAnon(0)) mut i32]); +// ... // _3 = const write_42(_4) -> bb1; // } // bb1: { // Validate(Acquire, [_3: bool]); // Validate(Release, [_3: bool]); +// ... // } // } // END rustc.node31.EraseRegions.after.mir // START rustc.node60.EraseRegions.after.mir // fn main::{{closure}}(_1: &ReErased [closure@NodeId(60)], _2: &ReErased mut i32) -> bool { +// ... // bb0: { // Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:10) => validate_4[8cd8]::main[0]::{{closure}}[0] }, BrEnv) [closure@NodeId(60)], _2: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:10) => validate_4[8cd8]::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]); // Validate(Release, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:10) => validate_4[8cd8]::main[0]::{{closure}}[0] }, BrEnv) [closure@NodeId(60)], _2: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:10) => validate_4[8cd8]::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]); // StorageLive(_3); +// ... // _0 = const write_42(_4) -> bb1; // } +// ... // } // END rustc.node60.EraseRegions.after.mir diff --git a/src/test/mir-opt/validate_5.rs b/src/test/mir-opt/validate_5.rs index 059d3adb407..44280539c41 100644 --- a/src/test/mir-opt/validate_5.rs +++ b/src/test/mir-opt/validate_5.rs @@ -35,15 +35,19 @@ fn main() { // END RUST SOURCE // START rustc.node17.EraseRegions.after.mir // fn test(_1: &ReErased mut i32) -> () { +// ... // bb0: { // Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(0:4) => validate_5[8cd8]::test[0] }, BrAnon(0)) mut i32]); +// ... // Validate(Release, [_3: bool, _4: *mut i32]); // _3 = const write_42(_4) -> bb1; // } +// ... // } // END rustc.node17.EraseRegions.after.mir // START rustc.node46.EraseRegions.after.mir // fn main::{{closure}}(_1: &ReErased [closure@NodeId(46)], _2: &ReErased mut i32) -> bool { +// ... // bb0: { // Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:9) => validate_5[8cd8]::main[0]::{{closure}}[0] }, BrEnv) [closure@NodeId(46)], _2: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:9) => validate_5[8cd8]::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]); // StorageLive(_3); @@ -59,5 +63,6 @@ fn main() { // Validate(Release, [_0: bool, _4: *mut i32]); // _0 = const write_42(_4) -> bb1; // } +// ... // } // END rustc.node46.EraseRegions.after.mir diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index dfdbabdcd4a..af9061ab367 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -25,6 +25,7 @@ use std::collections::HashSet; use std::env; use std::ffi::OsString; use std::fs::{self, File, create_dir_all}; +use std::fmt; use std::io::prelude::*; use std::io::{self, BufReader}; use std::path::{Path, PathBuf}; @@ -2228,7 +2229,7 @@ actual:\n\ let (_, tests_text) = test_file_contents.split_at(idx + "// END_RUST SOURCE".len()); let tests_text_str = String::from(tests_text); let mut curr_test : Option<&str> = None; - let mut curr_test_contents = Vec::new(); + let mut curr_test_contents = vec![ExpectedLine::Elision]; for l in tests_text_str.lines() { debug!("line: {:?}", l); if l.starts_with("// START ") { @@ -2242,11 +2243,14 @@ actual:\n\ self.compare_mir_test_output(curr_test.unwrap(), &curr_test_contents); curr_test = None; curr_test_contents.clear(); + curr_test_contents.push(ExpectedLine::Elision); } else if l.is_empty() { // ignore + } else if l.starts_with("//") && l.split_at("//".len()).1.trim() == "..." { + curr_test_contents.push(ExpectedLine::Elision) } else if l.starts_with("// ") { let (_, test_content) = l.split_at("// ".len()); - curr_test_contents.push(test_content); + curr_test_contents.push(ExpectedLine::Text(test_content)); } } } @@ -2264,7 +2268,7 @@ actual:\n\ } } - fn compare_mir_test_output(&self, test_name: &str, expected_content: &[&str]) { + fn compare_mir_test_output(&self, test_name: &str, expected_content: &[ExpectedLine<&str>]) { let mut output_file = PathBuf::new(); output_file.push(self.get_mir_dump_dir()); output_file.push(test_name); @@ -2276,38 +2280,77 @@ actual:\n\ let mut dumped_string = String::new(); dumped_file.read_to_string(&mut dumped_string).unwrap(); let mut dumped_lines = dumped_string.lines().filter(|l| !l.is_empty()); - let mut expected_lines = expected_content.iter().filter(|l| !l.is_empty()); + let mut expected_lines = expected_content.iter().filter(|&l| { + if let &ExpectedLine::Text(l) = l { + !l.is_empty() + } else { + true + } + }).peekable(); - // We expect each non-empty line from expected_content to appear - // in the dump in order, but there may be extra lines interleaved - while let Some(expected_line) = expected_lines.next() { + let compare = |expected_line, dumped_line| { let e_norm = normalize_mir_line(expected_line); - if e_norm.is_empty() { - continue; + let d_norm = normalize_mir_line(dumped_line); + debug!("found: {:?}", d_norm); + debug!("expected: {:?}", e_norm); + e_norm == d_norm + }; + + let error = |expected_line, extra_msg| { + let normalize_all = dumped_string.lines() + .map(nocomment_mir_line) + .filter(|l| !l.is_empty()) + .collect::<Vec<_>>() + .join("\n"); + let f = |l: &ExpectedLine<_>| match l { + &ExpectedLine::Elision => "... (elided)".into(), + &ExpectedLine::Text(t) => t }; - let mut found = false; - while let Some(dumped_line) = dumped_lines.next() { - let d_norm = normalize_mir_line(dumped_line); - debug!("found: {:?}", d_norm); - debug!("expected: {:?}", e_norm); - if e_norm == d_norm { - found = true; - break; - }; - } - if !found { - let normalize_all = dumped_string.lines() - .map(nocomment_mir_line) - .filter(|l| !l.is_empty()) - .collect::<Vec<_>>() - .join("\n"); - panic!("ran out of mir dump output to match against.\n\ - Did not find expected line: {:?}\n\ - Expected:\n{}\n\ - Actual:\n{}", - expected_line, - expected_content.join("\n"), - normalize_all); + let expected_content = expected_content.iter() + .map(|l| f(l)) + .collect::<Vec<_>>() + .join("\n"); + panic!("Did not find expected line, error: {}\n\ + Actual Line: {:?}\n\ + Expected:\n{}\n\ + Actual:\n{}", + extra_msg, + expected_line, + expected_content, + normalize_all); + }; + + // We expect each non-empty line to appear consecutively, non-consecutive lines + // must be separated by at least one Elision + while let Some(dumped_line) = dumped_lines.next() { + match expected_lines.next() { + Some(&ExpectedLine::Text(expected_line)) => + if !compare(expected_line, dumped_line) { + error(expected_line, + format!("Mismatch in lines\nExpected Line: {:?}", dumped_line)); + }, + Some(&ExpectedLine::Elision) => { + // skip any number of elisions in a row. + while let Some(&&ExpectedLine::Elision) = expected_lines.peek() { + expected_lines.next(); + } + if let Some(&ExpectedLine::Text(expected_line)) = expected_lines.next() { + let mut found = compare(expected_line, dumped_line); + if found { + continue; + } + while let Some(dumped_line) = dumped_lines.next() { + found = compare(expected_line, dumped_line); + if found { + break; + } + } + if !found { + error(expected_line, "ran out of mir dump to match against".into()); + } + } + }, + None => {}, } } } @@ -2430,6 +2473,25 @@ enum TargetLocation { ThisDirectory(PathBuf), } +#[derive(Clone, PartialEq, Eq)] +enum ExpectedLine<T: AsRef<str>> { + Elision, + Text(T) +} + +impl<T> fmt::Debug for ExpectedLine<T> +where + T: AsRef<str> + fmt::Debug +{ + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + if let &ExpectedLine::Text(ref t) = self { + write!(formatter, "{:?}", t) + } else { + write!(formatter, "\"...\" (Elision)") + } + } +} + fn normalize_mir_line(line: &str) -> String { nocomment_mir_line(line).replace(char::is_whitespace, "") } |
