1
Fork 0

Auto merge of #50847 - Mark-Simulacrum:rollup, r=Mark-Simulacrum

Rollup of 10 pull requests

Successful merges:

 - #50387 (Remove leftover tab in libtest outputs)
 - #50553 (Add Option::xor method)
 - #50610 (Improve format string errors)
 - #50649 (Tweak `nearest_common_ancestor()`.)
 - #50790 (Fix grammar documentation wrt Unicode identifiers)
 - #50791 (Fix null exclusions in grammar docs)
 - #50806 (Add `bless` x.py subcommand for easy ui test replacement)
 - #50818 (Speed up `opt_normalize_projection_type`)
 - #50837 (Revert #49767)
 - #50839 (Make sure people know the book is free oline)

Failed merges:
This commit is contained in:
bors 2018-05-18 02:58:13 +00:00
commit dfc07a48f6
31 changed files with 591 additions and 687 deletions

View file

@ -297,7 +297,12 @@ fn main() {
} }
if verbose > 1 { 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!("sysroot: {:?}", sysroot);
eprintln!("libdir: {:?}", libdir); eprintln!("libdir: {:?}", libdir);
} }

View file

@ -1460,6 +1460,7 @@ mod __test {
rustc_args: vec![], rustc_args: vec![],
fail_fast: true, fail_fast: true,
doc_tests: DocTests::No, doc_tests: DocTests::No,
bless: false,
}; };
let build = Build::new(config); let build = Build::new(config);

View file

