diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs index 76d0e6e28aeda..4607ca5cf9f48 100644 --- a/src/bootstrap/bin/rustc.rs +++ b/src/bootstrap/bin/rustc.rs @@ -297,7 +297,12 @@ fn main() { } if verbose > 1 { - eprintln!("rustc command: {:?}", cmd); + eprintln!( + "rustc command: {:?}={:?} {:?}", + bootstrap::util::dylib_path_var(), + env::join_paths(&dylib_path).unwrap(), + cmd, + ); eprintln!("sysroot: {:?}", sysroot); eprintln!("libdir: {:?}", libdir); } diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 8a17a8b091bee..cd646b76e832e 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -1460,6 +1460,7 @@ mod __test { rustc_args: vec![], fail_fast: true, doc_tests: DocTests::No, + bless: false, }; let build = Build::new(config); diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 5315a3028ffa9..90dd5d819b0da 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -59,6 +59,8 @@ pub enum Subcommand { }, Test { paths: Vec, + /// Whether to automatically update stderr/stdout files + bless: bool, test_args: Vec, rustc_args: Vec, fail_fast: bool, @@ -173,6 +175,7 @@ To learn more about a subcommand, run `./x.py -h`"); ); opts.optflag("", "no-doc", "do not run doc tests"); opts.optflag("", "doc", "only run doc tests"); + opts.optflag("", "bless", "update all stderr/stdout files of failing ui tests"); }, "bench" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); }, "clean" => { opts.optflag("", "all", "clean all build artifacts"); }, @@ -258,6 +261,7 @@ Arguments: ./x.py test src/test/run-pass ./x.py test src/libstd --test-args hash_map ./x.py test src/libstd --stage 0 + ./x.py test src/test/ui --bless If no arguments are passed then the complete artifacts for that stage are compiled and tested. @@ -322,6 +326,7 @@ Arguments: "test" => { Subcommand::Test { paths, + bless: matches.opt_present("bless"), test_args: matches.opt_strs("test-args"), rustc_args: matches.opt_strs("rustc-args"), fail_fast: !matches.opt_present("no-fail-fast"), @@ -424,6 +429,13 @@ impl Subcommand { _ => DocTests::Yes, } } + + pub fn bless(&self) -> bool { + match *self { + Subcommand::Test { bless, .. } => bless, + _ => false, + } + } } fn split(s: Vec) -> Vec { diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 1f81a617237cc..7a4924f03c8d2 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -47,6 +47,16 @@ pub enum TestKind { Bench, } +impl From for TestKind { + fn from(kind: Kind) -> Self { + match kind { + Kind::Test => TestKind::Test, + Kind::Bench => TestKind::Bench, + _ => panic!("unexpected kind in crate: {:?}", kind) + } + } +} + impl TestKind { // Return the cargo subcommand for this test kind fn subcommand(self) -> &'static str { @@ -951,6 +961,10 @@ impl Step for Compiletest { cmd.arg("--host").arg(&*compiler.host); cmd.arg("--llvm-filecheck").arg(builder.llvm_filecheck(builder.config.build)); + if builder.config.cmd.bless() { + cmd.arg("--bless"); + } + if let Some(ref nodejs) = builder.config.nodejs { cmd.arg("--nodejs").arg(nodejs); } @@ -1342,13 +1356,7 @@ impl Step for CrateLibrustc { for krate in builder.in_tree_crates("rustc-main") { if run.path.ends_with(&krate.path) { - let test_kind = if builder.kind == Kind::Test { - TestKind::Test - } else if builder.kind == Kind::Bench { - TestKind::Bench - } else { - panic!("unexpected builder.kind in crate: {:?}", builder.kind); - }; + let test_kind = builder.kind.into(); builder.ensure(CrateLibrustc { compiler, @@ -1394,13 +1402,7 @@ impl Step for CrateNotDefault { let builder = run.builder; let compiler = builder.compiler(builder.top_stage, run.host); - let test_kind = if builder.kind == Kind::Test { - TestKind::Test - } else if builder.kind == Kind::Bench { - TestKind::Bench - } else { - panic!("unexpected builder.kind in crate: {:?}", builder.kind); - }; + let test_kind = builder.kind.into(); builder.ensure(CrateNotDefault { compiler, @@ -1461,13 +1463,7 @@ impl Step for Crate { let compiler = builder.compiler(builder.top_stage, run.host); let make = |mode: Mode, krate: &CargoCrate| { - let test_kind = if builder.kind == Kind::Test { - TestKind::Test - } else if builder.kind == Kind::Bench { - TestKind::Bench - } else { - panic!("unexpected builder.kind in crate: {:?}", builder.kind); - }; + let test_kind = builder.kind.into(); builder.ensure(Crate { compiler, @@ -1625,13 +1621,7 @@ impl Step for CrateRustdoc { fn make_run(run: RunConfig) { let builder = run.builder; - let test_kind = if builder.kind == Kind::Test { - TestKind::Test - } else if builder.kind == Kind::Bench { - TestKind::Bench - } else { - panic!("unexpected builder.kind in crate: {:?}", builder.kind); - }; + let test_kind = builder.kind.into(); builder.ensure(CrateRustdoc { host: run.host, diff --git a/src/doc/grammar.md b/src/doc/grammar.md index 78432b6a96593..ee9135b6578f6 100644 --- a/src/doc/grammar.md +++ b/src/doc/grammar.md @@ -101,29 +101,24 @@ properties: `ident`, `non_null`, `non_eol`, `non_single_quote` and ### Identifiers -The `ident` production is any nonempty Unicode[^non_ascii_idents] string of +The `ident` production is any nonempty Unicode string of the following form: -[^non_ascii_idents]: Non-ASCII characters in identifiers are currently feature - gated. This is expected to improve soon. +- The first character is in one of the following ranges `U+0041` to `U+005A` +("A" to "Z"), `U+0061` to `U+007A` ("a" to "z"), or `U+005F` ("\_"). +- The remaining characters are in the range `U+0030` to `U+0039` ("0" to "9"), +or any of the prior valid initial characters. -- The first character has property `XID_start` -- The remaining characters have property `XID_continue` - -that does _not_ occur in the set of [keywords](#keywords). - -> **Note**: `XID_start` and `XID_continue` as character properties cover the -> character ranges used to form the more familiar C and Java language-family -> identifiers. +as long as the identifier does _not_ occur in the set of [keywords](#keywords). ### Delimiter-restricted productions Some productions are defined by exclusion of particular Unicode characters: - `non_null` is any single Unicode character aside from `U+0000` (null) -- `non_eol` is `non_null` restricted to exclude `U+000A` (`'\n'`) -- `non_single_quote` is `non_null` restricted to exclude `U+0027` (`'`) -- `non_double_quote` is `non_null` restricted to exclude `U+0022` (`"`) +- `non_eol` is any single Unicode character aside from `U+000A` (`'\n'`) +- `non_single_quote` is any single Unicode character aside from `U+0027` (`'`) +- `non_double_quote` is any single Unicode character aside from `U+0022` (`"`) ## Comments diff --git a/src/doc/tutorial.md b/src/doc/tutorial.md index 87f3a0c765c5e..320283f31b51f 100644 --- a/src/doc/tutorial.md +++ b/src/doc/tutorial.md @@ -1,3 +1,3 @@ % The Rust Tutorial -This tutorial has been deprecated in favor of [the Book](book/index.html). Go check that out instead! +This tutorial has been deprecated in favor of [the Book](book/index.html), which is available free online and in dead tree form. Go check that out instead! diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 5ec6cb6c71054..0663409c992f4 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -962,122 +962,59 @@ extern "rust-intrinsic" { /// value is not necessarily valid to be used to actually access memory. pub fn arith_offset(dst: *const T, offset: isize) -> *const T; - /// Copies `count * size_of::()` bytes from `src` to `dst`. The source - /// and destination must *not* overlap. + /// Copies `count * size_of` bytes from `src` to `dst`. The source + /// and destination may *not* overlap. /// - /// For regions of memory which might overlap, use [`copy`] instead. - /// - /// `copy_nonoverlapping` is semantically equivalent to C's [`memcpy`]. - /// - /// [`copy`]: ./fn.copy.html - /// [`memcpy`]: https://www.gnu.org/software/libc/manual/html_node/Copying-Strings-and-Arrays.html#index-memcpy + /// `copy_nonoverlapping` is semantically equivalent to C's `memcpy`. /// /// # Safety /// - /// Behavior is undefined if any of the following conditions are violated: - /// - /// * The region of memory which begins at `src` and has a length of - /// `count * size_of::()` bytes must be *both* valid and initialized. - /// - /// * The region of memory which begins at `dst` and has a length of - /// `count * size_of::()` bytes must be valid (but may or may not be - /// initialized). - /// - /// * The two regions of memory must *not* overlap. - /// - /// * `src` must be properly aligned. - /// - /// * `dst` must be properly aligned. - /// - /// Additionally, if `T` is not [`Copy`], only the region at `src` *or* the - /// region at `dst` can be used or dropped after calling - /// `copy_nonoverlapping`. `copy_nonoverlapping` creates bitwise copies of - /// `T`, regardless of whether `T: Copy`, which can result in undefined - /// behavior if both copies are used. - /// - /// [`Copy`]: ../marker/trait.Copy.html + /// Beyond requiring that the program must be allowed to access both regions + /// of memory, it is Undefined Behavior for source and destination to + /// overlap. Care must also be taken with the ownership of `src` and + /// `dst`. This method semantically moves the values of `src` into `dst`. + /// However it does not drop the contents of `dst`, or prevent the contents + /// of `src` from being dropped or used. /// /// # Examples /// - /// Manually implement [`Vec::append`]: + /// A safe swap function: /// /// ``` + /// use std::mem; /// use std::ptr; /// - /// /// Moves all the elements of `src` into `dst`, leaving `src` empty. - /// fn append(dst: &mut Vec, src: &mut Vec) { - /// let src_len = src.len(); - /// let dst_len = dst.len(); - /// - /// // Ensure that `dst` has enough capacity to hold all of `src`. - /// dst.reserve(src_len); - /// + /// # #[allow(dead_code)] + /// fn swap(x: &mut T, y: &mut T) { /// unsafe { - /// // The call to offset is always safe because `Vec` will never - /// // allocate more than `isize::MAX` bytes. - /// let dst = dst.as_mut_ptr().offset(dst_len as isize); - /// let src = src.as_ptr(); - /// - /// // The two regions cannot overlap becuase mutable references do - /// // not alias, and two different vectors cannot own the same - /// // memory. - /// ptr::copy_nonoverlapping(src, dst, src_len); - /// } + /// // Give ourselves some scratch space to work with + /// let mut t: T = mem::uninitialized(); /// - /// unsafe { - /// // Truncate `src` without dropping its contents. - /// src.set_len(0); + /// // Perform the swap, `&mut` pointers never alias + /// ptr::copy_nonoverlapping(x, &mut t, 1); + /// ptr::copy_nonoverlapping(y, x, 1); + /// ptr::copy_nonoverlapping(&t, y, 1); /// - /// // Notify `dst` that it now holds the contents of `src`. - /// dst.set_len(dst_len + src_len); + /// // y and t now point to the same thing, but we need to completely forget `t` + /// // because it's no longer relevant. + /// mem::forget(t); /// } /// } - /// - /// let mut a = vec!['r']; - /// let mut b = vec!['u', 's', 't']; - /// - /// append(&mut a, &mut b); - /// - /// assert_eq!(a, &['r', 'u', 's', 't']); - /// assert!(b.is_empty()); /// ``` - /// - /// [`Vec::append`]: ../../std/vec/struct.Vec.html#method.append #[stable(feature = "rust1", since = "1.0.0")] pub fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); - /// Copies `count * size_of::()` bytes from `src` to `dst`. The source + /// Copies `count * size_of` bytes from `src` to `dst`. The source /// and destination may overlap. /// - /// If the source and destination will *never* overlap, - /// [`copy_nonoverlapping`] can be used instead. - /// - /// `copy` is semantically equivalent to C's [`memmove`]. - /// - /// [`copy_nonoverlapping`]: ./fn.copy_nonoverlapping.html - /// [`memmove`]: https://www.gnu.org/software/libc/manual/html_node/Copying-Strings-and-Arrays.html#index-memmove + /// `copy` is semantically equivalent to C's `memmove`. /// /// # Safety /// - /// Behavior is undefined if any of the following conditions are violated: - /// - /// * The region of memory which begins at `src` and has a length of - /// `count * size_of::()` bytes must be *both* valid and initialized. - /// - /// * The region of memory which begins at `dst` and has a length of - /// `count * size_of::()` bytes must be valid (but may or may not be - /// initialized). - /// - /// * `src` must be properly aligned. - /// - /// * `dst` must be properly aligned. - /// - /// Additionally, if `T` is not [`Copy`], only the region at `src` *or* the - /// region at `dst` can be used or dropped after calling `copy`. `copy` - /// creates bitwise copies of `T`, regardless of whether `T: Copy`, which - /// can result in undefined behavior if both copies are used. - /// - /// [`Copy`]: ../marker/trait.Copy.html + /// Care must be taken with the ownership of `src` and `dst`. + /// This method semantically moves the values of `src` into `dst`. + /// However it does not drop the contents of `dst`, or prevent the contents of `src` + /// from being dropped or used. /// /// # Examples /// @@ -1094,34 +1031,15 @@ extern "rust-intrinsic" { /// dst /// } /// ``` + /// #[stable(feature = "rust1", since = "1.0.0")] pub fn copy(src: *const T, dst: *mut T, count: usize); - /// Sets `count * size_of::()` bytes of memory starting at `dst` to - /// `val`. - /// - /// `write_bytes` is semantically equivalent to C's [`memset`]. - /// - /// [`memset`]: https://www.gnu.org/software/libc/manual/html_node/Copying-Strings-and-Arrays.html#index-memset - /// - /// # Safety - /// - /// Behavior is undefined if any of the following conditions are violated: - /// - /// * The region of memory which begins at `dst` and has a length of - /// `count` bytes must be valid. - /// - /// * `dst` must be properly aligned. - /// - /// Additionally, the caller must ensure that writing `count` bytes to the - /// given region of memory results in a valid value of `T`. Creating an - /// invalid value of `T` can result in undefined behavior. An example is - /// provided below. + /// Invokes memset on the specified pointer, setting `count * size_of::()` + /// bytes of memory starting at `dst` to `val`. /// /// # Examples /// - /// Basic usage: - /// /// ``` /// use std::ptr; /// @@ -1132,23 +1050,6 @@ extern "rust-intrinsic" { /// } /// assert_eq!(vec, [b'a', b'a', 0, 0]); /// ``` - /// - /// Creating an invalid value: - /// - /// ```no_run - /// use std::{mem, ptr}; - /// - /// let mut v = Box::new(0i32); - /// - /// unsafe { - /// // Leaks the previously held value by overwriting the `Box` with - /// // a null pointer. - /// ptr::write_bytes(&mut v, 0, mem::size_of::>()); - /// } - /// - /// // At this point, using or dropping `v` results in undefined behavior. - /// // v = Box::new(0i32); // ERROR - /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn write_bytes(dst: *mut T, val: u8, count: usize); diff --git a/src/libcore/option.rs b/src/libcore/option.rs index 0dfdabee03182..28f37f72d6f9d 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -705,6 +705,42 @@ impl Option { } } + /// Returns [`Some`] if exactly one of `self`, `optb` is [`Some`], otherwise returns `None`. + /// + /// [`Some`]: #variant.Some + /// [`None`]: #variant.None + /// + /// # Examples + /// + /// ``` + /// #![feature(option_xor)] + /// + /// let x = Some(2); + /// let y: Option = None; + /// assert_eq!(x.xor(y), Some(2)); + /// + /// let x: Option = None; + /// let y = Some(2); + /// assert_eq!(x.xor(y), Some(2)); + /// + /// let x = Some(2); + /// let y = Some(2); + /// assert_eq!(x.xor(y), None); + /// + /// let x: Option = None; + /// let y: Option = None; + /// assert_eq!(x.xor(y), None); + /// ``` + #[inline] + #[unstable(feature = "option_xor", issue = "50512")] + pub fn xor(self, optb: Option) -> Option { + match (self, optb) { + (Some(a), None) => Some(a), + (None, Some(b)) => Some(b), + _ => None, + } + } + ///////////////////////////////////////////////////////////////////////// // Entry-like operations to insert if None and return a reference ///////////////////////////////////////////////////////////////////////// diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 63bcc02402015..5f778482f42f2 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -10,7 +10,7 @@ // FIXME: talk about offset, copy_memory, copy_nonoverlapping_memory -//! Manually manage memory through raw pointers. +//! Raw, unsafe pointers, `*const T`, and `*mut T`. //! //! *[See also the pointer primitive types](../../std/primitive.pointer.html).* @@ -38,62 +38,21 @@ pub use intrinsics::write_bytes; /// Executes the destructor (if any) of the pointed-to value. /// -/// This is semantically equivalent to calling [`ptr::read`] and discarding -/// the result, but has the following advantages: +/// This has two use cases: /// /// * It is *required* to use `drop_in_place` to drop unsized types like /// trait objects, because they can't be read out onto the stack and /// dropped normally. /// -/// * It is friendlier to the optimizer to do this over [`ptr::read`] when +/// * It is friendlier to the optimizer to do this over `ptr::read` when /// dropping manually allocated memory (e.g. when writing Box/Rc/Vec), /// as the compiler doesn't need to prove that it's sound to elide the /// copy. /// -/// [`ptr::read`]: ../ptr/fn.read.html -/// /// # Safety /// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `to_drop` must point to valid memory. -/// -/// * `to_drop` must be properly aligned. -/// -/// Additionally, if `T` is not [`Copy`], using the pointed-to value after -/// calling `drop_in_place` can cause undefined behavior. Note that `*to_drop = -/// foo` counts as a use because it will cause the the value to be dropped -/// again. [`write`] can be used to overwrite data without causing it to be -/// dropped. -/// -/// [`Copy`]: ../marker/trait.Copy.html -/// [`write`]: ../ptr/fn.write.html -/// -/// # Examples -/// -/// Manually remove the last item from a vector: -/// -/// ``` -/// use std::ptr; -/// use std::rc::Rc; -/// -/// let last = Rc::new(1); -/// let weak = Rc::downgrade(&last); -/// -/// let mut v = vec![Rc::new(0), last]; -/// -/// unsafe { -/// // Without a call `drop_in_place`, the last item would never be dropped, -/// // and the memory it manages would be leaked. -/// ptr::drop_in_place(&mut v[1]); -/// v.set_len(1); -/// } -/// -/// assert_eq!(v, &[0.into()]); -/// -/// // Ensure that the last item was dropped. -/// assert!(weak.upgrade().is_none()); -/// ``` +/// This has all the same safety problems as `ptr::read` with respect to +/// invalid pointers, types, and double drops. #[stable(feature = "drop_in_place", since = "1.8.0")] #[lang = "drop_in_place"] #[allow(unconditional_recursion)] @@ -134,25 +93,17 @@ pub const fn null_mut() -> *mut T { 0 as *mut T } /// Swaps the values at two mutable locations of the same type, without /// deinitializing either. /// -/// But for the following two exceptions, this function is semantically -/// equivalent to [`mem::swap`]: -/// -/// * It operates on raw pointers instead of references. When references are -/// available, [`mem::swap`] should be preferred. -/// -/// * The two pointed-to values may overlap. If the values do overlap, then the -/// overlapping region of memory from `x` will be used. This is demonstrated -/// in the examples below. -/// -/// [`mem::swap`]: ../mem/fn.swap.html +/// The values pointed at by `x` and `y` may overlap, unlike `mem::swap` which +/// is otherwise equivalent. If the values do overlap, then the overlapping +/// region of memory from `x` will be used. This is demonstrated in the +/// examples section below. /// /// # Safety /// -/// Behavior is undefined if any of the following conditions are violated: +/// This function copies the memory through the raw pointers passed to it +/// as arguments. /// -/// * `x` and `y` must point to valid, initialized memory. -/// -/// * `x` and `y` must be properly aligned. +/// Ensure that these pointers are valid before calling `swap`. /// /// # Examples /// @@ -288,39 +239,13 @@ unsafe fn swap_nonoverlapping_bytes(x: *mut u8, y: *mut u8, len: usize) { } } -/// Replaces the value at `dest` with `src`, returning the old value, without -/// dropping either. -/// -/// This function is semantically equivalent to [`mem::replace`] except that it -/// operates on raw pointers instead of references. When references are -/// available, [`mem::replace`] should be preferred. -/// -/// [`mem::replace`]: ../mem/fn.replace.html +/// Replaces the value at `dest` with `src`, returning the old +/// value, without dropping either. /// /// # Safety /// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `dest` must point to valid, initialized memory. -/// -/// * `dest` must be properly aligned. -/// -/// # Examples -/// -/// ``` -/// use std::ptr; -/// -/// let mut rust = vec!['b', 'u', 's', 't']; -/// -/// // `mem::replace` would have the same effect without requiring the unsafe -/// // block. -/// let b = unsafe { -/// ptr::replace(&mut rust[0], 'r') -/// }; -/// -/// assert_eq!(b, 'b'); -/// assert_eq!(rust, &['r', 'u', 's', 't']); -/// ``` +/// This is only unsafe because it accepts a raw pointer. +/// Otherwise, this operation is identical to `mem::replace`. #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn replace(dest: *mut T, mut src: T) -> T { @@ -333,23 +258,14 @@ pub unsafe fn replace(dest: *mut T, mut src: T) -> T { /// /// # Safety /// -/// Behavior is undefined if any of the following conditions are violated: +/// Beyond accepting a raw pointer, this is unsafe because it semantically +/// moves the value out of `src` without preventing further usage of `src`. +/// If `T` is not `Copy`, then care must be taken to ensure that the value at +/// `src` is not used before the data is overwritten again (e.g. with `write`, +/// `write_bytes`, or `copy`). Note that `*src = foo` counts as a use +/// because it will attempt to drop the value previously at `*src`. /// -/// * `src` must point to valid, initialized memory. -/// -/// * `src` must be properly aligned. Use [`read_unaligned`] if this is not the -/// case. -/// -/// Additionally, if `T` is not [`Copy`], only the returned value *or* the -/// pointed-to value can be used or dropped after calling `read`. `read` creates -/// a bitwise copy of `T`, regardless of whether `T: Copy`, which can result -/// in undefined behavior if both copies are used. Note that `*src = foo` counts -/// as a use because it will attempt to drop the value previously at `*src`. -/// [`write`] can be used to overwrite data without causing it to be dropped. -/// -/// [`Copy`]: ../marker/trait.Copy.html -/// [`read_unaligned`]: ./fn.read_unaligned.html -/// [`write`]: ./fn.write.html +/// The pointer must be aligned; use `read_unaligned` if that is not the case. /// /// # Examples /// @@ -363,44 +279,6 @@ pub unsafe fn replace(dest: *mut T, mut src: T) -> T { /// assert_eq!(std::ptr::read(y), 12); /// } /// ``` -/// -/// Manually implement [`mem::swap`]: -/// -/// ``` -/// use std::ptr; -/// -/// fn swap(a: &mut T, b: &mut T) { -/// unsafe { -/// // Create a bitwise copy of the value at `a` in `tmp`. -/// let tmp = ptr::read(a); -/// -/// // Exiting at this point (either by explicitly returning or by -/// // calling a function which panics) would cause the value in `tmp` to -/// // be dropped while the same value is still referenced by `a`. This -/// // could trigger undefined behavior if `T` is not `Copy`. -/// -/// // Create a bitwise copy of the value at `b` in `a`. -/// // This is safe because mutable references cannot alias. -/// ptr::copy_nonoverlapping(b, a, 1); -/// -/// // As above, exiting here could trigger undefined behavior because -/// // the same value is referenced by `a` and `b`. -/// -/// // Move `tmp` into `b`. -/// ptr::write(b, tmp); -/// } -/// } -/// -/// let mut foo = "foo".to_owned(); -/// let mut bar = "bar".to_owned(); -/// -/// swap(&mut foo, &mut bar); -/// -/// assert_eq!(foo, "bar"); -/// assert_eq!(bar, "foo"); -/// ``` -/// -/// [`mem::swap`]: ../mem/fn.swap.html #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn read(src: *const T) -> T { @@ -412,62 +290,28 @@ pub unsafe fn read(src: *const T) -> T { /// Reads the value from `src` without moving it. This leaves the /// memory in `src` unchanged. /// -/// Unlike [`read`], `read_unaligned` works with unaligned pointers. -/// -/// [`read`]: ./fn.read.html +/// Unlike `read`, the pointer may be unaligned. /// /// # Safety /// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `src` must point to valid, initialized memory. -/// -/// Additionally, if `T` is not [`Copy`], only the returned value *or* the -/// pointed-to value can be used or dropped after calling `read_unaligned`. -/// `read_unaligned` creates a bitwise copy of `T`, regardless of whether `T: -/// Copy`, and this can result in undefined behavior if both copies are used. -/// Note that `*src = foo` counts as a use because it will attempt to drop the -/// value previously at `*src`. [`write_unaligned`] can be used to overwrite -/// data without causing it to be dropped. -/// -/// [`Copy`]: ../marker/trait.Copy.html -/// [`write_unaligned`]: ./fn.write_unaligned.html +/// Beyond accepting a raw pointer, this is unsafe because it semantically +/// moves the value out of `src` without preventing further usage of `src`. +/// If `T` is not `Copy`, then care must be taken to ensure that the value at +/// `src` is not used before the data is overwritten again (e.g. with `write`, +/// `write_bytes`, or `copy`). Note that `*src = foo` counts as a use +/// because it will attempt to drop the value previously at `*src`. /// /// # Examples /// -/// Access members of a packed struct by reference: +/// Basic usage: /// /// ``` -/// use std::ptr; +/// let x = 12; +/// let y = &x as *const i32; /// -/// #[repr(packed, C)] -/// #[derive(Default)] -/// struct Packed { -/// _padding: u8, -/// unaligned: u32, +/// unsafe { +/// assert_eq!(std::ptr::read_unaligned(y), 12); /// } -/// -/// let x = Packed { -/// _padding: 0x00, -/// unaligned: 0x01020304, -/// }; -/// -/// let v = unsafe { -/// // Take a reference to a 32-bit integer which is not aligned. -/// let unaligned = &x.unaligned; -/// -/// // Dereferencing normally will emit an unaligned load instruction, -/// // causing undefined behavior. -/// // let v = *unaligned; // ERROR -/// -/// // Instead, use `read_unaligned` to read improperly aligned values. -/// let v = ptr::read_unaligned(unaligned); -/// -/// v -/// }; -/// -/// // Accessing unaligned values directly is safe. -/// assert!(x.unaligned == v); /// ``` #[inline] #[stable(feature = "ptr_unaligned", since = "1.17.0")] @@ -482,7 +326,11 @@ pub unsafe fn read_unaligned(src: *const T) -> T { /// Overwrites a memory location with the given value without reading or /// dropping the old value. /// -/// `write` does not drop the contents of `dst`. This is safe, but it could leak +/// # Safety +/// +/// This operation is marked unsafe because it accepts a raw pointer. +/// +/// It does not drop the contents of `dst`. This is safe, but it could leak /// allocations or resources, so care must be taken not to overwrite an object /// that should be dropped. /// @@ -490,20 +338,9 @@ pub unsafe fn read_unaligned(src: *const T) -> T { /// location pointed to by `dst`. /// /// This is appropriate for initializing uninitialized memory, or overwriting -/// memory that has previously been [`read`] from. -/// -/// [`read`]: ./fn.read.html -/// -/// # Safety +/// memory that has previously been `read` from. /// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `dst` must point to valid memory. -/// -/// * `dst` must be properly aligned. Use [`write_unaligned`] if this is not the -/// case. -/// -/// [`write_unaligned`]: ./fn.write_unaligned.html +/// The pointer must be aligned; use `write_unaligned` if that is not the case. /// /// # Examples /// @@ -519,30 +356,6 @@ pub unsafe fn read_unaligned(src: *const T) -> T { /// assert_eq!(std::ptr::read(y), 12); /// } /// ``` -/// -/// Manually implement [`mem::swap`]: -/// -/// ``` -/// use std::ptr; -/// -/// fn swap(a: &mut T, b: &mut T) { -/// unsafe { -/// let tmp = ptr::read(a); -/// ptr::copy_nonoverlapping(b, a, 1); -/// ptr::write(b, tmp); -/// } -/// } -/// -/// let mut foo = "foo".to_owned(); -/// let mut bar = "bar".to_owned(); -/// -/// swap(&mut foo, &mut bar); -/// -/// assert_eq!(foo, "bar"); -/// assert_eq!(bar, "foo"); -/// ``` -/// -/// [`mem::swap`]: ../mem/fn.swap.html #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn write(dst: *mut T, src: T) { @@ -552,58 +365,36 @@ pub unsafe fn write(dst: *mut T, src: T) { /// Overwrites a memory location with the given value without reading or /// dropping the old value. /// -/// Unlike [`write`], the pointer may be unaligned. +/// Unlike `write`, the pointer may be unaligned. +/// +/// # Safety /// -/// `write_unaligned` does not drop the contents of `dst`. This is safe, but it -/// could leak allocations or resources, so care must be taken not to overwrite -/// an object that should be dropped. +/// This operation is marked unsafe because it accepts a raw pointer. +/// +/// It does not drop the contents of `dst`. This is safe, but it could leak +/// allocations or resources, so care must be taken not to overwrite an object +/// that should be dropped. /// /// Additionally, it does not drop `src`. Semantically, `src` is moved into the /// location pointed to by `dst`. /// /// This is appropriate for initializing uninitialized memory, or overwriting -/// memory that has previously been read with [`read_unaligned`]. -/// -/// [`write`]: ./fn.write.html -/// [`read_unaligned`]: ./fn.read_unaligned.html -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `dst` must point to valid memory. +/// memory that has previously been `read` from. /// /// # Examples /// -/// Access fields in a packed struct: +/// Basic usage: /// /// ``` -/// use std::{mem, ptr}; -/// -/// #[repr(packed, C)] -/// #[derive(Default)] -/// struct Packed { -/// _padding: u8, -/// unaligned: u32, -/// } -/// -/// let v = 0x01020304; -/// let mut x: Packed = unsafe { mem::zeroed() }; +/// let mut x = 0; +/// let y = &mut x as *mut i32; +/// let z = 12; /// /// unsafe { -/// // Take a reference to a 32-bit integer which is not aligned. -/// let unaligned = &mut x.unaligned; -/// -/// // Dereferencing normally will emit an unaligned store instruction, -/// // causing undefined behavior. -/// // *unaligned = v; // ERROR -/// -/// // Instead, use `write_unaligned` to write improperly aligned values. -/// ptr::write_unaligned(unaligned, v); +/// std::ptr::write_unaligned(y, z); +/// assert_eq!(std::ptr::read_unaligned(y), 12); /// } -/// -/// // Accessing unaligned values directly is safe. -/// assert!(x.unaligned == v); +/// ``` #[inline] #[stable(feature = "ptr_unaligned", since = "1.17.0")] pub unsafe fn write_unaligned(dst: *mut T, src: T) { @@ -620,11 +411,6 @@ pub unsafe fn write_unaligned(dst: *mut T, src: T) { /// to not be elided or reordered by the compiler across other volatile /// operations. /// -/// Memory read with `read_volatile` should almost always be written to using -/// [`write_volatile`]. -/// -/// [`write_volatile`]: ./fn.write_volatile.html -/// /// # Notes /// /// Rust does not currently have a rigorously and formally defined memory model, @@ -641,19 +427,12 @@ pub unsafe fn write_unaligned(dst: *mut T, src: T) { /// /// # Safety /// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `src` must point to valid, initialized memory. -/// -/// * `src` must be properly aligned. -/// -/// Like [`read`], `read_volatile` creates a bitwise copy of the pointed-to -/// object, regardless of whether `T` is [`Copy`]. Using both values can cause -/// undefined behavior. However, storing non-[`Copy`] data in I/O memory is -/// almost certainly incorrect. -/// -/// [`Copy`]: ../marker/trait.Copy.html -/// [`read`]: ./fn.read.html +/// Beyond accepting a raw pointer, this is unsafe because it semantically +/// moves the value out of `src` without preventing further usage of `src`. +/// If `T` is not `Copy`, then care must be taken to ensure that the value at +/// `src` is not used before the data is overwritten again (e.g. with `write`, +/// `write_bytes`, or `copy`). Note that `*src = foo` counts as a use +/// because it will attempt to drop the value previously at `*src`. /// /// # Examples /// @@ -680,18 +459,6 @@ pub unsafe fn read_volatile(src: *const T) -> T { /// to not be elided or reordered by the compiler across other volatile /// operations. /// -/// Memory written with `write_volatile` should almost always be read from using -/// [`read_volatile`]. -/// -/// `write_volatile` does not drop the contents of `dst`. This is safe, but it -/// could leak allocations or resources, so care must be taken not to overwrite -/// an object that should be dropped. -/// -/// Additionally, it does not drop `src`. Semantically, `src` is moved into the -/// location pointed to by `dst`. -/// -/// [`read_volatile`]: ./fn.read_volatile.html -/// /// # Notes /// /// Rust does not currently have a rigorously and formally defined memory model, @@ -708,11 +475,14 @@ pub unsafe fn read_volatile(src: *const T) -> T { /// /// # Safety /// -/// Behavior is undefined if any of the following conditions are violated: +/// This operation is marked unsafe because it accepts a raw pointer. /// -/// * `dst` must point to valid memory. +/// It does not drop the contents of `dst`. This is safe, but it could leak +/// allocations or resources, so care must be taken not to overwrite an object +/// that should be dropped. /// -/// * `dst` must be properly aligned. +/// This is appropriate for initializing uninitialized memory, or overwriting +/// memory that has previously been `read` from. /// /// # Examples /// diff --git a/src/libfmt_macros/lib.rs b/src/libfmt_macros/lib.rs index a551b1b770a68..a77751d65d08c 100644 --- a/src/libfmt_macros/lib.rs +++ b/src/libfmt_macros/lib.rs @@ -127,6 +127,14 @@ pub enum Count<'a> { CountImplied, } +pub struct ParseError { + pub description: string::String, + pub note: Option, + pub label: string::String, + pub start: usize, + pub end: usize, +} + /// The parser structure for interpreting the input format string. This is /// modeled as an iterator over `Piece` structures to form a stream of tokens /// being output. @@ -137,7 +145,7 @@ pub struct Parser<'a> { input: &'a str, cur: iter::Peekable>, /// Error messages accumulated during parsing - pub errors: Vec<(string::String, Option)>, + pub errors: Vec, /// Current position of implicit positional argument pointer curarg: usize, } @@ -160,12 +168,17 @@ impl<'a> Iterator for Parser<'a> { } '}' => { self.cur.next(); + let pos = pos + 1; if self.consume('}') { - Some(String(self.string(pos + 1))) + Some(String(self.string(pos))) } else { - self.err_with_note("unmatched `}` found", - "if you intended to print `}`, \ - you can escape it using `}}`"); + self.err_with_note( + "unmatched `}` found", + "unmatched `}`", + "if you intended to print `}`, you can escape it using `}}`", + pos, + pos, + ); None } } @@ -191,15 +204,40 @@ impl<'a> Parser<'a> { /// Notifies of an error. The message doesn't actually need to be of type /// String, but I think it does when this eventually uses conditions so it /// might as well start using it now. - fn err(&mut self, msg: &str) { - self.errors.push((msg.to_owned(), None)); + fn err, S2: Into>( + &mut self, + description: S1, + label: S2, + start: usize, + end: usize, + ) { + self.errors.push(ParseError { + description: description.into(), + note: None, + label: label.into(), + start, + end, + }); } /// Notifies of an error. The message doesn't actually need to be of type /// String, but I think it does when this eventually uses conditions so it /// might as well start using it now. - fn err_with_note(&mut self, msg: &str, note: &str) { - self.errors.push((msg.to_owned(), Some(note.to_owned()))); + fn err_with_note, S2: Into, S3: Into>( + &mut self, + description: S1, + label: S2, + note: S3, + start: usize, + end: usize, + ) { + self.errors.push(ParseError { + description: description.into(), + note: Some(note.into()), + label: label.into(), + start, + end, + }); } /// Optionally consumes the specified character. If the character is not at @@ -222,19 +260,26 @@ impl<'a> Parser<'a> { /// found, an error is emitted. fn must_consume(&mut self, c: char) { self.ws(); - if let Some(&(_, maybe)) = self.cur.peek() { + if let Some(&(pos, maybe)) = self.cur.peek() { if c == maybe { self.cur.next(); } else { - self.err(&format!("expected `{:?}`, found `{:?}`", c, maybe)); + self.err(format!("expected `{:?}`, found `{:?}`", c, maybe), + format!("expected `{}`", c), + pos + 1, + pos + 1); } } else { - let msg = &format!("expected `{:?}` but string was terminated", c); + let msg = format!("expected `{:?}` but string was terminated", c); + let pos = self.input.len() + 1; // point at closing `"` if c == '}' { self.err_with_note(msg, - "if you intended to print `{`, you can escape it using `{{`"); + format!("expected `{:?}`", c), + "if you intended to print `{`, you can escape it using `{{`", + pos, + pos); } else { - self.err(msg); + self.err(msg, format!("expected `{:?}`", c), pos, pos); } } } @@ -300,6 +345,15 @@ impl<'a> Parser<'a> { } else { match self.cur.peek() { Some(&(_, c)) if c.is_alphabetic() => Some(ArgumentNamed(self.word())), + Some(&(pos, c)) if c == '_' => { + let invalid_name = self.string(pos); + self.err_with_note(format!("invalid argument name `{}`", invalid_name), + "invalid argument name", + "argument names cannot start with an underscore", + pos + 1, // add 1 to account for leading `{` + pos + 1 + invalid_name.len()); + Some(ArgumentNamed(invalid_name)) + }, // This is an `ArgumentNext`. // Record the fact and do the resolution after parsing the diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 71d7abed6c95a..42a08afe305a5 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -542,18 +542,6 @@ impl<'tcx> ScopeTree { assert!(previous.is_none()); } - fn closure_is_enclosed_by(&self, - mut sub_closure: hir::ItemLocalId, - sup_closure: hir::ItemLocalId) -> bool { - loop { - if sub_closure == sup_closure { return true; } - match self.closure_tree.get(&sub_closure) { - Some(&s) => { sub_closure = s; } - None => { return false; } - } - } - } - fn record_var_scope(&mut self, var: hir::ItemLocalId, lifetime: Scope) { debug!("record_var_scope(sub={:?}, sup={:?})", var, lifetime); assert!(var != lifetime.item_local_id()); @@ -688,65 +676,37 @@ impl<'tcx> ScopeTree { // requires a hash table lookup, and we often have very long scope // chains (10s or 100s of scopes) that only differ by a few elements at // the start. So this algorithm is faster. - let mut ma = Some(scope_a); - let mut mb = Some(scope_b); - let mut seen_a: SmallVec<[Scope; 32]> = SmallVec::new(); - let mut seen_b: SmallVec<[Scope; 32]> = SmallVec::new(); + + let mut ma = Some(&scope_a); + let mut mb = Some(&scope_b); + + // A HashSet is a more obvious choice for these, but SmallVec is + // faster because the set size is normally small so linear search is + // as good or better than a hash table lookup, plus the size is usually + // small enough to avoid a heap allocation. + let mut seen_a: SmallVec<[&Scope; 32]> = SmallVec::new(); + let mut seen_b: SmallVec<[&Scope; 32]> = SmallVec::new(); + loop { if let Some(a) = ma { - if seen_b.iter().position(|s| *s == a).is_some() { - return a; + if seen_b.iter().any(|s| *s == a) { + return *a; } seen_a.push(a); - ma = self.parent_map.get(&a).map(|s| *s); + ma = self.parent_map.get(&a); } if let Some(b) = mb { - if seen_a.iter().position(|s| *s == b).is_some() { - return b; + if seen_a.iter().any(|s| *s == b) { + return *b; } seen_b.push(b); - mb = self.parent_map.get(&b).map(|s| *s); + mb = self.parent_map.get(&b); } if ma.is_none() && mb.is_none() { - break; - } - }; - - fn outermost_scope(parent_map: &FxHashMap, scope: Scope) -> Scope { - let mut scope = scope; - loop { - match parent_map.get(&scope) { - Some(&superscope) => scope = superscope, - None => break scope, - } - } - } - - // In this (rare) case, the two regions belong to completely different - // functions. Compare those fn for lexical nesting. The reasoning - // behind this is subtle. See the "Modeling closures" section of the - // README in infer::region_constraints for more details. - let a_root_scope = outermost_scope(&self.parent_map, scope_a); - let b_root_scope = outermost_scope(&self.parent_map, scope_b); - match (a_root_scope.data(), b_root_scope.data()) { - (ScopeData::Destruction(a_root_id), - ScopeData::Destruction(b_root_id)) => { - if self.closure_is_enclosed_by(a_root_id, b_root_id) { - // `a` is enclosed by `b`, hence `b` is the ancestor of everything in `a` - scope_b - } else if self.closure_is_enclosed_by(b_root_id, a_root_id) { - // `b` is enclosed by `a`, hence `a` is the ancestor of everything in `b` - scope_a - } else { - // neither fn encloses the other - bug!() - } - } - _ => { - // root ids are always Node right now - bug!() + // No nearest common ancestor found. + bug!(); } } } diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index e4520bce68199..97ce730c59ec5 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -203,17 +203,19 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { obligation.cause.span, infer::LateBoundRegionConversionTime::HigherRankedType, data); - let normalized = super::normalize_projection_type( + let mut obligations = vec![]; + let normalized_ty = super::normalize_projection_type( &mut selcx, obligation.param_env, data.projection_ty, obligation.cause.clone(), - 0 + 0, + &mut obligations ); if let Err(error) = self.at(&obligation.cause, obligation.param_env) - .eq(normalized.value, data.ty) { + .eq(normalized_ty, data.ty) { values = Some(infer::ValuePairs::Types(ExpectedFound { - expected: normalized.value, + expected: normalized_ty, found: data.ty, })); err_buf = error; diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index 94ee39470772f..4447a2b6ed140 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -161,19 +161,18 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { // FIXME(#20304) -- cache let mut selcx = SelectionContext::new(infcx); - let normalized = project::normalize_projection_type(&mut selcx, - param_env, - projection_ty, - cause, - 0); - - for obligation in normalized.obligations { - self.register_predicate_obligation(infcx, obligation); - } - - debug!("normalize_projection_type: result={:?}", normalized.value); - - normalized.value + let mut obligations = vec![]; + let normalized_ty = project::normalize_projection_type(&mut selcx, + param_env, + projection_ty, + cause, + 0, + &mut obligations); + self.register_predicate_obligations(infcx, obligations); + + debug!("normalize_projection_type: result={:?}", normalized_ty); + + normalized_ty } /// Requires that `ty` must implement the trait with `def_id` in diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index 270e06a872bcb..174c35d1d69bf 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -225,12 +225,14 @@ fn project_and_unify_type<'cx, 'gcx, 'tcx>( debug!("project_and_unify_type(obligation={:?})", obligation); - let Normalized { value: normalized_ty, mut obligations } = + let mut obligations = vec![]; + let normalized_ty = match opt_normalize_projection_type(selcx, obligation.param_env, obligation.predicate.projection_ty, obligation.cause.clone(), - obligation.recursion_depth) { + obligation.recursion_depth, + &mut obligations) { Some(n) => n, None => return Ok(None), }; @@ -386,16 +388,15 @@ impl<'a, 'b, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for AssociatedTypeNormalizer<'a, // binder). It would be better to normalize in a // binding-aware fashion. - let Normalized { value: normalized_ty, obligations } = - normalize_projection_type(self.selcx, - self.param_env, - data.clone(), - self.cause.clone(), - self.depth); - debug!("AssociatedTypeNormalizer: depth={} normalized {:?} to {:?} \ - with {} add'l obligations", - self.depth, ty, normalized_ty, obligations.len()); - self.obligations.extend(obligations); + let normalized_ty = normalize_projection_type(self.selcx, + self.param_env, + data.clone(), + self.cause.clone(), + self.depth, + &mut self.obligations); + debug!("AssociatedTypeNormalizer: depth={} normalized {:?} to {:?}, \ + now with {} obligations", + self.depth, ty, normalized_ty, self.obligations.len()); normalized_ty } @@ -471,10 +472,12 @@ pub fn normalize_projection_type<'a, 'b, 'gcx, 'tcx>( param_env: ty::ParamEnv<'tcx>, projection_ty: ty::ProjectionTy<'tcx>, cause: ObligationCause<'tcx>, - depth: usize) - -> NormalizedTy<'tcx> + depth: usize, + obligations: &mut Vec>) + -> Ty<'tcx> { - opt_normalize_projection_type(selcx, param_env, projection_ty.clone(), cause.clone(), depth) + opt_normalize_projection_type(selcx, param_env, projection_ty.clone(), cause.clone(), depth, + obligations) .unwrap_or_else(move || { // if we bottom out in ambiguity, create a type variable // and a deferred predicate to resolve this when more type @@ -490,10 +493,8 @@ pub fn normalize_projection_type<'a, 'b, 'gcx, 'tcx>( }); let obligation = Obligation::with_depth( cause, depth + 1, param_env, projection.to_predicate()); - Normalized { - value: ty_var, - obligations: vec![obligation] - } + obligations.push(obligation); + ty_var }) } @@ -501,13 +502,20 @@ pub fn normalize_projection_type<'a, 'b, 'gcx, 'tcx>( /// as Trait>::Item`. The result is always a type (and possibly /// additional obligations). Returns `None` in the case of ambiguity, /// which indicates that there are unbound type variables. +/// +/// This function used to return `Option>`, which contains a +/// `Ty<'tcx>` and an obligations vector. But that obligation vector was very +/// often immediately appended to another obligations vector. So now this +/// function takes an obligations vector and appends to it directly, which is +/// slightly uglier but avoids the need for an extra short-lived allocation. fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( selcx: &'a mut SelectionContext<'b, 'gcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, projection_ty: ty::ProjectionTy<'tcx>, cause: ObligationCause<'tcx>, - depth: usize) - -> Option> + depth: usize, + obligations: &mut Vec>) + -> Option> { let infcx = selcx.infcx(); @@ -579,7 +587,9 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( projection_ty); selcx.infcx().report_overflow_error(&obligation, false); } - Err(ProjectionCacheEntry::NormalizedTy(mut ty)) => { + Err(ProjectionCacheEntry::NormalizedTy(ty)) => { + // This is the hottest path in this function. + // // If we find the value in the cache, then return it along // with the obligations that went along with it. Note // that, when using a fulfillment context, these @@ -596,29 +606,32 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( // Once we have inferred everything we need to know, we // can ignore the `obligations` from that point on. if !infcx.any_unresolved_type_vars(&ty.value) { - infcx.projection_cache.borrow_mut().complete(cache_key); - ty.obligations = vec![]; + infcx.projection_cache.borrow_mut().complete_normalized(cache_key, &ty); + // No need to extend `obligations`. + } else { + obligations.extend(ty.obligations); } - push_paranoid_cache_value_obligation(infcx, - param_env, - projection_ty, - cause, - depth, - &mut ty); - - return Some(ty); + obligations.push(get_paranoid_cache_value_obligation(infcx, + param_env, + projection_ty, + cause, + depth)); + return Some(ty.value); } Err(ProjectionCacheEntry::Error) => { debug!("opt_normalize_projection_type: \ found error"); - return Some(normalize_to_error(selcx, param_env, projection_ty, cause, depth)); + let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth); + obligations.extend(result.obligations); + return Some(result.value) } } let obligation = Obligation::with_depth(cause.clone(), depth, param_env, projection_ty); match project_type(selcx, &obligation) { - Ok(ProjectedTy::Progress(Progress { ty: projected_ty, mut obligations })) => { + Ok(ProjectedTy::Progress(Progress { ty: projected_ty, + obligations: mut projected_obligations })) => { // if projection succeeded, then what we get out of this // is also non-normalized (consider: it was derived from // an impl, where-clause etc) and hence we must @@ -627,10 +640,10 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( debug!("opt_normalize_projection_type: \ projected_ty={:?} \ depth={} \ - obligations={:?}", + projected_obligations={:?}", projected_ty, depth, - obligations); + projected_obligations); let result = if projected_ty.has_projections() { let mut normalizer = AssociatedTypeNormalizer::new(selcx, @@ -644,22 +657,22 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( normalized_ty, depth); - obligations.extend(normalizer.obligations); + projected_obligations.extend(normalizer.obligations); Normalized { value: normalized_ty, - obligations, + obligations: projected_obligations, } } else { Normalized { value: projected_ty, - obligations, + obligations: projected_obligations, } }; let cache_value = prune_cache_value_obligations(infcx, &result); infcx.projection_cache.borrow_mut().insert_ty(cache_key, cache_value); - - Some(result) + obligations.extend(result.obligations); + Some(result.value) } Ok(ProjectedTy::NoProgress(projected_ty)) => { debug!("opt_normalize_projection_type: \ @@ -670,7 +683,8 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( obligations: vec![] }; infcx.projection_cache.borrow_mut().insert_ty(cache_key, result.clone()); - Some(result) + // No need to extend `obligations`. + Some(result.value) } Err(ProjectionTyError::TooManyCandidates) => { debug!("opt_normalize_projection_type: \ @@ -688,7 +702,9 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( infcx.projection_cache.borrow_mut() .error(cache_key); - Some(normalize_to_error(selcx, param_env, projection_ty, cause, depth)) + let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth); + obligations.extend(result.obligations); + Some(result.value) } } } @@ -737,7 +753,7 @@ fn prune_cache_value_obligations<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx, /// may or may not be necessary -- in principle, all the obligations /// that must be proven to show that `T: Trait` were also returned /// when the cache was first populated. But there are some vague concerns, -/// and so we take the precatuionary measure of including `T: Trait` in +/// and so we take the precautionary measure of including `T: Trait` in /// the result: /// /// Concern #1. The current setup is fragile. Perhaps someone could @@ -754,19 +770,21 @@ fn prune_cache_value_obligations<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx, /// that may yet turn out to be wrong. This *may* lead to some sort /// of trouble, though we don't have a concrete example of how that /// can occur yet. But it seems risky at best. -fn push_paranoid_cache_value_obligation<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - projection_ty: ty::ProjectionTy<'tcx>, - cause: ObligationCause<'tcx>, - depth: usize, - result: &mut NormalizedTy<'tcx>) +fn get_paranoid_cache_value_obligation<'a, 'gcx, 'tcx>( + infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + projection_ty: ty::ProjectionTy<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize) + -> PredicateObligation<'tcx> { let trait_ref = projection_ty.trait_ref(infcx.tcx).to_poly_trait_ref(); - let trait_obligation = Obligation { cause, - recursion_depth: depth, - param_env, - predicate: trait_ref.to_predicate() }; - result.obligations.push(trait_obligation); + Obligation { + cause, + recursion_depth: depth, + param_env, + predicate: trait_ref.to_predicate(), + } } /// If we are projecting `::Item`, but `T: Trait` does not @@ -1682,6 +1700,23 @@ impl<'tcx> ProjectionCache<'tcx> { })); } + /// A specialized version of `complete` for when the key's value is known + /// to be a NormalizedTy. + pub fn complete_normalized(&mut self, key: ProjectionCacheKey<'tcx>, ty: &NormalizedTy<'tcx>) { + // We want to insert `ty` with no obligations. If the existing value + // already has no obligations (as is common) we can use `insert_noop` + // to do a minimal amount of work -- the HashMap insertion is skipped, + // and minimal changes are made to the undo log. + if ty.obligations.is_empty() { + self.map.insert_noop(); + } else { + self.map.insert(key, ProjectionCacheEntry::NormalizedTy(Normalized { + value: ty.value, + obligations: vec![] + })); + } + } + /// Indicates that trying to normalize `key` resulted in /// ambiguity. No point in trying it again then until we gain more /// type information (in which case, the "fully resolved" key will diff --git a/src/librustc_data_structures/snapshot_map/mod.rs b/src/librustc_data_structures/snapshot_map/mod.rs index cede6f147821b..6ee8c3579f543 100644 --- a/src/librustc_data_structures/snapshot_map/mod.rs +++ b/src/librustc_data_structures/snapshot_map/mod.rs @@ -67,6 +67,12 @@ impl SnapshotMap } } + pub fn insert_noop(&mut self) { + if !self.undo_log.is_empty() { + self.undo_log.push(UndoLog::Noop); + } + } + pub fn remove(&mut self, key: K) -> bool { match self.map.remove(&key) { Some(old_value) => { diff --git a/src/librustc_traits/normalize_projection_ty.rs b/src/librustc_traits/normalize_projection_ty.rs index 8fc00c937e69c..a9ac53972e475 100644 --- a/src/librustc_traits/normalize_projection_ty.rs +++ b/src/librustc_traits/normalize_projection_ty.rs @@ -9,8 +9,7 @@ // except according to those terms. use rustc::infer::canonical::{Canonical, QueryResult}; -use rustc::traits::{self, FulfillmentContext, Normalized, ObligationCause, - SelectionContext}; +use rustc::traits::{self, FulfillmentContext, ObligationCause, SelectionContext}; use rustc::traits::query::{CanonicalProjectionGoal, NoSolution, normalize::NormalizationResult}; use rustc::ty::{ParamEnvAnd, TyCtxt}; use rustc_data_structures::sync::Lrc; @@ -37,10 +36,9 @@ crate fn normalize_projection_ty<'tcx>( let fulfill_cx = &mut FulfillmentContext::new(); let selcx = &mut SelectionContext::new(infcx); let cause = ObligationCause::misc(DUMMY_SP, DUMMY_NODE_ID); - let Normalized { - value: answer, - obligations, - } = traits::normalize_projection_type(selcx, param_env, goal, cause, 0); + let mut obligations = vec![]; + let answer = + traits::normalize_projection_type(selcx, param_env, goal, cause, 0, &mut obligations); fulfill_cx.register_predicate_obligations(infcx, obligations); // Now that we have fulfilled as much as we can, create a solution diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs index e1ce6073ce436..4274e5c1e1f75 100644 --- a/src/librustc_typeck/check/autoderef.rs +++ b/src/librustc_typeck/check/autoderef.rs @@ -129,20 +129,20 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> { } let mut selcx = traits::SelectionContext::new(self.fcx); - let normalized = traits::normalize_projection_type(&mut selcx, - self.fcx.param_env, - ty::ProjectionTy::from_ref_and_name( - tcx, - trait_ref, - Symbol::intern("Target"), - ), - cause, - 0); - - debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized); - self.obligations.extend(normalized.obligations); - - Some(self.fcx.resolve_type_vars_if_possible(&normalized.value)) + let normalized_ty = traits::normalize_projection_type(&mut selcx, + self.fcx.param_env, + ty::ProjectionTy::from_ref_and_name( + tcx, + trait_ref, + Symbol::intern("Target"), + ), + cause, + 0, + &mut self.obligations); + + debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized_ty); + + Some(self.fcx.resolve_type_vars_if_possible(&normalized_ty)) } /// Returns the final type, generating an error if it is an diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs index f29cc75664da8..b22098408a332 100644 --- a/src/libsyntax_ext/format.rs +++ b/src/libsyntax_ext/format.rs @@ -767,9 +767,12 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, } if !parser.errors.is_empty() { - let (err, note) = parser.errors.remove(0); - let mut e = cx.ecx.struct_span_err(cx.fmtsp, &format!("invalid format string: {}", err)); - if let Some(note) = note { + let err = parser.errors.remove(0); + let sp = cx.fmtsp.from_inner_byte_pos(err.start, err.end); + let mut e = cx.ecx.struct_span_err(sp, &format!("invalid format string: {}", + err.description)); + e.span_label(sp, err.label + " in format string"); + if let Some(note) = err.note { e.note(¬e); } e.emit(); diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index 26ab5d0a34ba5..73f0e6a60181f 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -428,6 +428,13 @@ impl Span { ) } + pub fn from_inner_byte_pos(self, start: usize, end: usize) -> Span { + let span = self.data(); + Span::new(span.lo + BytePos::from_usize(start), + span.lo + BytePos::from_usize(end), + span.ctxt) + } + #[inline] pub fn apply_mark(self, mark: Mark) -> Span { let span = self.data(); diff --git a/src/libtest/formatters/pretty.rs b/src/libtest/formatters/pretty.rs index 8e5fa00b5f27d..f94780682a0c0 100644 --- a/src/libtest/formatters/pretty.rs +++ b/src/libtest/formatters/pretty.rs @@ -101,7 +101,7 @@ impl PrettyFormatter { for &(ref f, ref stdout) in &state.not_failures { successes.push(f.name.to_string()); if !stdout.is_empty() { - stdouts.push_str(&format!("---- {} stdout ----\n\t", f.name)); + stdouts.push_str(&format!("---- {} stdout ----\n", f.name)); let output = String::from_utf8_lossy(stdout); stdouts.push_str(&output); stdouts.push_str("\n"); @@ -127,7 +127,7 @@ impl PrettyFormatter { for &(ref f, ref stdout) in &state.failures { failures.push(f.name.to_string()); if !stdout.is_empty() { - fail_out.push_str(&format!("---- {} stdout ----\n\t", f.name)); + fail_out.push_str(&format!("---- {} stdout ----\n", f.name)); let output = String::from_utf8_lossy(stdout); fail_out.push_str(&output); fail_out.push_str("\n"); diff --git a/src/libtest/formatters/terse.rs b/src/libtest/formatters/terse.rs index 85286027d6921..22a06b9f605db 100644 --- a/src/libtest/formatters/terse.rs +++ b/src/libtest/formatters/terse.rs @@ -105,7 +105,7 @@ impl TerseFormatter { for &(ref f, ref stdout) in &state.not_failures { successes.push(f.name.to_string()); if !stdout.is_empty() { - stdouts.push_str(&format!("---- {} stdout ----\n\t", f.name)); + stdouts.push_str(&format!("---- {} stdout ----\n", f.name)); let output = String::from_utf8_lossy(stdout); stdouts.push_str(&output); stdouts.push_str("\n"); @@ -131,7 +131,7 @@ impl TerseFormatter { for &(ref f, ref stdout) in &state.failures { failures.push(f.name.to_string()); if !stdout.is_empty() { - fail_out.push_str(&format!("---- {} stdout ----\n\t", f.name)); + fail_out.push_str(&format!("---- {} stdout ----\n", f.name)); let output = String::from_utf8_lossy(stdout); fail_out.push_str(&output); fail_out.push_str("\n"); diff --git a/src/test/COMPILER_TESTS.md b/src/test/COMPILER_TESTS.md index 29f1e2e5b781e..7dabb1bddea77 100644 --- a/src/test/COMPILER_TESTS.md +++ b/src/test/COMPILER_TESTS.md @@ -140,13 +140,9 @@ check that the test compiles successfully. ### Editing and updating the reference files If you have changed the compiler's output intentionally, or you are -making a new test, you can use the script `ui/update-references.sh` to -update the references. When you run the test framework, it will report -various errors: in those errors is a command you can use to run the -`ui/update-references.sh` script, which will then copy over the files -from the build directory and use them as the new reference. You can -also just run `ui/update-all-references.sh`. In both cases, you can run -the script with `--help` to get a help message. +making a new test, you can pass `--bless` to the command you used to +run the tests. This will then copy over the files +from the build directory and use them as the new reference. ### Normalization diff --git a/src/test/ui/E0508.ast.nll.stderr b/src/test/ui/E0508.ast.nll.stderr new file mode 100644 index 0000000000000..28403644a234a --- /dev/null +++ b/src/test/ui/E0508.ast.nll.stderr @@ -0,0 +1,9 @@ +error[E0508]: cannot move out of type `[NonCopy; 1]`, a non-copy array + --> $DIR/E0508.rs:18:18 + | +LL | let _value = array[0]; //[ast]~ ERROR [E0508] + | ^^^^^^^^ cannot move out of here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0508`. diff --git a/src/test/ui/E0508.ast.stderr b/src/test/ui/E0508.ast.stderr new file mode 100644 index 0000000000000..5878b795b771c --- /dev/null +++ b/src/test/ui/E0508.ast.stderr @@ -0,0 +1,12 @@ +error[E0508]: cannot move out of type `[NonCopy; 1]`, a non-copy array + --> $DIR/E0508.rs:18:18 + | +LL | let _value = array[0]; //[ast]~ ERROR [E0508] + | ^^^^^^^^ + | | + | cannot move out of here + | help: consider using a reference instead: `&array[0]` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0508`. diff --git a/src/test/ui/E0508.mir.stderr b/src/test/ui/E0508.mir.stderr new file mode 100644 index 0000000000000..28403644a234a --- /dev/null +++ b/src/test/ui/E0508.mir.stderr @@ -0,0 +1,9 @@ +error[E0508]: cannot move out of type `[NonCopy; 1]`, a non-copy array + --> $DIR/E0508.rs:18:18 + | +LL | let _value = array[0]; //[ast]~ ERROR [E0508] + | ^^^^^^^^ cannot move out of here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0508`. diff --git a/src/test/ui/E0508.rs b/src/test/ui/E0508.rs new file mode 100644 index 0000000000000..0c3dce6b0346a --- /dev/null +++ b/src/test/ui/E0508.rs @@ -0,0 +1,20 @@ +// Copyright 2016 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. + +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + +struct NonCopy; + +fn main() { + let array = [NonCopy; 1]; + let _value = array[0]; //[ast]~ ERROR [E0508] + //[mir]~^ ERROR [E0508] +} diff --git a/src/test/ui/fmt/format-string-error.rs b/src/test/ui/fmt/format-string-error.rs index ec715b3f0ba62..5b13686240e7c 100644 --- a/src/test/ui/fmt/format-string-error.rs +++ b/src/test/ui/fmt/format-string-error.rs @@ -12,5 +12,14 @@ fn main() { println!("{"); println!("{{}}"); println!("}"); + let _ = format!("{_foo}", _foo = 6usize); + //~^ ERROR invalid format string: invalid argument name `_foo` + let _ = format!("{_}", _ = 6usize); + //~^ ERROR invalid format string: invalid argument name `_` + let _ = format!("{"); + //~^ ERROR invalid format string: expected `'}'` but string was terminated + let _ = format!("}"); + //~^ ERROR invalid format string: unmatched `}` found + let _ = format!("{\\}"); + //~^ ERROR invalid format string: expected `'}'`, found `'\\'` } - diff --git a/src/test/ui/fmt/format-string-error.stderr b/src/test/ui/fmt/format-string-error.stderr index a7a66722e527f..ff766ddc8fa67 100644 --- a/src/test/ui/fmt/format-string-error.stderr +++ b/src/test/ui/fmt/format-string-error.stderr @@ -2,7 +2,7 @@ error: invalid format string: expected `'}'` but string was terminated --> $DIR/format-string-error.rs:12:5 | LL | println!("{"); - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ expected `'}'` in format string | = note: if you intended to print `{`, you can escape it using `{{` = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) @@ -11,10 +11,48 @@ error: invalid format string: unmatched `}` found --> $DIR/format-string-error.rs:14:5 | LL | println!("}"); - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ unmatched `}` in format string | = note: if you intended to print `}`, you can escape it using `}}` = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) -error: aborting due to 2 previous errors +error: invalid format string: invalid argument name `_foo` + --> $DIR/format-string-error.rs:15:23 + | +LL | let _ = format!("{_foo}", _foo = 6usize); + | ^^^^ invalid argument name in format string + | + = note: argument names cannot start with an underscore + +error: invalid format string: invalid argument name `_` + --> $DIR/format-string-error.rs:17:23 + | +LL | let _ = format!("{_}", _ = 6usize); + | ^ invalid argument name in format string + | + = note: argument names cannot start with an underscore + +error: invalid format string: expected `'}'` but string was terminated + --> $DIR/format-string-error.rs:19:23 + | +LL | let _ = format!("{"); + | ^ expected `'}'` in format string + | + = note: if you intended to print `{`, you can escape it using `{{` + +error: invalid format string: unmatched `}` found + --> $DIR/format-string-error.rs:21:22 + | +LL | let _ = format!("}"); + | ^ unmatched `}` in format string + | + = note: if you intended to print `}`, you can escape it using `}}` + +error: invalid format string: expected `'}'`, found `'/'` + --> $DIR/format-string-error.rs:23:23 + | +LL | let _ = format!("{/}"); + | ^ expected `}` in format string + +error: aborting due to 7 previous errors diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 812e9c5f39d87..b2ce5ce52f719 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -118,6 +118,9 @@ impl CompareMode { #[derive(Clone)] pub struct Config { + /// Whether to overwrite stderr/stdout files instead of complaining about changes in output + pub bless: bool, + /// The library paths required for running the compiler pub compile_lib_path: PathBuf, diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 42a2cdfa55b5a..2bfc1ece09590 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -166,6 +166,11 @@ pub fn parse_config(args: Vec) -> Config { "FLAGS", ) .optflag("", "verbose", "run tests verbosely, showing all output") + .optflag( + "", + "bless", + "overwrite stderr/stdout files instead of complaining about a mismatch", + ) .optflag( "", "quiet", @@ -290,6 +295,7 @@ pub fn parse_config(args: Vec) -> Config { let src_base = opt_path(matches, "src-base"); let run_ignored = matches.opt_present("ignored"); Config { + bless: matches.opt_present("bless"), compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")), run_lib_path: make_absolute(opt_path(matches, "run-lib-path")), rustc_path: opt_path(matches, "rustc-path"), diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 780c8122734e6..49d3dec0a58b2 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2596,15 +2596,13 @@ impl<'test> TestCx<'test> { } if errors > 0 { - println!("To update references, run this command from build directory:"); + println!("To update references, rerun the tests and pass the `--bless` flag"); let relative_path_to_file = self.testpaths .relative_dir .join(self.testpaths.file.file_name().unwrap()); println!( - "{}/update-references.sh '{}' '{}'", - self.config.src_base.display(), - self.config.build_base.display(), - relative_path_to_file.display() + "To only update this specific test, also pass `--test-args {}`", + relative_path_to_file.display(), ); self.fatal_proc_rec( &format!("{} errors occurred comparing output.", errors), @@ -2926,29 +2924,31 @@ impl<'test> TestCx<'test> { return 0; } - if expected.is_empty() { - println!("normalized {}:\n{}\n", kind, actual); - } else { - println!("diff of {}:\n", kind); - let diff_results = make_diff(expected, actual, 3); - for result in diff_results { - let mut line_number = result.line_number; - for line in result.lines { - match line { - DiffLine::Expected(e) => { - println!("-\t{}", e); - line_number += 1; - } - DiffLine::Context(c) => { - println!("{}\t{}", line_number, c); - line_number += 1; - } - DiffLine::Resulting(r) => { - println!("+\t{}", r); + if !self.config.bless { + if expected.is_empty() { + println!("normalized {}:\n{}\n", kind, actual); + } else { + println!("diff of {}:\n", kind); + let diff_results = make_diff(expected, actual, 3); + for result in diff_results { + let mut line_number = result.line_number; + for line in result.lines { + match line { + DiffLine::Expected(e) => { + println!("-\t{}", e); + line_number += 1; + } + DiffLine::Context(c) => { + println!("{}\t{}", line_number, c); + line_number += 1; + } + DiffLine::Resulting(r) => { + println!("+\t{}", r); + } } } + println!(""); } - println!(""); } } @@ -2958,19 +2958,47 @@ impl<'test> TestCx<'test> { .with_extra_extension(mode) .with_extra_extension(kind); - match File::create(&output_file).and_then(|mut f| f.write_all(actual.as_bytes())) { - Ok(()) => {} - Err(e) => self.fatal(&format!( - "failed to write {} to `{}`: {}", + let mut files = vec![output_file]; + if self.config.bless { + files.push(expected_output_path( + self.testpaths, + self.revision, + &self.config.compare_mode, kind, - output_file.display(), - e - )), + )); + } + + for output_file in &files { + if actual.is_empty() { + if let Err(e) = ::std::fs::remove_file(output_file) { + self.fatal(&format!( + "failed to delete `{}`: {}", + output_file.display(), + e, + )); + } + } else { + match File::create(&output_file).and_then(|mut f| f.write_all(actual.as_bytes())) { + Ok(()) => {} + Err(e) => self.fatal(&format!( + "failed to write {} to `{}`: {}", + kind, + output_file.display(), + e + )), + } + } } println!("\nThe actual {0} differed from the expected {0}.", kind); - println!("Actual {} saved to {}", kind, output_file.display()); - 1 + for output_file in files { + println!("Actual {} saved to {}", kind, output_file.display()); + } + if self.config.bless { + 0 + } else { + 1 + } } fn create_stamp(&self) {