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