@ -59,6 +59,8 @@ pub enum Subcommand {
}, },
Test { Test {
paths: Vec<PathBuf>, paths: Vec<PathBuf>,
/// Whether to automatically update stderr/stdout files
bless: bool,
test_args: Vec<String>, test_args: Vec<String>,
rustc_args: Vec<String>, rustc_args: Vec<String>,
fail_fast: bool, fail_fast: bool,
@ -173,6 +175,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`");
); );
opts.optflag("", "no-doc", "do not run doc tests"); opts.optflag("", "no-doc", "do not run doc tests");
opts.optflag("", "doc", "only 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"); }, "bench" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); },
"clean" => { opts.optflag("", "all", "clean all build artifacts"); }, "clean" => { opts.optflag("", "all", "clean all build artifacts"); },
@ -258,6 +261,7 @@ Arguments:
./x.py test src/test/run-pass ./x.py test src/test/run-pass
./x.py test src/libstd --test-args hash_map ./x.py test src/libstd --test-args hash_map
./x.py test src/libstd --stage 0 ./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 If no arguments are passed then the complete artifacts for that stage are
compiled and tested. compiled and tested.
@ -322,6 +326,7 @@ Arguments:
"test" => { "test" => {
Subcommand::Test { Subcommand::Test {
paths, paths,
bless: matches.opt_present("bless"),
test_args: matches.opt_strs("test-args"), test_args: matches.opt_strs("test-args"),
rustc_args: matches.opt_strs("rustc-args"), rustc_args: matches.opt_strs("rustc-args"),
fail_fast: !matches.opt_present("no-fail-fast"), fail_fast: !matches.opt_present("no-fail-fast"),
@ -424,6 +429,13 @@ impl Subcommand {
_ => DocTests::Yes, _ => DocTests::Yes,
} }
} }
pub fn bless(&self) -> bool {
match *self {
Subcommand::Test { bless, .. } => bless,
_ => false,
}
}
} }
fn split(s: Vec<String>) -> Vec<String> { fn split(s: Vec<String>) -> Vec<String> {

View file

@ -47,6 +47,16 @@ pub enum TestKind {
Bench, Bench,
} }
impl From<Kind> 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 { impl TestKind {
// Return the cargo subcommand for this test kind // Return the cargo subcommand for this test kind
fn subcommand(self) -> &'static str { fn subcommand(self) -> &'static str {
@ -951,6 +961,10 @@ impl Step for Compiletest {
cmd.arg("--host").arg(&*compiler.host); cmd.arg("--host").arg(&*compiler.host);
cmd.arg("--llvm-filecheck").arg(builder.llvm_filecheck(builder.config.build)); 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 { if let Some(ref nodejs) = builder.config.nodejs {
cmd.arg("--nodejs").arg(nodejs); cmd.arg("--nodejs").arg(nodejs);
} }
@ -1342,13 +1356,7 @@ impl Step for CrateLibrustc {
for krate in builder.in_tree_crates("rustc-main") { for krate in builder.in_tree_crates("rustc-main") {
if run.path.ends_with(&krate.path) { if run.path.ends_with(&krate.path) {
let test_kind = if builder.kind == Kind::Test { let test_kind = builder.kind.into();
TestKind::Test
} else if builder.kind == Kind::Bench {
TestKind::Bench
} else {
panic!("unexpected builder.kind in crate: {:?}", builder.kind);
};
builder.ensure(CrateLibrustc { builder.ensure(CrateLibrustc {
compiler, compiler,
@ -1394,13 +1402,7 @@ impl Step for CrateNotDefault {
let builder = run.builder; let builder = run.builder;
let compiler = builder.compiler(builder.top_stage, run.host); let compiler = builder.compiler(builder.top_stage, run.host);
let test_kind = if builder.kind == Kind::Test { let test_kind = builder.kind.into();
TestKind::Test
} else if builder.kind == Kind::Bench {
TestKind::Bench
} else {
panic!("unexpected builder.kind in crate: {:?}", builder.kind);
};
builder.ensure(CrateNotDefault { builder.ensure(CrateNotDefault {
compiler, compiler,
@ -1461,13 +1463,7 @@ impl Step for Crate {
let compiler = builder.compiler(builder.top_stage, run.host); let compiler = builder.compiler(builder.top_stage, run.host);
let make = |mode: Mode, krate: &CargoCrate| { let make = |mode: Mode, krate: &CargoCrate| {
let test_kind = if builder.kind == Kind::Test { let test_kind = builder.kind.into();
TestKind::Test
} else if builder.kind == Kind::Bench {
TestKind::Bench
} else {
panic!("unexpected builder.kind in crate: {:?}", builder.kind);
};
builder.ensure(Crate { builder.ensure(Crate {
compiler, compiler,
@ -1625,13 +1621,7 @@ impl Step for CrateRustdoc {
fn make_run(run: RunConfig) { fn make_run(run: RunConfig) {
let builder = run.builder; let builder = run.builder;
let test_kind = if builder.kind == Kind::Test { let test_kind = builder.kind.into();
TestKind::Test
} else if builder.kind == Kind::Bench {
TestKind::Bench
} else {
panic!("unexpected builder.kind in crate: {:?}", builder.kind);
};
builder.ensure(CrateRustdoc { builder.ensure(CrateRustdoc {
host: run.host, host: run.host,

View file

@ -101,29 +101,24 @@ properties: `ident`, `non_null`, `non_eol`, `non_single_quote` and
### Identifiers ### 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: the following form:
[^non_ascii_idents]: Non-ASCII characters in identifiers are currently feature - The first character is in one of the following ranges `U+0041` to `U+005A`
gated. This is expected to improve soon. ("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` as long as the identifier does _not_ occur in the set of [keywords](#keywords).
- 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.
### Delimiter-restricted productions ### Delimiter-restricted productions
Some productions are defined by exclusion of particular Unicode characters: Some productions are defined by exclusion of particular Unicode characters:
- `non_null` is any single Unicode character aside from `U+0000` (null) - `non_null` is any single Unicode character aside from `U+0000` (null)
- `non_eol` is `non_null` restricted to exclude `U+000A` (`'\n'`) - `non_eol` is any single Unicode character aside from `U+000A` (`'\n'`)
- `non_single_quote` is `non_null` restricted to exclude `U+0027` (`'`) - `non_single_quote` is any single Unicode character aside from `U+0027` (`'`)
- `non_double_quote` is `non_null` restricted to exclude `U+0022` (`"`) - `non_double_quote` is any single Unicode character aside from `U+0022` (`"`)
## Comments ## Comments

View file

@ -1,3 +1,3 @@
% The Rust Tutorial % 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!

View file

@ -962,122 +962,59 @@ extern "rust-intrinsic" {
/// value is not necessarily valid to be used to actually access memory. /// value is not necessarily valid to be used to actually access memory.
pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T; pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T;
/// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source /// Copies `count * size_of<T>` bytes from `src` to `dst`. The source
/// and destination must *not* overlap. /// 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_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
/// ///
/// # Safety /// # Safety
/// ///
/// Behavior is undefined if any of the following conditions are violated: /// Beyond requiring that the program must be allowed to access both regions
/// /// of memory, it is Undefined Behavior for source and destination to
/// * The region of memory which begins at `src` and has a length of /// overlap. Care must also be taken with the ownership of `src` and
/// `count * size_of::<T>()` bytes must be *both* valid and initialized. /// `dst`. This method semantically moves the values of `src` into `dst`.
/// /// However it does not drop the contents of `dst`, or prevent the contents
/// * The region of memory which begins at `dst` and has a length of /// of `src` from being dropped or used.
/// `count * size_of::<T>()` 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
/// ///
/// # Examples /// # Examples
/// ///
/// Manually implement [`Vec::append`]: /// A safe swap function:
/// ///
/// ``` /// ```
/// use std::mem;
/// use std::ptr; /// use std::ptr;
/// ///
/// /// Moves all the elements of `src` into `dst`, leaving `src` empty. /// # #[allow(dead_code)]
/// fn append<T>(dst: &mut Vec<T>, src: &mut Vec<T>) { /// fn swap<T>(x: &mut T, y: &mut T) {
/// 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);
///
/// unsafe { /// unsafe {
/// // The call to offset is always safe because `Vec` will never /// // Give ourselves some scratch space to work with
/// // allocate more than `isize::MAX` bytes. /// let mut t: T = mem::uninitialized();
/// 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 /// // Perform the swap, `&mut` pointers never alias
/// // not alias, and two different vectors cannot own the same /// ptr::copy_nonoverlapping(x, &mut t, 1);
/// // memory. /// ptr::copy_nonoverlapping(y, x, 1);
/// ptr::copy_nonoverlapping(src, dst, src_len); /// ptr::copy_nonoverlapping(&t, y, 1);
/// }
/// ///
/// unsafe { /// // y and t now point to the same thing, but we need to completely forget `t`
/// // Truncate `src` without dropping its contents. /// // because it's no longer relevant.
/// src.set_len(0); /// mem::forget(t);
///
/// // Notify `dst` that it now holds the contents of `src`.
/// dst.set_len(dst_len + src_len);
/// } /// }
/// } /// }
///
/// 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")] #[stable(feature = "rust1", since = "1.0.0")]
pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize); pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
/// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source /// Copies `count * size_of<T>` bytes from `src` to `dst`. The source
/// and destination may overlap. /// and destination may overlap.
/// ///
/// If the source and destination will *never* overlap, /// `copy` is semantically equivalent to C's `memmove`.
/// [`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
/// ///
/// # Safety /// # Safety
/// ///
/// Behavior is undefined if any of the following conditions are violated: /// Care must be taken with the ownership of `src` and `dst`.
/// /// This method semantically moves the values of `src` into `dst`.
/// * The region of memory which begins at `src` and has a length of /// However it does not drop the contents of `dst`, or prevent the contents of `src`
/// `count * size_of::<T>()` bytes must be *both* valid and initialized. /// from being dropped or used.
///
/// * The region of memory which begins at `dst` and has a length of
/// `count * size_of::<T>()` 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
/// ///
/// # Examples /// # Examples
/// ///
@ -1094,34 +1031,15 @@ extern "rust-intrinsic" {
/// dst /// dst
/// } /// }
/// ``` /// ```
///
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub fn copy<T>(src: *const T, dst: *mut T, count: usize); pub fn copy<T>(src: *const T, dst: *mut T, count: usize);
/// Sets `count * size_of::<T>()` bytes of memory starting at `dst` to /// Invokes memset on the specified pointer, setting `count * size_of::<T>()`
/// `val`. /// 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.
/// ///
/// # Examples /// # Examples
/// ///
/// Basic usage:
///
/// ``` /// ```
/// use std::ptr; /// use std::ptr;
/// ///
@ -1132,23 +1050,6 @@ extern "rust-intrinsic" {
/// } /// }
/// assert_eq!(vec, [b'a', b'a', 0, 0]); /// 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<T>` with
/// // a null pointer.
/// ptr::write_bytes(&mut v, 0, mem::size_of::<Box<i32>>());
/// }
///
/// // At this point, using or dropping `v` results in undefined behavior.
/// // v = Box::new(0i32); // ERROR
/// ```
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub fn write_bytes<T>(dst: *mut T, val: u8, count: usize); pub fn write_bytes<T>(dst: *mut T, val: u8, count: usize);

View file

@ -705,6 +705,42 @@ impl<T> Option<T> {
} }
} }
/// 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<u32> = None;
/// assert_eq!(x.xor(y), Some(2));
///
/// let x: Option<u32> = 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<u32> = None;
/// let y: Option<u32> = None;
/// assert_eq!(x.xor(y), None);
/// ```
#[inline]
#[unstable(feature = "option_xor", issue = "50512")]
pub fn xor(self, optb: Option<T>) -> Option<T> {
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 // Entry-like operations to insert if None and return a reference
///////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////

View file

@ -10,7 +10,7 @@
// FIXME: talk about offset, copy_memory, copy_nonoverlapping_memory // 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).* //! *[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. /// Executes the destructor (if any) of the pointed-to value.
/// ///
/// This is semantically equivalent to calling [`ptr::read`] and discarding /// This has two use cases:
/// the result, but has the following advantages:
/// ///
/// * It is *required* to use `drop_in_place` to drop unsized types like /// * 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 /// trait objects, because they can't be read out onto the stack and
/// dropped normally. /// 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), /// 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 /// as the compiler doesn't need to prove that it's sound to elide the
/// copy. /// copy.
/// ///
/// [`ptr::read`]: ../ptr/fn.read.html
///
/// # Safety /// # Safety
/// ///
/// Behavior is undefined if any of the following conditions are violated: /// This has all the same safety problems as `ptr::read` with respect to
/// /// invalid pointers, types, and double drops.
/// * `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());
/// ```
#[stable(feature = "drop_in_place", since = "1.8.0")] #[stable(feature = "drop_in_place", since = "1.8.0")]
#[lang = "drop_in_place"] #[lang = "drop_in_place"]
#[allow(unconditional_recursion)] #[allow(unconditional_recursion)]
@ -134,25 +93,17 @@ pub const fn null_mut<T>() -> *mut T { 0 as *mut T }
/// Swaps the values at two mutable locations of the same type, without /// Swaps the values at two mutable locations of the same type, without
/// deinitializing either. /// deinitializing either.
/// ///
/// But for the following two exceptions, this function is semantically /// The values pointed at by `x` and `y` may overlap, unlike `mem::swap` which
/// equivalent to [`mem::swap`]: /// is otherwise equivalent. If the values do overlap, then the overlapping
/// /// region of memory from `x` will be used. This is demonstrated in the
/// * It operates on raw pointers instead of references. When references are /// examples section below.
/// 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
/// ///
/// # Safety /// # 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. /// Ensure that these pointers are valid before calling `swap`.
///
/// * `x` and `y` must be properly aligned.
/// ///
/// # Examples /// # 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 /// Replaces the value at `dest` with `src`, returning the old
/// dropping either. /// 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
/// ///
/// # Safety /// # Safety
/// ///
/// Behavior is undefined if any of the following conditions are violated: /// This is only unsafe because it accepts a raw pointer.
/// /// Otherwise, this operation is identical to `mem::replace`.
/// * `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']);
/// ```
#[inline] #[inline]
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub unsafe fn replace<T>(dest: *mut T, mut src: T) -> T { pub unsafe fn replace<T>(dest: *mut T, mut src: T) -> T {
@ -333,23 +258,14 @@ pub unsafe fn replace<T>(dest: *mut T, mut src: T) -> T {
/// ///
/// # Safety /// # 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. /// The pointer must be aligned; use `read_unaligned` if that is not the case.
///
/// * `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
/// ///
/// # Examples /// # Examples
/// ///
@ -363,44 +279,6 @@ pub unsafe fn replace<T>(dest: *mut T, mut src: T) -> T {
/// assert_eq!(std::ptr::read(y), 12); /// assert_eq!(std::ptr::read(y), 12);
/// } /// }
/// ``` /// ```
///
/// Manually implement [`mem::swap`]:
///
/// ```
/// use std::ptr;
///
/// fn swap<T>(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] #[inline]
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub unsafe fn read<T>(src: *const T) -> T { pub unsafe fn read<T>(src: *const T) -> T {
@ -412,62 +290,28 @@ pub unsafe fn read<T>(src: *const T) -> T {
/// Reads the value from `src` without moving it. This leaves the /// Reads the value from `src` without moving it. This leaves the
/// memory in `src` unchanged. /// memory in `src` unchanged.
/// ///
/// Unlike [`read`], `read_unaligned` works with unaligned pointers. /// Unlike `read`, the pointer may be unaligned.
///
/// [`read`]: ./fn.read.html
/// ///
/// # Safety /// # 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`.
/// * `src` must point to valid, initialized memory. /// 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`,
/// Additionally, if `T` is not [`Copy`], only the returned value *or* the /// `write_bytes`, or `copy`). Note that `*src = foo` counts as a use
/// pointed-to value can be used or dropped after calling `read_unaligned`. /// because it will attempt to drop the value previously at `*src`.
/// `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
/// ///
/// # Examples /// # 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)] /// unsafe {
/// #[derive(Default)] /// assert_eq!(std::ptr::read_unaligned(y), 12);
/// struct Packed {
/// _padding: u8,
/// unaligned: u32,
/// } /// }
///
/// 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] #[inline]
#[stable(feature = "ptr_unaligned", since = "1.17.0")] #[stable(feature = "ptr_unaligned", since = "1.17.0")]
@ -482,7 +326,11 @@ pub unsafe fn read_unaligned<T>(src: *const T) -> T {
/// Overwrites a memory location with the given value without reading or /// Overwrites a memory location with the given value without reading or
/// dropping the old value. /// 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 /// allocations or resources, so care must be taken not to overwrite an object
/// that should be dropped. /// that should be dropped.
/// ///
@ -490,20 +338,9 @@ pub unsafe fn read_unaligned<T>(src: *const T) -> T {
/// location pointed to by `dst`. /// location pointed to by `dst`.
/// ///
/// This is appropriate for initializing uninitialized memory, or overwriting /// This is appropriate for initializing uninitialized memory, or overwriting
/// memory that has previously been [`read`] from. /// memory that has previously been `read` from.
/// ///
/// [`read`]: ./fn.read.html /// The pointer must be aligned; use `write_unaligned` if that is not the case.
///
/// # Safety
///
/// 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
/// ///
/// # Examples /// # Examples
/// ///
@ -519,30 +356,6 @@ pub unsafe fn read_unaligned<T>(src: *const T) -> T {
/// assert_eq!(std::ptr::read(y), 12); /// assert_eq!(std::ptr::read(y), 12);
/// } /// }
/// ``` /// ```
///
/// Manually implement [`mem::swap`]:
///
/// ```
/// use std::ptr;
///
/// fn swap<T>(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] #[inline]
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub unsafe fn write<T>(dst: *mut T, src: T) { pub unsafe fn write<T>(dst: *mut T, src: T) {
@ -552,58 +365,36 @@ pub unsafe fn write<T>(dst: *mut T, src: T) {
/// Overwrites a memory location with the given value without reading or /// Overwrites a memory location with the given value without reading or
/// dropping the old value. /// dropping the old value.
/// ///
/// Unlike [`write`], the pointer may be unaligned. /// Unlike `write`, the pointer may be unaligned.
/// ///
/// `write_unaligned` does not drop the contents of `dst`. This is safe, but it /// # Safety
/// 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 /// Additionally, it does not drop `src`. Semantically, `src` is moved into the
/// location pointed to by `dst`. /// location pointed to by `dst`.
/// ///
/// This is appropriate for initializing uninitialized memory, or overwriting /// This is appropriate for initializing uninitialized memory, or overwriting
/// memory that has previously been read with [`read_unaligned`]. /// memory that has previously been `read` from.
///
/// [`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.
/// ///
/// # Examples /// # Examples
/// ///
/// Access fields in a packed struct: /// Basic usage:
/// ///
/// ``` /// ```
/// use std::{mem, ptr}; /// let mut x = 0;
/// /// let y = &mut x as *mut i32;
/// #[repr(packed, C)] /// let z = 12;
/// #[derive(Default)]
/// struct Packed {
/// _padding: u8,
/// unaligned: u32,
/// }
///
/// let v = 0x01020304;
/// let mut x: Packed = unsafe { mem::zeroed() };
/// ///
/// unsafe { /// unsafe {
/// // Take a reference to a 32-bit integer which is not aligned. /// std::ptr::write_unaligned(y, z);
/// let unaligned = &mut x.unaligned; /// assert_eq!(std::ptr::read_unaligned(y), 12);
///
/// // 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);
/// } /// }
/// /// ```
/// // Accessing unaligned values directly is safe.
/// assert!(x.unaligned == v);
#[inline] #[inline]
#[stable(feature = "ptr_unaligned", since = "1.17.0")] #[stable(feature = "ptr_unaligned", since = "1.17.0")]
pub unsafe fn write_unaligned<T>(dst: *mut T, src: T) { pub unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
@ -620,11 +411,6 @@ pub unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
/// to not be elided or reordered by the compiler across other volatile /// to not be elided or reordered by the compiler across other volatile
/// operations. /// operations.
/// ///
/// Memory read with `read_volatile` should almost always be written to using
/// [`write_volatile`].
///
/// [`write_volatile`]: ./fn.write_volatile.html
///
/// # Notes /// # Notes
/// ///
/// Rust does not currently have a rigorously and formally defined memory model, /// Rust does not currently have a rigorously and formally defined memory model,
@ -641,19 +427,12 @@ pub unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
/// ///
/// # Safety /// # 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`.
/// * `src` must point to valid, initialized memory. /// 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`,
/// * `src` must be properly aligned. /// `write_bytes`, or `copy`). Note that `*src = foo` counts as a use
/// /// because it will attempt to drop the value previously at `*src`.
/// 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
/// ///
/// # Examples /// # Examples
/// ///
@ -680,18 +459,6 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T {
/// to not be elided or reordered by the compiler across other volatile /// to not be elided or reordered by the compiler across other volatile
/// operations. /// 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 /// # Notes
/// ///
/// Rust does not currently have a rigorously and formally defined memory model, /// Rust does not currently have a rigorously and formally defined memory model,
@ -708,11 +475,14 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T {
/// ///
/// # Safety /// # 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 /// # Examples
/// ///

View file

@ -127,6 +127,14 @@ pub enum Count<'a> {
CountImplied, CountImplied,
} }
pub struct ParseError {
pub description: string::String,
pub note: Option<string::String>,
pub label: string::String,
pub start: usize,
pub end: usize,
}
/// The parser structure for interpreting the input format string. This is /// The parser structure for interpreting the input format string. This is
/// modeled as an iterator over `Piece` structures to form a stream of tokens /// modeled as an iterator over `Piece` structures to form a stream of tokens
/// being output. /// being output.
@ -137,7 +145,7 @@ pub struct Parser<'a> {
input: &'a str, input: &'a str,
cur: iter::Peekable<str::CharIndices<'a>>, cur: iter::Peekable<str::CharIndices<'a>>,
/// Error messages accumulated during parsing /// Error messages accumulated during parsing
pub errors: Vec<(string::String, Option<string::String>)>, pub errors: Vec<ParseError>,
/// Current position of implicit positional argument pointer /// Current position of implicit positional argument pointer
curarg: usize, curarg: usize,
} }
@ -160,12 +168,17 @@ impl<'a> Iterator for Parser<'a> {
} }
'}' => { '}' => {
self.cur.next(); self.cur.next();
let pos = pos + 1;
if self.consume('}') { if self.consume('}') {
Some(String(self.string(pos + 1))) Some(String(self.string(pos)))
} else { } else {
self.err_with_note("unmatched `}` found", self.err_with_note(
"if you intended to print `}`, \ "unmatched `}` found",
you can escape it using `}}`"); "unmatched `}`",
"if you intended to print `}`, you can escape it using `}}`",
pos,
pos,
);
None None
} }
} }
@ -191,15 +204,40 @@ impl<'a> Parser<'a> {
/// Notifies of an error. The message doesn't actually need to be of type /// 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 /// String, but I think it does when this eventually uses conditions so it
/// might as well start using it now. /// might as well start using it now.
fn err(&mut self, msg: &str) { fn err<S1: Into<string::String>, S2: Into<string::String>>(
self.errors.push((msg.to_owned(), None)); &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 /// 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 /// String, but I think it does when this eventually uses conditions so it
/// might as well start using it now. /// might as well start using it now.
fn err_with_note(&mut self, msg: &str, note: &str) { fn err_with_note<S1: Into<string::String>, S2: Into<string::String>, S3: Into<string::String>>(
self.errors.push((msg.to_owned(), Some(note.to_owned()))); &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 /// Optionally consumes the specified character. If the character is not at
@ -222,19 +260,26 @@ impl<'a> Parser<'a> {
/// found, an error is emitted. /// found, an error is emitted.
fn must_consume(&mut self, c: char) { fn must_consume(&mut self, c: char) {
self.ws(); self.ws();
if let Some(&(_, maybe)) = self.cur.peek() { if let Some(&(pos, maybe)) = self.cur.peek() {
if c == maybe { if c == maybe {
self.cur.next(); self.cur.next();
} else { } else {
self.err(&format!("expected `{:?}`, found `{:?}`", c, maybe)); self.err(format!("expected `{:?}`, found `{:?}`", c, maybe),
format!("expected `{}`", c),
pos + 1,
pos + 1);
} }
} else { } 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 == '}' { if c == '}' {
self.err_with_note(msg, 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 { } else {
self.err(msg); self.err(msg, format!("expected `{:?}`", c), pos, pos);
} }
} }
} }
@ -300,6 +345,15 @@ impl<'a> Parser<'a> {
} else { } else {
match self.cur.peek() { match self.cur.peek() {
Some(&(_, c)) if c.is_alphabetic() => Some(ArgumentNamed(self.word())), 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`. // This is an `ArgumentNext`.
// Record the fact and do the resolution after parsing the // Record the fact and do the resolution after parsing the

View file

@ -542,18 +542,6 @@ impl<'tcx> ScopeTree {
assert!(previous.is_none()); 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) { fn record_var_scope(&mut self, var: hir::ItemLocalId, lifetime: Scope) {
debug!("record_var_scope(sub={:?}, sup={:?})", var, lifetime); debug!("record_var_scope(sub={:?}, sup={:?})", var, lifetime);
assert!(var != lifetime.item_local_id()); 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 // 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 // chains (10s or 100s of scopes) that only differ by a few elements at
// the start. So this algorithm is faster. // the start. So this algorithm is faster.
let mut ma = Some(scope_a);
let mut mb = Some(scope_b); let mut ma = Some(&scope_a);
let mut seen_a: SmallVec<[Scope; 32]> = SmallVec::new(); let mut mb = Some(&scope_b);
let mut seen_b: SmallVec<[Scope; 32]> = SmallVec::new();
// A HashSet<Scope> 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 { loop {
if let Some(a) = ma { if let Some(a) = ma {
if seen_b.iter().position(|s| *s == a).is_some() { if seen_b.iter().any(|s| *s == a) {
return a; return *a;
} }
seen_a.push(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 let Some(b) = mb {
if seen_a.iter().position(|s| *s == b).is_some() { if seen_a.iter().any(|s| *s == b) {
return b; return *b;
} }
seen_b.push(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() { if ma.is_none() && mb.is_none() {
break; // No nearest common ancestor found.
} bug!();
};
fn outermost_scope(parent_map: &FxHashMap<Scope, Scope>, 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!()
} }
} }
} }

View file

@ -203,17 +203,19 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
obligation.cause.span, obligation.cause.span,
infer::LateBoundRegionConversionTime::HigherRankedType, infer::LateBoundRegionConversionTime::HigherRankedType,
data); data);
let normalized = super::normalize_projection_type( let mut obligations = vec![];
let normalized_ty = super::normalize_projection_type(
&mut selcx, &mut selcx,
obligation.param_env, obligation.param_env,
data.projection_ty, data.projection_ty,
obligation.cause.clone(), obligation.cause.clone(),
0 0,
&mut obligations
); );
if let Err(error) = self.at(&obligation.cause, obligation.param_env) 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 { values = Some(infer::ValuePairs::Types(ExpectedFound {
expected: normalized.value, expected: normalized_ty,
found: data.ty, found: data.ty,
})); }));
err_buf = error; err_buf = error;

View file

@ -161,19 +161,18 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
// FIXME(#20304) -- cache // FIXME(#20304) -- cache
let mut selcx = SelectionContext::new(infcx); let mut selcx = SelectionContext::new(infcx);
let normalized = project::normalize_projection_type(&mut selcx, let mut obligations = vec![];
let normalized_ty = project::normalize_projection_type(&mut selcx,
param_env, param_env,
projection_ty, projection_ty,
cause, cause,
0); 0,
&mut obligations);
self.register_predicate_obligations(infcx, obligations);
for obligation in normalized.obligations { debug!("normalize_projection_type: result={:?}", normalized_ty);
self.register_predicate_obligation(infcx, obligation);
}
debug!("normalize_projection_type: result={:?}", normalized.value); normalized_ty
normalized.value
} }
/// Requires that `ty` must implement the trait with `def_id` in /// Requires that `ty` must implement the trait with `def_id` in

View file

@ -225,12 +225,14 @@ fn project_and_unify_type<'cx, 'gcx, 'tcx>(
debug!("project_and_unify_type(obligation={:?})", debug!("project_and_unify_type(obligation={:?})",
obligation); obligation);
let Normalized { value: normalized_ty, mut obligations } = let mut obligations = vec![];
let normalized_ty =
match opt_normalize_projection_type(selcx, match opt_normalize_projection_type(selcx,
obligation.param_env, obligation.param_env,
obligation.predicate.projection_ty, obligation.predicate.projection_ty,
obligation.cause.clone(), obligation.cause.clone(),
obligation.recursion_depth) { obligation.recursion_depth,
&mut obligations) {
Some(n) => n, Some(n) => n,
None => return Ok(None), 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 // binder). It would be better to normalize in a
// binding-aware fashion. // binding-aware fashion.
let Normalized { value: normalized_ty, obligations } = let normalized_ty = normalize_projection_type(self.selcx,
normalize_projection_type(self.selcx,
self.param_env, self.param_env,
data.clone(), data.clone(),
self.cause.clone(), self.cause.clone(),
self.depth); self.depth,
debug!("AssociatedTypeNormalizer: depth={} normalized {:?} to {:?} \ &mut self.obligations);
with {} add'l obligations", debug!("AssociatedTypeNormalizer: depth={} normalized {:?} to {:?}, \
self.depth, ty, normalized_ty, obligations.len()); now with {} obligations",
self.obligations.extend(obligations); self.depth, ty, normalized_ty, self.obligations.len());
normalized_ty normalized_ty
} }
@ -471,10 +472,12 @@ pub fn normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
param_env: ty::ParamEnv<'tcx>, param_env: ty::ParamEnv<'tcx>,
projection_ty: ty::ProjectionTy<'tcx>, projection_ty: ty::ProjectionTy<'tcx>,
cause: ObligationCause<'tcx>, cause: ObligationCause<'tcx>,
depth: usize) depth: usize,
-> NormalizedTy<'tcx> obligations: &mut Vec<PredicateObligation<'tcx>>)
-> 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 || { .unwrap_or_else(move || {
// if we bottom out in ambiguity, create a type variable // if we bottom out in ambiguity, create a type variable
// and a deferred predicate to resolve this when more type // 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( let obligation = Obligation::with_depth(
cause, depth + 1, param_env, projection.to_predicate()); cause, depth + 1, param_env, projection.to_predicate());
Normalized { obligations.push(obligation);
value: ty_var, ty_var
obligations: vec![obligation]
}
}) })
} }
@ -501,13 +502,20 @@ pub fn normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
/// as Trait>::Item`. The result is always a type (and possibly /// as Trait>::Item`. The result is always a type (and possibly
/// additional obligations). Returns `None` in the case of ambiguity, /// additional obligations). Returns `None` in the case of ambiguity,
/// which indicates that there are unbound type variables. /// which indicates that there are unbound type variables.
///
/// This function used to return `Option<NormalizedTy<'tcx>>`, 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>( fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
selcx: &'a mut SelectionContext<'b, 'gcx, 'tcx>, selcx: &'a mut SelectionContext<'b, 'gcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>, param_env: ty::ParamEnv<'tcx>,
projection_ty: ty::ProjectionTy<'tcx>, projection_ty: ty::ProjectionTy<'tcx>,
cause: ObligationCause<'tcx>, cause: ObligationCause<'tcx>,
depth: usize) depth: usize,
-> Option<NormalizedTy<'tcx>> obligations: &mut Vec<PredicateObligation<'tcx>>)
-> Option<Ty<'tcx>>
{ {
let infcx = selcx.infcx(); let infcx = selcx.infcx();
@ -579,7 +587,9 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
projection_ty); projection_ty);
selcx.infcx().report_overflow_error(&obligation, false); 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 // If we find the value in the cache, then return it along
// with the obligations that went along with it. Note // with the obligations that went along with it. Note
// that, when using a fulfillment context, these // 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 // Once we have inferred everything we need to know, we
// can ignore the `obligations` from that point on. // can ignore the `obligations` from that point on.
if !infcx.any_unresolved_type_vars(&ty.value) { if !infcx.any_unresolved_type_vars(&ty.value) {
infcx.projection_cache.borrow_mut().complete(cache_key); infcx.projection_cache.borrow_mut().complete_normalized(cache_key, &ty);
ty.obligations = vec![]; // No need to extend `obligations`.
} else {
obligations.extend(ty.obligations);
} }
push_paranoid_cache_value_obligation(infcx, obligations.push(get_paranoid_cache_value_obligation(infcx,
param_env, param_env,
projection_ty, projection_ty,
cause, cause,
depth, depth));
&mut ty); return Some(ty.value);
return Some(ty);
} }
Err(ProjectionCacheEntry::Error) => { Err(ProjectionCacheEntry::Error) => {
debug!("opt_normalize_projection_type: \ debug!("opt_normalize_projection_type: \
found error"); 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); let obligation = Obligation::with_depth(cause.clone(), depth, param_env, projection_ty);
match project_type(selcx, &obligation) { 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 // if projection succeeded, then what we get out of this
// is also non-normalized (consider: it was derived from // is also non-normalized (consider: it was derived from
// an impl, where-clause etc) and hence we must // 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: \ debug!("opt_normalize_projection_type: \
projected_ty={:?} \ projected_ty={:?} \
depth={} \ depth={} \
obligations={:?}", projected_obligations={:?}",
projected_ty, projected_ty,
depth, depth,
obligations); projected_obligations);
let result = if projected_ty.has_projections() { let result = if projected_ty.has_projections() {
let mut normalizer = AssociatedTypeNormalizer::new(selcx, let mut normalizer = AssociatedTypeNormalizer::new(selcx,
@ -644,22 +657,22 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
normalized_ty, normalized_ty,
depth); depth);
obligations.extend(normalizer.obligations); projected_obligations.extend(normalizer.obligations);
Normalized { Normalized {
value: normalized_ty, value: normalized_ty,
obligations, obligations: projected_obligations,
} }
} else { } else {
Normalized { Normalized {
value: projected_ty, value: projected_ty,
obligations, obligations: projected_obligations,
} }
}; };
let cache_value = prune_cache_value_obligations(infcx, &result); let cache_value = prune_cache_value_obligations(infcx, &result);
infcx.projection_cache.borrow_mut().insert_ty(cache_key, cache_value); infcx.projection_cache.borrow_mut().insert_ty(cache_key, cache_value);
obligations.extend(result.obligations);
Some(result) Some(result.value)
} }
Ok(ProjectedTy::NoProgress(projected_ty)) => { Ok(ProjectedTy::NoProgress(projected_ty)) => {
debug!("opt_normalize_projection_type: \ debug!("opt_normalize_projection_type: \
@ -670,7 +683,8 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
obligations: vec![] obligations: vec![]
}; };
infcx.projection_cache.borrow_mut().insert_ty(cache_key, result.clone()); infcx.projection_cache.borrow_mut().insert_ty(cache_key, result.clone());
Some(result) // No need to extend `obligations`.
Some(result.value)
} }
Err(ProjectionTyError::TooManyCandidates) => { Err(ProjectionTyError::TooManyCandidates) => {
debug!("opt_normalize_projection_type: \ debug!("opt_normalize_projection_type: \
@ -688,7 +702,9 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
infcx.projection_cache.borrow_mut() infcx.projection_cache.borrow_mut()
.error(cache_key); .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 /// may or may not be necessary -- in principle, all the obligations
/// that must be proven to show that `T: Trait` were also returned /// that must be proven to show that `T: Trait` were also returned
/// when the cache was first populated. But there are some vague concerns, /// 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: /// the result:
/// ///
/// Concern #1. The current setup is fragile. Perhaps someone could /// 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 /// 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 /// of trouble, though we don't have a concrete example of how that
/// can occur yet. But it seems risky at best. /// can occur yet. But it seems risky at best.
fn push_paranoid_cache_value_obligation<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, fn get_paranoid_cache_value_obligation<'a, 'gcx, 'tcx>(
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>, param_env: ty::ParamEnv<'tcx>,
projection_ty: ty::ProjectionTy<'tcx>, projection_ty: ty::ProjectionTy<'tcx>,
cause: ObligationCause<'tcx>, cause: ObligationCause<'tcx>,
depth: usize, depth: usize)
result: &mut NormalizedTy<'tcx>) -> PredicateObligation<'tcx>
{ {
let trait_ref = projection_ty.trait_ref(infcx.tcx).to_poly_trait_ref(); let trait_ref = projection_ty.trait_ref(infcx.tcx).to_poly_trait_ref();
let trait_obligation = Obligation { cause, Obligation {
cause,
recursion_depth: depth, recursion_depth: depth,
param_env, param_env,
predicate: trait_ref.to_predicate() }; predicate: trait_ref.to_predicate(),
result.obligations.push(trait_obligation); }
} }
/// If we are projecting `<T as Trait>::Item`, but `T: Trait` does not /// If we are projecting `<T as Trait>::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 /// Indicates that trying to normalize `key` resulted in
/// ambiguity. No point in trying it again then until we gain more /// ambiguity. No point in trying it again then until we gain more
/// type information (in which case, the "fully resolved" key will /// type information (in which case, the "fully resolved" key will

View file

@ -67,6 +67,12 @@ impl<K, V> SnapshotMap<K, V>
} }
} }
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 { pub fn remove(&mut self, key: K) -> bool {
match self.map.remove(&key) { match self.map.remove(&key) {
Some(old_value) => { Some(old_value) => {

View file

@ -9,8 +9,7 @@
// except according to those terms. // except according to those terms.
use rustc::infer::canonical::{Canonical, QueryResult}; use rustc::infer::canonical::{Canonical, QueryResult};
use rustc::traits::{self, FulfillmentContext, Normalized, ObligationCause, use rustc::traits::{self, FulfillmentContext, ObligationCause, SelectionContext};
SelectionContext};
use rustc::traits::query::{CanonicalProjectionGoal, NoSolution, normalize::NormalizationResult}; use rustc::traits::query::{CanonicalProjectionGoal, NoSolution, normalize::NormalizationResult};
use rustc::ty::{ParamEnvAnd, TyCtxt}; use rustc::ty::{ParamEnvAnd, TyCtxt};
use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::Lrc;
@ -37,10 +36,9 @@ crate fn normalize_projection_ty<'tcx>(
let fulfill_cx = &mut FulfillmentContext::new(); let fulfill_cx = &mut FulfillmentContext::new();
let selcx = &mut SelectionContext::new(infcx); let selcx = &mut SelectionContext::new(infcx);
let cause = ObligationCause::misc(DUMMY_SP, DUMMY_NODE_ID); let cause = ObligationCause::misc(DUMMY_SP, DUMMY_NODE_ID);
let Normalized { let mut obligations = vec![];
value: answer, let answer =
obligations, traits::normalize_projection_type(selcx, param_env, goal, cause, 0, &mut obligations);
} = traits::normalize_projection_type(selcx, param_env, goal, cause, 0);
fulfill_cx.register_predicate_obligations(infcx, obligations); fulfill_cx.register_predicate_obligations(infcx, obligations);
// Now that we have fulfilled as much as we can, create a solution // Now that we have fulfilled as much as we can, create a solution

View file

@ -129,7 +129,7 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
} }
let mut selcx = traits::SelectionContext::new(self.fcx); let mut selcx = traits::SelectionContext::new(self.fcx);
let normalized = traits::normalize_projection_type(&mut selcx, let normalized_ty = traits::normalize_projection_type(&mut selcx,
self.fcx.param_env, self.fcx.param_env,
ty::ProjectionTy::from_ref_and_name( ty::ProjectionTy::from_ref_and_name(
tcx, tcx,
@ -137,12 +137,12 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
Symbol::intern("Target"), Symbol::intern("Target"),
), ),
cause, cause,
0); 0,
&mut self.obligations);
debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized); debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized_ty);
self.obligations.extend(normalized.obligations);
Some(self.fcx.resolve_type_vars_if_possible(&normalized.value)) Some(self.fcx.resolve_type_vars_if_possible(&normalized_ty))
} }
/// Returns the final type, generating an error if it is an /// Returns the final type, generating an error if it is an

View file

@ -767,9 +767,12 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
} }
if !parser.errors.is_empty() { if !parser.errors.is_empty() {
let (err, note) = parser.errors.remove(0); let err = parser.errors.remove(0);
let mut e = cx.ecx.struct_span_err(cx.fmtsp, &format!("invalid format string: {}", err)); let sp = cx.fmtsp.from_inner_byte_pos(err.start, err.end);
if let Some(note) = note { 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(&note); e.note(&note);
} }
e.emit(); e.emit();

View file

@ -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] #[inline]
pub fn apply_mark(self, mark: Mark) -> Span { pub fn apply_mark(self, mark: Mark) -> Span {
let span = self.data(); let span = self.data();

View file

@ -101,7 +101,7 @@ impl<T: Write> PrettyFormatter<T> {
for &(ref f, ref stdout) in &state.not_failures { for &(ref f, ref stdout) in &state.not_failures {
successes.push(f.name.to_string()); successes.push(f.name.to_string());
if !stdout.is_empty() { 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); let output = String::from_utf8_lossy(stdout);
stdouts.push_str(&output); stdouts.push_str(&output);
stdouts.push_str("\n"); stdouts.push_str("\n");
@ -127,7 +127,7 @@ impl<T: Write> PrettyFormatter<T> {
for &(ref f, ref stdout) in &state.failures { for &(ref f, ref stdout) in &state.failures {
failures.push(f.name.to_string()); failures.push(f.name.to_string());
if !stdout.is_empty() { 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); let output = String::from_utf8_lossy(stdout);
fail_out.push_str(&output); fail_out.push_str(&output);
fail_out.push_str("\n"); fail_out.push_str("\n");

View file

@ -105,7 +105,7 @@ impl<T: Write> TerseFormatter<T> {
for &(ref f, ref stdout) in &state.not_failures { for &(ref f, ref stdout) in &state.not_failures {
successes.push(f.name.to_string()); successes.push(f.name.to_string());
if !stdout.is_empty() { 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); let output = String::from_utf8_lossy(stdout);
stdouts.push_str(&output); stdouts.push_str(&output);
stdouts.push_str("\n"); stdouts.push_str("\n");
@ -131,7 +131,7 @@ impl<T: Write> TerseFormatter<T> {
for &(ref f, ref stdout) in &state.failures { for &(ref f, ref stdout) in &state.failures {
failures.push(f.name.to_string()); failures.push(f.name.to_string());
if !stdout.is_empty() { 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); let output = String::from_utf8_lossy(stdout);
fail_out.push_str(&output); fail_out.push_str(&output);
fail_out.push_str("\n"); fail_out.push_str("\n");

View file

@ -140,13 +140,9 @@ check that the test compiles successfully.
### Editing and updating the reference files ### Editing and updating the reference files
If you have changed the compiler's output intentionally, or you are 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 making a new test, you can pass `--bless` to the command you used to
update the references. When you run the test framework, it will report run the tests. This will then copy over the files
various errors: in those errors is a command you can use to run the from the build directory and use them as the new reference.
`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.
### Normalization ### Normalization

View file

@ -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`.

View file

@ -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`.

View file

@ -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`.

20
src/test/ui/E0508.rs Normal file
View file

@ -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 <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.
// 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]
}

View file

@ -12,5 +12,14 @@ fn main() {
println!("{"); println!("{");
println!("{{}}"); 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 `'\\'`
} }

View file

@ -2,7 +2,7 @@ error: invalid format string: expected `'}'` but string was terminated
--> $DIR/format-string-error.rs:12:5 --> $DIR/format-string-error.rs:12:5
| |
LL | println!("{"); LL | println!("{");
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^ expected `'}'` in format string
| |
= note: if you intended to print `{`, you can escape it using `{{` = 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) = 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 --> $DIR/format-string-error.rs:14:5
| |
LL | println!("}"); LL | println!("}");
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^ unmatched `}` in format string
| |
= note: if you intended to print `}`, you can escape it using `}}` = 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) = 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

View file

@ -118,6 +118,9 @@ impl CompareMode {
#[derive(Clone)] #[derive(Clone)]
pub struct Config { 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 /// The library paths required for running the compiler
pub compile_lib_path: PathBuf, pub compile_lib_path: PathBuf,

View file

@ -166,6 +166,11 @@ pub fn parse_config(args: Vec<String>) -> Config {
"FLAGS", "FLAGS",
) )
.optflag("", "verbose", "run tests verbosely, showing all output") .optflag("", "verbose", "run tests verbosely, showing all output")
.optflag(
"",
"bless",
"overwrite stderr/stdout files instead of complaining about a mismatch",
)
.optflag( .optflag(
"", "",
"quiet", "quiet",
@ -290,6 +295,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
let src_base = opt_path(matches, "src-base"); let src_base = opt_path(matches, "src-base");
let run_ignored = matches.opt_present("ignored"); let run_ignored = matches.opt_present("ignored");
Config { Config {
bless: matches.opt_present("bless"),
compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")), compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
run_lib_path: make_absolute(opt_path(matches, "run-lib-path")), run_lib_path: make_absolute(opt_path(matches, "run-lib-path")),
rustc_path: opt_path(matches, "rustc-path"), rustc_path: opt_path(matches, "rustc-path"),

View file

@ -2596,15 +2596,13 @@ impl<'test> TestCx<'test> {
} }
if errors > 0 { 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 let relative_path_to_file = self.testpaths
.relative_dir .relative_dir
.join(self.testpaths.file.file_name().unwrap()); .join(self.testpaths.file.file_name().unwrap());
println!( println!(
"{}/update-references.sh '{}' '{}'", "To only update this specific test, also pass `--test-args {}`",
self.config.src_base.display(), relative_path_to_file.display(),
self.config.build_base.display(),
relative_path_to_file.display()
); );
self.fatal_proc_rec( self.fatal_proc_rec(
&format!("{} errors occurred comparing output.", errors), &format!("{} errors occurred comparing output.", errors),
@ -2926,6 +2924,7 @@ impl<'test> TestCx<'test> {
return 0; return 0;
} }
if !self.config.bless {
if expected.is_empty() { if expected.is_empty() {
println!("normalized {}:\n{}\n", kind, actual); println!("normalized {}:\n{}\n", kind, actual);
} else { } else {
@ -2951,6 +2950,7 @@ impl<'test> TestCx<'test> {
println!(""); println!("");
} }
} }
}
let mode = self.config.compare_mode.as_ref().map_or("", |m| m.to_str()); let mode = self.config.compare_mode.as_ref().map_or("", |m| m.to_str());
let output_file = self.output_base_name() let output_file = self.output_base_name()
@ -2958,6 +2958,26 @@ impl<'test> TestCx<'test> {
.with_extra_extension(mode) .with_extra_extension(mode)
.with_extra_extension(kind); .with_extra_extension(kind);
let mut files = vec![output_file];
if self.config.bless {
files.push(expected_output_path(
self.testpaths,
self.revision,
&self.config.compare_mode,
kind,
));
}
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())) { match File::create(&output_file).and_then(|mut f| f.write_all(actual.as_bytes())) {
Ok(()) => {} Ok(()) => {}
Err(e) => self.fatal(&format!( Err(e) => self.fatal(&format!(
@ -2967,11 +2987,19 @@ impl<'test> TestCx<'test> {
e e
)), )),
} }
}
}
println!("\nThe actual {0} differed from the expected {0}.", kind); println!("\nThe actual {0} differed from the expected {0}.", kind);
for output_file in files {
println!("Actual {} saved to {}", kind, output_file.display()); println!("Actual {} saved to {}", kind, output_file.display());
}
if self.config.bless {
0
} else {
1 1
} }
}
fn create_stamp(&self) { fn create_stamp(&self) {
let mut f = File::create(::stamp(&self.config, self.testpaths, self.revision)).unwrap(); let mut f = File::create(::stamp(&self.config, self.testpaths, self.revision)).unwrap();