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:
commit
dfc07a48f6
31 changed files with 591 additions and 687 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -1460,6 +1460,7 @@ mod __test {
|
|||
rustc_args: vec![],
|
||||
fail_fast: true,
|
||||
doc_tests: DocTests::No,
|
||||
bless: false,
|
||||
};
|
||||
|
||||
let build = Build::new(config);
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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
|
||||
///
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -767,9 +767,12 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
|
|||
}
|
||||
|
||||
if !parser.errors.is_empty() {
|
||||
let (err, note) = parser.errors.remove(0);
|
||||
let mut e = cx.ecx.struct_span_err(cx.fmtsp, &format!("invalid format string: {}", err));
|
||||
if let Some(note) = note {
|
||||
let err = parser.errors.remove(0);
|
||||
let sp = cx.fmtsp.from_inner_byte_pos(err.start, err.end);
|
||||
let mut e = cx.ecx.struct_span_err(sp, &format!("invalid format string: {}",
|
||||
err.description));
|
||||
e.span_label(sp, err.label + " in format string");
|
||||
if let Some(note) = err.note {
|
||||
e.note(¬e);
|
||||
}
|
||||
e.emit();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
9
src/test/ui/E0508.ast.nll.stderr
Normal file
9
src/test/ui/E0508.ast.nll.stderr
Normal 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`.
|
12
src/test/ui/E0508.ast.stderr
Normal file
12
src/test/ui/E0508.ast.stderr
Normal 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`.
|
9
src/test/ui/E0508.mir.stderr
Normal file
9
src/test/ui/E0508.mir.stderr
Normal 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
20
src/test/ui/E0508.rs
Normal 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]
|
||||
}
|
|
@ -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 `'\\'`
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue