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 {
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);
}

View file

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

View file

@ -59,6 +59,8 @@ pub enum Subcommand {
},
Test {
paths: Vec<PathBuf>,
/// Whether to automatically update stderr/stdout files
bless: bool,
test_args: Vec<String>,
rustc_args: Vec<String>,
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("", "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<String>) -> Vec<String> {

View file

@ -47,6 +47,16 @@ pub enum TestKind {
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 {
// 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,

View file

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

View file

@ -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!

View file

@ -962,122 +962,59 @@ extern "rust-intrinsic" {
/// value is not necessarily valid to be used to actually access memory.
pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T;
/// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
/// and destination must *not* overlap.
/// Copies `count * size_of<T>` 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::<T>()` bytes must be *both* valid and initialized.
///
/// * 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).
///
/// * 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<T>(dst: &mut Vec<T>, src: &mut Vec<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);
///
/// # #[allow(dead_code)]
/// fn swap<T>(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();
/// // Give ourselves some scratch space to work with
/// let mut t: T = mem::uninitialized();
///
/// // 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);
/// }
/// // 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);
///
/// unsafe {
/// // Truncate `src` without dropping its contents.
/// src.set_len(0);
///
/// // 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<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.
///
/// 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::<T>()` bytes must be *both* valid and initialized.
///
/// * 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
/// 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<T>(src: *const T, dst: *mut T, count: usize);
/// Sets `count * size_of::<T>()` 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::<T>()`
/// 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<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")]
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
/////////////////////////////////////////////////////////////////////////

View file

@ -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<T>() -> *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<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
///
/// 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<T>(dest: *mut T, mut src: T) -> T {
/// 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]
#[stable(feature = "rust1", since = "1.0.0")]
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
/// 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<T>(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<T>(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.
/// memory that has previously been `read` from.
///
/// [`read`]: ./fn.read.html
///
/// # 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
/// The pointer must be aligned; use `write_unaligned` if that is not the case.
///
/// # Examples
///
@ -519,30 +356,6 @@ pub unsafe fn read_unaligned<T>(src: *const T) -> T {
/// 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]
#[stable(feature = "rust1", since = "1.0.0")]
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
/// 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
/// could leak allocations or resources, so care must be taken not to overwrite
/// an object that should be dropped.
/// # 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.
///
/// 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<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
/// 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<T>(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<T>(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<T>(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
///

View file

@ -127,6 +127,14 @@ pub enum Count<'a> {
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
/// 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<str::CharIndices<'a>>,
/// Error messages accumulated during parsing
pub errors: Vec<(string::String, Option<string::String>)>,
pub errors: Vec<ParseError>,
/// 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<S1: Into<string::String>, S2: Into<string::String>>(
&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<S1: Into<string::String>, S2: Into<string::String>, S3: Into<string::String>>(
&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

View file

@ -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<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 {
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: 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!();
}
}
}

View file

@ -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;

View file

@ -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,
let mut obligations = vec![];
let normalized_ty = project::normalize_projection_type(&mut selcx,
param_env,
projection_ty,
cause,
0);
0,
&mut obligations);
self.register_predicate_obligations(infcx, obligations);
for obligation in normalized.obligations {
self.register_predicate_obligation(infcx, obligation);
}
debug!("normalize_projection_type: result={:?}", normalized_ty);
debug!("normalize_projection_type: result={:?}", normalized.value);
normalized.value
normalized_ty
}
/// 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={:?})",
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,
let normalized_ty = 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);
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<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 || {
// 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<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>(
selcx: &'a mut SelectionContext<'b, 'gcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
projection_ty: ty::ProjectionTy<'tcx>,
cause: ObligationCause<'tcx>,
depth: usize)
-> Option<NormalizedTy<'tcx>>
depth: usize,
obligations: &mut Vec<PredicateObligation<'tcx>>)
-> Option<Ty<'tcx>>
{
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,
obligations.push(get_paranoid_cache_value_obligation(infcx,
param_env,
projection_ty,
cause,
depth,
&mut ty);
return Some(ty);
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>,
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,
result: &mut NormalizedTy<'tcx>)
depth: usize)
-> PredicateObligation<'tcx>
{
let trait_ref = projection_ty.trait_ref(infcx.tcx).to_poly_trait_ref();
let trait_obligation = Obligation { cause,
Obligation {
cause,
recursion_depth: depth,
param_env,
predicate: trait_ref.to_predicate() };
result.obligations.push(trait_obligation);
predicate: trait_ref.to_predicate(),
}
}
/// 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
/// ambiguity. No point in trying it again then until we gain more
/// 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 {
match self.map.remove(&key) {
Some(old_value) => {

View file

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

View file

@ -129,7 +129,7 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
}
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,
ty::ProjectionTy::from_ref_and_name(
tcx,
@ -137,12 +137,12 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
Symbol::intern("Target"),
),
cause,
0);
0,
&mut self.obligations);
debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized);
self.obligations.extend(normalized.obligations);
debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized_ty);
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

View file

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

View file

@ -101,7 +101,7 @@ impl<T: Write> PrettyFormatter<T> {
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<T: Write> PrettyFormatter<T> {
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");

View file

@ -105,7 +105,7 @@ impl<T: Write> TerseFormatter<T> {
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<T: Write> TerseFormatter<T> {
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");

View file

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

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!("}");
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
|
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

View file

@ -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,

View file

@ -166,6 +166,11 @@ pub fn parse_config(args: Vec<String>) -> 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<String>) -> 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"),

View file

@ -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,6 +2924,7 @@ impl<'test> TestCx<'test> {
return 0;
}
if !self.config.bless {
if expected.is_empty() {
println!("normalized {}:\n{}\n", kind, actual);
} else {
@ -2951,6 +2950,7 @@ impl<'test> TestCx<'test> {
println!("");
}
}
}
let mode = self.config.compare_mode.as_ref().map_or("", |m| m.to_str());
let output_file = self.output_base_name()
@ -2958,6 +2958,26 @@ impl<'test> TestCx<'test> {
.with_extra_extension(mode)
.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())) {
Ok(()) => {}
Err(e) => self.fatal(&format!(
@ -2967,11 +2987,19 @@ impl<'test> TestCx<'test> {
e
)),
}
}
}
println!("\nThe actual {0} differed from the expected {0}.", kind);
for output_file in files {
println!("Actual {} saved to {}", kind, output_file.display());
}
if self.config.bless {
0
} else {
1
}
}
fn create_stamp(&self) {
let mut f = File::create(::stamp(&self.config, self.testpaths, self.revision)).unwrap();