[[package]]
name = "racer"
-version = "2.0.13"
+version = "2.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_errors 0.52.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.52.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "racer 2.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "racer 2.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rls-analysis 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rls-blacklist 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rls-data 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "rls-rustc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rls-rustc 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rls-vfs 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rustfmt-nightly 0.7.0",
"serde_derive 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
[[package]]
name = "rls-rustc"
-version = "0.2.2"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "racer 2.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "racer 2.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
-[[package]]
-name = "toml"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
[[package]]
name = "toml"
version = "0.4.6"
"checksum quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7b0ff51282f28dc1b53fd154298feaa2e77c5ea0dba68e1fd8b03b72fbe13d2a"
-"checksum racer 2.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "40d44bc30fc8d403b665286b2c9a83466ddbf69297668fb02b785c3e58eb8e0d"
+"checksum racer 2.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "e713729f45f12df5c5e182d39506766f76c09133fb661d3622e0ddf8078911c2"
"checksum radix_trie 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "03d0d770481e8af620ca61d3d304bf014f965d7f78e923dc58545e6a545070a9"
"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1"
"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
"checksum rls-analysis 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "da9794cd1f80f2cb888c00641a32f9855d0226c954ee31cef145784914c7142e"
"checksum rls-blacklist 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e4a9cc2545ccb7e05b355bfe047b8039a6ec12270d5f3c996b766b340a50f7d2"
"checksum rls-data 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3dd20763e1c60ae8945384c8a8fa4ac44f8afa7b0a817511f5e8927e5d24f988"
-"checksum rls-rustc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "885f66b92757420572cbb02e033d4a9558c7413ca9b7ac206f28fd58ffdb44ea"
+"checksum rls-rustc 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ed5342b2bbbe8663c04600af506c8902b6b4d3e627b006eb1bd65aa14805f4d"
"checksum rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d7c7046dc6a92f2ae02ed302746db4382e75131b9ce20ce967259f6b5867a6a"
"checksum rls-vfs 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "be231e1e559c315bc60ced5ad2cc2d7a9c208ed7d4e2c126500149836fda19bb"
"checksum rustc-ap-rustc_cratesio_shim 128.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7374a2b466e6e3ce489e045302e304849355faf7fd033d4d95e6e86e48c313b4"
"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693"
"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
"checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098"
-"checksum toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "736b60249cb25337bc196faa43ee12c705e426f3d55c214d73a4e7be06f92cb4"
"checksum toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a0263c6c02c4db6c8f7681f9fd35e90de799ebd4cfdeab77a38f4ff6b3d8c0d9"
"checksum toml-query 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6854664bfc6df0360c695480836ee90e2d0c965f06db291d10be9344792d43e8"
"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d"
}
if verbose > 1 {
- eprintln!("rustc command: {:?}", cmd);
+ eprintln!(
+ "rustc command: {:?}={:?} {:?}",
+ bootstrap::util::dylib_path_var(),
+ env::join_paths(&dylib_path).unwrap(),
+ cmd,
+ );
eprintln!("sysroot: {:?}", sysroot);
eprintln!("libdir: {:?}", libdir);
}
cargo.env("RUST_CHECK", "1");
}
- // If we were invoked from `make` then that's already got a jobserver
- // set up for us so no need to tell Cargo about jobs all over again.
- if env::var_os("MAKEFLAGS").is_none() && env::var_os("MFLAGS").is_none() {
- cargo.arg("-j").arg(self.jobs().to_string());
- }
+ cargo.arg("-j").arg(self.jobs().to_string());
+ // Remove make-related flags to ensure Cargo can correctly set things up
+ cargo.env_remove("MAKEFLAGS");
+ cargo.env_remove("MFLAGS");
// FIXME: Temporary fix for https://github.com/rust-lang/cargo/issues/3005
// Force cargo to output binaries with disambiguating hashes in the name
rustc_args: vec![],
fail_fast: true,
doc_tests: DocTests::No,
+ bless: false,
};
let build = Build::new(config);
use config::Config;
// The version number
-pub const CFG_RELEASE_NUM: &str = "1.27.0";
+pub const CFG_RELEASE_NUM: &str = "1.28.0";
pub struct GitInfo {
inner: Option<Info>,
// Link the compiler binary itself into place
let out_dir = builder.cargo_out(build_compiler, Mode::Librustc, host);
- let rustc = out_dir.join(exe("rustc", &*host));
+ let rustc = out_dir.join(exe("rustc_binary", &*host));
let bindir = sysroot.join("bin");
t!(fs::create_dir_all(&bindir));
let compiler = builder.rustc(target_compiler);
},
Test {
paths: Vec<PathBuf>,
+ /// Whether to automatically update stderr/stdout files
+ bless: bool,
test_args: Vec<String>,
rustc_args: Vec<String>,
fail_fast: bool,
);
opts.optflag("", "no-doc", "do not run doc tests");
opts.optflag("", "doc", "only run doc tests");
+ opts.optflag("", "bless", "update all stderr/stdout files of failing ui tests");
},
"bench" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); },
"clean" => { opts.optflag("", "all", "clean all build artifacts"); },
./x.py test src/test/run-pass
./x.py test src/libstd --test-args hash_map
./x.py test src/libstd --stage 0
+ ./x.py test src/test/ui --bless
If no arguments are passed then the complete artifacts for that stage are
compiled and tested.
"test" => {
Subcommand::Test {
paths,
+ bless: matches.opt_present("bless"),
test_args: matches.opt_strs("test-args"),
rustc_args: matches.opt_strs("rustc-args"),
fail_fast: !matches.opt_present("no-fail-fast"),
_ => DocTests::Yes,
}
}
+
+ pub fn bless(&self) -> bool {
+ match *self {
+ Subcommand::Test { bless, .. } => bless,
+ _ => false,
+ }
+ }
}
fn split(s: Vec<String>) -> Vec<String> {
Bench,
}
+impl From<Kind> for TestKind {
+ fn from(kind: Kind) -> Self {
+ match kind {
+ Kind::Test => TestKind::Test,
+ Kind::Bench => TestKind::Bench,
+ _ => panic!("unexpected kind in crate: {:?}", kind)
+ }
+ }
+}
+
impl TestKind {
// Return the cargo subcommand for this test kind
fn subcommand(self) -> &'static str {
cmd.arg("--host").arg(&*compiler.host);
cmd.arg("--llvm-filecheck").arg(builder.llvm_filecheck(builder.config.build));
+ if builder.config.cmd.bless() {
+ cmd.arg("--bless");
+ }
+
if let Some(ref nodejs) = builder.config.nodejs {
cmd.arg("--nodejs").arg(nodejs);
}
for krate in builder.in_tree_crates("rustc-main") {
if run.path.ends_with(&krate.path) {
- let test_kind = if builder.kind == Kind::Test {
- TestKind::Test
- } else if builder.kind == Kind::Bench {
- TestKind::Bench
- } else {
- panic!("unexpected builder.kind in crate: {:?}", builder.kind);
- };
+ let test_kind = builder.kind.into();
builder.ensure(CrateLibrustc {
compiler,
let builder = run.builder;
let compiler = builder.compiler(builder.top_stage, run.host);
- let test_kind = if builder.kind == Kind::Test {
- TestKind::Test
- } else if builder.kind == Kind::Bench {
- TestKind::Bench
- } else {
- panic!("unexpected builder.kind in crate: {:?}", builder.kind);
- };
+ let test_kind = builder.kind.into();
builder.ensure(CrateNotDefault {
compiler,
let compiler = builder.compiler(builder.top_stage, run.host);
let make = |mode: Mode, krate: &CargoCrate| {
- let test_kind = if builder.kind == Kind::Test {
- TestKind::Test
- } else if builder.kind == Kind::Bench {
- TestKind::Bench
- } else {
- panic!("unexpected builder.kind in crate: {:?}", builder.kind);
- };
+ let test_kind = builder.kind.into();
builder.ensure(Crate {
compiler,
fn make_run(run: RunConfig) {
let builder = run.builder;
- let test_kind = if builder.kind == Kind::Test {
- TestKind::Test
- } else if builder.kind == Kind::Bench {
- TestKind::Bench
- } else {
- panic!("unexpected builder.kind in crate: {:?}", builder.kind);
- };
+ let test_kind = builder.kind.into();
builder.ensure(CrateRustdoc {
host: run.host,
// the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool"
// rustdoc a different name.
let tool_rustdoc = builder.cargo_out(build_compiler, Mode::Tool, target)
- .join(exe("rustdoc-tool-binary", &target_compiler.host));
+ .join(exe("rustdoc_tool_binary", &target_compiler.host));
// don't create a stage0-sysroot/bin directory.
if target_compiler.stage > 0 {
### Identifiers
-The `ident` production is any nonempty Unicode[^non_ascii_idents] string of
+The `ident` production is any nonempty Unicode string of
the following form:
-[^non_ascii_idents]: Non-ASCII characters in identifiers are currently feature
- gated. This is expected to improve soon.
+- The first character is in one of the following ranges `U+0041` to `U+005A`
+("A" to "Z"), `U+0061` to `U+007A` ("a" to "z"), or `U+005F` ("\_").
+- The remaining characters are in the range `U+0030` to `U+0039` ("0" to "9"),
+or any of the prior valid initial characters.
-- The first character has property `XID_start`
-- The remaining characters have property `XID_continue`
-
-that does _not_ occur in the set of [keywords](#keywords).
-
-> **Note**: `XID_start` and `XID_continue` as character properties cover the
-> character ranges used to form the more familiar C and Java language-family
-> identifiers.
+as long as the identifier does _not_ occur in the set of [keywords](#keywords).
### Delimiter-restricted productions
Some productions are defined by exclusion of particular Unicode characters:
- `non_null` is any single Unicode character aside from `U+0000` (null)
-- `non_eol` is `non_null` restricted to exclude `U+000A` (`'\n'`)
-- `non_single_quote` is `non_null` restricted to exclude `U+0027` (`'`)
-- `non_double_quote` is `non_null` restricted to exclude `U+0022` (`"`)
+- `non_eol` is any single Unicode character aside from `U+000A` (`'\n'`)
+- `non_single_quote` is any single Unicode character aside from `U+0027` (`'`)
+- `non_double_quote` is any single Unicode character aside from `U+0022` (`"`)
## Comments
% 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!
authors = ["The Rust Project Developers"]
name = "alloc"
version = "0.0.0"
+autotests = false
+autobenches = false
[lib]
name = "alloc"
#[doc(inline)]
pub use core::alloc::*;
-#[cfg(stage0)]
-extern "Rust" {
- #[allocator]
- #[rustc_allocator_nounwind]
- fn __rust_alloc(size: usize, align: usize, err: *mut u8) -> *mut u8;
- #[cold]
- #[rustc_allocator_nounwind]
- fn __rust_oom(err: *const u8) -> !;
- #[rustc_allocator_nounwind]
- fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize);
- #[rustc_allocator_nounwind]
- fn __rust_realloc(ptr: *mut u8,
- old_size: usize,
- old_align: usize,
- new_size: usize,
- new_align: usize,
- err: *mut u8) -> *mut u8;
- #[rustc_allocator_nounwind]
- fn __rust_alloc_zeroed(size: usize, align: usize, err: *mut u8) -> *mut u8;
-}
-
-#[cfg(not(stage0))]
extern "Rust" {
#[allocator]
#[rustc_allocator_nounwind]
unsafe impl GlobalAlloc for Global {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut Opaque {
- #[cfg(not(stage0))]
let ptr = __rust_alloc(layout.size(), layout.align());
- #[cfg(stage0)]
- let ptr = __rust_alloc(layout.size(), layout.align(), &mut 0);
ptr as *mut Opaque
}
#[inline]
unsafe fn realloc(&self, ptr: *mut Opaque, layout: Layout, new_size: usize) -> *mut Opaque {
- #[cfg(not(stage0))]
let ptr = __rust_realloc(ptr as *mut u8, layout.size(), layout.align(), new_size);
- #[cfg(stage0)]
- let ptr = __rust_realloc(ptr as *mut u8, layout.size(), layout.align(),
- new_size, layout.align(), &mut 0);
ptr as *mut Opaque
}
#[inline]
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque {
- #[cfg(not(stage0))]
let ptr = __rust_alloc_zeroed(layout.size(), layout.align());
- #[cfg(stage0)]
- let ptr = __rust_alloc_zeroed(layout.size(), layout.align(), &mut 0);
ptr as *mut Opaque
}
}
}
}
-#[cfg(stage0)]
-#[lang = "box_free"]
-#[inline]
-unsafe fn old_box_free<T: ?Sized>(ptr: *mut T) {
- box_free(Unique::new_unchecked(ptr))
-}
-
-#[cfg_attr(not(any(test, stage0)), lang = "box_free")]
+#[cfg_attr(not(test), lang = "box_free")]
#[inline]
pub(crate) unsafe fn box_free<T: ?Sized>(ptr: Unique<T>) {
let ptr = ptr.as_ptr();
}
}
-#[cfg(stage0)]
-pub fn oom() -> ! {
- unsafe { ::core::intrinsics::abort() }
-}
-
-#[cfg(not(stage0))]
pub fn oom() -> ! {
extern {
#[lang = "oom"]
#![deny(missing_debug_implementations)]
#![cfg_attr(test, allow(deprecated))] // rand
-#![cfg_attr(all(not(test), stage0), feature(float_internals))]
#![cfg_attr(not(test), feature(exact_size_is_empty))]
#![cfg_attr(not(test), feature(generator_trait))]
#![cfg_attr(test, feature(rand, test))]
#![feature(collections_range)]
#![feature(const_fn)]
#![feature(core_intrinsics)]
-#![cfg_attr(stage0, feature(core_slice_ext))]
-#![cfg_attr(stage0, feature(core_str_ext))]
#![feature(custom_attribute)]
#![feature(dropck_eyepatch)]
#![feature(exact_size_is_empty)]
#![feature(fmt_internals)]
-#![cfg_attr(stage0, feature(fn_must_use))]
#![feature(from_ref)]
#![feature(fundamental)]
#![feature(lang_items)]
#![feature(exact_chunks)]
#![feature(pointer_methods)]
#![feature(inclusive_range_methods)]
-#![cfg_attr(stage0, feature(generic_param_attrs))]
#![feature(rustc_const_unstable)]
#![feature(const_vec_new)]
#[unstable(feature = "allocator_api", issue = "32838")]
#[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")]
/// Use the `alloc` module instead.
-#[cfg(not(stage0))]
pub mod heap {
pub use alloc::*;
}
-#[unstable(feature = "allocator_api", issue = "32838")]
-#[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")]
-#[cfg(stage0)]
-pub mod heap;
// Primitive types using the heaps above
use core::mem::size_of;
use core::mem;
use core::ptr;
-#[cfg(stage0)] use core::slice::SliceExt;
use core::{u8, u16, u32};
use borrow::{Borrow, BorrowMut, ToOwned};
}
}
-#[cfg_attr(stage0, lang = "slice")]
-#[cfg_attr(not(stage0), lang = "slice_alloc")]
+#[lang = "slice_alloc"]
#[cfg(not(test))]
impl<T> [T] {
- #[cfg(stage0)]
- slice_core_methods!();
-
/// Sorts the slice.
///
/// This sort is stable (i.e. does not reorder equal elements) and `O(n log n)` worst-case.
}
}
-#[cfg_attr(stage0, lang = "slice_u8")]
-#[cfg_attr(not(stage0), lang = "slice_u8_alloc")]
+#[lang = "slice_u8_alloc"]
#[cfg(not(test))]
impl [u8] {
/// Returns a vector containing a copy of this slice where each byte
me.make_ascii_lowercase();
me
}
-
- #[cfg(stage0)]
- slice_u8_core_methods!();
}
////////////////////////////////////////////////////////////////////////////////
use core::fmt;
use core::str as core_str;
-#[cfg(stage0)] use core::str::StrExt;
use core::str::pattern::Pattern;
use core::str::pattern::{Searcher, ReverseSearcher, DoubleEndedSearcher};
use core::mem;
}
/// Methods for string slices.
-#[cfg_attr(stage0, lang = "str")]
-#[cfg_attr(not(stage0), lang = "str_alloc")]
+#[lang = "str_alloc"]
#[cfg(not(test))]
impl str {
- #[cfg(stage0)]
- str_core_methods!();
-
/// Converts a `Box<str>` into a `Box<[u8]>` without copying or allocating.
///
/// # Examples
#![feature(try_reserve)]
#![feature(unboxed_closures)]
#![feature(exact_chunks)]
-#![feature(inclusive_range_methods)]
extern crate alloc_system;
extern crate core;
use core::iter::{FromIterator, FusedIterator, TrustedLen};
use core::marker::PhantomData;
use core::mem;
-#[cfg(not(test))]
-#[cfg(stage0)]
-use core::num::Float;
use core::ops::Bound::{Excluded, Included, Unbounded};
use core::ops::{Index, IndexMut, RangeBounds};
use core::ops;
ptr
}
- #[cfg(stage0)]
- #[no_mangle]
- #[rustc_std_internal_symbol]
- pub unsafe extern fn __rde_oom() -> ! {
- ::core::intrinsics::abort();
- }
-
#[no_mangle]
#[rustc_std_internal_symbol]
pub unsafe extern fn __rde_dealloc(ptr: *mut u8,
}
}
-#[cfg(stage0)]
-#[unstable(feature = "allocator_api", issue = "32838")]
-unsafe impl<'a> Alloc for &'a System {
- #[inline]
- unsafe fn alloc(&mut self, layout: Layout) -> Result<NonNull<Opaque>, AllocErr> {
- NonNull::new(GlobalAlloc::alloc(*self, layout)).ok_or(AllocErr)
- }
-
- #[inline]
- unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<NonNull<Opaque>, AllocErr> {
- NonNull::new(GlobalAlloc::alloc_zeroed(*self, layout)).ok_or(AllocErr)
- }
-
- #[inline]
- unsafe fn dealloc(&mut self, ptr: NonNull<Opaque>, layout: Layout) {
- GlobalAlloc::dealloc(*self, ptr.as_ptr(), layout)
- }
-
- #[inline]
- unsafe fn realloc(&mut self,
- ptr: NonNull<Opaque>,
- layout: Layout,
- new_size: usize) -> Result<NonNull<Opaque>, AllocErr> {
- NonNull::new(GlobalAlloc::realloc(*self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr)
- }
-}
-
#[cfg(any(windows, unix, target_os = "cloudabi", target_os = "redox"))]
mod realloc_fallback {
use core::alloc::{GlobalAlloc, Opaque, Layout};
#![feature(alloc)]
#![feature(core_intrinsics)]
#![feature(dropck_eyepatch)]
-#![cfg_attr(stage0, feature(generic_param_attrs))]
#![cfg_attr(test, feature(test))]
#![allow(deprecated)]
authors = ["The Rust Project Developers"]
name = "core"
version = "0.0.0"
+autotests = false
+autobenches = false
[lib]
name = "core"
///
/// Implementations that cannot be described in Rust
/// are implemented in `SelectionContext::copy_clone_conditions()` in librustc.
-#[cfg(not(stage0))]
mod impls {
use super::Clone;
}
}
}
-
-#[cfg(stage0)]
-macro_rules! public_in_stage0 {
- ( { $(#[$attr:meta])* } $($Item: tt)*) => {
- $(#[$attr])* pub $($Item)*
- }
-}
-
-#[cfg(not(stage0))]
-macro_rules! public_in_stage0 {
- ( { $(#[$attr:meta])* } $($Item: tt)*) => {
- $(#[$attr])* pub(crate) $($Item)*
- }
-}
/// value is not necessarily valid to be used to actually access memory.
pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T;
- /// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
- /// and destination must *not* overlap.
+ /// Copies `count * size_of<T>` bytes from `src` to `dst`. The source
+ /// and destination may *not* overlap.
///
- /// For regions of memory which might overlap, use [`copy`] instead.
- ///
- /// `copy_nonoverlapping` is semantically equivalent to C's [`memcpy`].
- ///
- /// [`copy`]: ./fn.copy.html
- /// [`memcpy`]: https://www.gnu.org/software/libc/manual/html_node/Copying-Strings-and-Arrays.html#index-memcpy
+ /// `copy_nonoverlapping` is semantically equivalent to C's `memcpy`.
///
/// # Safety
///
- /// Behavior is undefined if any of the following conditions are violated:
- ///
- /// * The region of memory which begins at `src` and has a length of
- /// `count * size_of::<T>()` bytes must be *both* valid and initialized.
- ///
- /// * The region of memory which begins at `dst` and has a length of
- /// `count * size_of::<T>()` bytes must be valid (but may or may not be
- /// initialized).
- ///
- /// * The two regions of memory must *not* overlap.
- ///
- /// * `src` must be properly aligned.
- ///
- /// * `dst` must be properly aligned.
- ///
- /// Additionally, if `T` is not [`Copy`], only the region at `src` *or* the
- /// region at `dst` can be used or dropped after calling
- /// `copy_nonoverlapping`. `copy_nonoverlapping` creates bitwise copies of
- /// `T`, regardless of whether `T: Copy`, which can result in undefined
- /// behavior if both copies are used.
- ///
- /// [`Copy`]: ../marker/trait.Copy.html
+ /// Beyond requiring that the program must be allowed to access both regions
+ /// of memory, it is Undefined Behavior for source and destination to
+ /// overlap. Care must also be taken with the ownership of `src` and
+ /// `dst`. This method semantically moves the values of `src` into `dst`.
+ /// However it does not drop the contents of `dst`, or prevent the contents
+ /// of `src` from being dropped or used.
///
/// # Examples
///
- /// Manually implement [`Vec::append`]:
+ /// A safe swap function:
///
/// ```
+ /// use std::mem;
/// use std::ptr;
///
- /// /// Moves all the elements of `src` into `dst`, leaving `src` empty.
- /// fn append<T>(dst: &mut Vec<T>, src: &mut Vec<T>) {
- /// let src_len = src.len();
- /// let dst_len = dst.len();
- ///
- /// // Ensure that `dst` has enough capacity to hold all of `src`.
- /// dst.reserve(src_len);
- ///
+ /// # #[allow(dead_code)]
+ /// fn swap<T>(x: &mut T, y: &mut T) {
/// unsafe {
- /// // The call to offset is always safe because `Vec` will never
- /// // allocate more than `isize::MAX` bytes.
- /// let dst = dst.as_mut_ptr().offset(dst_len as isize);
- /// let src = src.as_ptr();
- ///
- /// // The two regions cannot overlap becuase mutable references do
- /// // not alias, and two different vectors cannot own the same
- /// // memory.
- /// ptr::copy_nonoverlapping(src, dst, src_len);
- /// }
+ /// // Give ourselves some scratch space to work with
+ /// let mut t: T = mem::uninitialized();
///
- /// unsafe {
- /// // Truncate `src` without dropping its contents.
- /// src.set_len(0);
+ /// // Perform the swap, `&mut` pointers never alias
+ /// ptr::copy_nonoverlapping(x, &mut t, 1);
+ /// ptr::copy_nonoverlapping(y, x, 1);
+ /// ptr::copy_nonoverlapping(&t, y, 1);
///
- /// // Notify `dst` that it now holds the contents of `src`.
- /// dst.set_len(dst_len + src_len);
+ /// // y and t now point to the same thing, but we need to completely forget `t`
+ /// // because it's no longer relevant.
+ /// mem::forget(t);
/// }
/// }
- ///
- /// let mut a = vec!['r'];
- /// let mut b = vec!['u', 's', 't'];
- ///
- /// append(&mut a, &mut b);
- ///
- /// assert_eq!(a, &['r', 'u', 's', 't']);
- /// assert!(b.is_empty());
/// ```
- ///
- /// [`Vec::append`]: ../../std/vec/struct.Vec.html#method.append
#[stable(feature = "rust1", since = "1.0.0")]
pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
- /// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
+ /// Copies `count * size_of<T>` bytes from `src` to `dst`. The source
/// and destination may overlap.
///
- /// If the source and destination will *never* overlap,
- /// [`copy_nonoverlapping`] can be used instead.
- ///
- /// `copy` is semantically equivalent to C's [`memmove`].
- ///
- /// [`copy_nonoverlapping`]: ./fn.copy_nonoverlapping.html
- /// [`memmove`]: https://www.gnu.org/software/libc/manual/html_node/Copying-Strings-and-Arrays.html#index-memmove
+ /// `copy` is semantically equivalent to C's `memmove`.
///
/// # Safety
///
- /// Behavior is undefined if any of the following conditions are violated:
- ///
- /// * The region of memory which begins at `src` and has a length of
- /// `count * size_of::<T>()` bytes must be *both* valid and initialized.
- ///
- /// * The region of memory which begins at `dst` and has a length of
- /// `count * size_of::<T>()` bytes must be valid (but may or may not be
- /// initialized).
- ///
- /// * `src` must be properly aligned.
- ///
- /// * `dst` must be properly aligned.
- ///
- /// Additionally, if `T` is not [`Copy`], only the region at `src` *or* the
- /// region at `dst` can be used or dropped after calling `copy`. `copy`
- /// creates bitwise copies of `T`, regardless of whether `T: Copy`, which
- /// can result in undefined behavior if both copies are used.
- ///
- /// [`Copy`]: ../marker/trait.Copy.html
+ /// Care must be taken with the ownership of `src` and `dst`.
+ /// This method semantically moves the values of `src` into `dst`.
+ /// However it does not drop the contents of `dst`, or prevent the contents of `src`
+ /// from being dropped or used.
///
/// # Examples
///
/// dst
/// }
/// ```
+ ///
#[stable(feature = "rust1", since = "1.0.0")]
pub fn copy<T>(src: *const T, dst: *mut T, count: usize);
- /// Sets `count * size_of::<T>()` bytes of memory starting at `dst` to
- /// `val`.
- ///
- /// `write_bytes` is semantically equivalent to C's [`memset`].
- ///
- /// [`memset`]: https://www.gnu.org/software/libc/manual/html_node/Copying-Strings-and-Arrays.html#index-memset
- ///
- /// # Safety
- ///
- /// Behavior is undefined if any of the following conditions are violated:
- ///
- /// * The region of memory which begins at `dst` and has a length of
- /// `count` bytes must be valid.
- ///
- /// * `dst` must be properly aligned.
- ///
- /// Additionally, the caller must ensure that writing `count` bytes to the
- /// given region of memory results in a valid value of `T`. Creating an
- /// invalid value of `T` can result in undefined behavior. An example is
- /// provided below.
+ /// Invokes memset on the specified pointer, setting `count * size_of::<T>()`
+ /// bytes of memory starting at `dst` to `val`.
///
/// # Examples
///
- /// Basic usage:
- ///
/// ```
/// use std::ptr;
///
/// }
/// assert_eq!(vec, [b'a', b'a', 0, 0]);
/// ```
- ///
- /// Creating an invalid value:
- ///
- /// ```no_run
- /// use std::{mem, ptr};
- ///
- /// let mut v = Box::new(0i32);
- ///
- /// unsafe {
- /// // Leaks the previously held value by overwriting the `Box<T>` with
- /// // a null pointer.
- /// ptr::write_bytes(&mut v, 0, mem::size_of::<Box<i32>>());
- /// }
- ///
- /// // At this point, using or dropping `v` results in undefined behavior.
- /// // v = Box::new(0i32); // ERROR
- /// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
/// source as well as std's catch implementation.
pub fn try(f: fn(*mut u8), data: *mut u8, local_ptr: *mut u8) -> i32;
- /// Computes the byte offset that needs to be applied to `ptr` in order to
- /// make it aligned to `align`.
- /// If it is not possible to align `ptr`, the implementation returns
- /// `usize::max_value()`.
- ///
- /// There are no guarantees whatsover that offsetting the pointer will not
- /// overflow or go beyond the allocation that `ptr` points into.
- /// It is up to the caller to ensure that the returned offset is correct
- /// in all terms other than alignment.
- ///
- /// # Examples
- ///
- /// Accessing adjacent `u8` as `u16`
- ///
- /// ```
- /// # #![feature(core_intrinsics)]
- /// # fn foo(n: usize) {
- /// # use std::intrinsics::align_offset;
- /// # use std::mem::align_of;
- /// # unsafe {
- /// let x = [5u8, 6u8, 7u8, 8u8, 9u8];
- /// let ptr = &x[n] as *const u8;
- /// let offset = align_offset(ptr as *const (), align_of::<u16>());
- /// if offset < x.len() - n - 1 {
- /// let u16_ptr = ptr.offset(offset as isize) as *const u16;
- /// assert_ne!(*u16_ptr, 500);
- /// } else {
- /// // while the pointer can be aligned via `offset`, it would point
- /// // outside the allocation
- /// }
- /// # } }
- /// ```
+ #[cfg(stage0)]
+ /// docs my friends, its friday!
pub fn align_offset(ptr: *const (), align: usize) -> usize;
/// Emits a `!nontemporal` store according to LLVM (see their docs).
#![feature(unwind_attributes)]
#![feature(doc_alias)]
#![feature(inclusive_range_methods)]
-
-#![cfg_attr(not(stage0), feature(mmx_target_feature))]
-#![cfg_attr(not(stage0), feature(tbm_target_feature))]
-#![cfg_attr(not(stage0), feature(sse4a_target_feature))]
-#![cfg_attr(not(stage0), feature(arm_target_feature))]
-#![cfg_attr(not(stage0), feature(powerpc_target_feature))]
-#![cfg_attr(not(stage0), feature(mips_target_feature))]
-#![cfg_attr(not(stage0), feature(aarch64_target_feature))]
-
-#![cfg_attr(stage0, feature(target_feature))]
-#![cfg_attr(stage0, feature(cfg_target_feature))]
-#![cfg_attr(stage0, feature(fn_must_use))]
+#![feature(mmx_target_feature)]
+#![feature(tbm_target_feature)]
+#![feature(sse4a_target_feature)]
+#![feature(arm_target_feature)]
+#![feature(powerpc_target_feature)]
+#![feature(mips_target_feature)]
+#![feature(aarch64_target_feature)]
#[prelude_import]
#[allow(unused)]
///
/// Implementations that cannot be described in Rust
/// are implemented in `SelectionContext::copy_clone_conditions()` in librustc.
-#[cfg(not(stage0))]
mod copy_impls {
use super::Copy;
use mem;
use num::Float;
-#[cfg(not(stage0))] use num::FpCategory;
+use num::FpCategory;
use num::FpCategory as Fp;
/// The radix or base of the internal representation of `f32`.
// FIXME: remove (inline) this macro and the Float trait
// when updating to a bootstrap compiler that has the new lang items.
-#[cfg_attr(stage0, macro_export)]
#[unstable(feature = "core_float", issue = "32110")]
macro_rules! f32_core_methods { () => {
/// Returns `true` if this value is `NaN` and false otherwise.
#[lang = "f32"]
#[cfg(not(test))]
-#[cfg(not(stage0))]
impl f32 {
f32_core_methods!();
}
use mem;
use num::Float;
-#[cfg(not(stage0))] use num::FpCategory;
+use num::FpCategory;
use num::FpCategory as Fp;
/// The radix or base of the internal representation of `f64`.
// FIXME: remove (inline) this macro and the Float trait
// when updating to a bootstrap compiler that has the new lang items.
-#[cfg_attr(stage0, macro_export)]
#[unstable(feature = "core_float", issue = "32110")]
macro_rules! f64_core_methods { () => {
/// Returns `true` if this value is `NaN` and false otherwise.
#[lang = "f64"]
#[cfg(not(test))]
-#[cfg(not(stage0))]
impl f64 {
f64_core_methods!();
}
/// assert_eq!(m, -22016);
/// ```
#[unstable(feature = "reverse_bits", issue = "48763")]
- #[cfg(not(stage0))]
#[inline]
pub fn reverse_bits(self) -> Self {
(self as $UnsignedT).reverse_bits() as Self
/// assert_eq!(m, 43520);
/// ```
#[unstable(feature = "reverse_bits", issue = "48763")]
- #[cfg(not(stage0))]
#[inline]
pub fn reverse_bits(self) -> Self {
unsafe { intrinsics::bitreverse(self as $ActualT) as Self }
/// # Examples
///
/// ```
-/// #![feature(inclusive_range_methods)]
-///
/// assert_eq!((3..=5), std::ops::RangeInclusive::new(3, 5));
/// assert_eq!(3 + 4 + 5, (3..=5).sum());
///
// but it is known that LLVM is not able to optimize loops following that RFC.
// Consider adding an extra `bool` field to indicate emptiness of the range.
// See #45222 for performance test cases.
- #[cfg(not(stage0))]
pub(crate) start: Idx,
- #[cfg(not(stage0))]
pub(crate) end: Idx,
- /// The lower bound of the range (inclusive).
- #[cfg(stage0)]
- #[unstable(feature = "inclusive_range_fields", issue = "49022")]
- pub start: Idx,
- /// The upper bound of the range (inclusive).
- #[cfg(stage0)]
- #[unstable(feature = "inclusive_range_fields", issue = "49022")]
- pub end: Idx,
}
impl<Idx> RangeInclusive<Idx> {
/// # Examples
///
/// ```
- /// #![feature(inclusive_range_methods)]
/// use std::ops::RangeInclusive;
///
/// assert_eq!(3..=5, RangeInclusive::new(3, 5));
/// ```
- #[unstable(feature = "inclusive_range_methods", issue = "49022")]
+ #[stable(feature = "inclusive_range_methods", since = "1.27.0")]
#[inline]
pub const fn new(start: Idx, end: Idx) -> Self {
Self { start, end }
/// whether the inclusive range is empty, use the [`is_empty()`] method
/// instead of comparing `start() > end()`.
///
+ /// Note: the value returned by this method is unspecified after the range
+ /// has been iterated to exhaustion.
+ ///
/// [`end()`]: #method.end
/// [`is_empty()`]: #method.is_empty
///
/// # Examples
///
/// ```
- /// #![feature(inclusive_range_methods)]
- ///
/// assert_eq!((3..=5).start(), &3);
/// ```
- #[unstable(feature = "inclusive_range_methods", issue = "49022")]
+ #[stable(feature = "inclusive_range_methods", since = "1.27.0")]
#[inline]
pub fn start(&self) -> &Idx {
&self.start
/// whether the inclusive range is empty, use the [`is_empty()`] method
/// instead of comparing `start() > end()`.
///
+ /// Note: the value returned by this method is unspecified after the range
+ /// has been iterated to exhaustion.
+ ///
/// [`start()`]: #method.start
/// [`is_empty()`]: #method.is_empty
///
/// # Examples
///
/// ```
- /// #![feature(inclusive_range_methods)]
- ///
/// assert_eq!((3..=5).end(), &5);
/// ```
- #[unstable(feature = "inclusive_range_methods", issue = "49022")]
+ #[stable(feature = "inclusive_range_methods", since = "1.27.0")]
#[inline]
pub fn end(&self) -> &Idx {
&self.end
}
- /// Destructures the RangeInclusive into (lower bound, upper (inclusive) bound).
+ /// Destructures the `RangeInclusive` into (lower bound, upper (inclusive) bound).
+ ///
+ /// Note: the value returned by this method is unspecified after the range
+ /// has been iterated to exhaustion.
///
/// # Examples
///
/// ```
- /// #![feature(inclusive_range_methods)]
- ///
/// assert_eq!((3..=5).into_inner(), (3, 5));
/// ```
- #[unstable(feature = "inclusive_range_methods", issue = "49022")]
+ #[stable(feature = "inclusive_range_methods", since = "1.27.0")]
#[inline]
pub fn into_inner(self) -> (Idx, Idx) {
(self.start, self.end)
}
}
+ /// 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
/////////////////////////////////////////////////////////////////////////
#[stable(feature = "core_prelude", since = "1.4.0")]
#[doc(no_inline)]
pub use result::Result::{self, Ok, Err};
-
-// Re-exported extension traits for primitive types
-#[stable(feature = "core_prelude", since = "1.4.0")]
-#[doc(no_inline)]
-#[cfg(stage0)]
-pub use slice::SliceExt;
-#[stable(feature = "core_prelude", since = "1.4.0")]
-#[doc(no_inline)]
-#[cfg(stage0)]
-pub use str::StrExt;
// 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).*
/// Executes the destructor (if any) of the pointed-to value.
///
-/// This is semantically equivalent to calling [`ptr::read`] and discarding
-/// the result, but has the following advantages:
+/// This has two use cases:
///
/// * It is *required* to use `drop_in_place` to drop unsized types like
/// trait objects, because they can't be read out onto the stack and
/// dropped normally.
///
-/// * It is friendlier to the optimizer to do this over [`ptr::read`] when
+/// * It is friendlier to the optimizer to do this over `ptr::read` when
/// dropping manually allocated memory (e.g. when writing Box/Rc/Vec),
/// as the compiler doesn't need to prove that it's sound to elide the
/// copy.
///
-/// [`ptr::read`]: ../ptr/fn.read.html
-///
/// # Safety
///
-/// Behavior is undefined if any of the following conditions are violated:
-///
-/// * `to_drop` must point to valid memory.
-///
-/// * `to_drop` must be properly aligned.
-///
-/// Additionally, if `T` is not [`Copy`], using the pointed-to value after
-/// calling `drop_in_place` can cause undefined behavior. Note that `*to_drop =
-/// foo` counts as a use because it will cause the the value to be dropped
-/// again. [`write`] can be used to overwrite data without causing it to be
-/// dropped.
-///
-/// [`Copy`]: ../marker/trait.Copy.html
-/// [`write`]: ../ptr/fn.write.html
-///
-/// # Examples
-///
-/// Manually remove the last item from a vector:
-///
-/// ```
-/// use std::ptr;
-/// use std::rc::Rc;
-///
-/// let last = Rc::new(1);
-/// let weak = Rc::downgrade(&last);
-///
-/// let mut v = vec![Rc::new(0), last];
-///
-/// unsafe {
-/// // Without a call `drop_in_place`, the last item would never be dropped,
-/// // and the memory it manages would be leaked.
-/// ptr::drop_in_place(&mut v[1]);
-/// v.set_len(1);
-/// }
-///
-/// assert_eq!(v, &[0.into()]);
-///
-/// // Ensure that the last item was dropped.
-/// assert!(weak.upgrade().is_none());
-/// ```
+/// This has all the same safety problems as `ptr::read` with respect to
+/// invalid pointers, types, and double drops.
#[stable(feature = "drop_in_place", since = "1.8.0")]
#[lang = "drop_in_place"]
#[allow(unconditional_recursion)]
/// Swaps the values at two mutable locations of the same type, without
/// deinitializing either.
///
-/// But for the following two exceptions, this function is semantically
-/// equivalent to [`mem::swap`]:
-///
-/// * It operates on raw pointers instead of references. When references are
-/// available, [`mem::swap`] should be preferred.
-///
-/// * The two pointed-to values may overlap. If the values do overlap, then the
-/// overlapping region of memory from `x` will be used. This is demonstrated
-/// in the examples below.
-///
-/// [`mem::swap`]: ../mem/fn.swap.html
+/// The values pointed at by `x` and `y` may overlap, unlike `mem::swap` which
+/// is otherwise equivalent. If the values do overlap, then the overlapping
+/// region of memory from `x` will be used. This is demonstrated in the
+/// examples section below.
///
/// # Safety
///
-/// Behavior is undefined if any of the following conditions are violated:
+/// This function copies the memory through the raw pointers passed to it
+/// as arguments.
///
-/// * `x` and `y` must point to valid, initialized memory.
-///
-/// * `x` and `y` must be properly aligned.
+/// Ensure that these pointers are valid before calling `swap`.
///
/// # Examples
///
}
}
-/// Replaces the value at `dest` with `src`, returning the old value, without
-/// dropping either.
-///
-/// This function is semantically equivalent to [`mem::replace`] except that it
-/// operates on raw pointers instead of references. When references are
-/// available, [`mem::replace`] should be preferred.
-///
-/// [`mem::replace`]: ../mem/fn.replace.html
+/// Replaces the value at `dest` with `src`, returning the old
+/// value, without dropping either.
///
/// # Safety
///
-/// Behavior is undefined if any of the following conditions are violated:
-///
-/// * `dest` must point to valid, initialized memory.
-///
-/// * `dest` must be properly aligned.
-///
-/// # Examples
-///
-/// ```
-/// use std::ptr;
-///
-/// let mut rust = vec!['b', 'u', 's', 't'];
-///
-/// // `mem::replace` would have the same effect without requiring the unsafe
-/// // block.
-/// let b = unsafe {
-/// ptr::replace(&mut rust[0], 'r')
-/// };
-///
-/// assert_eq!(b, 'b');
-/// assert_eq!(rust, &['r', 'u', 's', 't']);
-/// ```
+/// This is only unsafe because it accepts a raw pointer.
+/// Otherwise, this operation is identical to `mem::replace`.
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub unsafe fn replace<T>(dest: *mut T, mut src: T) -> T {
///
/// # Safety
///
-/// Behavior is undefined if any of the following conditions are violated:
-///
-/// * `src` must point to valid, initialized memory.
+/// 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 be properly aligned. Use [`read_unaligned`] if this is not the
-/// case.
-///
-/// Additionally, if `T` is not [`Copy`], only the returned value *or* the
-/// pointed-to value can be used or dropped after calling `read`. `read` creates
-/// a bitwise copy of `T`, regardless of whether `T: Copy`, which can result
-/// in undefined behavior if both copies are used. Note that `*src = foo` counts
-/// as a use because it will attempt to drop the value previously at `*src`.
-/// [`write`] can be used to overwrite data without causing it to be dropped.
-///
-/// [`Copy`]: ../marker/trait.Copy.html
-/// [`read_unaligned`]: ./fn.read_unaligned.html
-/// [`write`]: ./fn.write.html
+/// The pointer must be aligned; use `read_unaligned` if that is not the case.
///
/// # Examples
///
/// assert_eq!(std::ptr::read(y), 12);
/// }
/// ```
-///
-/// Manually implement [`mem::swap`]:
-///
-/// ```
-/// use std::ptr;
-///
-/// fn swap<T>(a: &mut T, b: &mut T) {
-/// unsafe {
-/// // Create a bitwise copy of the value at `a` in `tmp`.
-/// let tmp = ptr::read(a);
-///
-/// // Exiting at this point (either by explicitly returning or by
-/// // calling a function which panics) would cause the value in `tmp` to
-/// // be dropped while the same value is still referenced by `a`. This
-/// // could trigger undefined behavior if `T` is not `Copy`.
-///
-/// // Create a bitwise copy of the value at `b` in `a`.
-/// // This is safe because mutable references cannot alias.
-/// ptr::copy_nonoverlapping(b, a, 1);
-///
-/// // As above, exiting here could trigger undefined behavior because
-/// // the same value is referenced by `a` and `b`.
-///
-/// // Move `tmp` into `b`.
-/// ptr::write(b, tmp);
-/// }
-/// }
-///
-/// let mut foo = "foo".to_owned();
-/// let mut bar = "bar".to_owned();
-///
-/// swap(&mut foo, &mut bar);
-///
-/// assert_eq!(foo, "bar");
-/// assert_eq!(bar, "foo");
-/// ```
-///
-/// [`mem::swap`]: ../mem/fn.swap.html
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub unsafe fn read<T>(src: *const T) -> T {
/// Reads the value from `src` without moving it. This leaves the
/// memory in `src` unchanged.
///
-/// Unlike [`read`], `read_unaligned` works with unaligned pointers.
-///
-/// [`read`]: ./fn.read.html
+/// Unlike `read`, the pointer may be unaligned.
///
/// # Safety
///
-/// Behavior is undefined if any of the following conditions are violated:
-///
-/// * `src` must point to valid, initialized memory.
-///
-/// Additionally, if `T` is not [`Copy`], only the returned value *or* the
-/// pointed-to value can be used or dropped after calling `read_unaligned`.
-/// `read_unaligned` creates a bitwise copy of `T`, regardless of whether `T:
-/// Copy`, and this can result in undefined behavior if both copies are used.
-/// Note that `*src = foo` counts as a use because it will attempt to drop the
-/// value previously at `*src`. [`write_unaligned`] can be used to overwrite
-/// data without causing it to be dropped.
-///
-/// [`Copy`]: ../marker/trait.Copy.html
-/// [`write_unaligned`]: ./fn.write_unaligned.html
+/// Beyond accepting a raw pointer, this is unsafe because it semantically
+/// moves the value out of `src` without preventing further usage of `src`.
+/// If `T` is not `Copy`, then care must be taken to ensure that the value at
+/// `src` is not used before the data is overwritten again (e.g. with `write`,
+/// `write_bytes`, or `copy`). Note that `*src = foo` counts as a use
+/// because it will attempt to drop the value previously at `*src`.
///
/// # Examples
///
-/// Access members of a packed struct by reference:
+/// Basic usage:
///
/// ```
-/// use std::ptr;
+/// let x = 12;
+/// let y = &x as *const i32;
///
-/// #[repr(packed, C)]
-/// #[derive(Default)]
-/// struct Packed {
-/// _padding: u8,
-/// unaligned: u32,
+/// unsafe {
+/// assert_eq!(std::ptr::read_unaligned(y), 12);
/// }
-///
-/// let x = Packed {
-/// _padding: 0x00,
-/// unaligned: 0x01020304,
-/// };
-///
-/// let v = unsafe {
-/// // Take a reference to a 32-bit integer which is not aligned.
-/// let unaligned = &x.unaligned;
-///
-/// // Dereferencing normally will emit an unaligned load instruction,
-/// // causing undefined behavior.
-/// // let v = *unaligned; // ERROR
-///
-/// // Instead, use `read_unaligned` to read improperly aligned values.
-/// let v = ptr::read_unaligned(unaligned);
-///
-/// v
-/// };
-///
-/// // Accessing unaligned values directly is safe.
-/// assert!(x.unaligned == v);
/// ```
#[inline]
#[stable(feature = "ptr_unaligned", since = "1.17.0")]
/// Overwrites a memory location with the given value without reading or
/// dropping the old value.
///
-/// `write` does not drop the contents of `dst`. This is safe, but it could leak
+/// # Safety
+///
+/// This operation is marked unsafe because it accepts a raw pointer.
+///
+/// It does not drop the contents of `dst`. This is safe, but it could leak
/// allocations or resources, so care must be taken not to overwrite an object
/// that should be dropped.
///
/// location pointed to by `dst`.
///
/// This is appropriate for initializing uninitialized memory, or overwriting
-/// memory that has previously been [`read`] from.
+/// memory that has previously been `read` from.
///
-/// [`read`]: ./fn.read.html
-///
-/// # Safety
-///
-/// Behavior is undefined if any of the following conditions are violated:
-///
-/// * `dst` must point to valid memory.
-///
-/// * `dst` must be properly aligned. Use [`write_unaligned`] if this is not the
-/// case.
-///
-/// [`write_unaligned`]: ./fn.write_unaligned.html
+/// The pointer must be aligned; use `write_unaligned` if that is not the case.
///
/// # Examples
///
/// assert_eq!(std::ptr::read(y), 12);
/// }
/// ```
-///
-/// Manually implement [`mem::swap`]:
-///
-/// ```
-/// use std::ptr;
-///
-/// fn swap<T>(a: &mut T, b: &mut T) {
-/// unsafe {
-/// let tmp = ptr::read(a);
-/// ptr::copy_nonoverlapping(b, a, 1);
-/// ptr::write(b, tmp);
-/// }
-/// }
-///
-/// let mut foo = "foo".to_owned();
-/// let mut bar = "bar".to_owned();
-///
-/// swap(&mut foo, &mut bar);
-///
-/// assert_eq!(foo, "bar");
-/// assert_eq!(bar, "foo");
-/// ```
-///
-/// [`mem::swap`]: ../mem/fn.swap.html
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub unsafe fn write<T>(dst: *mut T, src: T) {
/// Overwrites a memory location with the given value without reading or
/// dropping the old value.
///
-/// Unlike [`write`], the pointer may be unaligned.
+/// Unlike `write`, the pointer may be unaligned.
+///
+/// # Safety
+///
+/// This operation is marked unsafe because it accepts a raw pointer.
///
-/// `write_unaligned` does not drop the contents of `dst`. This is safe, but it
-/// could leak allocations or resources, so care must be taken not to overwrite
-/// an object that should be dropped.
+/// It does not drop the contents of `dst`. This is safe, but it could leak
+/// allocations or resources, so care must be taken not to overwrite an object
+/// that should be dropped.
///
/// Additionally, it does not drop `src`. Semantically, `src` is moved into the
/// location pointed to by `dst`.
///
/// This is appropriate for initializing uninitialized memory, or overwriting
-/// memory that has previously been read with [`read_unaligned`].
-///
-/// [`write`]: ./fn.write.html
-/// [`read_unaligned`]: ./fn.read_unaligned.html
-///
-/// # Safety
-///
-/// Behavior is undefined if any of the following conditions are violated:
-///
-/// * `dst` must point to valid memory.
+/// memory that has previously been `read` from.
///
/// # Examples
///
-/// Access fields in a packed struct:
+/// Basic usage:
///
/// ```
-/// use std::{mem, ptr};
-///
-/// #[repr(packed, C)]
-/// #[derive(Default)]
-/// struct Packed {
-/// _padding: u8,
-/// unaligned: u32,
-/// }
-///
-/// let v = 0x01020304;
-/// let mut x: Packed = unsafe { mem::zeroed() };
+/// let mut x = 0;
+/// let y = &mut x as *mut i32;
+/// let z = 12;
///
/// unsafe {
-/// // Take a reference to a 32-bit integer which is not aligned.
-/// let unaligned = &mut x.unaligned;
-///
-/// // Dereferencing normally will emit an unaligned store instruction,
-/// // causing undefined behavior.
-/// // *unaligned = v; // ERROR
-///
-/// // Instead, use `write_unaligned` to write improperly aligned values.
-/// ptr::write_unaligned(unaligned, v);
+/// std::ptr::write_unaligned(y, z);
+/// assert_eq!(std::ptr::read_unaligned(y), 12);
/// }
-///
-/// // Accessing unaligned values directly is safe.
-/// assert!(x.unaligned == v);
+/// ```
#[inline]
#[stable(feature = "ptr_unaligned", since = "1.17.0")]
pub unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
/// to not be elided or reordered by the compiler across other volatile
/// operations.
///
-/// Memory read with `read_volatile` should almost always be written to using
-/// [`write_volatile`].
-///
-/// [`write_volatile`]: ./fn.write_volatile.html
-///
/// # Notes
///
/// Rust does not currently have a rigorously and formally defined memory model,
///
/// # Safety
///
-/// Behavior is undefined if any of the following conditions are violated:
-///
-/// * `src` must point to valid, initialized memory.
-///
-/// * `src` must be properly aligned.
-///
-/// Like [`read`], `read_volatile` creates a bitwise copy of the pointed-to
-/// object, regardless of whether `T` is [`Copy`]. Using both values can cause
-/// undefined behavior. However, storing non-[`Copy`] data in I/O memory is
-/// almost certainly incorrect.
-///
-/// [`Copy`]: ../marker/trait.Copy.html
-/// [`read`]: ./fn.read.html
+/// Beyond accepting a raw pointer, this is unsafe because it semantically
+/// moves the value out of `src` without preventing further usage of `src`.
+/// If `T` is not `Copy`, then care must be taken to ensure that the value at
+/// `src` is not used before the data is overwritten again (e.g. with `write`,
+/// `write_bytes`, or `copy`). Note that `*src = foo` counts as a use
+/// because it will attempt to drop the value previously at `*src`.
///
/// # Examples
///
/// to not be elided or reordered by the compiler across other volatile
/// operations.
///
-/// Memory written with `write_volatile` should almost always be read from using
-/// [`read_volatile`].
-///
-/// `write_volatile` does not drop the contents of `dst`. This is safe, but it
-/// could leak allocations or resources, so care must be taken not to overwrite
-/// an object that should be dropped.
-///
-/// Additionally, it does not drop `src`. Semantically, `src` is moved into the
-/// location pointed to by `dst`.
-///
-/// [`read_volatile`]: ./fn.read_volatile.html
-///
/// # Notes
///
/// Rust does not currently have a rigorously and formally defined memory model,
///
/// # 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
///
copy_nonoverlapping(self, dest, count)
}
- /// Computes the byte offset that needs to be applied in order to
- /// make the pointer aligned to `align`.
+ /// Computes the offset that needs to be applied to the pointer in order to make it aligned to
+ /// `align`.
+ ///
/// If it is not possible to align the pointer, the implementation returns
/// `usize::max_value()`.
///
- /// There are no guarantees whatsover that offsetting the pointer will not
- /// overflow or go beyond the allocation that the pointer points into.
- /// It is up to the caller to ensure that the returned offset is correct
- /// in all terms other than alignment.
+ /// The offset is expressed in number of `T` elements, and not bytes. The value returned can be
+ /// used with the `offset` or `offset_to` methods.
+ ///
+ /// There are no guarantees whatsover that offsetting the pointer will not overflow or go
+ /// beyond the allocation that the pointer points into. It is up to the caller to ensure that
+ /// the returned offset is correct in all terms other than alignment.
+ ///
+ /// # Panics
+ ///
+ /// The function panics if `align` is not a power-of-two.
///
/// # Examples
///
/// # } }
/// ```
#[unstable(feature = "align_offset", issue = "44488")]
- pub fn align_offset(self, align: usize) -> usize {
+ #[cfg(not(stage0))]
+ pub fn align_offset(self, align: usize) -> usize where T: Sized {
+ if !align.is_power_of_two() {
+ panic!("align_offset: align is not a power-of-two");
+ }
unsafe {
- intrinsics::align_offset(self as *const _, align)
+ align_offset(self, align)
+ }
+ }
+
+ /// definitely docs.
+ #[unstable(feature = "align_offset", issue = "44488")]
+ #[cfg(stage0)]
+ pub fn align_offset(self, align: usize) -> usize where T: Sized {
+ if !align.is_power_of_two() {
+ panic!("align_offset: align is not a power-of-two");
+ }
+ unsafe {
+ intrinsics::align_offset(self as *const (), align)
}
}
}
+
#[lang = "mut_ptr"]
impl<T: ?Sized> *mut T {
/// Returns `true` if the pointer is null.
(self as *const T).wrapping_offset_from(origin)
}
- /// Computes the byte offset that needs to be applied in order to
- /// make the pointer aligned to `align`.
- /// If it is not possible to align the pointer, the implementation returns
- /// `usize::max_value()`.
- ///
- /// There are no guarantees whatsover that offsetting the pointer will not
- /// overflow or go beyond the allocation that the pointer points into.
- /// It is up to the caller to ensure that the returned offset is correct
- /// in all terms other than alignment.
- ///
- /// # Examples
- ///
- /// Accessing adjacent `u8` as `u16`
- ///
- /// ```
- /// # #![feature(align_offset)]
- /// # fn foo(n: usize) {
- /// # use std::mem::align_of;
- /// # unsafe {
- /// let x = [5u8, 6u8, 7u8, 8u8, 9u8];
- /// let ptr = &x[n] as *const u8;
- /// let offset = ptr.align_offset(align_of::<u16>());
- /// if offset < x.len() - n - 1 {
- /// let u16_ptr = ptr.offset(offset as isize) as *const u16;
- /// assert_ne!(*u16_ptr, 500);
- /// } else {
- /// // while the pointer can be aligned via `offset`, it would point
- /// // outside the allocation
- /// }
- /// # } }
- /// ```
- #[unstable(feature = "align_offset", issue = "44488")]
- pub fn align_offset(self, align: usize) -> usize {
- unsafe {
- intrinsics::align_offset(self as *const _, align)
- }
- }
-
/// Calculates the offset from a pointer (convenience for `.offset(count as isize)`).
///
/// `count` is in units of T; e.g. a `count` of 3 represents a pointer
{
swap(self, with)
}
+
+ /// Computes the offset that needs to be applied to the pointer in order to make it aligned to
+ /// `align`.
+ ///
+ /// If it is not possible to align the pointer, the implementation returns
+ /// `usize::max_value()`.
+ ///
+ /// The offset is expressed in number of `T` elements, and not bytes. The value returned can be
+ /// used with the `offset` or `offset_to` methods.
+ ///
+ /// There are no guarantees whatsover that offsetting the pointer will not overflow or go
+ /// beyond the allocation that the pointer points into. It is up to the caller to ensure that
+ /// the returned offset is correct in all terms other than alignment.
+ ///
+ /// # Panics
+ ///
+ /// The function panics if `align` is not a power-of-two.
+ ///
+ /// # Examples
+ ///
+ /// Accessing adjacent `u8` as `u16`
+ ///
+ /// ```
+ /// # #![feature(align_offset)]
+ /// # fn foo(n: usize) {
+ /// # use std::mem::align_of;
+ /// # unsafe {
+ /// let x = [5u8, 6u8, 7u8, 8u8, 9u8];
+ /// let ptr = &x[n] as *const u8;
+ /// let offset = ptr.align_offset(align_of::<u16>());
+ /// if offset < x.len() - n - 1 {
+ /// let u16_ptr = ptr.offset(offset as isize) as *const u16;
+ /// assert_ne!(*u16_ptr, 500);
+ /// } else {
+ /// // while the pointer can be aligned via `offset`, it would point
+ /// // outside the allocation
+ /// }
+ /// # } }
+ /// ```
+ #[unstable(feature = "align_offset", issue = "44488")]
+ #[cfg(not(stage0))]
+ pub fn align_offset(self, align: usize) -> usize where T: Sized {
+ if !align.is_power_of_two() {
+ panic!("align_offset: align is not a power-of-two");
+ }
+ unsafe {
+ align_offset(self, align)
+ }
+ }
+
+ /// definitely docs.
+ #[unstable(feature = "align_offset", issue = "44488")]
+ #[cfg(stage0)]
+ pub fn align_offset(self, align: usize) -> usize where T: Sized {
+ if !align.is_power_of_two() {
+ panic!("align_offset: align is not a power-of-two");
+ }
+ unsafe {
+ intrinsics::align_offset(self as *const (), align)
+ }
+ }
+}
+
+/// Align pointer `p`.
+///
+/// Calculate offset (in terms of elements of `stride` stride) that has to be applied
+/// to pointer `p` so that pointer `p` would get aligned to `a`.
+///
+/// Note: This implementation has been carefully tailored to not panic. It is UB for this to panic.
+/// The only real change that can be made here is change of `INV_TABLE_MOD_16` and associated
+/// constants.
+///
+/// If we ever decide to make it possible to call the intrinsic with `a` that is not a
+/// power-of-two, it will probably be more prudent to just change to a naive implementation rather
+/// than trying to adapt this to accomodate that change.
+///
+/// Any questions go to @nagisa.
+#[lang="align_offset"]
+#[cfg(not(stage0))]
+pub(crate) unsafe fn align_offset<T: Sized>(p: *const T, a: usize) -> usize {
+ /// Calculate multiplicative modular inverse of `x` modulo `m`.
+ ///
+ /// This implementation is tailored for align_offset and has following preconditions:
+ ///
+ /// * `m` is a power-of-two;
+ /// * `x < m`; (if `x ≥ m`, pass in `x % m` instead)
+ ///
+ /// Implementation of this function shall not panic. Ever.
+ #[inline]
+ fn mod_inv(x: usize, m: usize) -> usize {
+ /// Multiplicative modular inverse table modulo 2⁴ = 16.
+ ///
+ /// Note, that this table does not contain values where inverse does not exist (i.e. for
+ /// `0⁻¹ mod 16`, `2⁻¹ mod 16`, etc.)
+ const INV_TABLE_MOD_16: [usize; 8] = [1, 11, 13, 7, 9, 3, 5, 15];
+ /// Modulo for which the `INV_TABLE_MOD_16` is intended.
+ const INV_TABLE_MOD: usize = 16;
+ /// INV_TABLE_MOD²
+ const INV_TABLE_MOD_SQUARED: usize = INV_TABLE_MOD * INV_TABLE_MOD;
+
+ let table_inverse = INV_TABLE_MOD_16[(x & (INV_TABLE_MOD - 1)) >> 1];
+ if m <= INV_TABLE_MOD {
+ return table_inverse & (m - 1);
+ } else {
+ // We iterate "up" using the following formula:
+ //
+ // $$ xy ≡ 1 (mod 2ⁿ) → xy (2 - xy) ≡ 1 (mod 2²ⁿ) $$
+ //
+ // until 2²ⁿ ≥ m. Then we can reduce to our desired `m` by taking the result `mod m`.
+ let mut inverse = table_inverse;
+ let mut going_mod = INV_TABLE_MOD_SQUARED;
+ loop {
+ // y = y * (2 - xy) mod n
+ //
+ // Note, that we use wrapping operations here intentionally – the original formula
+ // uses e.g. subtraction `mod n`. It is entirely fine to do them `mod
+ // usize::max_value()` instead, because we take the result `mod n` at the end
+ // anyway.
+ inverse = inverse.wrapping_mul(
+ 2usize.wrapping_sub(x.wrapping_mul(inverse))
+ ) & (going_mod - 1);
+ if going_mod > m {
+ return inverse & (m - 1);
+ }
+ going_mod = going_mod.wrapping_mul(going_mod);
+ }
+ }
+ }
+
+ let stride = ::mem::size_of::<T>();
+ let a_minus_one = a.wrapping_sub(1);
+ let pmoda = p as usize & a_minus_one;
+
+ if pmoda == 0 {
+ // Already aligned. Yay!
+ return 0;
+ }
+
+ if stride <= 1 {
+ return if stride == 0 {
+ // If the pointer is not aligned, and the element is zero-sized, then no amount of
+ // elements will ever align the pointer.
+ !0
+ } else {
+ a.wrapping_sub(pmoda)
+ };
+ }
+
+ let smoda = stride & a_minus_one;
+ // a is power-of-two so cannot be 0. stride = 0 is handled above.
+ let gcdpow = intrinsics::cttz_nonzero(stride).min(intrinsics::cttz_nonzero(a));
+ let gcd = 1usize << gcdpow;
+
+ if gcd == 1 {
+ // This branch solves for the variable $o$ in following linear congruence equation:
+ //
+ // ⎰ p + o ≡ 0 (mod a) # $p + o$ must be aligned to specified alignment $a$
+ // ⎱ o ≡ 0 (mod s) # offset $o$ must be a multiple of stride $s$
+ //
+ // where
+ //
+ // * a, s are co-prime
+ //
+ // This gives us the formula below:
+ //
+ // o = (a - (p mod a)) * (s⁻¹ mod a) * s
+ //
+ // The first term is “the relative alignment of p to a”, the second term is “how does
+ // incrementing p by one s change the relative alignment of p”, the third term is
+ // translating change in units of s to a byte count.
+ //
+ // Furthermore, the result produced by this solution is not “minimal”, so it is necessary
+ // to take the result $o mod lcm(s, a)$. Since $s$ and $a$ are co-prime (i.e. $gcd(s, a) =
+ // 1$) and $lcm(s, a) = s * a / gcd(s, a)$, we can replace $lcm(s, a)$ with just a $s * a$.
+ //
+ // (Author note: we decided later on to express the offset in "elements" rather than bytes,
+ // which drops the multiplication by `s` on both sides of the modulo.)
+ return intrinsics::unchecked_rem(a.wrapping_sub(pmoda).wrapping_mul(mod_inv(smoda, a)), a);
+ }
+
+ if p as usize & (gcd - 1) == 0 {
+ // This can be aligned, but `a` and `stride` are not co-prime, so a somewhat adapted
+ // formula is used.
+ let j = a.wrapping_sub(pmoda) >> gcdpow;
+ let k = smoda >> gcdpow;
+ return intrinsics::unchecked_rem(j.wrapping_mul(mod_inv(k, a)), a >> gcdpow);
+ }
+
+ // Cannot be aligned at all.
+ return usize::max_value();
}
+
+
// Equality for pointers
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> PartialEq for *const T {
// Extension traits
//
-public_in_stage0! {
-{
-/// Extension methods for slices.
-#[unstable(feature = "core_slice_ext",
- reason = "stable interface provided by `impl [T]` in later crates",
- issue = "32110")]
-#[allow(missing_docs)] // documented elsewhere
-}
-trait SliceExt {
- type Item;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn split_at(&self, mid: usize) -> (&[Self::Item], &[Self::Item]);
-
- #[stable(feature = "core", since = "1.6.0")]
- fn iter(&self) -> Iter<Self::Item>;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn split<P>(&self, pred: P) -> Split<Self::Item, P>
- where P: FnMut(&Self::Item) -> bool;
-
- #[stable(feature = "slice_rsplit", since = "1.27.0")]
- fn rsplit<P>(&self, pred: P) -> RSplit<Self::Item, P>
- where P: FnMut(&Self::Item) -> bool;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn splitn<P>(&self, n: usize, pred: P) -> SplitN<Self::Item, P>
- where P: FnMut(&Self::Item) -> bool;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn rsplitn<P>(&self, n: usize, pred: P) -> RSplitN<Self::Item, P>
- where P: FnMut(&Self::Item) -> bool;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn windows(&self, size: usize) -> Windows<Self::Item>;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn chunks(&self, size: usize) -> Chunks<Self::Item>;
-
- #[unstable(feature = "exact_chunks", issue = "47115")]
- fn exact_chunks(&self, size: usize) -> ExactChunks<Self::Item>;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn get<I>(&self, index: I) -> Option<&I::Output>
- where I: SliceIndex<Self>;
- #[stable(feature = "core", since = "1.6.0")]
- fn first(&self) -> Option<&Self::Item>;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn split_first(&self) -> Option<(&Self::Item, &[Self::Item])>;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn split_last(&self) -> Option<(&Self::Item, &[Self::Item])>;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn last(&self) -> Option<&Self::Item>;
-
- #[stable(feature = "core", since = "1.6.0")]
- unsafe fn get_unchecked<I>(&self, index: I) -> &I::Output
- where I: SliceIndex<Self>;
- #[stable(feature = "core", since = "1.6.0")]
- fn as_ptr(&self) -> *const Self::Item;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn binary_search(&self, x: &Self::Item) -> Result<usize, usize>
- where Self::Item: Ord;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn binary_search_by<'a, F>(&'a self, f: F) -> Result<usize, usize>
- where F: FnMut(&'a Self::Item) -> Ordering;
-
- #[stable(feature = "slice_binary_search_by_key", since = "1.10.0")]
- fn binary_search_by_key<'a, B, F>(&'a self, b: &B, f: F) -> Result<usize, usize>
- where F: FnMut(&'a Self::Item) -> B,
- B: Ord;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn len(&self) -> usize;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn is_empty(&self) -> bool { self.len() == 0 }
-
- #[stable(feature = "core", since = "1.6.0")]
- fn get_mut<I>(&mut self, index: I) -> Option<&mut I::Output>
- where I: SliceIndex<Self>;
- #[stable(feature = "core", since = "1.6.0")]
- fn iter_mut(&mut self) -> IterMut<Self::Item>;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn first_mut(&mut self) -> Option<&mut Self::Item>;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn split_first_mut(&mut self) -> Option<(&mut Self::Item, &mut [Self::Item])>;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn split_last_mut(&mut self) -> Option<(&mut Self::Item, &mut [Self::Item])>;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn last_mut(&mut self) -> Option<&mut Self::Item>;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn split_mut<P>(&mut self, pred: P) -> SplitMut<Self::Item, P>
- where P: FnMut(&Self::Item) -> bool;
-
- #[stable(feature = "slice_rsplit", since = "1.27.0")]
- fn rsplit_mut<P>(&mut self, pred: P) -> RSplitMut<Self::Item, P>
- where P: FnMut(&Self::Item) -> bool;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn splitn_mut<P>(&mut self, n: usize, pred: P) -> SplitNMut<Self::Item, P>
- where P: FnMut(&Self::Item) -> bool;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn rsplitn_mut<P>(&mut self, n: usize, pred: P) -> RSplitNMut<Self::Item, P>
- where P: FnMut(&Self::Item) -> bool;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<Self::Item>;
-
- #[unstable(feature = "exact_chunks", issue = "47115")]
- fn exact_chunks_mut(&mut self, size: usize) -> ExactChunksMut<Self::Item>;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn swap(&mut self, a: usize, b: usize);
-
- #[stable(feature = "core", since = "1.6.0")]
- fn split_at_mut(&mut self, mid: usize) -> (&mut [Self::Item], &mut [Self::Item]);
-
- #[stable(feature = "core", since = "1.6.0")]
- fn reverse(&mut self);
-
- #[stable(feature = "core", since = "1.6.0")]
- unsafe fn get_unchecked_mut<I>(&mut self, index: I) -> &mut I::Output
- where I: SliceIndex<Self>;
- #[stable(feature = "core", since = "1.6.0")]
- fn as_mut_ptr(&mut self) -> *mut Self::Item;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn contains(&self, x: &Self::Item) -> bool where Self::Item: PartialEq;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn starts_with(&self, needle: &[Self::Item]) -> bool where Self::Item: PartialEq;
-
- #[stable(feature = "core", since = "1.6.0")]
- fn ends_with(&self, needle: &[Self::Item]) -> bool where Self::Item: PartialEq;
-
- #[stable(feature = "slice_rotate", since = "1.26.0")]
- fn rotate_left(&mut self, mid: usize);
-
- #[stable(feature = "slice_rotate", since = "1.26.0")]
- fn rotate_right(&mut self, k: usize);
-
- #[stable(feature = "clone_from_slice", since = "1.7.0")]
- fn clone_from_slice(&mut self, src: &[Self::Item]) where Self::Item: Clone;
-
- #[stable(feature = "copy_from_slice", since = "1.9.0")]
- fn copy_from_slice(&mut self, src: &[Self::Item]) where Self::Item: Copy;
-
- #[stable(feature = "swap_with_slice", since = "1.27.0")]
- fn swap_with_slice(&mut self, src: &mut [Self::Item]);
-
- #[stable(feature = "sort_unstable", since = "1.20.0")]
- fn sort_unstable(&mut self)
- where Self::Item: Ord;
-
- #[stable(feature = "sort_unstable", since = "1.20.0")]
- fn sort_unstable_by<F>(&mut self, compare: F)
- where F: FnMut(&Self::Item, &Self::Item) -> Ordering;
-
- #[stable(feature = "sort_unstable", since = "1.20.0")]
- fn sort_unstable_by_key<B, F>(&mut self, f: F)
- where F: FnMut(&Self::Item) -> B,
- B: Ord;
-}}
-
-// Use macros to be generic over const/mut
-macro_rules! slice_offset {
- ($ptr:expr, $by:expr) => {{
- let ptr = $ptr;
- if size_from_ptr(ptr) == 0 {
- (ptr as *mut i8).wrapping_offset($by) as _
- } else {
- ptr.offset($by)
- }
- }};
-}
-
-// make a &T from a *const T
-macro_rules! make_ref {
- ($ptr:expr) => {{
- let ptr = $ptr;
- if size_from_ptr(ptr) == 0 {
- // Use a non-null pointer value
- &*(1 as *mut _)
- } else {
- &*ptr
- }
- }};
-}
-
-// make a &mut T from a *mut T
-macro_rules! make_ref_mut {
- ($ptr:expr) => {{
- let ptr = $ptr;
- if size_from_ptr(ptr) == 0 {
- // Use a non-null pointer value
- &mut *(1 as *mut _)
- } else {
- &mut *ptr
- }
- }};
-}
-
-#[unstable(feature = "core_slice_ext",
- reason = "stable interface provided by `impl [T]` in later crates",
- issue = "32110")]
-impl<T> SliceExt for [T] {
- type Item = T;
-
- #[inline]
- fn split_at(&self, mid: usize) -> (&[T], &[T]) {
- (&self[..mid], &self[mid..])
- }
-
- #[inline]
- fn iter(&self) -> Iter<T> {
- unsafe {
- let p = if mem::size_of::<T>() == 0 {
- 1 as *const _
- } else {
- let p = self.as_ptr();
- assume(!p.is_null());
- p
- };
-
- Iter {
- ptr: p,
- end: slice_offset!(p, self.len() as isize),
- _marker: marker::PhantomData
- }
- }
- }
-
- #[inline]
- fn split<P>(&self, pred: P) -> Split<T, P>
- where P: FnMut(&T) -> bool
- {
- Split {
- v: self,
- pred,
- finished: false
- }
- }
-
- #[inline]
- fn rsplit<P>(&self, pred: P) -> RSplit<T, P>
- where P: FnMut(&T) -> bool
- {
- RSplit { inner: self.split(pred) }
- }
-
- #[inline]
- fn splitn<P>(&self, n: usize, pred: P) -> SplitN<T, P>
- where P: FnMut(&T) -> bool
- {
- SplitN {
- inner: GenericSplitN {
- iter: self.split(pred),
- count: n
- }
- }
- }
-
- #[inline]
- fn rsplitn<P>(&self, n: usize, pred: P) -> RSplitN<T, P>
- where P: FnMut(&T) -> bool
- {
- RSplitN {
- inner: GenericSplitN {
- iter: self.rsplit(pred),
- count: n
- }
- }
- }
-
- #[inline]
- fn windows(&self, size: usize) -> Windows<T> {
- assert!(size != 0);
- Windows { v: self, size: size }
- }
-
- #[inline]
- fn chunks(&self, chunk_size: usize) -> Chunks<T> {
- assert!(chunk_size != 0);
- Chunks { v: self, chunk_size: chunk_size }
- }
-
- #[inline]
- fn exact_chunks(&self, chunk_size: usize) -> ExactChunks<T> {
- assert!(chunk_size != 0);
- let rem = self.len() % chunk_size;
- let len = self.len() - rem;
- ExactChunks { v: &self[..len], chunk_size: chunk_size}
- }
-
- #[inline]
- fn get<I>(&self, index: I) -> Option<&I::Output>
- where I: SliceIndex<[T]>
- {
- index.get(self)
- }
-
- #[inline]
- fn first(&self) -> Option<&T> {
- if self.is_empty() { None } else { Some(&self[0]) }
- }
-
- #[inline]
- fn split_first(&self) -> Option<(&T, &[T])> {
- if self.is_empty() { None } else { Some((&self[0], &self[1..])) }
- }
-
- #[inline]
- fn split_last(&self) -> Option<(&T, &[T])> {
- let len = self.len();
- if len == 0 { None } else { Some((&self[len - 1], &self[..(len - 1)])) }
- }
-
- #[inline]
- fn last(&self) -> Option<&T> {
- if self.is_empty() { None } else { Some(&self[self.len() - 1]) }
- }
-
- #[inline]
- unsafe fn get_unchecked<I>(&self, index: I) -> &I::Output
- where I: SliceIndex<[T]>
- {
- index.get_unchecked(self)
- }
-
- #[inline]
- fn as_ptr(&self) -> *const T {
- self as *const [T] as *const T
- }
-
- fn binary_search_by<'a, F>(&'a self, mut f: F) -> Result<usize, usize>
- where F: FnMut(&'a T) -> Ordering
- {
- let s = self;
- let mut size = s.len();
- if size == 0 {
- return Err(0);
- }
- let mut base = 0usize;
- while size > 1 {
- let half = size / 2;
- let mid = base + half;
- // mid is always in [0, size), that means mid is >= 0 and < size.
- // mid >= 0: by definition
- // mid < size: mid = size / 2 + size / 4 + size / 8 ...
- let cmp = f(unsafe { s.get_unchecked(mid) });
- base = if cmp == Greater { base } else { mid };
- size -= half;
- }
- // base is always in [0, size) because base <= mid.
- let cmp = f(unsafe { s.get_unchecked(base) });
- if cmp == Equal { Ok(base) } else { Err(base + (cmp == Less) as usize) }
- }
-
- #[inline]
- fn len(&self) -> usize {
- unsafe {
- mem::transmute::<&[T], Repr<T>>(self).len
- }
- }
-
- #[inline]
- fn get_mut<I>(&mut self, index: I) -> Option<&mut I::Output>
- where I: SliceIndex<[T]>
- {
- index.get_mut(self)
- }
-
- #[inline]
- fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) {
- let len = self.len();
- let ptr = self.as_mut_ptr();
-
- unsafe {
- assert!(mid <= len);
-
- (from_raw_parts_mut(ptr, mid),
- from_raw_parts_mut(ptr.offset(mid as isize), len - mid))
- }
- }
-
- #[inline]
- fn iter_mut(&mut self) -> IterMut<T> {
- unsafe {
- let p = if mem::size_of::<T>() == 0 {
- 1 as *mut _
- } else {
- let p = self.as_mut_ptr();
- assume(!p.is_null());
- p
- };
-
- IterMut {
- ptr: p,
- end: slice_offset!(p, self.len() as isize),
- _marker: marker::PhantomData
- }
- }
- }
-
- #[inline]
- fn last_mut(&mut self) -> Option<&mut T> {
- let len = self.len();
- if len == 0 { return None; }
- Some(&mut self[len - 1])
- }
-
- #[inline]
- fn first_mut(&mut self) -> Option<&mut T> {
- if self.is_empty() { None } else { Some(&mut self[0]) }
- }
-
- #[inline]
- fn split_first_mut(&mut self) -> Option<(&mut T, &mut [T])> {
- if self.is_empty() { None } else {
- let split = self.split_at_mut(1);
- Some((&mut split.0[0], split.1))
- }
- }
-
- #[inline]
- fn split_last_mut(&mut self) -> Option<(&mut T, &mut [T])> {
- let len = self.len();
- if len == 0 { None } else {
- let split = self.split_at_mut(len - 1);
- Some((&mut split.1[0], split.0))
- }
- }
-
- #[inline]
- fn split_mut<P>(&mut self, pred: P) -> SplitMut<T, P>
- where P: FnMut(&T) -> bool
- {
- SplitMut { v: self, pred: pred, finished: false }
- }
-
- #[inline]
- fn rsplit_mut<P>(&mut self, pred: P) -> RSplitMut<T, P>
- where P: FnMut(&T) -> bool
- {
- RSplitMut { inner: self.split_mut(pred) }
- }
-
- #[inline]
- fn splitn_mut<P>(&mut self, n: usize, pred: P) -> SplitNMut<T, P>
- where P: FnMut(&T) -> bool
- {
- SplitNMut {
- inner: GenericSplitN {
- iter: self.split_mut(pred),
- count: n
- }
- }
- }
-
- #[inline]
- fn rsplitn_mut<P>(&mut self, n: usize, pred: P) -> RSplitNMut<T, P> where
- P: FnMut(&T) -> bool,
- {
- RSplitNMut {
- inner: GenericSplitN {
- iter: self.rsplit_mut(pred),
- count: n
- }
- }
- }
-
- #[inline]
- fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<T> {
- assert!(chunk_size != 0);
- ChunksMut { v: self, chunk_size: chunk_size }
- }
-
- #[inline]
- fn exact_chunks_mut(&mut self, chunk_size: usize) -> ExactChunksMut<T> {
- assert!(chunk_size != 0);
- let rem = self.len() % chunk_size;
- let len = self.len() - rem;
- ExactChunksMut { v: &mut self[..len], chunk_size: chunk_size}
- }
-
- #[inline]
- fn swap(&mut self, a: usize, b: usize) {
- unsafe {
- // Can't take two mutable loans from one vector, so instead just cast
- // them to their raw pointers to do the swap
- let pa: *mut T = &mut self[a];
- let pb: *mut T = &mut self[b];
- ptr::swap(pa, pb);
- }
- }
-
- fn reverse(&mut self) {
- let mut i: usize = 0;
- let ln = self.len();
-
- // For very small types, all the individual reads in the normal
- // path perform poorly. We can do better, given efficient unaligned
- // load/store, by loading a larger chunk and reversing a register.
-
- // Ideally LLVM would do this for us, as it knows better than we do
- // whether unaligned reads are efficient (since that changes between
- // different ARM versions, for example) and what the best chunk size
- // would be. Unfortunately, as of LLVM 4.0 (2017-05) it only unrolls
- // the loop, so we need to do this ourselves. (Hypothesis: reverse
- // is troublesome because the sides can be aligned differently --
- // will be, when the length is odd -- so there's no way of emitting
- // pre- and postludes to use fully-aligned SIMD in the middle.)
-
- let fast_unaligned =
- cfg!(any(target_arch = "x86", target_arch = "x86_64"));
-
- if fast_unaligned && mem::size_of::<T>() == 1 {
- // Use the llvm.bswap intrinsic to reverse u8s in a usize
- let chunk = mem::size_of::<usize>();
- while i + chunk - 1 < ln / 2 {
- unsafe {
- let pa: *mut T = self.get_unchecked_mut(i);
- let pb: *mut T = self.get_unchecked_mut(ln - i - chunk);
- let va = ptr::read_unaligned(pa as *mut usize);
- let vb = ptr::read_unaligned(pb as *mut usize);
- ptr::write_unaligned(pa as *mut usize, vb.swap_bytes());
- ptr::write_unaligned(pb as *mut usize, va.swap_bytes());
- }
- i += chunk;
- }
- }
-
- if fast_unaligned && mem::size_of::<T>() == 2 {
- // Use rotate-by-16 to reverse u16s in a u32
- let chunk = mem::size_of::<u32>() / 2;
- while i + chunk - 1 < ln / 2 {
- unsafe {
- let pa: *mut T = self.get_unchecked_mut(i);
- let pb: *mut T = self.get_unchecked_mut(ln - i - chunk);
- let va = ptr::read_unaligned(pa as *mut u32);
- let vb = ptr::read_unaligned(pb as *mut u32);
- ptr::write_unaligned(pa as *mut u32, vb.rotate_left(16));
- ptr::write_unaligned(pb as *mut u32, va.rotate_left(16));
- }
- i += chunk;
- }
- }
-
- while i < ln / 2 {
- // Unsafe swap to avoid the bounds check in safe swap.
- unsafe {
- let pa: *mut T = self.get_unchecked_mut(i);
- let pb: *mut T = self.get_unchecked_mut(ln - i - 1);
- ptr::swap(pa, pb);
- }
- i += 1;
- }
- }
-
- #[inline]
- unsafe fn get_unchecked_mut<I>(&mut self, index: I) -> &mut I::Output
- where I: SliceIndex<[T]>
- {
- index.get_unchecked_mut(self)
- }
-
- #[inline]
- fn as_mut_ptr(&mut self) -> *mut T {
- self as *mut [T] as *mut T
- }
-
- #[inline]
- fn contains(&self, x: &T) -> bool where T: PartialEq {
- x.slice_contains(self)
- }
-
- #[inline]
- fn starts_with(&self, needle: &[T]) -> bool where T: PartialEq {
- let n = needle.len();
- self.len() >= n && needle == &self[..n]
- }
-
- #[inline]
- fn ends_with(&self, needle: &[T]) -> bool where T: PartialEq {
- let (m, n) = (self.len(), needle.len());
- m >= n && needle == &self[m-n..]
- }
-
- fn binary_search(&self, x: &T) -> Result<usize, usize>
- where T: Ord
- {
- self.binary_search_by(|p| p.cmp(x))
- }
-
- fn rotate_left(&mut self, mid: usize) {
- assert!(mid <= self.len());
- let k = self.len() - mid;
-
- unsafe {
- let p = self.as_mut_ptr();
- rotate::ptr_rotate(mid, p.offset(mid as isize), k);
- }
- }
-
- fn rotate_right(&mut self, k: usize) {
- assert!(k <= self.len());
- let mid = self.len() - k;
-
- unsafe {
- let p = self.as_mut_ptr();
- rotate::ptr_rotate(mid, p.offset(mid as isize), k);
- }
- }
-
- #[inline]
- fn clone_from_slice(&mut self, src: &[T]) where T: Clone {
- assert!(self.len() == src.len(),
- "destination and source slices have different lengths");
- // NOTE: We need to explicitly slice them to the same length
- // for bounds checking to be elided, and the optimizer will
- // generate memcpy for simple cases (for example T = u8).
- let len = self.len();
- let src = &src[..len];
- for i in 0..len {
- self[i].clone_from(&src[i]);
+// Use macros to be generic over const/mut
+macro_rules! slice_offset {
+ ($ptr:expr, $by:expr) => {{
+ let ptr = $ptr;
+ if size_from_ptr(ptr) == 0 {
+ (ptr as *mut i8).wrapping_offset($by) as _
+ } else {
+ ptr.offset($by)
}
- }
+ }};
+}
- #[inline]
- fn copy_from_slice(&mut self, src: &[T]) where T: Copy {
- assert!(self.len() == src.len(),
- "destination and source slices have different lengths");
- unsafe {
- ptr::copy_nonoverlapping(
- src.as_ptr(), self.as_mut_ptr(), self.len());
+// make a &T from a *const T
+macro_rules! make_ref {
+ ($ptr:expr) => {{
+ let ptr = $ptr;
+ if size_from_ptr(ptr) == 0 {
+ // Use a non-null pointer value
+ &*(1 as *mut _)
+ } else {
+ &*ptr
}
- }
+ }};
+}
- #[inline]
- fn swap_with_slice(&mut self, src: &mut [T]) {
- assert!(self.len() == src.len(),
- "destination and source slices have different lengths");
- unsafe {
- ptr::swap_nonoverlapping(
- self.as_mut_ptr(), src.as_mut_ptr(), self.len());
+// make a &mut T from a *mut T
+macro_rules! make_ref_mut {
+ ($ptr:expr) => {{
+ let ptr = $ptr;
+ if size_from_ptr(ptr) == 0 {
+ // Use a non-null pointer value
+ &mut *(1 as *mut _)
+ } else {
+ &mut *ptr
}
- }
-
- #[inline]
- fn binary_search_by_key<'a, B, F>(&'a self, b: &B, mut f: F) -> Result<usize, usize>
- where F: FnMut(&'a Self::Item) -> B,
- B: Ord
- {
- self.binary_search_by(|k| f(k).cmp(b))
- }
-
- #[inline]
- fn sort_unstable(&mut self)
- where Self::Item: Ord
- {
- sort::quicksort(self, |a, b| a.lt(b));
- }
-
- #[inline]
- fn sort_unstable_by<F>(&mut self, mut compare: F)
- where F: FnMut(&Self::Item, &Self::Item) -> Ordering
- {
- sort::quicksort(self, |a, b| compare(a, b) == Ordering::Less);
- }
-
- #[inline]
- fn sort_unstable_by_key<B, F>(&mut self, mut f: F)
- where F: FnMut(&Self::Item) -> B,
- B: Ord
- {
- sort::quicksort(self, |a, b| f(a).lt(&f(b)));
- }
+ }};
}
-// FIXME: remove (inline) this macro and the SliceExt trait
-// when updating to a bootstrap compiler that has the new lang items.
-#[cfg_attr(stage0, macro_export)]
-#[unstable(feature = "core_slice_ext", issue = "32110")]
-macro_rules! slice_core_methods { () => {
+#[lang = "slice"]
+#[cfg(not(test))]
+impl<T> [T] {
/// Returns the number of elements in the slice.
///
/// # Examples
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn len(&self) -> usize {
- SliceExt::len(self)
+ unsafe {
+ mem::transmute::<&[T], Repr<T>>(self).len
+ }
}
/// Returns `true` if the slice has a length of 0.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn is_empty(&self) -> bool {
- SliceExt::is_empty(self)
+ self.len() == 0
}
/// Returns the first element of the slice, or `None` if it is empty.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn first(&self) -> Option<&T> {
- SliceExt::first(self)
+ if self.is_empty() { None } else { Some(&self[0]) }
}
/// Returns a mutable pointer to the first element of the slice, or `None` if it is empty.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn first_mut(&mut self) -> Option<&mut T> {
- SliceExt::first_mut(self)
+ if self.is_empty() { None } else { Some(&mut self[0]) }
}
/// Returns the first and all the rest of the elements of the slice, or `None` if it is empty.
#[stable(feature = "slice_splits", since = "1.5.0")]
#[inline]
pub fn split_first(&self) -> Option<(&T, &[T])> {
- SliceExt::split_first(self)
+ if self.is_empty() { None } else { Some((&self[0], &self[1..])) }
}
/// Returns the first and all the rest of the elements of the slice, or `None` if it is empty.
#[stable(feature = "slice_splits", since = "1.5.0")]
#[inline]
pub fn split_first_mut(&mut self) -> Option<(&mut T, &mut [T])> {
- SliceExt::split_first_mut(self)
+ if self.is_empty() { None } else {
+ let split = self.split_at_mut(1);
+ Some((&mut split.0[0], split.1))
+ }
}
/// Returns the last and all the rest of the elements of the slice, or `None` if it is empty.
#[stable(feature = "slice_splits", since = "1.5.0")]
#[inline]
pub fn split_last(&self) -> Option<(&T, &[T])> {
- SliceExt::split_last(self)
+ let len = self.len();
+ if len == 0 { None } else { Some((&self[len - 1], &self[..(len - 1)])) }
}
/// Returns the last and all the rest of the elements of the slice, or `None` if it is empty.
#[stable(feature = "slice_splits", since = "1.5.0")]
#[inline]
pub fn split_last_mut(&mut self) -> Option<(&mut T, &mut [T])> {
- SliceExt::split_last_mut(self)
+ let len = self.len();
+ if len == 0 { None } else {
+ let split = self.split_at_mut(len - 1);
+ Some((&mut split.1[0], split.0))
+ }
+
}
/// Returns the last element of the slice, or `None` if it is empty.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn last(&self) -> Option<&T> {
- SliceExt::last(self)
+ if self.is_empty() { None } else { Some(&self[self.len() - 1]) }
}
/// Returns a mutable pointer to the last item in the slice.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn last_mut(&mut self) -> Option<&mut T> {
- SliceExt::last_mut(self)
+ let len = self.len();
+ if len == 0 { return None; }
+ Some(&mut self[len - 1])
}
/// Returns a reference to an element or subslice depending on the type of
pub fn get<I>(&self, index: I) -> Option<&I::Output>
where I: SliceIndex<Self>
{
- SliceExt::get(self, index)
+ index.get(self)
}
/// Returns a mutable reference to an element or subslice depending on the
pub fn get_mut<I>(&mut self, index: I) -> Option<&mut I::Output>
where I: SliceIndex<Self>
{
- SliceExt::get_mut(self, index)
+ index.get_mut(self)
}
/// Returns a reference to an element or subslice, without doing bounds
pub unsafe fn get_unchecked<I>(&self, index: I) -> &I::Output
where I: SliceIndex<Self>
{
- SliceExt::get_unchecked(self, index)
+ index.get_unchecked(self)
}
/// Returns a mutable reference to an element or subslice, without doing
pub unsafe fn get_unchecked_mut<I>(&mut self, index: I) -> &mut I::Output
where I: SliceIndex<Self>
{
- SliceExt::get_unchecked_mut(self, index)
+ index.get_unchecked_mut(self)
}
/// Returns a raw pointer to the slice's buffer.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn as_ptr(&self) -> *const T {
- SliceExt::as_ptr(self)
+ self as *const [T] as *const T
}
/// Returns an unsafe mutable pointer to the slice's buffer.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut T {
- SliceExt::as_mut_ptr(self)
+ self as *mut [T] as *mut T
}
/// Swaps two elements in the slice.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn swap(&mut self, a: usize, b: usize) {
- SliceExt::swap(self, a, b)
+ unsafe {
+ // Can't take two mutable loans from one vector, so instead just cast
+ // them to their raw pointers to do the swap
+ let pa: *mut T = &mut self[a];
+ let pb: *mut T = &mut self[b];
+ ptr::swap(pa, pb);
+ }
}
- /// Reverses the order of elements in the slice, in place.
- ///
- /// # Examples
- ///
- /// ```
- /// let mut v = [1, 2, 3];
- /// v.reverse();
- /// assert!(v == [3, 2, 1]);
- /// ```
- #[stable(feature = "rust1", since = "1.0.0")]
- #[inline]
- pub fn reverse(&mut self) {
- SliceExt::reverse(self)
+ /// Reverses the order of elements in the slice, in place.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// let mut v = [1, 2, 3];
+ /// v.reverse();
+ /// assert!(v == [3, 2, 1]);
+ /// ```
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[inline]
+ pub fn reverse(&mut self) {
+ let mut i: usize = 0;
+ let ln = self.len();
+
+ // For very small types, all the individual reads in the normal
+ // path perform poorly. We can do better, given efficient unaligned
+ // load/store, by loading a larger chunk and reversing a register.
+
+ // Ideally LLVM would do this for us, as it knows better than we do
+ // whether unaligned reads are efficient (since that changes between
+ // different ARM versions, for example) and what the best chunk size
+ // would be. Unfortunately, as of LLVM 4.0 (2017-05) it only unrolls
+ // the loop, so we need to do this ourselves. (Hypothesis: reverse
+ // is troublesome because the sides can be aligned differently --
+ // will be, when the length is odd -- so there's no way of emitting
+ // pre- and postludes to use fully-aligned SIMD in the middle.)
+
+ let fast_unaligned =
+ cfg!(any(target_arch = "x86", target_arch = "x86_64"));
+
+ if fast_unaligned && mem::size_of::<T>() == 1 {
+ // Use the llvm.bswap intrinsic to reverse u8s in a usize
+ let chunk = mem::size_of::<usize>();
+ while i + chunk - 1 < ln / 2 {
+ unsafe {
+ let pa: *mut T = self.get_unchecked_mut(i);
+ let pb: *mut T = self.get_unchecked_mut(ln - i - chunk);
+ let va = ptr::read_unaligned(pa as *mut usize);
+ let vb = ptr::read_unaligned(pb as *mut usize);
+ ptr::write_unaligned(pa as *mut usize, vb.swap_bytes());
+ ptr::write_unaligned(pb as *mut usize, va.swap_bytes());
+ }
+ i += chunk;
+ }
+ }
+
+ if fast_unaligned && mem::size_of::<T>() == 2 {
+ // Use rotate-by-16 to reverse u16s in a u32
+ let chunk = mem::size_of::<u32>() / 2;
+ while i + chunk - 1 < ln / 2 {
+ unsafe {
+ let pa: *mut T = self.get_unchecked_mut(i);
+ let pb: *mut T = self.get_unchecked_mut(ln - i - chunk);
+ let va = ptr::read_unaligned(pa as *mut u32);
+ let vb = ptr::read_unaligned(pb as *mut u32);
+ ptr::write_unaligned(pa as *mut u32, vb.rotate_left(16));
+ ptr::write_unaligned(pb as *mut u32, va.rotate_left(16));
+ }
+ i += chunk;
+ }
+ }
+
+ while i < ln / 2 {
+ // Unsafe swap to avoid the bounds check in safe swap.
+ unsafe {
+ let pa: *mut T = self.get_unchecked_mut(i);
+ let pb: *mut T = self.get_unchecked_mut(ln - i - 1);
+ ptr::swap(pa, pb);
+ }
+ i += 1;
+ }
}
/// Returns an iterator over the slice.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn iter(&self) -> Iter<T> {
- SliceExt::iter(self)
+ unsafe {
+ let p = if mem::size_of::<T>() == 0 {
+ 1 as *const _
+ } else {
+ let p = self.as_ptr();
+ assume(!p.is_null());
+ p
+ };
+
+ Iter {
+ ptr: p,
+ end: slice_offset!(p, self.len() as isize),
+ _marker: marker::PhantomData
+ }
+ }
}
/// Returns an iterator that allows modifying each value.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn iter_mut(&mut self) -> IterMut<T> {
- SliceExt::iter_mut(self)
+ unsafe {
+ let p = if mem::size_of::<T>() == 0 {
+ 1 as *mut _
+ } else {
+ let p = self.as_mut_ptr();
+ assume(!p.is_null());
+ p
+ };
+
+ IterMut {
+ ptr: p,
+ end: slice_offset!(p, self.len() as isize),
+ _marker: marker::PhantomData
+ }
+ }
}
/// Returns an iterator over all contiguous windows of length
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn windows(&self, size: usize) -> Windows<T> {
- SliceExt::windows(self, size)
+ assert!(size != 0);
+ Windows { v: self, size: size }
}
/// Returns an iterator over `chunk_size` elements of the slice at a
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn chunks(&self, chunk_size: usize) -> Chunks<T> {
- SliceExt::chunks(self, chunk_size)
+ assert!(chunk_size != 0);
+ Chunks { v: self, chunk_size: chunk_size }
}
/// Returns an iterator over `chunk_size` elements of the slice at a
#[unstable(feature = "exact_chunks", issue = "47115")]
#[inline]
pub fn exact_chunks(&self, chunk_size: usize) -> ExactChunks<T> {
- SliceExt::exact_chunks(self, chunk_size)
+ assert!(chunk_size != 0);
+ let rem = self.len() % chunk_size;
+ let len = self.len() - rem;
+ ExactChunks { v: &self[..len], chunk_size: chunk_size}
}
/// Returns an iterator over `chunk_size` elements of the slice at a time.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<T> {
- SliceExt::chunks_mut(self, chunk_size)
+ assert!(chunk_size != 0);
+ ChunksMut { v: self, chunk_size: chunk_size }
}
/// Returns an iterator over `chunk_size` elements of the slice at a time.
#[unstable(feature = "exact_chunks", issue = "47115")]
#[inline]
pub fn exact_chunks_mut(&mut self, chunk_size: usize) -> ExactChunksMut<T> {
- SliceExt::exact_chunks_mut(self, chunk_size)
+ assert!(chunk_size != 0);
+ let rem = self.len() % chunk_size;
+ let len = self.len() - rem;
+ ExactChunksMut { v: &mut self[..len], chunk_size: chunk_size}
}
/// Divides one slice into two at an index.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn split_at(&self, mid: usize) -> (&[T], &[T]) {
- SliceExt::split_at(self, mid)
+ (&self[..mid], &self[mid..])
}
/// Divides one mutable slice into two at an index.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) {
- SliceExt::split_at_mut(self, mid)
+ let len = self.len();
+ let ptr = self.as_mut_ptr();
+
+ unsafe {
+ assert!(mid <= len);
+
+ (from_raw_parts_mut(ptr, mid),
+ from_raw_parts_mut(ptr.offset(mid as isize), len - mid))
+ }
}
/// Returns an iterator over subslices separated by elements that match
pub fn split<F>(&self, pred: F) -> Split<T, F>
where F: FnMut(&T) -> bool
{
- SliceExt::split(self, pred)
+ Split {
+ v: self,
+ pred,
+ finished: false
+ }
}
/// Returns an iterator over mutable subslices separated by elements that
pub fn split_mut<F>(&mut self, pred: F) -> SplitMut<T, F>
where F: FnMut(&T) -> bool
{
- SliceExt::split_mut(self, pred)
+ SplitMut { v: self, pred: pred, finished: false }
}
/// Returns an iterator over subslices separated by elements that match
pub fn rsplit<F>(&self, pred: F) -> RSplit<T, F>
where F: FnMut(&T) -> bool
{
- SliceExt::rsplit(self, pred)
+ RSplit { inner: self.split(pred) }
}
/// Returns an iterator over mutable subslices separated by elements that
pub fn rsplit_mut<F>(&mut self, pred: F) -> RSplitMut<T, F>
where F: FnMut(&T) -> bool
{
- SliceExt::rsplit_mut(self, pred)
+ RSplitMut { inner: self.split_mut(pred) }
}
/// Returns an iterator over subslices separated by elements that match
pub fn splitn<F>(&self, n: usize, pred: F) -> SplitN<T, F>
where F: FnMut(&T) -> bool
{
- SliceExt::splitn(self, n, pred)
+ SplitN {
+ inner: GenericSplitN {
+ iter: self.split(pred),
+ count: n
+ }
+ }
}
/// Returns an iterator over subslices separated by elements that match
pub fn splitn_mut<F>(&mut self, n: usize, pred: F) -> SplitNMut<T, F>
where F: FnMut(&T) -> bool
{
- SliceExt::splitn_mut(self, n, pred)
+ SplitNMut {
+ inner: GenericSplitN {
+ iter: self.split_mut(pred),
+ count: n
+ }
+ }
}
/// Returns an iterator over subslices separated by elements that match
pub fn rsplitn<F>(&self, n: usize, pred: F) -> RSplitN<T, F>
where F: FnMut(&T) -> bool
{
- SliceExt::rsplitn(self, n, pred)
+ RSplitN {
+ inner: GenericSplitN {
+ iter: self.rsplit(pred),
+ count: n
+ }
+ }
}
/// Returns an iterator over subslices separated by elements that match
pub fn rsplitn_mut<F>(&mut self, n: usize, pred: F) -> RSplitNMut<T, F>
where F: FnMut(&T) -> bool
{
- SliceExt::rsplitn_mut(self, n, pred)
+ RSplitNMut {
+ inner: GenericSplitN {
+ iter: self.rsplit_mut(pred),
+ count: n
+ }
+ }
}
/// Returns `true` if the slice contains an element with the given value.
pub fn contains(&self, x: &T) -> bool
where T: PartialEq
{
- SliceExt::contains(self, x)
+ x.slice_contains(self)
}
/// Returns `true` if `needle` is a prefix of the slice.
pub fn starts_with(&self, needle: &[T]) -> bool
where T: PartialEq
{
- SliceExt::starts_with(self, needle)
+ let n = needle.len();
+ self.len() >= n && needle == &self[..n]
}
/// Returns `true` if `needle` is a suffix of the slice.
pub fn ends_with(&self, needle: &[T]) -> bool
where T: PartialEq
{
- SliceExt::ends_with(self, needle)
+ let (m, n) = (self.len(), needle.len());
+ m >= n && needle == &self[m-n..]
}
/// Binary searches this sorted slice for a given element.
pub fn binary_search(&self, x: &T) -> Result<usize, usize>
where T: Ord
{
- SliceExt::binary_search(self, x)
+ self.binary_search_by(|p| p.cmp(x))
}
/// Binary searches this sorted slice with a comparator function.
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
- pub fn binary_search_by<'a, F>(&'a self, f: F) -> Result<usize, usize>
+ pub fn binary_search_by<'a, F>(&'a self, mut f: F) -> Result<usize, usize>
where F: FnMut(&'a T) -> Ordering
{
- SliceExt::binary_search_by(self, f)
+ let s = self;
+ let mut size = s.len();
+ if size == 0 {
+ return Err(0);
+ }
+ let mut base = 0usize;
+ while size > 1 {
+ let half = size / 2;
+ let mid = base + half;
+ // mid is always in [0, size), that means mid is >= 0 and < size.
+ // mid >= 0: by definition
+ // mid < size: mid = size / 2 + size / 4 + size / 8 ...
+ let cmp = f(unsafe { s.get_unchecked(mid) });
+ base = if cmp == Greater { base } else { mid };
+ size -= half;
+ }
+ // base is always in [0, size) because base <= mid.
+ let cmp = f(unsafe { s.get_unchecked(base) });
+ if cmp == Equal { Ok(base) } else { Err(base + (cmp == Less) as usize) }
+
}
/// Binary searches this sorted slice with a key extraction function.
/// ```
#[stable(feature = "slice_binary_search_by_key", since = "1.10.0")]
#[inline]
- pub fn binary_search_by_key<'a, B, F>(&'a self, b: &B, f: F) -> Result<usize, usize>
+ pub fn binary_search_by_key<'a, B, F>(&'a self, b: &B, mut f: F) -> Result<usize, usize>
where F: FnMut(&'a T) -> B,
B: Ord
{
- SliceExt::binary_search_by_key(self, b, f)
+ self.binary_search_by(|k| f(k).cmp(b))
}
/// Sorts the slice, but may not preserve the order of equal elements.
pub fn sort_unstable(&mut self)
where T: Ord
{
- SliceExt::sort_unstable(self);
+ sort::quicksort(self, |a, b| a.lt(b));
}
/// Sorts the slice with a comparator function, but may not preserve the order of equal
/// [pdqsort]: https://github.com/orlp/pdqsort
#[stable(feature = "sort_unstable", since = "1.20.0")]
#[inline]
- pub fn sort_unstable_by<F>(&mut self, compare: F)
+ pub fn sort_unstable_by<F>(&mut self, mut compare: F)
where F: FnMut(&T, &T) -> Ordering
{
- SliceExt::sort_unstable_by(self, compare);
+ sort::quicksort(self, |a, b| compare(a, b) == Ordering::Less);
}
/// Sorts the slice with a key extraction function, but may not preserve the order of equal
/// [pdqsort]: https://github.com/orlp/pdqsort
#[stable(feature = "sort_unstable", since = "1.20.0")]
#[inline]
- pub fn sort_unstable_by_key<K, F>(&mut self, f: F)
+ pub fn sort_unstable_by_key<K, F>(&mut self, mut f: F)
where F: FnMut(&T) -> K, K: Ord
{
- SliceExt::sort_unstable_by_key(self, f);
+ sort::quicksort(self, |a, b| f(a).lt(&f(b)));
}
/// Rotates the slice in-place such that the first `mid` elements of the
/// ```
#[stable(feature = "slice_rotate", since = "1.26.0")]
pub fn rotate_left(&mut self, mid: usize) {
- SliceExt::rotate_left(self, mid);
+ assert!(mid <= self.len());
+ let k = self.len() - mid;
+
+ unsafe {
+ let p = self.as_mut_ptr();
+ rotate::ptr_rotate(mid, p.offset(mid as isize), k);
+ }
}
/// Rotates the slice in-place such that the first `self.len() - k`
/// ```
#[stable(feature = "slice_rotate", since = "1.26.0")]
pub fn rotate_right(&mut self, k: usize) {
- SliceExt::rotate_right(self, k);
+ assert!(k <= self.len());
+ let mid = self.len() - k;
+
+ unsafe {
+ let p = self.as_mut_ptr();
+ rotate::ptr_rotate(mid, p.offset(mid as isize), k);
+ }
}
/// Copies the elements from `src` into `self`.
/// [`split_at_mut`]: #method.split_at_mut
#[stable(feature = "clone_from_slice", since = "1.7.0")]
pub fn clone_from_slice(&mut self, src: &[T]) where T: Clone {
- SliceExt::clone_from_slice(self, src)
+ assert!(self.len() == src.len(),
+ "destination and source slices have different lengths");
+ // NOTE: We need to explicitly slice them to the same length
+ // for bounds checking to be elided, and the optimizer will
+ // generate memcpy for simple cases (for example T = u8).
+ let len = self.len();
+ let src = &src[..len];
+ for i in 0..len {
+ self[i].clone_from(&src[i]);
+ }
+
}
/// Copies all elements from `src` into `self`, using a memcpy.
/// [`split_at_mut`]: #method.split_at_mut
#[stable(feature = "copy_from_slice", since = "1.9.0")]
pub fn copy_from_slice(&mut self, src: &[T]) where T: Copy {
- SliceExt::copy_from_slice(self, src)
+ assert!(self.len() == src.len(),
+ "destination and source slices have different lengths");
+ unsafe {
+ ptr::copy_nonoverlapping(
+ src.as_ptr(), self.as_mut_ptr(), self.len());
+ }
}
/// Swaps all elements in `self` with those in `other`.
/// [`split_at_mut`]: #method.split_at_mut
#[stable(feature = "swap_with_slice", since = "1.27.0")]
pub fn swap_with_slice(&mut self, other: &mut [T]) {
- SliceExt::swap_with_slice(self, other)
+ assert!(self.len() == other.len(),
+ "destination and source slices have different lengths");
+ unsafe {
+ ptr::swap_nonoverlapping(
+ self.as_mut_ptr(), other.as_mut_ptr(), self.len());
+ }
}
-}}
-#[lang = "slice"]
-#[cfg(not(test))]
-#[cfg(not(stage0))]
-impl<T> [T] {
- slice_core_methods!();
+ /// Function to calculate lenghts of the middle and trailing slice for `align_to{,_mut}`.
+ fn align_to_offsets<U>(&self) -> (usize, usize) {
+ // What we gonna do about `rest` is figure out what multiple of `U`s we can put in a
+ // lowest number of `T`s. And how many `T`s we need for each such "multiple".
+ //
+ // Consider for example T=u8 U=u16. Then we can put 1 U in 2 Ts. Simple. Now, consider
+ // for example a case where size_of::<T> = 16, size_of::<U> = 24. We can put 2 Us in
+ // place of every 3 Ts in the `rest` slice. A bit more complicated.
+ //
+ // Formula to calculate this is:
+ //
+ // Us = lcm(size_of::<T>, size_of::<U>) / size_of::<U>
+ // Ts = lcm(size_of::<T>, size_of::<U>) / size_of::<T>
+ //
+ // Expanded and simplified:
+ //
+ // Us = size_of::<T> / gcd(size_of::<T>, size_of::<U>)
+ // Ts = size_of::<U> / gcd(size_of::<T>, size_of::<U>)
+ //
+ // Luckily since all this is constant-evaluated... performance here matters not!
+ #[inline]
+ fn gcd(a: usize, b: usize) -> usize {
+ // iterative stein’s algorithm
+ // We should still make this `const fn` (and revert to recursive algorithm if we do)
+ // because relying on llvm to consteval all this is… well, it makes me
+ let (ctz_a, mut ctz_b) = unsafe {
+ if a == 0 { return b; }
+ if b == 0 { return a; }
+ (::intrinsics::cttz_nonzero(a), ::intrinsics::cttz_nonzero(b))
+ };
+ let k = ctz_a.min(ctz_b);
+ let mut a = a >> ctz_a;
+ let mut b = b;
+ loop {
+ // remove all factors of 2 from b
+ b >>= ctz_b;
+ if a > b {
+ ::mem::swap(&mut a, &mut b);
+ }
+ b = b - a;
+ unsafe {
+ if b == 0 {
+ break;
+ }
+ ctz_b = ::intrinsics::cttz_nonzero(b);
+ }
+ }
+ return a << k;
+ }
+ let gcd: usize = gcd(::mem::size_of::<T>(), ::mem::size_of::<U>());
+ let ts: usize = ::mem::size_of::<U>() / gcd;
+ let us: usize = ::mem::size_of::<T>() / gcd;
+
+ // Armed with this knowledge, we can find how many `U`s we can fit!
+ let us_len = self.len() / ts * us;
+ // And how many `T`s will be in the trailing slice!
+ let ts_len = self.len() % ts;
+ return (us_len, ts_len);
+ }
+
+ /// Transmute the slice to a slice of another type, ensuring aligment of the types is
+ /// maintained.
+ ///
+ /// This method splits the slice into three distinct slices: prefix, correctly aligned middle
+ /// slice of a new type, and the suffix slice. The middle slice will have the greatest length
+ /// possible for a given type and input slice.
+ ///
+ /// This method has no purpose when either input element `T` or output element `U` are
+ /// zero-sized and will return the original slice without splitting anything.
+ ///
+ /// # Unsafety
+ ///
+ /// This method is essentially a `transmute` with respect to the elements in the returned
+ /// middle slice, so all the usual caveats pertaining to `transmute::<T, U>` also apply here.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// # #![feature(slice_align_to)]
+ /// unsafe {
+ /// let bytes: [u8; 7] = [1, 2, 3, 4, 5, 6, 7];
+ /// let (prefix, shorts, suffix) = bytes.align_to::<u16>();
+ /// // less_efficient_algorithm_for_bytes(prefix);
+ /// // more_efficient_algorithm_for_aligned_shorts(shorts);
+ /// // less_efficient_algorithm_for_bytes(suffix);
+ /// }
+ /// ```
+ #[unstable(feature = "slice_align_to", issue = "44488")]
+ #[cfg(not(stage0))]
+ pub unsafe fn align_to<U>(&self) -> (&[T], &[U], &[T]) {
+ // Note that most of this function will be constant-evaluated,
+ if ::mem::size_of::<U>() == 0 || ::mem::size_of::<T>() == 0 {
+ // handle ZSTs specially, which is – don't handle them at all.
+ return (self, &[], &[]);
+ }
+
+ // First, find at what point do we split between the first and 2nd slice. Easy with
+ // ptr.align_offset.
+ let ptr = self.as_ptr();
+ let offset = ::ptr::align_offset(ptr, ::mem::align_of::<U>());
+ if offset > self.len() {
+ return (self, &[], &[]);
+ } else {
+ let (left, rest) = self.split_at(offset);
+ let (us_len, ts_len) = rest.align_to_offsets::<U>();
+ return (left,
+ from_raw_parts(rest.as_ptr() as *const U, us_len),
+ from_raw_parts(rest.as_ptr().offset((rest.len() - ts_len) as isize), ts_len))
+ }
+ }
+
+ /// Transmute the slice to a slice of another type, ensuring aligment of the types is
+ /// maintained.
+ ///
+ /// This method splits the slice into three distinct slices: prefix, correctly aligned middle
+ /// slice of a new type, and the suffix slice. The middle slice will have the greatest length
+ /// possible for a given type and input slice.
+ ///
+ /// This method has no purpose when either input element `T` or output element `U` are
+ /// zero-sized and will return the original slice without splitting anything.
+ ///
+ /// # Unsafety
+ ///
+ /// This method is essentially a `transmute` with respect to the elements in the returned
+ /// middle slice, so all the usual caveats pertaining to `transmute::<T, U>` also apply here.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// # #![feature(slice_align_to)]
+ /// unsafe {
+ /// let mut bytes: [u8; 7] = [1, 2, 3, 4, 5, 6, 7];
+ /// let (prefix, shorts, suffix) = bytes.align_to_mut::<u16>();
+ /// // less_efficient_algorithm_for_bytes(prefix);
+ /// // more_efficient_algorithm_for_aligned_shorts(shorts);
+ /// // less_efficient_algorithm_for_bytes(suffix);
+ /// }
+ /// ```
+ #[unstable(feature = "slice_align_to", issue = "44488")]
+ #[cfg(not(stage0))]
+ pub unsafe fn align_to_mut<U>(&mut self) -> (&mut [T], &mut [U], &mut [T]) {
+ // Note that most of this function will be constant-evaluated,
+ if ::mem::size_of::<U>() == 0 || ::mem::size_of::<T>() == 0 {
+ // handle ZSTs specially, which is – don't handle them at all.
+ return (self, &mut [], &mut []);
+ }
+
+ // First, find at what point do we split between the first and 2nd slice. Easy with
+ // ptr.align_offset.
+ let ptr = self.as_ptr();
+ let offset = ::ptr::align_offset(ptr, ::mem::align_of::<U>());
+ if offset > self.len() {
+ return (self, &mut [], &mut []);
+ } else {
+ let (left, rest) = self.split_at_mut(offset);
+ let (us_len, ts_len) = rest.align_to_offsets::<U>();
+ let mut_ptr = rest.as_mut_ptr();
+ return (left,
+ from_raw_parts_mut(mut_ptr as *mut U, us_len),
+ from_raw_parts_mut(mut_ptr.offset((rest.len() - ts_len) as isize), ts_len))
+ }
+ }
}
-// FIXME: remove (inline) this macro
-// when updating to a bootstrap compiler that has the new lang items.
-#[cfg_attr(stage0, macro_export)]
-#[unstable(feature = "core_slice_ext", issue = "32110")]
-macro_rules! slice_u8_core_methods { () => {
+#[lang = "slice_u8"]
+#[cfg(not(test))]
+impl [u8] {
/// Checks if all bytes in this slice are within the ASCII range.
#[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")]
#[inline]
byte.make_ascii_lowercase();
}
}
-}}
-#[lang = "slice_u8"]
-#[cfg(not(test))]
-#[cfg(not(stage0))]
-impl [u8] {
- slice_u8_core_methods!();
}
#[stable(feature = "rust1", since = "1.0.0")]
(..self.end+1).index_mut(slice)
}
}
-
-}
-
-public_in_stage0! {
-{
-/// Methods for string slices
-#[allow(missing_docs)]
-#[doc(hidden)]
-#[unstable(feature = "core_str_ext",
- reason = "stable interface provided by `impl str` in later crates",
- issue = "32110")]
}
-trait StrExt {
- // NB there are no docs here are they're all located on the StrExt trait in
- // liballoc, not here.
-
- #[stable(feature = "core", since = "1.6.0")]
- fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool;
- #[stable(feature = "core", since = "1.6.0")]
- fn chars(&self) -> Chars;
- #[stable(feature = "core", since = "1.6.0")]
- fn bytes(&self) -> Bytes;
- #[stable(feature = "core", since = "1.6.0")]
- fn char_indices(&self) -> CharIndices;
- #[stable(feature = "core", since = "1.6.0")]
- fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P>;
- #[stable(feature = "core", since = "1.6.0")]
- fn rsplit<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplit<'a, P>
- where P::Searcher: ReverseSearcher<'a>;
- #[stable(feature = "core", since = "1.6.0")]
- fn splitn<'a, P: Pattern<'a>>(&'a self, count: usize, pat: P) -> SplitN<'a, P>;
- #[stable(feature = "core", since = "1.6.0")]
- fn rsplitn<'a, P: Pattern<'a>>(&'a self, count: usize, pat: P) -> RSplitN<'a, P>
- where P::Searcher: ReverseSearcher<'a>;
- #[stable(feature = "core", since = "1.6.0")]
- fn split_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitTerminator<'a, P>;
- #[stable(feature = "core", since = "1.6.0")]
- fn rsplit_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplitTerminator<'a, P>
- where P::Searcher: ReverseSearcher<'a>;
- #[stable(feature = "core", since = "1.6.0")]
- fn matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> Matches<'a, P>;
- #[stable(feature = "core", since = "1.6.0")]
- fn rmatches<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatches<'a, P>
- where P::Searcher: ReverseSearcher<'a>;
- #[stable(feature = "core", since = "1.6.0")]
- fn match_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> MatchIndices<'a, P>;
- #[stable(feature = "core", since = "1.6.0")]
- fn rmatch_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatchIndices<'a, P>
- where P::Searcher: ReverseSearcher<'a>;
- #[stable(feature = "core", since = "1.6.0")]
- fn lines(&self) -> Lines;
- #[stable(feature = "core", since = "1.6.0")]
- #[rustc_deprecated(since = "1.6.0", reason = "use lines() instead now")]
- #[allow(deprecated)]
- fn lines_any(&self) -> LinesAny;
- #[stable(feature = "str_checked_slicing", since = "1.20.0")]
- fn get<I: SliceIndex<str>>(&self, i: I) -> Option<&I::Output>;
- #[stable(feature = "str_checked_slicing", since = "1.20.0")]
- fn get_mut<I: SliceIndex<str>>(&mut self, i: I) -> Option<&mut I::Output>;
- #[stable(feature = "str_checked_slicing", since = "1.20.0")]
- unsafe fn get_unchecked<I: SliceIndex<str>>(&self, i: I) -> &I::Output;
- #[stable(feature = "str_checked_slicing", since = "1.20.0")]
- unsafe fn get_unchecked_mut<I: SliceIndex<str>>(&mut self, i: I) -> &mut I::Output;
- #[stable(feature = "core", since = "1.6.0")]
- unsafe fn slice_unchecked(&self, begin: usize, end: usize) -> &str;
- #[stable(feature = "core", since = "1.6.0")]
- unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut str;
- #[stable(feature = "core", since = "1.6.0")]
- fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool;
- #[stable(feature = "core", since = "1.6.0")]
- fn ends_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool
- where P::Searcher: ReverseSearcher<'a>;
- #[stable(feature = "core", since = "1.6.0")]
- fn trim_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str
- where P::Searcher: DoubleEndedSearcher<'a>;
- #[stable(feature = "core", since = "1.6.0")]
- fn trim_left_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str;
- #[stable(feature = "core", since = "1.6.0")]
- fn trim_right_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str
- where P::Searcher: ReverseSearcher<'a>;
- #[stable(feature = "is_char_boundary", since = "1.9.0")]
- fn is_char_boundary(&self, index: usize) -> bool;
- #[stable(feature = "core", since = "1.6.0")]
- fn as_bytes(&self) -> &[u8];
- #[stable(feature = "str_mut_extras", since = "1.20.0")]
- unsafe fn as_bytes_mut(&mut self) -> &mut [u8];
- #[stable(feature = "core", since = "1.6.0")]
- fn find<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option<usize>;
- #[stable(feature = "core", since = "1.6.0")]
- fn rfind<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option<usize>
- where P::Searcher: ReverseSearcher<'a>;
- fn find_str<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option<usize>;
- #[stable(feature = "core", since = "1.6.0")]
- fn split_at(&self, mid: usize) -> (&str, &str);
- #[stable(feature = "core", since = "1.6.0")]
- fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str);
- #[stable(feature = "core", since = "1.6.0")]
- fn as_ptr(&self) -> *const u8;
- #[stable(feature = "core", since = "1.6.0")]
- fn len(&self) -> usize;
- #[stable(feature = "core", since = "1.6.0")]
- fn is_empty(&self) -> bool;
- #[stable(feature = "core", since = "1.6.0")]
- fn parse<T: FromStr>(&self) -> Result<T, T::Err>;
- #[stable(feature = "split_whitespace", since = "1.1.0")]
- fn split_whitespace<'a>(&'a self) -> SplitWhitespace<'a>;
- #[stable(feature = "rust1", since = "1.0.0")]
- fn trim(&self) -> &str;
- #[stable(feature = "rust1", since = "1.0.0")]
- fn trim_left(&self) -> &str;
- #[stable(feature = "rust1", since = "1.0.0")]
- fn trim_right(&self) -> &str;
-}}
// truncate `&str` to length at most equal to `max`
// return `true` if it were truncated, and the new str.
index, ch, char_range, s_trunc, ellipsis);
}
-#[stable(feature = "core", since = "1.6.0")]
-impl StrExt for str {
- #[inline]
- fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool {
- pat.is_contained_in(self)
- }
-
- #[inline]
- fn chars(&self) -> Chars {
- Chars{iter: self.as_bytes().iter()}
- }
-
- #[inline]
- fn bytes(&self) -> Bytes {
- Bytes(self.as_bytes().iter().cloned())
- }
-
- #[inline]
- fn char_indices(&self) -> CharIndices {
- CharIndices { front_offset: 0, iter: self.chars() }
- }
-
- #[inline]
- fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P> {
- Split(SplitInternal {
- start: 0,
- end: self.len(),
- matcher: pat.into_searcher(self),
- allow_trailing_empty: true,
- finished: false,
- })
- }
-
- #[inline]
- fn rsplit<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplit<'a, P>
- where P::Searcher: ReverseSearcher<'a>
- {
- RSplit(self.split(pat).0)
- }
-
- #[inline]
- fn splitn<'a, P: Pattern<'a>>(&'a self, count: usize, pat: P) -> SplitN<'a, P> {
- SplitN(SplitNInternal {
- iter: self.split(pat).0,
- count,
- })
- }
-
- #[inline]
- fn rsplitn<'a, P: Pattern<'a>>(&'a self, count: usize, pat: P) -> RSplitN<'a, P>
- where P::Searcher: ReverseSearcher<'a>
- {
- RSplitN(self.splitn(count, pat).0)
- }
-
- #[inline]
- fn split_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitTerminator<'a, P> {
- SplitTerminator(SplitInternal {
- allow_trailing_empty: false,
- ..self.split(pat).0
- })
- }
-
- #[inline]
- fn rsplit_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplitTerminator<'a, P>
- where P::Searcher: ReverseSearcher<'a>
- {
- RSplitTerminator(self.split_terminator(pat).0)
- }
-
- #[inline]
- fn matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> Matches<'a, P> {
- Matches(MatchesInternal(pat.into_searcher(self)))
- }
-
- #[inline]
- fn rmatches<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatches<'a, P>
- where P::Searcher: ReverseSearcher<'a>
- {
- RMatches(self.matches(pat).0)
- }
-
- #[inline]
- fn match_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> MatchIndices<'a, P> {
- MatchIndices(MatchIndicesInternal(pat.into_searcher(self)))
- }
-
- #[inline]
- fn rmatch_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatchIndices<'a, P>
- where P::Searcher: ReverseSearcher<'a>
- {
- RMatchIndices(self.match_indices(pat).0)
- }
- #[inline]
- fn lines(&self) -> Lines {
- Lines(self.split_terminator('\n').map(LinesAnyMap))
- }
-
- #[inline]
- #[allow(deprecated)]
- fn lines_any(&self) -> LinesAny {
- LinesAny(self.lines())
- }
-
- #[inline]
- fn get<I: SliceIndex<str>>(&self, i: I) -> Option<&I::Output> {
- i.get(self)
- }
-
- #[inline]
- fn get_mut<I: SliceIndex<str>>(&mut self, i: I) -> Option<&mut I::Output> {
- i.get_mut(self)
- }
-
- #[inline]
- unsafe fn get_unchecked<I: SliceIndex<str>>(&self, i: I) -> &I::Output {
- i.get_unchecked(self)
- }
-
- #[inline]
- unsafe fn get_unchecked_mut<I: SliceIndex<str>>(&mut self, i: I) -> &mut I::Output {
- i.get_unchecked_mut(self)
- }
-
- #[inline]
- unsafe fn slice_unchecked(&self, begin: usize, end: usize) -> &str {
- (begin..end).get_unchecked(self)
- }
-
- #[inline]
- unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut str {
- (begin..end).get_unchecked_mut(self)
- }
-
- #[inline]
- fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool {
- pat.is_prefix_of(self)
- }
-
- #[inline]
- fn ends_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool
- where P::Searcher: ReverseSearcher<'a>
- {
- pat.is_suffix_of(self)
- }
-
- #[inline]
- fn trim_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str
- where P::Searcher: DoubleEndedSearcher<'a>
- {
- let mut i = 0;
- let mut j = 0;
- let mut matcher = pat.into_searcher(self);
- if let Some((a, b)) = matcher.next_reject() {
- i = a;
- j = b; // Remember earliest known match, correct it below if
- // last match is different
- }
- if let Some((_, b)) = matcher.next_reject_back() {
- j = b;
- }
- unsafe {
- // Searcher is known to return valid indices
- self.slice_unchecked(i, j)
- }
- }
-
- #[inline]
- fn trim_left_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str {
- let mut i = self.len();
- let mut matcher = pat.into_searcher(self);
- if let Some((a, _)) = matcher.next_reject() {
- i = a;
- }
- unsafe {
- // Searcher is known to return valid indices
- self.slice_unchecked(i, self.len())
- }
- }
-
- #[inline]
- fn trim_right_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str
- where P::Searcher: ReverseSearcher<'a>
- {
- let mut j = 0;
- let mut matcher = pat.into_searcher(self);
- if let Some((_, b)) = matcher.next_reject_back() {
- j = b;
- }
- unsafe {
- // Searcher is known to return valid indices
- self.slice_unchecked(0, j)
- }
- }
-
- #[inline]
- fn is_char_boundary(&self, index: usize) -> bool {
- // 0 and len are always ok.
- // Test for 0 explicitly so that it can optimize out the check
- // easily and skip reading string data for that case.
- if index == 0 || index == self.len() { return true; }
- match self.as_bytes().get(index) {
- None => false,
- // This is bit magic equivalent to: b < 128 || b >= 192
- Some(&b) => (b as i8) >= -0x40,
- }
- }
-
- #[inline]
- fn as_bytes(&self) -> &[u8] {
- unsafe { &*(self as *const str as *const [u8]) }
- }
-
- #[inline]
- unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
- &mut *(self as *mut str as *mut [u8])
- }
-
- fn find<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option<usize> {
- pat.into_searcher(self).next_match().map(|(i, _)| i)
- }
-
- fn rfind<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option<usize>
- where P::Searcher: ReverseSearcher<'a>
- {
- pat.into_searcher(self).next_match_back().map(|(i, _)| i)
- }
-
- fn find_str<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option<usize> {
- self.find(pat)
- }
-
- #[inline]
- fn split_at(&self, mid: usize) -> (&str, &str) {
- // is_char_boundary checks that the index is in [0, .len()]
- if self.is_char_boundary(mid) {
- unsafe {
- (self.slice_unchecked(0, mid),
- self.slice_unchecked(mid, self.len()))
- }
- } else {
- slice_error_fail(self, 0, mid)
- }
- }
-
- fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str) {
- // is_char_boundary checks that the index is in [0, .len()]
- if self.is_char_boundary(mid) {
- let len = self.len();
- let ptr = self.as_ptr() as *mut u8;
- unsafe {
- (from_raw_parts_mut(ptr, mid),
- from_raw_parts_mut(ptr.offset(mid as isize), len - mid))
- }
- } else {
- slice_error_fail(self, 0, mid)
- }
- }
-
- #[inline]
- fn as_ptr(&self) -> *const u8 {
- self as *const str as *const u8
- }
-
- #[inline]
- fn len(&self) -> usize {
- self.as_bytes().len()
- }
-
- #[inline]
- fn is_empty(&self) -> bool { self.len() == 0 }
-
- #[inline]
- fn parse<T: FromStr>(&self) -> Result<T, T::Err> { FromStr::from_str(self) }
-
- #[inline]
- fn split_whitespace(&self) -> SplitWhitespace {
- SplitWhitespace { inner: self.split(IsWhitespace).filter(IsNotEmpty) }
- }
-
- #[inline]
- fn trim(&self) -> &str {
- self.trim_matches(|c: char| c.is_whitespace())
- }
-
- #[inline]
- fn trim_left(&self) -> &str {
- self.trim_left_matches(|c: char| c.is_whitespace())
- }
-
- #[inline]
- fn trim_right(&self) -> &str {
- self.trim_right_matches(|c: char| c.is_whitespace())
- }
-}
-
-// FIXME: remove (inline) this macro and the SliceExt trait
-// when updating to a bootstrap compiler that has the new lang items.
-#[cfg_attr(stage0, macro_export)]
-#[unstable(feature = "core_str_ext", issue = "32110")]
-macro_rules! str_core_methods { () => {
+#[lang = "str"]
+#[cfg(not(test))]
+impl str {
/// Returns the length of `self`.
///
/// This length is in bytes, not [`char`]s or graphemes. In other words,
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn len(&self) -> usize {
- StrExt::len(self)
+ self.as_bytes().len()
}
/// Returns `true` if `self` has a length of zero bytes.
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn is_empty(&self) -> bool {
- StrExt::is_empty(self)
+ self.len() == 0
}
/// Checks that `index`-th byte lies at the start and/or end of a
#[stable(feature = "is_char_boundary", since = "1.9.0")]
#[inline]
pub fn is_char_boundary(&self, index: usize) -> bool {
- StrExt::is_char_boundary(self, index)
+ // 0 and len are always ok.
+ // Test for 0 explicitly so that it can optimize out the check
+ // easily and skip reading string data for that case.
+ if index == 0 || index == self.len() { return true; }
+ match self.as_bytes().get(index) {
+ None => false,
+ // This is bit magic equivalent to: b < 128 || b >= 192
+ Some(&b) => (b as i8) >= -0x40,
+ }
}
/// Converts a string slice to a byte slice. To convert the byte slice back
#[stable(feature = "rust1", since = "1.0.0")]
#[inline(always)]
pub fn as_bytes(&self) -> &[u8] {
- StrExt::as_bytes(self)
+ unsafe { &*(self as *const str as *const [u8]) }
}
/// Converts a mutable string slice to a mutable byte slice. To convert the
#[stable(feature = "str_mut_extras", since = "1.20.0")]
#[inline(always)]
pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
- StrExt::as_bytes_mut(self)
+ &mut *(self as *mut str as *mut [u8])
}
/// Converts a string slice to a raw pointer.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn as_ptr(&self) -> *const u8 {
- StrExt::as_ptr(self)
+ self as *const str as *const u8
}
/// Returns a subslice of `str`.
#[stable(feature = "str_checked_slicing", since = "1.20.0")]
#[inline]
pub fn get<I: SliceIndex<str>>(&self, i: I) -> Option<&I::Output> {
- StrExt::get(self, i)
+ i.get(self)
}
/// Returns a mutable subslice of `str`.
#[stable(feature = "str_checked_slicing", since = "1.20.0")]
#[inline]
pub fn get_mut<I: SliceIndex<str>>(&mut self, i: I) -> Option<&mut I::Output> {
- StrExt::get_mut(self, i)
+ i.get_mut(self)
}
/// Returns a unchecked subslice of `str`.
#[stable(feature = "str_checked_slicing", since = "1.20.0")]
#[inline]
pub unsafe fn get_unchecked<I: SliceIndex<str>>(&self, i: I) -> &I::Output {
- StrExt::get_unchecked(self, i)
+ i.get_unchecked(self)
}
/// Returns a mutable, unchecked subslice of `str`.
#[stable(feature = "str_checked_slicing", since = "1.20.0")]
#[inline]
pub unsafe fn get_unchecked_mut<I: SliceIndex<str>>(&mut self, i: I) -> &mut I::Output {
- StrExt::get_unchecked_mut(self, i)
+ i.get_unchecked_mut(self)
}
/// Creates a string slice from another string slice, bypassing safety
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub unsafe fn slice_unchecked(&self, begin: usize, end: usize) -> &str {
- StrExt::slice_unchecked(self, begin, end)
+ (begin..end).get_unchecked(self)
}
/// Creates a string slice from another string slice, bypassing safety
#[stable(feature = "str_slice_mut", since = "1.5.0")]
#[inline]
pub unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut str {
- StrExt::slice_mut_unchecked(self, begin, end)
+ (begin..end).get_unchecked_mut(self)
}
/// Divide one string slice into two at an index.
#[inline]
#[stable(feature = "str_split_at", since = "1.4.0")]
pub fn split_at(&self, mid: usize) -> (&str, &str) {
- StrExt::split_at(self, mid)
+ // is_char_boundary checks that the index is in [0, .len()]
+ if self.is_char_boundary(mid) {
+ unsafe {
+ (self.slice_unchecked(0, mid),
+ self.slice_unchecked(mid, self.len()))
+ }
+ } else {
+ slice_error_fail(self, 0, mid)
+ }
}
/// Divide one mutable string slice into two at an index.
#[inline]
#[stable(feature = "str_split_at", since = "1.4.0")]
pub fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str) {
- StrExt::split_at_mut(self, mid)
+ // is_char_boundary checks that the index is in [0, .len()]
+ if self.is_char_boundary(mid) {
+ let len = self.len();
+ let ptr = self.as_ptr() as *mut u8;
+ unsafe {
+ (from_raw_parts_mut(ptr, mid),
+ from_raw_parts_mut(ptr.offset(mid as isize), len - mid))
+ }
+ } else {
+ slice_error_fail(self, 0, mid)
+ }
}
/// Returns an iterator over the [`char`]s of a string slice.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn chars(&self) -> Chars {
- StrExt::chars(self)
+ Chars{iter: self.as_bytes().iter()}
}
+
/// Returns an iterator over the [`char`]s of a string slice, and their
/// positions.
///
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn char_indices(&self) -> CharIndices {
- StrExt::char_indices(self)
+ CharIndices { front_offset: 0, iter: self.chars() }
}
/// An iterator over the bytes of a string slice.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn bytes(&self) -> Bytes {
- StrExt::bytes(self)
+ Bytes(self.as_bytes().iter().cloned())
}
/// Split a string slice by whitespace.
#[stable(feature = "split_whitespace", since = "1.1.0")]
#[inline]
pub fn split_whitespace(&self) -> SplitWhitespace {
- StrExt::split_whitespace(self)
+ SplitWhitespace { inner: self.split(IsWhitespace).filter(IsNotEmpty) }
}
/// An iterator over the lines of a string, as string slices.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn lines(&self) -> Lines {
- StrExt::lines(self)
+ Lines(self.split_terminator('\n').map(LinesAnyMap))
}
/// An iterator over the lines of a string.
#[inline]
#[allow(deprecated)]
pub fn lines_any(&self) -> LinesAny {
- StrExt::lines_any(self)
+ LinesAny(self.lines())
}
/// Returns an iterator of `u16` over the string encoded as UTF-16.
/// ```
#[stable(feature = "encode_utf16", since = "1.8.0")]
pub fn encode_utf16(&self) -> EncodeUtf16 {
- EncodeUtf16::new(self)
+ EncodeUtf16 { chars: self.chars(), extra: 0 }
}
/// Returns `true` if the given pattern matches a sub-slice of
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool {
- StrExt::contains(self, pat)
+ pat.is_contained_in(self)
}
/// Returns `true` if the given pattern matches a prefix of this
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool {
- StrExt::starts_with(self, pat)
+ pat.is_prefix_of(self)
}
/// Returns `true` if the given pattern matches a suffix of this
pub fn ends_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool
where P::Searcher: ReverseSearcher<'a>
{
- StrExt::ends_with(self, pat)
+ pat.is_suffix_of(self)
}
/// Returns the byte index of the first character of this string slice that
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn find<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option<usize> {
- StrExt::find(self, pat)
+ pat.into_searcher(self).next_match().map(|(i, _)| i)
}
/// Returns the byte index of the last character of this string slice that
pub fn rfind<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option<usize>
where P::Searcher: ReverseSearcher<'a>
{
- StrExt::rfind(self, pat)
+ pat.into_searcher(self).next_match_back().map(|(i, _)| i)
}
/// An iterator over substrings of this string slice, separated by
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P> {
- StrExt::split(self, pat)
+ Split(SplitInternal {
+ start: 0,
+ end: self.len(),
+ matcher: pat.into_searcher(self),
+ allow_trailing_empty: true,
+ finished: false,
+ })
}
/// An iterator over substrings of the given string slice, separated by
pub fn rsplit<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplit<'a, P>
where P::Searcher: ReverseSearcher<'a>
{
- StrExt::rsplit(self, pat)
+ RSplit(self.split(pat).0)
}
/// An iterator over substrings of the given string slice, separated by
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn split_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitTerminator<'a, P> {
- StrExt::split_terminator(self, pat)
+ SplitTerminator(SplitInternal {
+ allow_trailing_empty: false,
+ ..self.split(pat).0
+ })
}
/// An iterator over substrings of `self`, separated by characters
pub fn rsplit_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplitTerminator<'a, P>
where P::Searcher: ReverseSearcher<'a>
{
- StrExt::rsplit_terminator(self, pat)
+ RSplitTerminator(self.split_terminator(pat).0)
}
/// An iterator over substrings of the given string slice, separated by a
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn splitn<'a, P: Pattern<'a>>(&'a self, n: usize, pat: P) -> SplitN<'a, P> {
- StrExt::splitn(self, n, pat)
+ SplitN(SplitNInternal {
+ iter: self.split(pat).0,
+ count: n,
+ })
}
/// An iterator over substrings of this string slice, separated by a
pub fn rsplitn<'a, P: Pattern<'a>>(&'a self, n: usize, pat: P) -> RSplitN<'a, P>
where P::Searcher: ReverseSearcher<'a>
{
- StrExt::rsplitn(self, n, pat)
+ RSplitN(self.splitn(n, pat).0)
}
/// An iterator over the disjoint matches of a pattern within the given string
#[stable(feature = "str_matches", since = "1.2.0")]
#[inline]
pub fn matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> Matches<'a, P> {
- StrExt::matches(self, pat)
+ Matches(MatchesInternal(pat.into_searcher(self)))
}
/// An iterator over the disjoint matches of a pattern within this string slice,
pub fn rmatches<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatches<'a, P>
where P::Searcher: ReverseSearcher<'a>
{
- StrExt::rmatches(self, pat)
+ RMatches(self.matches(pat).0)
}
/// An iterator over the disjoint matches of a pattern within this string
#[stable(feature = "str_match_indices", since = "1.5.0")]
#[inline]
pub fn match_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> MatchIndices<'a, P> {
- StrExt::match_indices(self, pat)
+ MatchIndices(MatchIndicesInternal(pat.into_searcher(self)))
}
/// An iterator over the disjoint matches of a pattern within `self`,
pub fn rmatch_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatchIndices<'a, P>
where P::Searcher: ReverseSearcher<'a>
{
- StrExt::rmatch_indices(self, pat)
+ RMatchIndices(self.match_indices(pat).0)
}
/// Returns a string slice with leading and trailing whitespace removed.
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn trim(&self) -> &str {
- StrExt::trim(self)
+ self.trim_matches(|c: char| c.is_whitespace())
}
/// Returns a string slice with leading whitespace removed.
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn trim_left(&self) -> &str {
- StrExt::trim_left(self)
+ self.trim_left_matches(|c: char| c.is_whitespace())
}
/// Returns a string slice with trailing whitespace removed.
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn trim_right(&self) -> &str {
- StrExt::trim_right(self)
+ self.trim_right_matches(|c: char| c.is_whitespace())
}
/// Returns a string slice with all prefixes and suffixes that match a
pub fn trim_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str
where P::Searcher: DoubleEndedSearcher<'a>
{
- StrExt::trim_matches(self, pat)
+ let mut i = 0;
+ let mut j = 0;
+ let mut matcher = pat.into_searcher(self);
+ if let Some((a, b)) = matcher.next_reject() {
+ i = a;
+ j = b; // Remember earliest known match, correct it below if
+ // last match is different
+ }
+ if let Some((_, b)) = matcher.next_reject_back() {
+ j = b;
+ }
+ unsafe {
+ // Searcher is known to return valid indices
+ self.slice_unchecked(i, j)
+ }
}
/// Returns a string slice with all prefixes that match a pattern
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn trim_left_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str {
- StrExt::trim_left_matches(self, pat)
+ let mut i = self.len();
+ let mut matcher = pat.into_searcher(self);
+ if let Some((a, _)) = matcher.next_reject() {
+ i = a;
+ }
+ unsafe {
+ // Searcher is known to return valid indices
+ self.slice_unchecked(i, self.len())
+ }
}
/// Returns a string slice with all suffixes that match a pattern
pub fn trim_right_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str
where P::Searcher: ReverseSearcher<'a>
{
- StrExt::trim_right_matches(self, pat)
+ let mut j = 0;
+ let mut matcher = pat.into_searcher(self);
+ if let Some((_, b)) = matcher.next_reject_back() {
+ j = b;
+ }
+ unsafe {
+ // Searcher is known to return valid indices
+ self.slice_unchecked(0, j)
+ }
}
/// Parses this string slice into another type.
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn parse<F: FromStr>(&self) -> Result<F, F::Err> {
- StrExt::parse(self)
+ FromStr::from_str(self)
}
/// Checks if all characters in this string are within the ASCII range.
let me = unsafe { self.as_bytes_mut() };
me.make_ascii_lowercase()
}
-}}
-
-#[lang = "str"]
-#[cfg(not(test))]
-#[cfg(not(stage0))]
-impl str {
- str_core_methods!();
}
-
#[stable(feature = "rust1", since = "1.0.0")]
impl AsRef<[u8]> for str {
#[inline]
extra: u16,
}
-// FIXME: remove (inline) this method
-// when updating to a bootstrap compiler that has the new lang items.
-// For grepping purpose: #[cfg(stage0)]
-impl<'a> EncodeUtf16<'a> {
- #[unstable(feature = "core_str_ext", issue = "32110")]
- #[doc(hidden)]
- pub fn new(s: &'a str) -> Self {
- EncodeUtf16 { chars: s.chars(), extra: 0 }
- }
-}
-
#[stable(feature = "collection_debug", since = "1.17.0")]
impl<'a> fmt::Debug for EncodeUtf16<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
#![feature(try_from)]
#![feature(try_trait)]
#![feature(exact_chunks)]
-#![cfg_attr(stage0, feature(atomic_nand))]
+#![feature(slice_align_to)]
+#![feature(align_offset)]
#![feature(reverse_bits)]
-#![feature(inclusive_range_methods)]
#![feature(iterator_find_map)]
#![feature(slice_internals)]
}
#[test]
- #[cfg(not(stage0))]
fn test_reverse_bits() {
assert_eq!(A.reverse_bits().reverse_bits(), A);
assert_eq!(B.reverse_bits().reverse_bits(), B);
}
DROPS.with(|d| assert_eq!(*d.borrow(), [0]));
}
+
+#[test]
+fn align_offset_zst() {
+ // For pointers of stride = 0, the pointer is already aligned or it cannot be aligned at
+ // all, because no amount of elements will align the pointer.
+ let mut p = 1;
+ while p < 1024 {
+ assert_eq!((p as *const ()).align_offset(p), 0);
+ if p != 1 {
+ assert_eq!(((p + 1) as *const ()).align_offset(p), !0);
+ }
+ p = (p + 1).next_power_of_two();
+ }
+}
+
+#[test]
+fn align_offset_stride1() {
+ // For pointers of stride = 1, the pointer can always be aligned. The offset is equal to
+ // number of bytes.
+ let mut align = 1;
+ while align < 1024 {
+ for ptr in 1..2*align {
+ let expected = ptr % align;
+ let offset = if expected == 0 { 0 } else { align - expected };
+ assert_eq!((ptr as *const u8).align_offset(align), offset,
+ "ptr = {}, align = {}, size = 1", ptr, align);
+ }
+ align = (align + 1).next_power_of_two();
+ }
+}
+
+#[test]
+fn align_offset_weird_strides() {
+ #[repr(packed)]
+ struct A3(u16, u8);
+ struct A4(u32);
+ #[repr(packed)]
+ struct A5(u32, u8);
+ #[repr(packed)]
+ struct A6(u32, u16);
+ #[repr(packed)]
+ struct A7(u32, u16, u8);
+ #[repr(packed)]
+ struct A8(u32, u32);
+ #[repr(packed)]
+ struct A9(u32, u32, u8);
+ #[repr(packed)]
+ struct A10(u32, u32, u16);
+
+ unsafe fn test_weird_stride<T>(ptr: *const T, align: usize) -> bool {
+ let numptr = ptr as usize;
+ let mut expected = usize::max_value();
+ // Naive but definitely correct way to find the *first* aligned element of stride::<T>.
+ for el in 0..align {
+ if (numptr + el * ::std::mem::size_of::<T>()) % align == 0 {
+ expected = el;
+ break;
+ }
+ }
+ let got = ptr.align_offset(align);
+ if got != expected {
+ eprintln!("aligning {:p} (with stride of {}) to {}, expected {}, got {}", ptr,
+ ::std::mem::size_of::<T>(), align, expected, got);
+ return true;
+ }
+ return false;
+ }
+
+ // For pointers of stride != 1, we verify the algorithm against the naivest possible
+ // implementation
+ let mut align = 1;
+ let mut x = false;
+ while align < 1024 {
+ for ptr in 1usize..4*align {
+ unsafe {
+ x |= test_weird_stride::<A3>(ptr as *const A3, align);
+ x |= test_weird_stride::<A4>(ptr as *const A4, align);
+ x |= test_weird_stride::<A5>(ptr as *const A5, align);
+ x |= test_weird_stride::<A6>(ptr as *const A6, align);
+ x |= test_weird_stride::<A7>(ptr as *const A7, align);
+ x |= test_weird_stride::<A8>(ptr as *const A8, align);
+ x |= test_weird_stride::<A9>(ptr as *const A9, align);
+ x |= test_weird_stride::<A10>(ptr as *const A10, align);
+ }
+ }
+ align = (align + 1).next_power_of_two();
+ }
+ assert!(!x);
+}
}
}
}
+
+#[test]
+fn test_align_to_simple() {
+ let bytes = [1u8, 2, 3, 4, 5, 6, 7];
+ let (prefix, aligned, suffix) = unsafe { bytes.align_to::<u16>() };
+ assert_eq!(aligned.len(), 3);
+ assert!(prefix == [1] || suffix == [7]);
+ let expect1 = [1 << 8 | 2, 3 << 8 | 4, 5 << 8 | 6];
+ let expect2 = [1 | 2 << 8, 3 | 4 << 8, 5 | 6 << 8];
+ let expect3 = [2 << 8 | 3, 4 << 8 | 5, 6 << 8 | 7];
+ let expect4 = [2 | 3 << 8, 4 | 5 << 8, 6 | 7 << 8];
+ assert!(aligned == expect1 || aligned == expect2 || aligned == expect3 || aligned == expect4,
+ "aligned={:?} expected={:?} || {:?} || {:?} || {:?}",
+ aligned, expect1, expect2, expect3, expect4);
+}
+
+#[test]
+fn test_align_to_zst() {
+ let bytes = [1, 2, 3, 4, 5, 6, 7];
+ let (prefix, aligned, suffix) = unsafe { bytes.align_to::<()>() };
+ assert_eq!(aligned.len(), 0);
+ assert!(prefix == [1, 2, 3, 4, 5, 6, 7] || suffix == [1, 2, 3, 4, 5, 6, 7]);
+}
+
+#[test]
+fn test_align_to_non_trivial() {
+ #[repr(align(8))] struct U64(u64, u64);
+ #[repr(align(8))] struct U64U64U32(u64, u64, u32);
+ let data = [U64(1, 2), U64(3, 4), U64(5, 6), U64(7, 8), U64(9, 10), U64(11, 12), U64(13, 14),
+ U64(15, 16)];
+ let (prefix, aligned, suffix) = unsafe { data.align_to::<U64U64U32>() };
+ assert_eq!(aligned.len(), 4);
+ assert_eq!(prefix.len() + suffix.len(), 2);
+}
CountImplied,
}
+pub struct ParseError {
+ pub description: string::String,
+ pub note: Option<string::String>,
+ pub label: string::String,
+ pub start: usize,
+ pub end: usize,
+}
+
/// The parser structure for interpreting the input format string. This is
/// modeled as an iterator over `Piece` structures to form a stream of tokens
/// being output.
input: &'a str,
cur: iter::Peekable<str::CharIndices<'a>>,
/// Error messages accumulated during parsing
- pub errors: Vec<(string::String, Option<string::String>)>,
+ pub errors: Vec<ParseError>,
/// Current position of implicit positional argument pointer
curarg: usize,
}
}
'}' => {
self.cur.next();
+ let pos = pos + 1;
if self.consume('}') {
- Some(String(self.string(pos + 1)))
+ Some(String(self.string(pos)))
} else {
- self.err_with_note("unmatched `}` found",
- "if you intended to print `}`, \
- you can escape it using `}}`");
+ self.err_with_note(
+ "unmatched `}` found",
+ "unmatched `}`",
+ "if you intended to print `}`, you can escape it using `}}`",
+ pos,
+ pos,
+ );
None
}
}
/// Notifies of an error. The message doesn't actually need to be of type
/// String, but I think it does when this eventually uses conditions so it
/// might as well start using it now.
- fn err(&mut self, msg: &str) {
- self.errors.push((msg.to_owned(), None));
+ fn err<S1: Into<string::String>, S2: Into<string::String>>(
+ &mut self,
+ description: S1,
+ label: S2,
+ start: usize,
+ end: usize,
+ ) {
+ self.errors.push(ParseError {
+ description: description.into(),
+ note: None,
+ label: label.into(),
+ start,
+ end,
+ });
}
/// Notifies of an error. The message doesn't actually need to be of type
/// String, but I think it does when this eventually uses conditions so it
/// might as well start using it now.
- fn err_with_note(&mut self, msg: &str, note: &str) {
- self.errors.push((msg.to_owned(), Some(note.to_owned())));
+ fn err_with_note<S1: Into<string::String>, S2: Into<string::String>, S3: Into<string::String>>(
+ &mut self,
+ description: S1,
+ label: S2,
+ note: S3,
+ start: usize,
+ end: usize,
+ ) {
+ self.errors.push(ParseError {
+ description: description.into(),
+ note: Some(note.into()),
+ label: label.into(),
+ start,
+ end,
+ });
}
/// Optionally consumes the specified character. If the character is not at
/// found, an error is emitted.
fn must_consume(&mut self, c: char) {
self.ws();
- if let Some(&(_, maybe)) = self.cur.peek() {
+ if let Some(&(pos, maybe)) = self.cur.peek() {
if c == maybe {
self.cur.next();
} else {
- self.err(&format!("expected `{:?}`, found `{:?}`", c, maybe));
+ self.err(format!("expected `{:?}`, found `{:?}`", c, maybe),
+ format!("expected `{}`", c),
+ pos + 1,
+ pos + 1);
}
} else {
- let msg = &format!("expected `{:?}` but string was terminated", c);
+ let msg = format!("expected `{:?}` but string was terminated", c);
+ let pos = self.input.len() + 1; // point at closing `"`
if c == '}' {
self.err_with_note(msg,
- "if you intended to print `{`, you can escape it using `{{`");
+ format!("expected `{:?}`", c),
+ "if you intended to print `{`, you can escape it using `{{`",
+ pos,
+ pos);
} else {
- self.err(msg);
+ self.err(msg, format!("expected `{:?}`", c), pos, pos);
}
}
}
} else {
match self.cur.peek() {
Some(&(_, c)) if c.is_alphabetic() => Some(ArgumentNamed(self.word())),
+ Some(&(pos, c)) if c == '_' => {
+ let invalid_name = self.string(pos);
+ self.err_with_note(format!("invalid argument name `{}`", invalid_name),
+ "invalid argument name",
+ "argument names cannot start with an underscore",
+ pos + 1, // add 1 to account for leading `{`
+ pos + 1 + invalid_name.len());
+ Some(ArgumentNamed(invalid_name))
+ },
// This is an `ArgumentNext`.
// Record the fact and do the resolution after parsing the
pub fn new_raw(string: &str, span: Span) -> Ident {
let mut ident = Ident::new(string, span);
if ident.sym == keywords::Underscore.name() ||
- token::is_path_segment_keyword(ast::Ident::with_empty_ctxt(ident.sym)) {
+ ast::Ident::with_empty_ctxt(ident.sym).is_path_segment_keyword() {
panic!("`{:?}` is not a valid raw identifier", string)
}
ident.is_raw = true;
span: Some(span),
allow_internal_unstable: true,
allow_internal_unsafe: false,
+ edition: codemap::hygiene::default_edition(),
},
});
span.with_ctxt(SyntaxContext::empty().apply_mark(mark))
use rustc_target::spec::abi::Abi;
use syntax::ast;
use syntax::codemap::{CodeMap, Spanned};
-use syntax::parse::{token, ParseSess};
+use syntax::parse::ParseSess;
use syntax::parse::lexer::comments;
use syntax::print::pp::{self, Breaks};
use syntax::print::pp::Breaks::{Consistent, Inconsistent};
}
pub fn print_name(&mut self, name: ast::Name) -> io::Result<()> {
- if token::is_raw_guess(ast::Ident::with_empty_ctxt(name)) {
+ if name.to_ident().is_raw_guess() {
self.s.word(&format!("r#{}", name))?;
} else {
self.s.word(&name.as_str())?;
rustc_const_unstable
});
+impl<'a> HashStable<StableHashingContext<'a>>
+for ::syntax::edition::Edition {
+ fn hash_stable<W: StableHasherResult>(&self,
+ hcx: &mut StableHashingContext<'a>,
+ hasher: &mut StableHasher<W>) {
+ mem::discriminant(self).hash_stable(hcx, hasher);
+ }
+}
+
impl<'a> HashStable<StableHashingContext<'a>>
for ::syntax::attr::StabilityLevel {
fn hash_stable<W: StableHasherResult>(&self,
format,
allow_internal_unstable,
allow_internal_unsafe,
+ edition,
span
});
#![feature(core_intrinsics)]
#![feature(drain_filter)]
#![feature(entry_or_default)]
-#![cfg_attr(stage0, feature(dyn_trait))]
#![feature(from_ref)]
#![feature(fs_read_write)]
#![feature(iterator_find_map)]
#![feature(trusted_len)]
#![feature(catch_expr)]
#![feature(test)]
-#![feature(inclusive_range_methods)]
#![recursion_limit="512"]
declare_lint! {
pub CONST_ERR,
- Warn,
+ Deny,
"constant evaluation detected erroneous expression"
}
use std::any::Any;
use std::path::{Path, PathBuf};
use syntax::ast;
+use syntax::edition::Edition;
use syntax::ext::base::SyntaxExtension;
use syntax::symbol::Symbol;
use syntax_pos::Span;
fn crate_name_untracked(&self, cnum: CrateNum) -> Symbol;
fn crate_disambiguator_untracked(&self, cnum: CrateNum) -> CrateDisambiguator;
fn crate_hash_untracked(&self, cnum: CrateNum) -> Svh;
+ fn crate_edition_untracked(&self, cnum: CrateNum) -> Edition;
fn struct_field_names_untracked(&self, def: DefId) -> Vec<ast::Name>;
fn item_children_untracked(&self, did: DefId, sess: &Session) -> Vec<def::Export>;
fn load_macro_untracked(&self, did: DefId, sess: &Session) -> LoadedMacro;
bug!("crate_disambiguator")
}
fn crate_hash_untracked(&self, cnum: CrateNum) -> Svh { bug!("crate_hash") }
+ fn crate_edition_untracked(&self, cnum: CrateNum) -> Edition { bug!("crate_edition_untracked") }
// resolve
fn def_key(&self, def: DefId) -> DefKey { bug!("def_key") }
I128ShroFnLangItem, "i128_shro", i128_shro_fn;
U128ShroFnLangItem, "u128_shro", u128_shro_fn;
+ // Align offset for stride != 1, must not panic.
+ AlignOffsetLangItem, "align_offset", align_offset_fn;
+
TerminationTraitLangItem, "termination", termination;
}
assert!(previous.is_none());
}
- fn closure_is_enclosed_by(&self,
- mut sub_closure: hir::ItemLocalId,
- sup_closure: hir::ItemLocalId) -> bool {
- loop {
- if sub_closure == sup_closure { return true; }
- match self.closure_tree.get(&sub_closure) {
- Some(&s) => { sub_closure = s; }
- None => { return false; }
- }
- }
- }
-
fn record_var_scope(&mut self, var: hir::ItemLocalId, lifetime: Scope) {
debug!("record_var_scope(sub={:?}, sup={:?})", var, lifetime);
assert!(var != lifetime.item_local_id());
// requires a hash table lookup, and we often have very long scope
// chains (10s or 100s of scopes) that only differ by a few elements at
// the start. So this algorithm is faster.
- let mut ma = Some(scope_a);
- let mut mb = Some(scope_b);
- let mut seen_a: SmallVec<[Scope; 32]> = SmallVec::new();
- let mut seen_b: SmallVec<[Scope; 32]> = SmallVec::new();
+
+ let mut ma = Some(&scope_a);
+ let mut mb = Some(&scope_b);
+
+ // A HashSet<Scope> is a more obvious choice for these, but SmallVec is
+ // faster because the set size is normally small so linear search is
+ // as good or better than a hash table lookup, plus the size is usually
+ // small enough to avoid a heap allocation.
+ let mut seen_a: SmallVec<[&Scope; 32]> = SmallVec::new();
+ let mut seen_b: SmallVec<[&Scope; 32]> = SmallVec::new();
+
loop {
if let Some(a) = ma {
- if seen_b.iter().position(|s| *s == a).is_some() {
- return a;
+ if seen_b.iter().any(|s| *s == a) {
+ return *a;
}
seen_a.push(a);
- ma = self.parent_map.get(&a).map(|s| *s);
+ ma = self.parent_map.get(&a);
}
if let Some(b) = mb {
- if seen_a.iter().position(|s| *s == b).is_some() {
- return b;
+ if seen_a.iter().any(|s| *s == b) {
+ return *b;
}
seen_b.push(b);
- mb = self.parent_map.get(&b).map(|s| *s);
+ mb = self.parent_map.get(&b);
}
if ma.is_none() && mb.is_none() {
- break;
- }
- };
-
- fn outermost_scope(parent_map: &FxHashMap<Scope, Scope>, scope: Scope) -> Scope {
- let mut scope = scope;
- loop {
- match parent_map.get(&scope) {
- Some(&superscope) => scope = superscope,
- None => break scope,
- }
- }
- }
-
- // In this (rare) case, the two regions belong to completely different
- // functions. Compare those fn for lexical nesting. The reasoning
- // behind this is subtle. See the "Modeling closures" section of the
- // README in infer::region_constraints for more details.
- let a_root_scope = outermost_scope(&self.parent_map, scope_a);
- let b_root_scope = outermost_scope(&self.parent_map, scope_b);
- match (a_root_scope.data(), b_root_scope.data()) {
- (ScopeData::Destruction(a_root_id),
- ScopeData::Destruction(b_root_id)) => {
- if self.closure_is_enclosed_by(a_root_id, b_root_id) {
- // `a` is enclosed by `b`, hence `b` is the ancestor of everything in `a`
- scope_b
- } else if self.closure_is_enclosed_by(b_root_id, a_root_id) {
- // `b` is enclosed by `a`, hence `a` is the ancestor of everything in `b`
- scope_a
- } else {
- // neither fn encloses the other
- bug!()
- }
- }
- _ => {
- // root ids are always Node right now
- bug!()
+ // No nearest common ancestor found.
+ bug!();
}
}
}
obligation.cause.span,
infer::LateBoundRegionConversionTime::HigherRankedType,
data);
- let normalized = super::normalize_projection_type(
+ let mut obligations = vec![];
+ let normalized_ty = super::normalize_projection_type(
&mut selcx,
obligation.param_env,
data.projection_ty,
obligation.cause.clone(),
- 0
+ 0,
+ &mut obligations
);
if let Err(error) = self.at(&obligation.cause, obligation.param_env)
- .eq(normalized.value, data.ty) {
+ .eq(normalized_ty, data.ty) {
values = Some(infer::ValuePairs::Types(ExpectedFound {
- expected: normalized.value,
+ expected: normalized_ty,
found: data.ty,
}));
err_buf = error;
// FIXME(#20304) -- cache
let mut selcx = SelectionContext::new(infcx);
- let normalized = project::normalize_projection_type(&mut selcx,
- param_env,
- projection_ty,
- cause,
- 0);
-
- for obligation in normalized.obligations {
- self.register_predicate_obligation(infcx, obligation);
- }
-
- debug!("normalize_projection_type: result={:?}", normalized.value);
-
- normalized.value
+ let mut obligations = vec![];
+ let normalized_ty = project::normalize_projection_type(&mut selcx,
+ param_env,
+ projection_ty,
+ cause,
+ 0,
+ &mut obligations);
+ self.register_predicate_obligations(infcx, obligations);
+
+ debug!("normalize_projection_type: result={:?}", normalized_ty);
+
+ normalized_ty
}
/// Requires that `ty` must implement the trait with `def_id` in
debug!("project_and_unify_type(obligation={:?})",
obligation);
- let Normalized { value: normalized_ty, mut obligations } =
+ let mut obligations = vec![];
+ let normalized_ty =
match opt_normalize_projection_type(selcx,
obligation.param_env,
obligation.predicate.projection_ty,
obligation.cause.clone(),
- obligation.recursion_depth) {
+ obligation.recursion_depth,
+ &mut obligations) {
Some(n) => n,
None => return Ok(None),
};
// binder). It would be better to normalize in a
// binding-aware fashion.
- let Normalized { value: normalized_ty, obligations } =
- normalize_projection_type(self.selcx,
- self.param_env,
- data.clone(),
- self.cause.clone(),
- self.depth);
- debug!("AssociatedTypeNormalizer: depth={} normalized {:?} to {:?} \
- with {} add'l obligations",
- self.depth, ty, normalized_ty, obligations.len());
- self.obligations.extend(obligations);
+ let normalized_ty = normalize_projection_type(self.selcx,
+ self.param_env,
+ data.clone(),
+ self.cause.clone(),
+ self.depth,
+ &mut self.obligations);
+ debug!("AssociatedTypeNormalizer: depth={} normalized {:?} to {:?}, \
+ now with {} obligations",
+ self.depth, ty, normalized_ty, self.obligations.len());
normalized_ty
}
param_env: ty::ParamEnv<'tcx>,
projection_ty: ty::ProjectionTy<'tcx>,
cause: ObligationCause<'tcx>,
- depth: usize)
- -> NormalizedTy<'tcx>
+ depth: usize,
+ obligations: &mut Vec<PredicateObligation<'tcx>>)
+ -> Ty<'tcx>
{
- opt_normalize_projection_type(selcx, param_env, projection_ty.clone(), cause.clone(), depth)
+ opt_normalize_projection_type(selcx, param_env, projection_ty.clone(), cause.clone(), depth,
+ obligations)
.unwrap_or_else(move || {
// if we bottom out in ambiguity, create a type variable
// and a deferred predicate to resolve this when more type
});
let obligation = Obligation::with_depth(
cause, depth + 1, param_env, projection.to_predicate());
- Normalized {
- value: ty_var,
- obligations: vec![obligation]
- }
+ obligations.push(obligation);
+ ty_var
})
}
/// as Trait>::Item`. The result is always a type (and possibly
/// additional obligations). Returns `None` in the case of ambiguity,
/// which indicates that there are unbound type variables.
+///
+/// This function used to return `Option<NormalizedTy<'tcx>>`, which contains a
+/// `Ty<'tcx>` and an obligations vector. But that obligation vector was very
+/// often immediately appended to another obligations vector. So now this
+/// function takes an obligations vector and appends to it directly, which is
+/// slightly uglier but avoids the need for an extra short-lived allocation.
fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
selcx: &'a mut SelectionContext<'b, 'gcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
projection_ty: ty::ProjectionTy<'tcx>,
cause: ObligationCause<'tcx>,
- depth: usize)
- -> Option<NormalizedTy<'tcx>>
+ depth: usize,
+ obligations: &mut Vec<PredicateObligation<'tcx>>)
+ -> Option<Ty<'tcx>>
{
let infcx = selcx.infcx();
projection_ty);
selcx.infcx().report_overflow_error(&obligation, false);
}
- Err(ProjectionCacheEntry::NormalizedTy(mut ty)) => {
+ Err(ProjectionCacheEntry::NormalizedTy(ty)) => {
+ // This is the hottest path in this function.
+ //
// If we find the value in the cache, then return it along
// with the obligations that went along with it. Note
// that, when using a fulfillment context, these
// Once we have inferred everything we need to know, we
// can ignore the `obligations` from that point on.
if !infcx.any_unresolved_type_vars(&ty.value) {
- infcx.projection_cache.borrow_mut().complete(cache_key);
- ty.obligations = vec![];
+ infcx.projection_cache.borrow_mut().complete_normalized(cache_key, &ty);
+ // No need to extend `obligations`.
+ } else {
+ obligations.extend(ty.obligations);
}
- push_paranoid_cache_value_obligation(infcx,
- param_env,
- projection_ty,
- cause,
- depth,
- &mut ty);
-
- return Some(ty);
+ obligations.push(get_paranoid_cache_value_obligation(infcx,
+ param_env,
+ projection_ty,
+ cause,
+ depth));
+ return Some(ty.value);
}
Err(ProjectionCacheEntry::Error) => {
debug!("opt_normalize_projection_type: \
found error");
- return Some(normalize_to_error(selcx, param_env, projection_ty, cause, depth));
+ let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth);
+ obligations.extend(result.obligations);
+ return Some(result.value)
}
}
let obligation = Obligation::with_depth(cause.clone(), depth, param_env, projection_ty);
match project_type(selcx, &obligation) {
- Ok(ProjectedTy::Progress(Progress { ty: projected_ty, mut obligations })) => {
+ Ok(ProjectedTy::Progress(Progress { ty: projected_ty,
+ obligations: mut projected_obligations })) => {
// if projection succeeded, then what we get out of this
// is also non-normalized (consider: it was derived from
// an impl, where-clause etc) and hence we must
debug!("opt_normalize_projection_type: \
projected_ty={:?} \
depth={} \
- obligations={:?}",
+ projected_obligations={:?}",
projected_ty,
depth,
- obligations);
+ projected_obligations);
let result = if projected_ty.has_projections() {
let mut normalizer = AssociatedTypeNormalizer::new(selcx,
normalized_ty,
depth);
- obligations.extend(normalizer.obligations);
+ projected_obligations.extend(normalizer.obligations);
Normalized {
value: normalized_ty,
- obligations,
+ obligations: projected_obligations,
}
} else {
Normalized {
value: projected_ty,
- obligations,
+ obligations: projected_obligations,
}
};
let cache_value = prune_cache_value_obligations(infcx, &result);
infcx.projection_cache.borrow_mut().insert_ty(cache_key, cache_value);
-
- Some(result)
+ obligations.extend(result.obligations);
+ Some(result.value)
}
Ok(ProjectedTy::NoProgress(projected_ty)) => {
debug!("opt_normalize_projection_type: \
obligations: vec![]
};
infcx.projection_cache.borrow_mut().insert_ty(cache_key, result.clone());
- Some(result)
+ // No need to extend `obligations`.
+ Some(result.value)
}
Err(ProjectionTyError::TooManyCandidates) => {
debug!("opt_normalize_projection_type: \
infcx.projection_cache.borrow_mut()
.error(cache_key);
- Some(normalize_to_error(selcx, param_env, projection_ty, cause, depth))
+ let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth);
+ obligations.extend(result.obligations);
+ Some(result.value)
}
}
}
/// may or may not be necessary -- in principle, all the obligations
/// that must be proven to show that `T: Trait` were also returned
/// when the cache was first populated. But there are some vague concerns,
-/// and so we take the precatuionary measure of including `T: Trait` in
+/// and so we take the precautionary measure of including `T: Trait` in
/// the result:
///
/// Concern #1. The current setup is fragile. Perhaps someone could
/// that may yet turn out to be wrong. This *may* lead to some sort
/// of trouble, though we don't have a concrete example of how that
/// can occur yet. But it seems risky at best.
-fn push_paranoid_cache_value_obligation<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- projection_ty: ty::ProjectionTy<'tcx>,
- cause: ObligationCause<'tcx>,
- depth: usize,
- result: &mut NormalizedTy<'tcx>)
+fn get_paranoid_cache_value_obligation<'a, 'gcx, 'tcx>(
+ infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ projection_ty: ty::ProjectionTy<'tcx>,
+ cause: ObligationCause<'tcx>,
+ depth: usize)
+ -> PredicateObligation<'tcx>
{
let trait_ref = projection_ty.trait_ref(infcx.tcx).to_poly_trait_ref();
- let trait_obligation = Obligation { cause,
- recursion_depth: depth,
- param_env,
- predicate: trait_ref.to_predicate() };
- result.obligations.push(trait_obligation);
+ Obligation {
+ cause,
+ recursion_depth: depth,
+ param_env,
+ predicate: trait_ref.to_predicate(),
+ }
}
/// If we are projecting `<T as Trait>::Item`, but `T: Trait` does not
}));
}
+ /// A specialized version of `complete` for when the key's value is known
+ /// to be a NormalizedTy.
+ pub fn complete_normalized(&mut self, key: ProjectionCacheKey<'tcx>, ty: &NormalizedTy<'tcx>) {
+ // We want to insert `ty` with no obligations. If the existing value
+ // already has no obligations (as is common) we can use `insert_noop`
+ // to do a minimal amount of work -- the HashMap insertion is skipped,
+ // and minimal changes are made to the undo log.
+ if ty.obligations.is_empty() {
+ self.map.insert_noop();
+ } else {
+ self.map.insert(key, ProjectionCacheEntry::NormalizedTy(Normalized {
+ value: ty.value,
+ obligations: vec![]
+ }));
+ }
+ }
+
/// Indicates that trying to normalize `key` resulted in
/// ambiguity. No point in trying it again then until we gain more
/// type information (in which case, the "fully resolved" key will
use syntax::ext::base::Resolver;
use syntax::ext::build::AstBuilder;
use syntax::ext::expand::ExpansionConfig;
-use syntax::ext::hygiene::{Mark, SyntaxContext};
+use syntax::ext::hygiene::{self, Mark, SyntaxContext};
use syntax::fold::{self, Folder};
use syntax::parse::ParseSess;
use syntax::ptr::P;
span: None,
allow_internal_unstable: true,
allow_internal_unsafe: false,
+ edition: hygiene::default_edition(),
},
});
let span = item.span.with_ctxt(SyntaxContext::empty().apply_mark(mark));
args[0].deref(bx.cx).codegen_get_discr(bx, ret_ty)
}
- "align_offset" => {
- // `ptr as usize`
- let ptr_val = bx.ptrtoint(args[0].immediate(), bx.cx.isize_ty);
- // `ptr_val % align`
- let align = args[1].immediate();
- let offset = bx.urem(ptr_val, align);
- let zero = C_null(bx.cx.isize_ty);
- // `offset == 0`
- let is_zero = bx.icmp(llvm::IntPredicate::IntEQ, offset, zero);
- // `if offset == 0 { 0 } else { align - offset }`
- bx.select(is_zero, zero, bx.sub(align, offset))
- }
name if name.starts_with("simd_") => {
match generic_simd_intrinsic(bx, name,
callee_ty,
#![feature(rustc_diagnostic_macros)]
#![feature(slice_sort_by_cached_key)]
#![feature(optin_builtin_traits)]
-#![feature(inclusive_range_methods)]
use rustc::dep_graph::WorkProduct;
use syntax_pos::symbol::Symbol;
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::collections::BTreeMap;
+use indexed_vec::{Idx, IndexVec};
use std::collections::btree_map::Entry;
-use std::marker::PhantomData;
+use std::collections::BTreeMap;
use std::iter::FromIterator;
-use indexed_vec::{Idx, IndexVec};
+use std::marker::PhantomData;
type Word = u128;
const WORD_BITS: usize = 128;
if read != write {
let (bit_set_read, bit_set_write) = self.vector.pick2_mut(read, write);
- for read_val in bit_set_read.iter() {
- changed = changed | bit_set_write.insert(read_val);
+ for read_chunk in bit_set_read.chunks() {
+ changed = changed | bit_set_write.insert_chunk(read_chunk).any();
}
}
changed
}
+ /// True if `sub` is a subset of `sup`
+ pub fn is_subset(&self, sub: R, sup: R) -> bool {
+ sub == sup || {
+ let bit_set_sub = &self.vector[sub];
+ let bit_set_sup = &self.vector[sup];
+ bit_set_sub
+ .chunks()
+ .all(|read_chunk| read_chunk.bits_eq(bit_set_sup.contains_chunk(read_chunk)))
+ }
+ }
+
/// Iterates through all the columns set to true in a given row of
/// the matrix.
pub fn iter<'a>(&'a self, row: R) -> impl Iterator<Item = C> + 'a {
}
impl<I: Idx> SparseChunk<I> {
+ #[inline]
pub fn one(index: I) -> Self {
let index = index.index();
let key_usize = index / 128;
}
}
+ #[inline]
pub fn any(&self) -> bool {
self.bits != 0
}
+ #[inline]
+ pub fn bits_eq(&self, other: SparseChunk<I>) -> bool {
+ self.bits == other.bits
+ }
+
pub fn iter(&self) -> impl Iterator<Item = I> {
let base = self.key as usize * 128;
let mut bits = self.bits;
self.chunk_bits.len() * 128
}
+ /// Returns a chunk containing only those bits that are already
+ /// present. You can test therefore if `self` contains all the
+ /// bits in chunk already by doing `chunk ==
+ /// self.contains_chunk(chunk)`.
pub fn contains_chunk(&self, chunk: SparseChunk<I>) -> SparseChunk<I> {
SparseChunk {
bits: self.chunk_bits
}
}
+ /// Modifies `self` to contain all the bits from `chunk` (in
+ /// addition to any pre-existing bits); returns a new chunk that
+ /// contains only those bits that were newly added. You can test
+ /// if anything was inserted by invoking `any()` on the returned
+ /// value.
pub fn insert_chunk(&mut self, chunk: SparseChunk<I>) -> SparseChunk<I> {
if chunk.bits == 0 {
return chunk;
}
}
+ pub fn insert_noop(&mut self) {
+ if !self.undo_log.is_empty() {
+ self.undo_log.push(UndoLog::Noop);
+ }
+ }
+
pub fn remove(&mut self, key: K) -> bool {
match self.map.remove(&key) {
Some(old_value) => {
use syntax::codemap::{CodeMap, FileLoader, RealFileLoader};
use syntax::feature_gate::{GatedCfg, UnstableFeatures};
use syntax::parse::{self, PResult};
-use syntax_pos::{DUMMY_SP, MultiSpan, FileName};
+use syntax_pos::{hygiene, DUMMY_SP, MultiSpan, FileName};
#[cfg(test)]
mod test;
};
let (sopts, cfg) = config::build_session_options_and_crate_config(&matches);
+ hygiene::set_default_edition(sopts.edition);
driver::spawn_thread_pool(sopts, |sopts| {
run_compiler_with_pool(matches, sopts, cfg, callbacks, file_loader, emitter_dest)
use syntax::ast;
use syntax::attr;
+use syntax::edition::Edition;
use syntax::ext::base::SyntaxExtension;
use syntax::symbol::Symbol;
use syntax::visit;
mem::transmute::<*mut u8, fn(&mut Registry)>(sym)
};
- struct MyRegistrar(Vec<(ast::Name, Lrc<SyntaxExtension>)>);
+ struct MyRegistrar {
+ extensions: Vec<(ast::Name, Lrc<SyntaxExtension>)>,
+ edition: Edition,
+ }
impl Registry for MyRegistrar {
fn register_custom_derive(&mut self,
attributes: &[&'static str]) {
let attrs = attributes.iter().cloned().map(Symbol::intern).collect::<Vec<_>>();
let derive = ProcMacroDerive::new(expand, attrs.clone());
- let derive = SyntaxExtension::ProcMacroDerive(Box::new(derive), attrs);
- self.0.push((Symbol::intern(trait_name), Lrc::new(derive)));
+ let derive = SyntaxExtension::ProcMacroDerive(
+ Box::new(derive), attrs, self.edition
+ );
+ self.extensions.push((Symbol::intern(trait_name), Lrc::new(derive)));
}
fn register_attr_proc_macro(&mut self,
name: &str,
expand: fn(TokenStream, TokenStream) -> TokenStream) {
let expand = SyntaxExtension::AttrProcMacro(
- Box::new(AttrProcMacro { inner: expand })
+ Box::new(AttrProcMacro { inner: expand }), self.edition
);
- self.0.push((Symbol::intern(name), Lrc::new(expand)));
+ self.extensions.push((Symbol::intern(name), Lrc::new(expand)));
}
fn register_bang_proc_macro(&mut self,
name: &str,
expand: fn(TokenStream) -> TokenStream) {
let expand = SyntaxExtension::ProcMacro(
- Box::new(BangProcMacro { inner: expand })
+ Box::new(BangProcMacro { inner: expand }), self.edition
);
- self.0.push((Symbol::intern(name), Lrc::new(expand)));
+ self.extensions.push((Symbol::intern(name), Lrc::new(expand)));
}
}
- let mut my_registrar = MyRegistrar(Vec::new());
+ let mut my_registrar = MyRegistrar { extensions: Vec::new(), edition: root.edition };
registrar(&mut my_registrar);
// Intentionally leak the dynamic library. We can't ever unload it
// since the library can make things that will live arbitrarily long.
mem::forget(lib);
- my_registrar.0
+ my_registrar.extensions
}
/// Look for a plugin registrar. Returns library path, crate
use rustc_data_structures::sync::{Lrc, RwLock, Lock};
use syntax::{ast, attr};
+use syntax::edition::Edition;
use syntax::ext::base::SyntaxExtension;
use syntax::symbol::Symbol;
use syntax_pos;
pub fn panic_strategy(&self) -> PanicStrategy {
self.root.panic_strategy.clone()
}
+
+ pub fn edition(&self) -> Edition {
+ self.root.edition
+ }
}
use syntax::ast;
use syntax::attr;
use syntax::codemap;
+use syntax::edition::Edition;
use syntax::ext::base::SyntaxExtension;
use syntax::parse::filemap_to_stream;
use syntax::symbol::Symbol;
self.get_crate_data(cnum).hash()
}
+ fn crate_edition_untracked(&self, cnum: CrateNum) -> Edition
+ {
+ self.get_crate_data(cnum).edition()
+ }
+
/// Returns the `DefKey` for a given `DefId`. This indicates the
/// parent `DefId` as well as some idea of what kind of data the
/// `DefId` refers to.
return LoadedMacro::ProcMacro(proc_macros[id.index.to_proc_macro_index()].1.clone());
} else if data.name == "proc_macro" &&
self.get_crate_data(id.krate).item_name(id.index) == "quote" {
- let ext = SyntaxExtension::ProcMacro(Box::new(::proc_macro::__internal::Quoter));
+ let ext = SyntaxExtension::ProcMacro(Box::new(::proc_macro::__internal::Quoter),
+ data.edition());
return LoadedMacro::ProcMacro(Lrc::new(ext));
}
use syntax::codemap::Spanned;
use syntax::attr;
use syntax::symbol::Symbol;
-use syntax_pos::{self, FileName, FileMap, Span, DUMMY_SP};
+use syntax_pos::{self, hygiene, FileName, FileMap, Span, DUMMY_SP};
use rustc::hir::{self, PatKind};
use rustc::hir::itemlikevisit::ItemLikeVisitor;
hash: link_meta.crate_hash,
disambiguator: tcx.sess.local_crate_disambiguator(),
panic_strategy: tcx.sess.panic_strategy(),
+ edition: hygiene::default_edition(),
has_global_allocator: has_global_allocator,
has_default_lib_allocator: has_default_lib_allocator,
plugin_registrar_fn: tcx.sess
use rustc_serialize as serialize;
use syntax::{ast, attr};
+use syntax::edition::Edition;
use syntax::symbol::Symbol;
use syntax_pos::{self, Span};
pub hash: hir::svh::Svh,
pub disambiguator: CrateDisambiguator,
pub panic_strategy: PanicStrategy,
+ pub edition: Edition,
pub has_global_allocator: bool,
pub has_default_lib_allocator: bool,
pub plugin_registrar_fn: Option<DefIndex>,
//! This query borrow-checks the MIR to (further) ensure it is not broken.
-use borrow_check::nll::region_infer::{RegionCausalInfo, RegionInferenceContext};
+use borrow_check::nll::region_infer::RegionInferenceContext;
use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::hir::map::definitions::DefPathData;
nonlexical_regioncx: regioncx,
used_mut: FxHashSet(),
used_mut_upvars: SmallVec::new(),
- nonlexical_cause_info: None,
borrow_set,
dominators,
};
/// contains the results from region inference and lets us e.g.
/// find out which CFG points are contained in each borrow region.
nonlexical_regioncx: Rc<RegionInferenceContext<'tcx>>,
- nonlexical_cause_info: Option<RegionCausalInfo>,
/// The set of borrows extracted from the MIR
borrow_set: Rc<BorrowSet<'tcx>>,
}
}
Reservation(WriteKind::Move)
+ | Write(WriteKind::Move)
| Reservation(WriteKind::StorageDeadOrDrop)
| Reservation(WriteKind::MutableBorrow(BorrowKind::Shared))
- | Write(WriteKind::Move)
| Write(WriteKind::StorageDeadOrDrop)
| Write(WriteKind::MutableBorrow(BorrowKind::Shared)) => {
if let Err(_place_err) = self.is_mutable(place, is_local_mutation_allowed) {
// mutated, then it is justified to be annotated with the `mut`
// keyword, since the mutation may be a possible reassignment.
let mpi = self.move_data.rev_lookup.find_local(*local);
- if flow_state.inits.contains(&mpi) {
- self.used_mut.insert(*local);
+ let ii = &self.move_data.init_path_map[mpi];
+ for index in ii {
+ if flow_state.ever_inits.contains(index) {
+ self.used_mut.insert(*local);
+ }
}
}
}
let regioncx = &&self.nonlexical_regioncx;
let mir = self.mir;
- if self.nonlexical_cause_info.is_none() {
- self.nonlexical_cause_info = Some(regioncx.compute_causal_info(mir));
- }
-
- let cause_info = self.nonlexical_cause_info.as_ref().unwrap();
- if let Some(cause) = cause_info.why_region_contains_point(borrow.region, context.loc) {
- match *cause.root_cause() {
+ let borrow_region_vid = regioncx.to_region_vid(borrow.region);
+ if let Some(cause) = regioncx.why_region_contains_point(borrow_region_vid, context.loc) {
+ match cause {
Cause::LiveVar(local, location) => {
match find_regular_use(mir, regioncx, borrow, location, local) {
Some(p) => {
});
// Also dump the inference graph constraints as a graphviz file.
- let _: io::Result<()> = do_catch! {{
+ let _: io::Result<()> = do catch {
let mut file =
pretty::create_dump_file(infcx.tcx, "regioncx.dot", None, "nll", &0, source)?;
regioncx.dump_graphviz(&mut file)?;
- }};
+ };
}
fn dump_annotation<'a, 'gcx, 'tcx>(
+++ /dev/null
-// Copyright 2017 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.
-
-//! Module defining the `dfs` method on `RegionInferenceContext`, along with
-//! its associated helper traits.
-
-use borrow_check::nll::universal_regions::UniversalRegions;
-use borrow_check::nll::region_infer::RegionInferenceContext;
-use borrow_check::nll::region_infer::values::{RegionElementIndex, RegionValueElements,
- RegionValues};
-use syntax::codemap::Span;
-use rustc::mir::{Location, Mir};
-use rustc::ty::RegionVid;
-use rustc_data_structures::bitvec::BitVector;
-use rustc_data_structures::indexed_vec::Idx;
-
-pub(super) struct DfsStorage {
- stack: Vec<Location>,
- visited: BitVector,
-}
-
-impl<'tcx> RegionInferenceContext<'tcx> {
- /// Creates dfs storage for use by dfs; this should be shared
- /// across as many calls to dfs as possible to amortize allocation
- /// costs.
- pub(super) fn new_dfs_storage(&self) -> DfsStorage {
- let num_elements = self.elements.num_elements();
- DfsStorage {
- stack: vec![],
- visited: BitVector::new(num_elements),
- }
- }
-
- /// Function used to satisfy or test a `R1: R2 @ P`
- /// constraint. The core idea is that it performs a DFS starting
- /// from `P`. The precise actions *during* that DFS depend on the
- /// `op` supplied, so see (e.g.) `CopyFromSourceToTarget` for more
- /// details.
- ///
- /// Returns:
- ///
- /// - `Ok(true)` if the walk was completed and something changed
- /// along the way;
- /// - `Ok(false)` if the walk was completed with no changes;
- /// - `Err(early)` if the walk was existed early by `op`. `earlyelem` is the
- /// value that `op` returned.
- #[inline(never)] // ensure dfs is identifiable in profiles
- pub(super) fn dfs<C>(
- &self,
- mir: &Mir<'tcx>,
- dfs: &mut DfsStorage,
- mut op: C,
- ) -> Result<bool, C::Early>
- where
- C: DfsOp,
- {
- let mut changed = false;
-
- dfs.visited.clear();
- dfs.stack.push(op.start_point());
- while let Some(p) = dfs.stack.pop() {
- let point_index = self.elements.index(p);
-
- if !op.source_region_contains(point_index) {
- debug!(" not in from-region");
- continue;
- }
-
- if !dfs.visited.insert(point_index.index()) {
- debug!(" already visited");
- continue;
- }
-
- let new = op.add_to_target_region(point_index)?;
- changed |= new;
-
- let block_data = &mir[p.block];
-
- let start_stack_len = dfs.stack.len();
-
- if p.statement_index < block_data.statements.len() {
- dfs.stack.push(Location {
- statement_index: p.statement_index + 1,
- ..p
- });
- } else {
- dfs.stack.extend(
- block_data
- .terminator()
- .successors()
- .map(|&basic_block| Location {
- statement_index: 0,
- block: basic_block,
- }),
- );
- }
-
- if dfs.stack.len() == start_stack_len {
- // If we reach the END point in the graph, then copy
- // over any skolemized end points in the `from_region`
- // and make sure they are included in the `to_region`.
- changed |= op.add_universal_regions_outlived_by_source_to_target()?;
- }
- }
-
- Ok(changed)
- }
-}
-
-/// Customizes the operation of the `dfs` function. This function is
-/// used during inference to satisfy a `R1: R2 @ P` constraint.
-pub(super) trait DfsOp {
- /// If this op stops the walk early, what type does it propagate?
- type Early;
-
- /// Returns the point from which to start the DFS.
- fn start_point(&self) -> Location;
-
- /// Returns true if the source region contains the given point.
- fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool;
-
- /// Adds the given point to the target region, returning true if
- /// something has changed. Returns `Err` if we should abort the
- /// walk early.
- fn add_to_target_region(
- &mut self,
- point_index: RegionElementIndex,
- ) -> Result<bool, Self::Early>;
-
- /// Adds all universal regions in the source region to the target region, returning
- /// true if something has changed.
- fn add_universal_regions_outlived_by_source_to_target(&mut self) -> Result<bool, Self::Early>;
-}
-
-/// Used during inference to enforce a `R1: R2 @ P` constraint. For
-/// each point Q we reach along the DFS, we check if Q is in R2 (the
-/// "source region"). If not, we stop the walk. Otherwise, we add Q to
-/// R1 (the "target region") and continue to Q's successors. If we
-/// reach the end of the graph, then we add any universal regions from
-/// R2 into R1.
-pub(super) struct CopyFromSourceToTarget<'v> {
- pub source_region: RegionVid,
- pub target_region: RegionVid,
- pub inferred_values: &'v mut RegionValues,
- pub constraint_point: Location,
- pub constraint_span: Span,
-}
-
-impl<'v> DfsOp for CopyFromSourceToTarget<'v> {
- /// We never stop the walk early.
- type Early = !;
-
- fn start_point(&self) -> Location {
- self.constraint_point
- }
-
- fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool {
- self.inferred_values
- .contains(self.source_region, point_index)
- }
-
- fn add_to_target_region(&mut self, point_index: RegionElementIndex) -> Result<bool, !> {
- Ok(self.inferred_values.add_due_to_outlives(
- self.source_region,
- self.target_region,
- point_index,
- self.constraint_point,
- self.constraint_span,
- ))
- }
-
- fn add_universal_regions_outlived_by_source_to_target(&mut self) -> Result<bool, !> {
- Ok(self.inferred_values.add_universal_regions_outlived_by(
- self.source_region,
- self.target_region,
- self.constraint_point,
- self.constraint_span,
- ))
- }
-}
-
-/// Used after inference to *test* a `R1: R2 @ P` constraint. For
-/// each point Q we reach along the DFS, we check if Q in R2 is also
-/// contained in R1. If not, we abort the walk early with an `Err`
-/// condition. Similarly, if we reach the end of the graph and find
-/// that R1 contains some universal region that R2 does not contain,
-/// we abort the walk early.
-pub(super) struct TestTargetOutlivesSource<'v, 'tcx: 'v> {
- pub source_region: RegionVid,
- pub target_region: RegionVid,
- pub elements: &'v RegionValueElements,
- pub universal_regions: &'v UniversalRegions<'tcx>,
- pub inferred_values: &'v RegionValues,
- pub constraint_point: Location,
-}
-
-impl<'v, 'tcx> DfsOp for TestTargetOutlivesSource<'v, 'tcx> {
- /// The element that was not found within R2.
- type Early = RegionElementIndex;
-
- fn start_point(&self) -> Location {
- self.constraint_point
- }
-
- fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool {
- self.inferred_values
- .contains(self.source_region, point_index)
- }
-
- fn add_to_target_region(
- &mut self,
- point_index: RegionElementIndex,
- ) -> Result<bool, RegionElementIndex> {
- if !self.inferred_values
- .contains(self.target_region, point_index)
- {
- return Err(point_index);
- }
-
- Ok(false)
- }
-
- fn add_universal_regions_outlived_by_source_to_target(
- &mut self,
- ) -> Result<bool, RegionElementIndex> {
- // For all `ur_in_source` in `source_region`.
- for ur_in_source in self.inferred_values
- .universal_regions_outlived_by(self.source_region)
- {
- // Check that `target_region` outlives `ur_in_source`.
-
- // If `ur_in_source` is a member of `target_region`, OK.
- //
- // (This is implied by the loop below, actually, just an
- // irresistible micro-opt. Mm. Premature optimization. So
- // tasty.)
- if self.inferred_values
- .contains(self.target_region, ur_in_source)
- {
- continue;
- }
-
- // If there is some other element X such that `target_region: X` and
- // `X: ur_in_source`, OK.
- if self.inferred_values
- .universal_regions_outlived_by(self.target_region)
- .any(|ur_in_target| self.universal_regions.outlives(ur_in_target, ur_in_source))
- {
- continue;
- }
-
- // Otherwise, not known to be true.
- return Err(self.elements.index(ur_in_source));
- }
-
- Ok(false)
- }
-}
// except according to those terms.
use super::universal_regions::UniversalRegions;
+use borrow_check::nll::region_infer::values::ToElementIndex;
use rustc::hir::def_id::DefId;
+use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
+use rustc::infer::region_constraints::{GenericKind, VarInfos};
use rustc::infer::InferCtxt;
use rustc::infer::NLLRegionVariableOrigin;
use rustc::infer::RegionObligation;
use rustc::infer::RegionVariableOrigin;
use rustc::infer::SubregionOrigin;
-use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
-use rustc::infer::region_constraints::{GenericKind, VarInfos};
-use rustc::mir::{ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements,
- Local, Location, Mir};
+use rustc::mir::{
+ ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, Local, Location,
+ Mir,
+};
use rustc::traits::ObligationCause;
use rustc::ty::{self, RegionVid, Ty, TypeFoldable};
use rustc::util::common::{self, ErrorReported};
use syntax_pos::Span;
mod annotation;
-mod dfs;
-use self::dfs::{CopyFromSourceToTarget, TestTargetOutlivesSource};
mod dump_mir;
mod graphviz;
mod values;
/// NB: The variants in `Cause` are intentionally ordered. Lower
/// values are preferred when it comes to error messages. Do not
/// reorder willy nilly.
-#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub(crate) enum Cause {
/// point inserted because Local was live at the given Location
LiveVar(Local, Location),
/// part of the initial set of values for a universally quantified region
UniversalRegion(RegionVid),
-
- /// Element E was added to R because there was some
- /// outlives obligation `R: R1 @ P` and `R1` contained `E`.
- Outlives {
- /// the reason that R1 had E
- original_cause: Rc<Cause>,
-
- /// the point P from the relation
- constraint_location: Location,
-
- /// The span indicating why we added the outlives constraint.
- constraint_span: Span,
- },
-}
-
-pub(crate) struct RegionCausalInfo {
- inferred_values: RegionValues,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
// Add all nodes in the CFG to liveness constraints
for point_index in self.elements.all_point_indices() {
- self.liveness_constraints.add(
+ self.liveness_constraints.add_element(
variable,
point_index,
&Cause::UniversalRegion(variable),
}
// Add `end(X)` into the set for X.
- self.liveness_constraints
- .add(variable, variable, &Cause::UniversalRegion(variable));
+ self.liveness_constraints.add_element(
+ variable,
+ variable,
+ &Cause::UniversalRegion(variable),
+ );
}
}
debug!("add_live_point: @{:?} Adding cause {:?}", point, cause);
let element = self.elements.index(point);
- if self.liveness_constraints.add(v, element, &cause) {
+ if self.liveness_constraints.add_element(v, element, &cause) {
true
} else {
false
) -> Option<ClosureRegionRequirements<'gcx>> {
assert!(self.inferred_values.is_none(), "values already inferred");
- let dfs_storage = &mut self.new_dfs_storage();
-
- self.propagate_constraints(mir, dfs_storage);
+ self.propagate_constraints(mir);
// If this is a closure, we can propagate unsatisfied
// `outlives_requirements` to our creator, so create a vector
None
};
- self.check_type_tests(
- infcx,
- mir,
- dfs_storage,
- mir_def_id,
- outlives_requirements.as_mut(),
- );
+ self.check_type_tests(infcx, mir, mir_def_id, outlives_requirements.as_mut());
self.check_universal_regions(infcx, mir_def_id, outlives_requirements.as_mut());
}
}
- /// Re-execute the region inference, this time tracking causal information.
- /// This is significantly slower, so it is done only when an error is being reported.
- pub(super) fn compute_causal_info(&self, mir: &Mir<'tcx>) -> RegionCausalInfo {
- let dfs_storage = &mut self.new_dfs_storage();
- let inferred_values = self.compute_region_values(mir, dfs_storage, TrackCauses(true));
- RegionCausalInfo { inferred_values }
- }
-
/// Propagate the region constraints: this will grow the values
/// for each region variable until all the constraints are
/// satisfied. Note that some values may grow **too** large to be
/// feasible, but we check this later.
- fn propagate_constraints(&mut self, mir: &Mir<'tcx>, dfs_storage: &mut dfs::DfsStorage) {
+ fn propagate_constraints(&mut self, mir: &Mir<'tcx>) {
self.dependency_map = Some(self.build_dependency_map());
- let inferred_values = self.compute_region_values(mir, dfs_storage, TrackCauses(false));
+ let inferred_values = self.compute_region_values(mir);
self.inferred_values = Some(inferred_values);
}
#[inline(never)] // ensure dfs is identifiable in profiles
- fn compute_region_values(
- &self,
- mir: &Mir<'tcx>,
- dfs_storage: &mut dfs::DfsStorage,
- track_causes: TrackCauses,
- ) -> RegionValues {
+ fn compute_region_values(&self, _mir: &Mir<'tcx>) -> RegionValues {
debug!("compute_region_values()");
debug!("compute_region_values: constraints={:#?}", {
let mut constraints: Vec<_> = self.constraints.iter().collect();
// The initial values for each region are derived from the liveness
// constraints we have accumulated.
- let mut inferred_values = self.liveness_constraints.duplicate(track_causes);
+ let mut inferred_values = self.liveness_constraints.duplicate(TrackCauses(false));
let dependency_map = self.dependency_map.as_ref().unwrap();
let constraint = &self.constraints[constraint_idx];
debug!("propagate_constraints: constraint={:?}", constraint);
- // Grow the value as needed to accommodate the
- // outlives constraint.
- let Ok(made_changes) = self.dfs(
- mir,
- dfs_storage,
- CopyFromSourceToTarget {
- source_region: constraint.sub,
- target_region: constraint.sup,
- inferred_values: &mut inferred_values,
- constraint_point: constraint.point,
- constraint_span: constraint.span,
- },
- );
-
- if made_changes {
+ if inferred_values.add_region(constraint.sup, constraint.sub) {
debug!("propagate_constraints: sub={:?}", constraint.sub);
debug!("propagate_constraints: sup={:?}", constraint.sup);
&self,
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
- dfs_storage: &mut dfs::DfsStorage,
mir_def_id: DefId,
mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'gcx>>>,
) {
for type_test in &self.type_tests {
debug!("check_type_test: {:?}", type_test);
- if self.eval_region_test(
- mir,
- dfs_storage,
- type_test.point,
- type_test.lower_bound,
- &type_test.test,
- ) {
+ if self.eval_region_test(mir, type_test.point, type_test.lower_bound, &type_test.test) {
continue;
}
fn eval_region_test(
&self,
mir: &Mir<'tcx>,
- dfs_storage: &mut dfs::DfsStorage,
point: Location,
lower_bound: RegionVid,
test: &RegionTest,
match test {
RegionTest::IsOutlivedByAllRegionsIn(regions) => regions
.iter()
- .all(|&r| self.eval_outlives(mir, dfs_storage, r, lower_bound, point)),
+ .all(|&r| self.eval_outlives(mir, r, lower_bound, point)),
RegionTest::IsOutlivedByAnyRegionIn(regions) => regions
.iter()
- .any(|&r| self.eval_outlives(mir, dfs_storage, r, lower_bound, point)),
+ .any(|&r| self.eval_outlives(mir, r, lower_bound, point)),
RegionTest::Any(tests) => tests
.iter()
- .any(|test| self.eval_region_test(mir, dfs_storage, point, lower_bound, test)),
+ .any(|test| self.eval_region_test(mir, point, lower_bound, test)),
RegionTest::All(tests) => tests
.iter()
- .all(|test| self.eval_region_test(mir, dfs_storage, point, lower_bound, test)),
+ .all(|test| self.eval_region_test(mir, point, lower_bound, test)),
}
}
// Evaluate whether `sup_region: sub_region @ point`.
fn eval_outlives(
&self,
- mir: &Mir<'tcx>,
- dfs_storage: &mut dfs::DfsStorage,
+ _mir: &Mir<'tcx>,
sup_region: RegionVid,
sub_region: RegionVid,
point: Location,
sup_region, sub_region, point
);
- // Roughly speaking, do a DFS of all region elements reachable
- // from `point` contained in `sub_region`. If any of those are
- // *not* present in `sup_region`, the DFS will abort early and
- // yield an `Err` result.
- match self.dfs(
- mir,
- dfs_storage,
- TestTargetOutlivesSource {
- source_region: sub_region,
- target_region: sup_region,
- constraint_point: point,
- elements: &self.elements,
- universal_regions: &self.universal_regions,
- inferred_values: self.inferred_values.as_ref().unwrap(),
- },
- ) {
- Ok(_) => {
- debug!("eval_outlives: true");
- true
- }
+ let inferred_values = self.inferred_values
+ .as_ref()
+ .expect("values for regions not yet inferred");
- Err(elem) => {
- debug!(
- "eval_outlives: false because `{:?}` is not present in `{:?}`",
- self.elements.to_element(elem),
- sup_region
- );
- false
- }
+ debug!(
+ "eval_outlives: sup_region's value = {:?}",
+ inferred_values.region_value_str(sup_region),
+ );
+ debug!(
+ "eval_outlives: sub_region's value = {:?}",
+ inferred_values.region_value_str(sub_region),
+ );
+
+ // Both the `sub_region` and `sup_region` consist of the union
+ // of some number of universal regions (along with the union
+ // of various points in the CFG; ignore those points for
+ // now). Therefore, the sup-region outlives the sub-region if,
+ // for each universal region R1 in the sub-region, there
+ // exists some region R2 in the sup-region that outlives R1.
+ let universal_outlives = inferred_values
+ .universal_regions_outlived_by(sub_region)
+ .all(|r1| {
+ inferred_values
+ .universal_regions_outlived_by(sup_region)
+ .any(|r2| self.universal_regions.outlives(r2, r1))
+ });
+
+ if !universal_outlives {
+ return false;
+ }
+
+ // Now we have to compare all the points in the sub region and make
+ // sure they exist in the sup region.
+
+ if self.universal_regions.is_universal_region(sup_region) {
+ // Micro-opt: universal regions contain all points.
+ return true;
}
+
+ inferred_values.contains_points(sup_region, sub_region)
}
/// Once regions have been propagated, this method is used to see
longer_fr, shorter_fr,
);
- let blame_span = self.blame_span(longer_fr, shorter_fr);
+ let blame_index = self.blame_constraint(longer_fr, shorter_fr);
+ let blame_span = self.constraints[blame_index].span;
if let Some(propagated_outlives_requirements) = propagated_outlives_requirements {
// Shrink `fr` until we find a non-local region (if we do).
diag.emit();
}
+ crate fn why_region_contains_point(&self, fr1: RegionVid, elem: Location) -> Option<Cause> {
+ // Find some constraint `X: Y` where:
+ // - `fr1: X` transitively
+ // - and `Y` is live at `elem`
+ let index = self.blame_constraint(fr1, elem);
+ let region_sub = self.constraints[index].sub;
+
+ // then return why `Y` was live at `elem`
+ self.liveness_constraints.cause(region_sub, elem)
+ }
+
/// Tries to finds a good span to blame for the fact that `fr1`
/// contains `fr2`.
- fn blame_span(&self, fr1: RegionVid, fr2: RegionVid) -> Span {
+ fn blame_constraint(&self, fr1: RegionVid, elem: impl ToElementIndex) -> ConstraintIndex {
// Find everything that influenced final value of `fr`.
let influenced_fr1 = self.dependencies(fr1);
// of dependencies, which doesn't account for the locations of
// contraints at all. But it will do for now.
let relevant_constraint = self.constraints
- .iter()
- .filter_map(|constraint| {
- if constraint.sub != fr2 {
- None
- } else {
- influenced_fr1[constraint.sup]
- .map(|distance| (distance, constraint.span))
- }
- })
- .min() // constraining fr1 with fewer hops *ought* to be more obvious
- .map(|(_dist, span)| span);
+ .iter_enumerated()
+ .filter_map(|(i, constraint)| {
+ if !self.liveness_constraints.contains(constraint.sub, elem) {
+ None
+ } else {
+ influenced_fr1[constraint.sup]
+ .map(|distance| (distance, i))
+ }
+ })
+ .min() // constraining fr1 with fewer hops *ought* to be more obvious
+ .map(|(_dist, i)| i);
relevant_constraint.unwrap_or_else(|| {
bug!(
"could not find any constraint to blame for {:?}: {:?}",
fr1,
- fr2
+ elem,
);
})
}
}
}
-impl RegionCausalInfo {
- /// Returns the *reason* that the region `r` contains the given point.
- pub(super) fn why_region_contains_point<R>(&self, r: R, p: Location) -> Option<Rc<Cause>>
- where
- R: ToRegionVid,
- {
- self.inferred_values.cause(r.to_region_vid(), p)
- }
-}
-
impl<'tcx> RegionDefinition<'tcx> {
fn new(origin: RegionVariableOrigin) -> Self {
// Create a new region definition. Note that, for free
})
}
}
-
-trait CauseExt {
- fn outlives(&self, constraint_location: Location, constraint_span: Span) -> Cause;
-}
-
-impl CauseExt for Rc<Cause> {
- /// Creates a derived cause due to an outlives constraint.
- fn outlives(&self, constraint_location: Location, constraint_span: Span) -> Cause {
- Cause::Outlives {
- original_cause: self.clone(),
- constraint_location,
- constraint_span,
- }
- }
-}
-
-impl Cause {
- pub(crate) fn root_cause(&self) -> &Cause {
- match self {
- Cause::LiveVar(..)
- | Cause::DropVar(..)
- | Cause::LiveOther(..)
- | Cause::UniversalRegion(..) => self,
-
- Cause::Outlives { original_cause, .. } => original_cause.root_cause(),
- }
- }
-}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::rc::Rc;
+use borrow_check::nll::region_infer::TrackCauses;
+use rustc::mir::{BasicBlock, Location, Mir};
+use rustc::ty::RegionVid;
use rustc_data_structures::bitvec::SparseBitMatrix;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::indexed_vec::Idx;
use rustc_data_structures::indexed_vec::IndexVec;
-use rustc::mir::{BasicBlock, Location, Mir};
-use rustc::ty::{self, RegionVid};
-use syntax::codemap::Span;
+use std::fmt::Debug;
+use std::rc::Rc;
-use super::{Cause, CauseExt, TrackCauses};
+use super::Cause;
/// Maps between the various kinds of elements of a region value to
/// the internal indices that w use.
(0..self.num_points).map(move |i| RegionElementIndex::new(i + self.num_universal_regions))
}
- /// Iterates over the `RegionElementIndex` for all points in the CFG.
- pub(super) fn all_universal_region_indices(&self) -> impl Iterator<Item = RegionElementIndex> {
- (0..self.num_universal_regions).map(move |i| RegionElementIndex::new(i))
- }
-
/// Converts a particular `RegionElementIndex` to the `RegionElement` it represents.
pub(super) fn to_element(&self, i: RegionElementIndex) -> RegionElement {
debug!("to_element(i={:?})", i);
UniversalRegion(RegionVid),
}
-pub(super) trait ToElementIndex {
+pub(super) trait ToElementIndex: Debug + Copy {
fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex;
}
causes: Option<CauseMap>,
}
-type CauseMap = FxHashMap<(RegionVid, RegionElementIndex), Rc<Cause>>;
+type CauseMap = FxHashMap<(RegionVid, RegionElementIndex), Cause>;
impl RegionValues {
/// Creates a new set of "region values" that tracks causal information.
/// Adds the given element to the value for the given region. Returns true if
/// the element is newly added (i.e., was not already present).
- pub(super) fn add<E: ToElementIndex>(&mut self, r: RegionVid, elem: E, cause: &Cause) -> bool {
+ pub(super) fn add_element<E: ToElementIndex>(
+ &mut self,
+ r: RegionVid,
+ elem: E,
+ cause: &Cause,
+ ) -> bool {
let i = self.elements.index(elem);
self.add_internal(r, i, |_| cause.clone())
}
+ /// Add all elements in `r_from` to `r_to` (because e.g. `r_to:
+ /// r_from`).
+ pub(super) fn add_region(&mut self, r_to: RegionVid, r_from: RegionVid) -> bool {
+ self.matrix.merge(r_from, r_to)
+ }
+
/// Internal method to add an element to a region.
///
/// Takes a "lazy" cause -- this function will return the cause, but it will only
debug!("add(r={:?}, i={:?})", r, self.elements.to_element(i));
if let Some(causes) = &mut self.causes {
- let cause = Rc::new(make_cause(causes));
+ let cause = make_cause(causes);
causes.insert((r, i), cause);
}
// #49998: compare using root cause alone to avoid
// useless traffic from similar outlives chains.
- let overwrite = if ty::tls::with(|tcx| {
- tcx.sess.opts.debugging_opts.nll_subminimal_causes
- }) {
- cause.root_cause() < old_cause.root_cause()
- } else {
- cause < **old_cause
- };
- if overwrite {
- *old_cause = Rc::new(cause);
+ if cause < *old_cause {
+ *old_cause = cause;
return true;
}
}
}
}
- /// Adds `elem` to `to_region` because of a relation:
- ///
- /// to_region: from_region @ constraint_location
- ///
- /// that was added by the cod at `constraint_span`.
- pub(super) fn add_due_to_outlives<T: ToElementIndex>(
- &mut self,
- from_region: RegionVid,
- to_region: RegionVid,
- elem: T,
- constraint_location: Location,
- constraint_span: Span,
- ) -> bool {
- let elem = self.elements.index(elem);
- self.add_internal(to_region, elem, |causes| {
- causes[&(from_region, elem)].outlives(constraint_location, constraint_span)
- })
- }
-
- /// Adds all the universal regions outlived by `from_region` to
- /// `to_region`.
- pub(super) fn add_universal_regions_outlived_by(
- &mut self,
- from_region: RegionVid,
- to_region: RegionVid,
- constraint_location: Location,
- constraint_span: Span,
- ) -> bool {
- // We could optimize this by improving `SparseBitMatrix::merge` so
- // it does not always merge an entire row. That would
- // complicate causal tracking though.
- debug!(
- "add_universal_regions_outlived_by(from_region={:?}, to_region={:?})",
- from_region, to_region
- );
- let mut changed = false;
- for elem in self.elements.all_universal_region_indices() {
- if self.contains(from_region, elem) {
- changed |= self.add_due_to_outlives(
- from_region,
- to_region,
- elem,
- constraint_location,
- constraint_span,
- );
- }
- }
- changed
- }
-
/// True if the region `r` contains the given element.
pub(super) fn contains<E: ToElementIndex>(&self, r: RegionVid, elem: E) -> bool {
let i = self.elements.index(elem);
self.matrix.contains(r, i)
}
+ /// True if `sup_region` contains all the CFG points that
+ /// `sub_region` contains. Ignores universal regions.
+ pub(super) fn contains_points(&self, sup_region: RegionVid, sub_region: RegionVid) -> bool {
+ // This could be done faster by comparing the bitsets. But I
+ // am lazy.
+ self.element_indices_contained_in(sub_region)
+ .skip_while(|&i| self.elements.to_universal_region(i).is_some())
+ .all(|e| self.contains(sup_region, e))
+ }
+
/// Iterate over the value of the region `r`, yielding up element
/// indices. You may prefer `universal_regions_outlived_by` or
/// `elements_contained_in`.
///
/// Returns None if cause tracking is disabled or `elem` is not
/// actually found in `r`.
- pub(super) fn cause<T: ToElementIndex>(&self, r: RegionVid, elem: T) -> Option<Rc<Cause>> {
+ pub(super) fn cause<T: ToElementIndex>(&self, r: RegionVid, elem: T) -> Option<Cause> {
let index = self.elements.index(elem);
if let Some(causes) = &self.causes {
causes.get(&(r, index)).cloned()
#![feature(const_fn)]
#![feature(core_intrinsics)]
#![feature(decl_macro)]
-#![cfg_attr(stage0, feature(dyn_trait))]
#![feature(fs_read_write)]
#![feature(macro_vis_matcher)]
#![feature(exhaustive_patterns)]
#![feature(range_contains)]
#![feature(rustc_diagnostic_macros)]
-#![feature(inclusive_range_methods)]
#![feature(crate_visibility_modifier)]
#![feature(never_type)]
#![feature(specialization)]
-#![cfg_attr(stage0, feature(try_trait))]
+#![feature(try_trait)]
extern crate arena;
#[macro_use]
extern crate rustc_apfloat;
extern crate byteorder;
-#[cfg(stage0)]
-macro_rules! do_catch {
- ($t:expr) => { (|| ::std::ops::Try::from_ok($t) )() }
-}
-
-#[cfg(not(stage0))]
-macro_rules! do_catch {
- ($t:expr) => { do catch { $t } }
-}
-
mod diagnostics;
mod borrow_check;
use rustc::mir::*;
use rustc::mir::visit::{PlaceContext, MutVisitor, Visitor};
use rustc::mir::traversal::ReversePostorder;
-use rustc::ty::TyCtxt;
+use rustc::ty::{self, TyCtxt};
use syntax_pos::Span;
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
-use std::iter;
-use std::mem;
-use std::usize;
+use std::{cmp, iter, mem, usize};
/// State of a temporary during collection and promotion.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
}
struct Promoter<'a, 'tcx: 'a> {
+ tcx: TyCtxt<'a, 'tcx, 'tcx>,
source: &'a mut Mir<'tcx>,
promoted: Mir<'tcx>,
temps: &'a mut IndexVec<Local, TempState>,
+ extra_statements: &'a mut Vec<(Location, Statement<'tcx>)>,
/// If true, all nested temps are also kept in the
/// source MIR, not moved to the promoted MIR.
}
fn promote_candidate(mut self, candidate: Candidate) {
- let span = self.promoted.span;
- let new_operand = Operand::Constant(box Constant {
- span,
- ty: self.promoted.return_ty(),
- literal: Literal::Promoted {
+ let mut rvalue = {
+ let promoted = &mut self.promoted;
+ let literal = Literal::Promoted {
index: Promoted::new(self.source.promoted.len())
- }
- });
- let mut rvalue = match candidate {
- Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
- let ref mut statement = self.source[bb].statements[stmt_idx];
- match statement.kind {
- StatementKind::Assign(_, ref mut rvalue) => {
- mem::replace(rvalue, Rvalue::Use(new_operand))
+ };
+ let operand = |ty, span| {
+ promoted.span = span;
+ promoted.local_decls[RETURN_PLACE] =
+ LocalDecl::new_return_place(ty, span);
+ Operand::Constant(box Constant {
+ span,
+ ty,
+ literal
+ })
+ };
+ let (blocks, local_decls) = self.source.basic_blocks_and_local_decls_mut();
+ match candidate {
+ Candidate::Ref(loc) => {
+ let ref mut statement = blocks[loc.block].statements[loc.statement_index];
+ match statement.kind {
+ StatementKind::Assign(_, Rvalue::Ref(r, bk, ref mut place)) => {
+ // Find the underlying local for this (necessarilly interior) borrow.
+ // HACK(eddyb) using a recursive function because of mutable borrows.
+ fn interior_base<'a, 'tcx>(place: &'a mut Place<'tcx>)
+ -> &'a mut Place<'tcx> {
+ if let Place::Projection(ref mut proj) = *place {
+ assert_ne!(proj.elem, ProjectionElem::Deref);
+ return interior_base(&mut proj.base);
+ }
+ place
+ }
+ let place = interior_base(place);
+
+ let ty = place.ty(local_decls, self.tcx).to_ty(self.tcx);
+ let ref_ty = self.tcx.mk_ref(r,
+ ty::TypeAndMut {
+ ty,
+ mutbl: bk.to_mutbl_lossy()
+ }
+ );
+ let span = statement.source_info.span;
+
+ // Create a temp to hold the promoted reference.
+ // This is because `*r` requires `r` to be a local,
+ // otherwise we would use the `promoted` directly.
+ let mut promoted_ref = LocalDecl::new_temp(ref_ty, span);
+ promoted_ref.source_info = statement.source_info;
+ let promoted_ref = local_decls.push(promoted_ref);
+ assert_eq!(self.temps.push(TempState::Unpromotable), promoted_ref);
+ self.extra_statements.push((loc, Statement {
+ source_info: statement.source_info,
+ kind: StatementKind::Assign(
+ Place::Local(promoted_ref),
+ Rvalue::Use(operand(ref_ty, span)),
+ )
+ }));
+ let promoted_place = Place::Local(promoted_ref).deref();
+
+ Rvalue::Ref(r, bk, mem::replace(place, promoted_place))
+ }
+ _ => bug!()
}
- _ => bug!()
}
- }
- Candidate::Argument { bb, index } => {
- match self.source[bb].terminator_mut().kind {
- TerminatorKind::Call { ref mut args, .. } => {
- Rvalue::Use(mem::replace(&mut args[index], new_operand))
+ Candidate::Argument { bb, index } => {
+ let terminator = blocks[bb].terminator_mut();
+ match terminator.kind {
+ TerminatorKind::Call { ref mut args, .. } => {
+ let ty = args[index].ty(local_decls, self.tcx);
+ let span = terminator.source_info.span;
+ Rvalue::Use(mem::replace(&mut args[index], operand(ty, span)))
+ }
+ _ => bug!()
}
- _ => bug!()
}
}
};
+
+ assert_eq!(self.new_block(), START_BLOCK);
self.visit_rvalue(&mut rvalue, Location {
block: BasicBlock::new(0),
statement_index: usize::MAX
});
+ let span = self.promoted.span;
self.assign(RETURN_PLACE, rvalue, span);
self.source.promoted.push(self.promoted);
}
candidates: Vec<Candidate>) {
// Visit candidates in reverse, in case they're nested.
debug!("promote_candidates({:?})", candidates);
+
+ let mut extra_statements = vec![];
for candidate in candidates.into_iter().rev() {
- let (span, ty) = match candidate {
- Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
- let statement = &mir[bb].statements[stmt_idx];
- let dest = match statement.kind {
- StatementKind::Assign(ref dest, _) => dest,
- _ => {
- span_bug!(statement.source_info.span,
- "expected assignment to promote");
- }
- };
- if let Place::Local(index) = *dest {
- if temps[index] == TempState::PromotedOut {
- // Already promoted.
- continue;
+ match candidate {
+ Candidate::Ref(Location { block, statement_index }) => {
+ match mir[block].statements[statement_index].kind {
+ StatementKind::Assign(Place::Local(local), _) => {
+ if temps[local] == TempState::PromotedOut {
+ // Already promoted.
+ continue;
+ }
}
+ _ => {}
}
- (statement.source_info.span, dest.ty(mir, tcx).to_ty(tcx))
- }
- Candidate::Argument { bb, index } => {
- let terminator = mir[bb].terminator();
- let ty = match terminator.kind {
- TerminatorKind::Call { ref args, .. } => {
- args[index].ty(mir, tcx)
- }
- _ => {
- span_bug!(terminator.source_info.span,
- "expected call argument to promote");
- }
- };
- (terminator.source_info.span, ty)
}
- };
+ Candidate::Argument { .. } => {}
+ }
+
- // Declare return place local
- let initial_locals = iter::once(LocalDecl::new_return_place(ty, span))
- .collect();
+ // Declare return place local so that `Mir::new` doesn't complain.
+ let initial_locals = iter::once(
+ LocalDecl::new_return_place(tcx.types.never, mir.span)
+ ).collect();
let mut promoter = Promoter {
promoted: Mir::new(
initial_locals,
0,
vec![],
- span
+ mir.span
),
+ tcx,
source: mir,
temps: &mut temps,
+ extra_statements: &mut extra_statements,
keep_original: false
};
- assert_eq!(promoter.new_block(), START_BLOCK);
promoter.promote_candidate(candidate);
}
+ // Insert each of `extra_statements` before its indicated location, which
+ // has to be done in reverse location order, to not invalidate the rest.
+ extra_statements.sort_by_key(|&(loc, _)| cmp::Reverse(loc));
+ for (loc, statement) in extra_statements {
+ mir[loc.block].statements.insert(loc.statement_index, statement);
+ }
+
// Eliminate assignments to, and drops of promoted temps.
let promoted = |index: Local| temps[index] == TempState::PromotedOut;
for block in mir.basic_blocks_mut() {
}
/// Check if a Local with the current qualifications is promotable.
- fn can_promote(&mut self) -> bool {
+ fn can_promote(&self, qualif: Qualif) -> bool {
// References to statics are allowed, but only in other statics.
if self.mode == Mode::Static || self.mode == Mode::StaticMut {
- (self.qualif - Qualif::STATIC_REF).is_empty()
+ (qualif - Qualif::STATIC_REF).is_empty()
} else {
- self.qualif.is_empty()
+ qualif.is_empty()
}
}
}
let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
+
+ // Default to forbidding the borrow and/or its promotion,
+ // due to the potential for direct or interior mutability,
+ // and only proceed by setting `forbidden_mut` to `false`.
+ let mut forbidden_mut = true;
+
if let BorrowKind::Mut { .. } = kind {
// In theory, any zero-sized value could be borrowed
// mutably without consequences. However, only &mut []
// is allowed right now, and only in functions.
- let allow = if self.mode == Mode::StaticMut {
+ if self.mode == Mode::StaticMut {
// Inside a `static mut`, &mut [...] is also allowed.
match ty.sty {
- ty::TyArray(..) | ty::TySlice(_) => true,
- _ => false
+ ty::TyArray(..) | ty::TySlice(_) => forbidden_mut = false,
+ _ => {}
}
} else if let ty::TyArray(_, len) = ty.sty {
- len.unwrap_usize(self.tcx) == 0 &&
- self.mode == Mode::Fn
- } else {
- false
- };
+ // FIXME(eddyb) the `self.mode == Mode::Fn` condition
+ // seems unnecessary, given that this is merely a ZST.
+ if len.unwrap_usize(self.tcx) == 0 && self.mode == Mode::Fn {
+ forbidden_mut = false;
+ }
+ }
- if !allow {
+ if forbidden_mut {
self.add(Qualif::NOT_CONST);
if self.mode != Mode::Fn {
let mut err = struct_span_err!(self.tcx.sess, self.span, E0017,
// it means that our "silent insertion of statics" could change
// initializer values (very bad).
if self.qualif.intersects(Qualif::MUTABLE_INTERIOR) {
- // Replace MUTABLE_INTERIOR with NOT_CONST to avoid
+ // A reference of a MUTABLE_INTERIOR place is instead
+ // NOT_CONST (see `if forbidden_mut` below), to avoid
// duplicate errors (from reborrowing, for example).
self.qualif = self.qualif - Qualif::MUTABLE_INTERIOR;
- self.add(Qualif::NOT_CONST);
if self.mode != Mode::Fn {
span_err!(self.tcx.sess, self.span, E0492,
"cannot borrow a constant which may contain \
interior mutability, create a static instead");
}
+ } else {
+ // We allow immutable borrows of frozen data.
+ forbidden_mut = false;
}
}
- // We might have a candidate for promotion.
- let candidate = Candidate::Ref(location);
- if self.can_promote() {
- // We can only promote direct borrows of temps.
+ if forbidden_mut {
+ self.add(Qualif::NOT_CONST);
+ } else {
+ // We might have a candidate for promotion.
+ let candidate = Candidate::Ref(location);
+ // We can only promote interior borrows of promotable temps.
+ let mut place = place;
+ while let Place::Projection(ref proj) = *place {
+ if proj.elem == ProjectionElem::Deref {
+ break;
+ }
+ place = &proj.base;
+ }
if let Place::Local(local) = *place {
if self.mir.local_kind(local) == LocalKind::Temp {
- self.promotion_candidates.push(candidate);
+ if let Some(qualif) = self.temp_qualif[local] {
+ // `forbidden_mut` is false, so we can safely ignore
+ // `MUTABLE_INTERIOR` from the local's qualifications.
+ // This allows borrowing fields which don't have
+ // `MUTABLE_INTERIOR`, from a type that does, e.g.:
+ // `let _: &'static _ = &(Cell::new(1), 2).1;`
+ if self.can_promote(qualif - Qualif::MUTABLE_INTERIOR) {
+ self.promotion_candidates.push(candidate);
+ }
+ }
}
}
}
}
let candidate = Candidate::Argument { bb, index: i };
if is_shuffle && i == 2 {
- if this.can_promote() {
+ if this.can_promote(this.qualif) {
this.promotion_candidates.push(candidate);
} else {
span_err!(this.tcx.sess, this.span, E0526,
if !constant_arguments.contains(&i) {
return
}
- if this.can_promote() {
+ if this.can_promote(this.qualif) {
this.promotion_candidates.push(candidate);
} else {
this.tcx.sess.span_err(this.span,
) where
F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
{
- let _: io::Result<()> = do_catch! {{
+ let _: io::Result<()> = do catch {
let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, source)?;
writeln!(file, "// MIR for `{}`", node_path)?;
writeln!(file, "// source = {:?}", source)?;
extra_data(PassWhere::BeforeCFG, &mut file)?;
write_mir_fn(tcx, source, mir, &mut extra_data, &mut file)?;
extra_data(PassWhere::AfterCFG, &mut file)?;
- }};
+ };
if tcx.sess.opts.debugging_opts.dump_mir_graphviz {
- let _: io::Result<()> = do_catch! {{
+ let _: io::Result<()> = do catch {
let mut file =
create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, source)?;
write_mir_fn_graphviz(tcx, source.def_id, mir, &mut file)?;
- }};
+ };
}
}
use syntax::ast::*;
use syntax::attr;
use syntax::codemap::Spanned;
-use syntax::parse::token;
use syntax::symbol::keywords;
use syntax::visit::{self, Visitor};
use syntax_pos::Span;
let valid_names = [keywords::UnderscoreLifetime.name(),
keywords::StaticLifetime.name(),
keywords::Invalid.name()];
- if !valid_names.contains(&ident.name) &&
- token::is_reserved_ident(ident.without_first_quote()) {
+ if !valid_names.contains(&ident.name) && ident.without_first_quote().is_reserved() {
self.err_handler().span_err(ident.span, "lifetimes cannot use keyword names");
}
}
fn check_label(&self, ident: Ident) {
- if token::is_reserved_ident(ident.without_first_quote()) {
+ if ident.without_first_quote().is_reserved() {
self.err_handler()
.span_err(ident.span, &format!("invalid label name `{}`", ident.name));
}
use syntax::ext::base::{SyntaxExtension, NamedSyntaxExtension, NormalTT, IdentTT};
use syntax::ext::base::MacroExpanderFn;
+use syntax::ext::hygiene;
use syntax::symbol::Symbol;
use syntax::ast;
use syntax::feature_gate::AttributeType;
def_info: _,
allow_internal_unstable,
allow_internal_unsafe,
- unstable_feature
+ unstable_feature,
+ edition,
} => {
let nid = ast::CRATE_NODE_ID;
NormalTT {
def_info: Some((nid, self.krate_span)),
allow_internal_unstable,
allow_internal_unsafe,
- unstable_feature
+ unstable_feature,
+ edition,
}
}
IdentTT(ext, _, allow_internal_unstable) => {
allow_internal_unstable: false,
allow_internal_unsafe: false,
unstable_feature: None,
+ edition: hygiene::default_edition(),
});
}
let ext = Lrc::new(macro_rules::compile(&self.session.parse_sess,
&self.session.features_untracked(),
- ¯o_def));
+ ¯o_def,
+ self.cstore.crate_edition_untracked(def_id.krate)));
self.macro_map.insert(def_id, ext.clone());
ext
}
use syntax::ast::{Label, Local, Mutability, Pat, PatKind, Path};
use syntax::ast::{QSelf, TraitItemKind, TraitRef, Ty, TyKind};
use syntax::feature_gate::{feature_err, GateIssue};
-use syntax::parse::token;
use syntax::ptr::P;
use syntax_pos::{Span, DUMMY_SP, MultiSpan};
// `$crate::a::b`
module = Some(self.resolve_crate_root(ident.span.ctxt(), true));
continue
- } else if i == 1 && !token::is_path_segment_keyword(ident) {
+ } else if i == 1 && !ident.is_path_segment_keyword() {
let prev_name = path[0].name;
if prev_name == keywords::Extern.name() ||
prev_name == keywords::CrateRoot.name() &&
use syntax::ext::base::{self, Annotatable, Determinacy, MultiModifier, MultiDecorator};
use syntax::ext::base::{MacroKind, SyntaxExtension, Resolver as SyntaxResolver};
use syntax::ext::expand::{Expansion, ExpansionKind, Invocation, InvocationKind, find_attr_invoc};
-use syntax::ext::hygiene::{Mark, MarkKind};
+use syntax::ext::hygiene::{self, Mark, MarkKind};
use syntax::ext::placeholders::placeholder;
use syntax::ext::tt::macro_rules;
use syntax::feature_gate::{self, emit_feature_err, GateIssue};
for did in self.unused_macros.iter() {
let id_span = match *self.macro_map[did] {
SyntaxExtension::NormalTT { def_info, .. } => def_info,
- SyntaxExtension::DeclMacro(.., osp) => osp,
+ SyntaxExtension::DeclMacro(.., osp, _) => osp,
_ => None,
};
if let Some((id, span)) = id_span {
};
for path in traits {
match self.resolve_macro(scope, path, MacroKind::Derive, force) {
- Ok(ext) => if let SyntaxExtension::ProcMacroDerive(_, ref inert_attrs) = *ext {
+ Ok(ext) => if let SyntaxExtension::ProcMacroDerive(_, ref inert_attrs, _) = *ext {
if inert_attrs.contains(&attr_name) {
// FIXME(jseyfried) Avoid `mem::replace` here.
let dummy_item = placeholder(ExpansionKind::Items, ast::DUMMY_NODE_ID)
let def_id = self.definitions.local_def_id(item.id);
let ext = Lrc::new(macro_rules::compile(&self.session.parse_sess,
&self.session.features_untracked(),
- item));
+ item, hygiene::default_edition()));
self.macro_map.insert(def_id, ext);
let def = match item.node { ast::ItemKind::MacroDef(ref def) => def, _ => unreachable!() };
match *ext {
// If `ext` is a procedural macro, check if we've already warned about it
- AttrProcMacro(_) | ProcMacro(_) => if !self.warned_proc_macros.insert(name) { return; },
+ AttrProcMacro(..) | ProcMacro(..) =>
+ if !self.warned_proc_macros.insert(name) { return; },
_ => return,
}
let warn_msg = match *ext {
- AttrProcMacro(_) => "attribute procedural macros cannot be \
- imported with `#[macro_use]`",
- ProcMacro(_) => "procedural macros cannot be imported with `#[macro_use]`",
+ AttrProcMacro(..) => "attribute procedural macros cannot be \
+ imported with `#[macro_use]`",
+ ProcMacro(..) => "procedural macros cannot be imported with `#[macro_use]`",
_ => return,
};
use syntax::ast::{Ident, Name, NodeId};
use syntax::ext::base::Determinacy::{self, Determined, Undetermined};
use syntax::ext::hygiene::Mark;
-use syntax::parse::token;
use syntax::symbol::keywords;
use syntax::util::lev_distance::find_best_match_for_name;
use syntax_pos::Span;
} else {
Some(self.resolve_crate_root(source.span.ctxt().modern(), false))
}
- } else if is_extern && !token::is_path_segment_keyword(source) {
+ } else if is_extern && !source.is_path_segment_keyword() {
let crate_id =
self.resolver.crate_loader.process_use_extern(
source.name,
}
PathResult::Failed(span, msg, true) => {
let (mut self_path, mut self_result) = (module_path.clone(), None);
- let is_special = |ident| token::is_path_segment_keyword(ident) &&
- ident.name != keywords::CrateRoot.name();
+ let is_special = |ident: Ident| ident.is_path_segment_keyword() &&
+ ident.name != keywords::CrateRoot.name();
if !self_path.is_empty() && !is_special(self_path[0]) &&
!(self_path.len() > 1 && is_special(self_path[1])) {
self_path[0].name = keywords::SelfValue.name();
#![feature(const_fn)]
#![feature(fs_read_write)]
#![feature(inclusive_range)]
-#![feature(inclusive_range_methods)]
#![feature(slice_patterns)]
#[macro_use]
// except according to those terms.
use rustc::infer::canonical::{Canonical, QueryResult};
-use rustc::traits::{self, FulfillmentContext, Normalized, ObligationCause,
- SelectionContext};
+use rustc::traits::{self, FulfillmentContext, ObligationCause, SelectionContext};
use rustc::traits::query::{CanonicalProjectionGoal, NoSolution, normalize::NormalizationResult};
use rustc::ty::{ParamEnvAnd, TyCtxt};
use rustc_data_structures::sync::Lrc;
let fulfill_cx = &mut FulfillmentContext::new();
let selcx = &mut SelectionContext::new(infcx);
let cause = ObligationCause::misc(DUMMY_SP, DUMMY_NODE_ID);
- let Normalized {
- value: answer,
- obligations,
- } = traits::normalize_projection_type(selcx, param_env, goal, cause, 0);
+ let mut obligations = vec![];
+ let answer =
+ traits::normalize_projection_type(selcx, param_env, goal, cause, 0, &mut obligations);
fulfill_cx.register_predicate_obligations(infcx, obligations);
// Now that we have fulfilled as much as we can, create a solution
}
let mut selcx = traits::SelectionContext::new(self.fcx);
- let normalized = traits::normalize_projection_type(&mut selcx,
- self.fcx.param_env,
- ty::ProjectionTy::from_ref_and_name(
- tcx,
- trait_ref,
- Symbol::intern("Target"),
- ),
- cause,
- 0);
-
- debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized);
- self.obligations.extend(normalized.obligations);
-
- Some(self.fcx.resolve_type_vars_if_possible(&normalized.value))
+ let normalized_ty = traits::normalize_projection_type(&mut selcx,
+ self.fcx.param_env,
+ ty::ProjectionTy::from_ref_and_name(
+ tcx,
+ trait_ref,
+ Symbol::intern("Target"),
+ ),
+ cause,
+ 0,
+ &mut self.obligations);
+
+ debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized_ty);
+
+ Some(self.fcx.resolve_type_vars_if_possible(&normalized_ty))
}
/// Returns the final type, generating an error if it is an
(0, vec![tcx.mk_fn_ptr(fn_ty), mut_u8, mut_u8], tcx.types.i32)
}
- "align_offset" => {
- let ptr_ty = tcx.mk_imm_ptr(tcx.mk_nil());
- (0, vec![ptr_ty, tcx.types.usize], tcx.types.usize)
- },
-
"nontemporal_store" => {
(1, vec![ tcx.mk_mut_ptr(param(0)), param(0) ], tcx.mk_nil())
}
#![allow(non_camel_case_types)]
-#![cfg_attr(stage0, feature(dyn_trait))]
-
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(crate_visibility_modifier)]
AdtKind::Enum => Def::Enum,
AdtKind::Union => Def::Union,
}
- _ => panic!("Unexpected type {:?}", def_id),
+ ty::TyInt(_) |
+ ty::TyUint(_) |
+ ty::TyFloat(_) |
+ ty::TyStr |
+ ty::TyBool |
+ ty::TyChar => return self.get_auto_trait_impls(def_id, &move |_: DefId| {
+ match ty.sty {
+ ty::TyInt(x) => Def::PrimTy(hir::TyInt(x)),
+ ty::TyUint(x) => Def::PrimTy(hir::TyUint(x)),
+ ty::TyFloat(x) => Def::PrimTy(hir::TyFloat(x)),
+ ty::TyStr => Def::PrimTy(hir::TyStr),
+ ty::TyBool => Def::PrimTy(hir::TyBool),
+ ty::TyChar => Def::PrimTy(hir::TyChar),
+ _ => unreachable!(),
+ }
+ }, None),
+ _ => {
+ debug!("Unexpected type {:?}", def_id);
+ return Vec::new()
+ }
};
- self.get_auto_trait_impls(def_id, def_ctor, None)
+ self.get_auto_trait_impls(def_id, &def_ctor, None)
}
pub fn get_with_node_id(&self, id: ast::NodeId, name: String) -> Vec<Item> {
_ => panic!("Unexpected type {:?} {:?}", item, id),
};
- self.get_auto_trait_impls(did, def_ctor, Some(name))
+ self.get_auto_trait_impls(did, &def_ctor, Some(name))
}
- pub fn get_auto_trait_impls(
+ pub fn get_auto_trait_impls<F>(
&self,
def_id: DefId,
- def_ctor: fn(DefId) -> Def,
+ def_ctor: &F,
name: Option<String>,
- ) -> Vec<Item> {
+ ) -> Vec<Item>
+ where F: Fn(DefId) -> Def {
if self.cx
.tcx
.get_attrs(def_id)
.has_word("hidden")
{
debug!(
- "get_auto_trait_impls(def_id={:?}, def_ctor={:?}): item has doc('hidden'), \
+ "get_auto_trait_impls(def_id={:?}, def_ctor=...): item has doc('hidden'), \
aborting",
- def_id, def_ctor
+ def_id
);
return Vec::new();
}
let generics = self.cx.tcx.generics_of(def_id);
debug!(
- "get_auto_trait_impls(def_id={:?}, def_ctor={:?}, generics={:?}",
- def_id, def_ctor, generics
+ "get_auto_trait_impls(def_id={:?}, def_ctor=..., generics={:?}",
+ def_id, generics
);
let auto_traits: Vec<_> = self.cx
.send_trait
auto_traits
}
- fn get_auto_trait_impl_for(
+ fn get_auto_trait_impl_for<F>(
&self,
def_id: DefId,
name: Option<String>,
generics: ty::Generics,
- def_ctor: fn(DefId) -> Def,
+ def_ctor: &F,
trait_def_id: DefId,
- ) -> Option<Item> {
+ ) -> Option<Item>
+ where F: Fn(DefId) -> Def {
if !self.cx
.generated_synthetics
.borrow_mut()
.insert((def_id, trait_def_id))
{
debug!(
- "get_auto_trait_impl_for(def_id={:?}, generics={:?}, def_ctor={:?}, \
+ "get_auto_trait_impl_for(def_id={:?}, generics={:?}, def_ctor=..., \
trait_def_id={:?}): already generated, aborting",
- def_id, generics, def_ctor, trait_def_id
+ def_id, generics, trait_def_id
);
return None;
}
for def_id in primitive_impls.iter().filter_map(|&def_id| def_id) {
if !def_id.is_local() {
build_impl(cx, def_id, &mut impls);
+
+ let auto_impls = get_auto_traits_with_def_id(cx, def_id);
+ let mut renderinfo = cx.renderinfo.borrow_mut();
+
+ let new_impls: Vec<clean::Item> = auto_impls.into_iter()
+ .filter(|i| renderinfo.inlined.insert(i.def_id)).collect();
+
+ impls.extend(new_impls);
}
}
}
}
-fn get_path_for_type(tcx: TyCtxt, def_id: DefId, def_ctor: fn(DefId) -> Def) -> hir::Path {
+fn get_path_for_type<F>(tcx: TyCtxt, def_id: DefId, def_ctor: F) -> hir::Path
+where F: Fn(DefId) -> Def {
struct AbsolutePathBuffer {
names: Vec<String>,
}
html_root_url = "https://doc.rust-lang.org/nightly/",
html_playground_url = "https://play.rust-lang.org/")]
-#![cfg_attr(stage0, feature(dyn_trait))]
-
#![feature(ascii_ctype)]
#![feature(rustc_private)]
#![feature(box_patterns)]
#[doc(inline)] pub use alloc_system::System;
#[doc(inline)] pub use core::alloc::*;
-#[cfg(not(stage0))]
#[cfg(not(test))]
#[doc(hidden)]
#[lang = "oom"]
System.alloc(layout) as *mut u8
}
- #[cfg(stage0)]
- #[no_mangle]
- #[rustc_std_internal_symbol]
- pub unsafe extern fn __rdl_oom() -> ! {
- super::oom()
- }
-
#[no_mangle]
#[rustc_std_internal_symbol]
pub unsafe extern fn __rdl_dealloc(ptr: *mut u8,
let layout = Layout::from_size_align_unchecked(size, align);
System.alloc_zeroed(layout) as *mut u8
}
-
- #[cfg(stage0)]
- pub mod stage0 {
- #[no_mangle]
- #[rustc_std_internal_symbol]
- pub unsafe extern fn __rdl_usable_size(_layout: *const u8,
- _min: *mut usize,
- _max: *mut usize) {
- unimplemented!()
- }
-
- #[no_mangle]
- #[rustc_std_internal_symbol]
- pub unsafe extern fn __rdl_alloc_excess(_size: usize,
- _align: usize,
- _excess: *mut usize,
- _err: *mut u8) -> *mut u8 {
- unimplemented!()
- }
-
- #[no_mangle]
- #[rustc_std_internal_symbol]
- pub unsafe extern fn __rdl_realloc_excess(_ptr: *mut u8,
- _old_size: usize,
- _old_align: usize,
- _new_size: usize,
- _new_align: usize,
- _excess: *mut usize,
- _err: *mut u8) -> *mut u8 {
- unimplemented!()
- }
-
- #[no_mangle]
- #[rustc_std_internal_symbol]
- pub unsafe extern fn __rdl_grow_in_place(_ptr: *mut u8,
- _old_size: usize,
- _old_align: usize,
- _new_size: usize,
- _new_align: usize) -> u8 {
- unimplemented!()
- }
-
- #[no_mangle]
- #[rustc_std_internal_symbol]
- pub unsafe extern fn __rdl_shrink_in_place(_ptr: *mut u8,
- _old_size: usize,
- _old_align: usize,
- _new_size: usize,
- _new_align: usize) -> u8 {
- unimplemented!()
- }
-
- }
}
#![stable(feature = "rust1", since = "1.0.0")]
#![allow(missing_docs)]
-#[cfg(not(test))]
-#[cfg(stage0)]
-use core::num::Float;
#[cfg(not(test))]
use intrinsics;
#[cfg(not(test))]
-#[cfg(stage0)]
-use num::FpCategory;
-#[cfg(not(test))]
use sys::cmath;
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::f32::consts;
#[cfg(not(test))]
-#[cfg_attr(stage0, lang = "f32")]
-#[cfg_attr(not(stage0), lang = "f32_runtime")]
+#[lang = "f32_runtime"]
impl f32 {
- #[cfg(stage0)]
- f32_core_methods!();
-
/// Returns the largest integer less than or equal to a number.
///
/// # Examples
#![stable(feature = "rust1", since = "1.0.0")]
#![allow(missing_docs)]
-#[cfg(not(test))]
-#[cfg(stage0)]
-use core::num::Float;
#[cfg(not(test))]
use intrinsics;
#[cfg(not(test))]
-#[cfg(stage0)]
-use num::FpCategory;
-#[cfg(not(test))]
use sys::cmath;
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::f64::consts;
#[cfg(not(test))]
-#[cfg_attr(stage0, lang = "f64")]
-#[cfg_attr(not(stage0), lang = "f64_runtime")]
+#[lang = "f64_runtime"]
impl f64 {
- #[cfg(stage0)]
- f64_core_methods!();
-
/// Returns the largest integer less than or equal to a number.
///
/// # Examples
#![feature(collections_range)]
#![feature(compiler_builtins_lib)]
#![feature(const_fn)]
-#![cfg_attr(stage0, feature(core_float))]
#![feature(core_intrinsics)]
#![feature(dropck_eyepatch)]
#![feature(exact_size_is_empty)]
#![feature(fs_read_write)]
#![feature(fixed_size_array)]
#![feature(float_from_str_radix)]
-#![cfg_attr(stage0, feature(float_internals))]
#![feature(fn_traits)]
#![feature(fnbox)]
-#![cfg_attr(stage0, feature(generic_param_attrs))]
#![feature(hashmap_internals)]
#![feature(heap_api)]
#![feature(int_error_internals)]
#![cfg_attr(test, feature(update_panic_count))]
#![cfg_attr(windows, feature(used))]
#![feature(doc_alias)]
+#![feature(float_internals)]
#![default_lib_allocator]
#[allow(unused_extern_crates)]
extern crate unwind;
-// compiler-rt intrinsics
-#[doc(masked)]
-#[cfg(stage0)]
-extern crate compiler_builtins;
-
// During testing, this crate is not actually the "real" std library, but rather
// it links to the real std library, which was compiled from this same source
// code. So any lang items std defines are conditionally excluded (or else they
// or starts with something like `self`/`super`/`$crate`/etc.
pub fn make_root(&self) -> Option<PathSegment> {
if let Some(ident) = self.segments.get(0).map(|seg| seg.ident) {
- if ::parse::token::is_path_segment_keyword(ident) &&
- ident.name != keywords::Crate.name() {
+ if ident.is_path_segment_keyword() && ident.name != keywords::Crate.name() {
return None;
}
}
+++ /dev/null
-// Copyright 2018 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.
-
-use std::fmt;
-use std::str::FromStr;
-
-/// The edition of the compiler (RFC 2052)
-#[derive(Clone, Copy, Hash, PartialOrd, Ord, Eq, PartialEq, Debug)]
-#[non_exhaustive]
-pub enum Edition {
- // editions must be kept in order, newest to oldest
-
- /// The 2015 edition
- Edition2015,
- /// The 2018 edition
- Edition2018,
-
- // when adding new editions, be sure to update:
- //
- // - Update the `ALL_EDITIONS` const
- // - Update the EDITION_NAME_LIST const
- // - add a `rust_####()` function to the session
- // - update the enum in Cargo's sources as well
-}
-
-// must be in order from oldest to newest
-pub const ALL_EDITIONS: &[Edition] = &[Edition::Edition2015, Edition::Edition2018];
-
-pub const EDITION_NAME_LIST: &'static str = "2015|2018";
-
-pub const DEFAULT_EDITION: Edition = Edition::Edition2015;
-
-impl fmt::Display for Edition {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let s = match *self {
- Edition::Edition2015 => "2015",
- Edition::Edition2018 => "2018",
- };
- write!(f, "{}", s)
- }
-}
-
-impl Edition {
- pub fn lint_name(&self) -> &'static str {
- match *self {
- Edition::Edition2015 => "rust_2015_compatibility",
- Edition::Edition2018 => "rust_2018_compatibility",
- }
- }
-
- pub fn feature_name(&self) -> &'static str {
- match *self {
- Edition::Edition2015 => "rust_2015_preview",
- Edition::Edition2018 => "rust_2018_preview",
- }
- }
-
- pub fn is_stable(&self) -> bool {
- match *self {
- Edition::Edition2015 => true,
- Edition::Edition2018 => false,
- }
- }
-}
-
-impl FromStr for Edition {
- type Err = ();
- fn from_str(s: &str) -> Result<Self, ()> {
- match s {
- "2015" => Ok(Edition::Edition2015),
- "2018" => Ok(Edition::Edition2018),
- _ => Err(())
- }
- }
-}
use attr::HasAttrs;
use codemap::{self, CodeMap, Spanned, respan};
use syntax_pos::{Span, MultiSpan, DUMMY_SP};
+use edition::Edition;
use errors::{DiagnosticBuilder, DiagnosticId};
use ext::expand::{self, Expansion, Invocation};
-use ext::hygiene::{Mark, SyntaxContext};
+use ext::hygiene::{self, Mark, SyntaxContext};
use fold::{self, Folder};
use parse::{self, parser, DirectoryOwnership};
use parse::token;
MultiModifier(Box<MultiItemModifier + sync::Sync + sync::Send>),
/// A function-like procedural macro. TokenStream -> TokenStream.
- ProcMacro(Box<ProcMacro + sync::Sync + sync::Send>),
+ ProcMacro(Box<ProcMacro + sync::Sync + sync::Send>, Edition),
/// An attribute-like procedural macro. TokenStream, TokenStream -> TokenStream.
/// The first TokenSteam is the attribute, the second is the annotated item.
/// Allows modification of the input items and adding new items, similar to
/// MultiModifier, but uses TokenStreams, rather than AST nodes.
- AttrProcMacro(Box<AttrProcMacro + sync::Sync + sync::Send>),
+ AttrProcMacro(Box<AttrProcMacro + sync::Sync + sync::Send>, Edition),
/// A normal, function-like syntax extension.
///
allow_internal_unsafe: bool,
/// The macro's feature name if it is unstable, and the stability feature
unstable_feature: Option<(Symbol, u32)>,
+ /// Edition of the crate in which the macro is defined
+ edition: Edition,
},
/// A function-like syntax extension that has an extra ident before
/// The input is the annotated item.
/// Allows generating code to implement a Trait for a given struct
/// or enum item.
- ProcMacroDerive(Box<MultiItemModifier +
- sync::Sync +
- sync::Send>, Vec<Symbol> /* inert attribute names */),
+ ProcMacroDerive(Box<MultiItemModifier + sync::Sync + sync::Send>,
+ Vec<Symbol> /* inert attribute names */, Edition),
/// An attribute-like procedural macro that derives a builtin trait.
BuiltinDerive(BuiltinDeriveFn),
/// A declarative macro, e.g. `macro m() {}`.
///
/// The second element is the definition site span.
- DeclMacro(Box<TTMacroExpander + sync::Sync + sync::Send>, Option<(ast::NodeId, Span)>),
+ DeclMacro(Box<TTMacroExpander + sync::Sync + sync::Send>, Option<(ast::NodeId, Span)>, Edition),
}
impl SyntaxExtension {
_ => false,
}
}
+
+ pub fn edition(&self) -> Edition {
+ match *self {
+ SyntaxExtension::NormalTT { edition, .. } |
+ SyntaxExtension::DeclMacro(.., edition) |
+ SyntaxExtension::ProcMacro(.., edition) |
+ SyntaxExtension::AttrProcMacro(.., edition) |
+ SyntaxExtension::ProcMacroDerive(.., edition) => edition,
+ // Unstable legacy stuff
+ SyntaxExtension::IdentTT(..) |
+ SyntaxExtension::MultiDecorator(..) |
+ SyntaxExtension::MultiModifier(..) |
+ SyntaxExtension::BuiltinDerive(..) => hygiene::default_edition(),
+ }
+ }
}
pub type NamedSyntaxExtension = (Name, SyntaxExtension);
use attr::HasAttrs;
use ast;
-use codemap::{ExpnInfo, NameAndSpan, ExpnFormat};
+use codemap::{hygiene, ExpnInfo, NameAndSpan, ExpnFormat};
use ext::base::ExtCtxt;
use ext::build::AstBuilder;
use parse::parser::PathStyle;
span: None,
allow_internal_unstable: true,
allow_internal_unsafe: false,
+ edition: hygiene::default_edition(),
},
});
use errors::FatalError;
use ext::base::*;
use ext::derive::{add_derived_markers, collect_derives};
-use ext::hygiene::{Mark, SyntaxContext};
+use ext::hygiene::{self, Mark, SyntaxContext};
use ext::placeholders::{placeholder, PlaceholderExpander};
use feature_gate::{self, Features, GateIssue, is_builtin_attr, emit_feature_err};
use fold;
span: None,
allow_internal_unstable: false,
allow_internal_unsafe: false,
+ edition: ext.edition(),
}
});
items.push(item);
Some(kind.expect_from_annotatables(items))
}
- AttrProcMacro(ref mac) => {
+ AttrProcMacro(ref mac, ..) => {
self.gate_proc_macro_attr_item(attr.span, &item);
let item_tok = TokenTree::Token(DUMMY_SP, Token::interpolated(match item {
Annotatable::Item(item) => token::NtItem(item),
allow_internal_unstable,
allow_internal_unsafe,
// can't infer this type
- unstable_feature: Option<(Symbol, u32)>| {
+ unstable_feature: Option<(Symbol, u32)>,
+ edition| {
// feature-gate the macro invocation
if let Some((feature, issue)) = unstable_feature {
span: def_site_span,
allow_internal_unstable,
allow_internal_unsafe,
+ edition,
},
});
Ok(())
};
let opt_expanded = match *ext {
- DeclMacro(ref expand, def_span) => {
+ DeclMacro(ref expand, def_span, edition) => {
if let Err(dummy_span) = validate_and_set_expn_info(self, def_span.map(|(_, s)| s),
- false, false, None) {
+ false, false, None,
+ edition) {
dummy_span
} else {
kind.make_from(expand.expand(self.cx, span, mac.node.stream()))
allow_internal_unstable,
allow_internal_unsafe,
unstable_feature,
+ edition,
} => {
if let Err(dummy_span) = validate_and_set_expn_info(self, def_info.map(|(_, s)| s),
allow_internal_unstable,
allow_internal_unsafe,
- unstable_feature) {
+ unstable_feature,
+ edition) {
dummy_span
} else {
kind.make_from(expander.expand(self.cx, span, mac.node.stream()))
span: tt_span,
allow_internal_unstable,
allow_internal_unsafe: false,
+ edition: hygiene::default_edition(),
}
});
kind.dummy(span)
}
- ProcMacro(ref expandfun) => {
+ ProcMacro(ref expandfun, edition) => {
if ident.name != keywords::Invalid.name() {
let msg =
format!("macro {}! expects no ident argument, given '{}'", path, ident);
// FIXME probably want to follow macro_rules macros here.
allow_internal_unstable: false,
allow_internal_unsafe: false,
+ edition,
},
});
span: None,
allow_internal_unstable: false,
allow_internal_unsafe: false,
+ edition: ext.edition(),
}
};
match *ext {
- ProcMacroDerive(ref ext, _) => {
+ ProcMacroDerive(ref ext, ..) => {
invoc.expansion_data.mark.set_expn_info(expn_info);
let span = span.with_ctxt(self.cx.backtrace());
let dummy = ast::MetaItem { // FIXME(jseyfried) avoid this
use {ast, attr};
use syntax_pos::{Span, DUMMY_SP};
+use edition::Edition;
use ext::base::{DummyResult, ExtCtxt, MacResult, SyntaxExtension};
use ext::base::{NormalTT, TTMacroExpander};
use ext::expand::{Expansion, ExpansionKind};
// Holy self-referential!
/// Converts a `macro_rules!` invocation into a syntax extension.
-pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item) -> SyntaxExtension {
+pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item, edition: Edition)
+ -> SyntaxExtension {
let lhs_nm = ast::Ident::with_empty_ctxt(Symbol::gensym("lhs"));
let rhs_nm = ast::Ident::with_empty_ctxt(Symbol::gensym("rhs"));
def_info: Some((def.id, def.span)),
allow_internal_unstable,
allow_internal_unsafe,
- unstable_feature
+ unstable_feature,
+ edition,
}
} else {
- SyntaxExtension::DeclMacro(expander, Some((def.id, def.span)))
+ SyntaxExtension::DeclMacro(expander, Some((def.id, def.span)), edition)
}
}
#![feature(unicode_internals)]
#![feature(rustc_diagnostic_macros)]
#![feature(slice_sort_by_cached_key)]
-#![feature(non_exhaustive)]
#![feature(const_atomic_usize_new)]
#![feature(rustc_attrs)]
#![feature(str_escape)]
#[macro_use]
pub mod config;
pub mod entry;
-pub mod edition;
pub mod feature_gate;
pub mod fold;
pub mod parse;
pub mod show_span;
pub mod std_inject;
pub mod str;
+pub use syntax_pos::edition;
pub use syntax_pos::symbol;
pub mod test;
pub mod tokenstream;
debug!(">>> block comment");
let p = rdr.pos;
let mut lines: Vec<String> = Vec::new();
- let col = rdr.col;
+
+ // Count the number of chars since the start of the line by rescanning.
+ let mut src_index = rdr.src_index(rdr.filemap.line_begin_pos());
+ let end_src_index = rdr.src_index(rdr.pos);
+ assert!(src_index <= end_src_index);
+ let mut n = 0;
+ while src_index < end_src_index {
+ let c = char_at(&rdr.src, src_index);
+ src_index += c.len_utf8();
+ n += 1;
+ }
+ let col = CharPos(n);
+
rdr.bump();
rdr.bump();
pub next_pos: BytePos,
/// The absolute offset within the codemap of the current character
pub pos: BytePos,
- /// The column of the next character to read
- pub col: CharPos,
/// The current character (which has been read from self.pos)
pub ch: Option<char>,
pub filemap: Lrc<syntax_pos::FileMap>,
- /// If Some, stop reading the source at this position (inclusive).
- pub terminator: Option<BytePos>,
+ /// Stop reading src at this index.
+ pub end_src_index: usize,
/// Whether to record new-lines and multibyte chars in filemap.
/// This is only necessary the first time a filemap is lexed.
/// If part of a filemap is being re-lexed, this should be set to false.
pub fatal_errs: Vec<DiagnosticBuilder<'a>>,
// cache a direct reference to the source text, so that we don't have to
// retrieve it via `self.filemap.src.as_ref().unwrap()` all the time.
- source_text: Lrc<String>,
+ src: Lrc<String>,
/// Stack of open delimiters and their spans. Used for error message.
token: token::Token,
span: Span,
self.unwrap_or_abort(res)
}
fn is_eof(&self) -> bool {
- if self.ch.is_none() {
- return true;
- }
-
- match self.terminator {
- Some(t) => self.next_pos > t,
- None => false,
- }
+ self.ch.is_none()
}
/// Return the next token. EFFECT: advances the string_reader.
pub fn try_next_token(&mut self) -> Result<TokenAndSpan, ()> {
filemap.name));
}
- let source_text = (*filemap.src.as_ref().unwrap()).clone();
+ let src = (*filemap.src.as_ref().unwrap()).clone();
StringReader {
sess,
next_pos: filemap.start_pos,
pos: filemap.start_pos,
- col: CharPos(0),
ch: Some('\n'),
filemap,
- terminator: None,
+ end_src_index: src.len(),
save_new_lines_and_multibyte: true,
// dummy values; not read
peek_tok: token::Eof,
peek_span: syntax_pos::DUMMY_SP,
- source_text,
+ src,
fatal_errs: Vec::new(),
token: token::Eof,
span: syntax_pos::DUMMY_SP,
// Seek the lexer to the right byte range.
sr.save_new_lines_and_multibyte = false;
sr.next_pos = span.lo();
- sr.terminator = Some(span.hi());
+ sr.end_src_index = sr.src_index(span.hi());
sr.bump();
/// offending string to the error message
fn fatal_span_verbose(&self, from_pos: BytePos, to_pos: BytePos, mut m: String) -> FatalError {
m.push_str(": ");
- let from = self.byte_offset(from_pos).to_usize();
- let to = self.byte_offset(to_pos).to_usize();
- m.push_str(&self.source_text[from..to]);
+ m.push_str(&self.src[self.src_index(from_pos)..self.src_index(to_pos)]);
self.fatal_span_(from_pos, to_pos, &m[..])
}
Ok(())
}
- fn byte_offset(&self, pos: BytePos) -> BytePos {
- (pos - self.filemap.start_pos)
+ #[inline]
+ fn src_index(&self, pos: BytePos) -> usize {
+ (pos - self.filemap.start_pos).to_usize()
}
/// Calls `f` with a string slice of the source text spanning from `start`
fn with_str_from_to<T, F>(&self, start: BytePos, end: BytePos, f: F) -> T
where F: FnOnce(&str) -> T
{
- f(&self.source_text[self.byte_offset(start).to_usize()..self.byte_offset(end).to_usize()])
+ f(&self.src[self.src_index(start)..self.src_index(end)])
}
/// Converts CRLF to LF in the given string, raising an error on bare CR.
}
}
-
/// Advance the StringReader by one character. If a newline is
/// discovered, add it to the FileMap's list of line start offsets.
pub fn bump(&mut self) {
- let new_pos = self.next_pos;
- let new_byte_offset = self.byte_offset(new_pos).to_usize();
- let end = self.terminator.map_or(self.source_text.len(), |t| {
- self.byte_offset(t).to_usize()
- });
- if new_byte_offset < end {
- let old_ch_is_newline = self.ch.unwrap() == '\n';
- let new_ch = char_at(&self.source_text, new_byte_offset);
- let new_ch_len = new_ch.len_utf8();
-
- self.ch = Some(new_ch);
- self.pos = new_pos;
- self.next_pos = new_pos + Pos::from_usize(new_ch_len);
- if old_ch_is_newline {
+ let next_src_index = self.src_index(self.next_pos);
+ if next_src_index < self.end_src_index {
+ let next_ch = char_at(&self.src, next_src_index);
+ let next_ch_len = next_ch.len_utf8();
+
+ if self.ch.unwrap() == '\n' {
if self.save_new_lines_and_multibyte {
- self.filemap.next_line(self.pos);
+ self.filemap.next_line(self.next_pos);
}
- self.col = CharPos(0);
- } else {
- self.col = self.col + CharPos(1);
}
- if new_ch_len > 1 {
+ if next_ch_len > 1 {
if self.save_new_lines_and_multibyte {
- self.filemap.record_multibyte_char(self.pos, new_ch_len);
+ self.filemap.record_multibyte_char(self.next_pos, next_ch_len);
}
}
- self.filemap.record_width(self.pos, new_ch);
+ self.filemap.record_width(self.next_pos, next_ch);
+
+ self.ch = Some(next_ch);
+ self.pos = self.next_pos;
+ self.next_pos = self.next_pos + Pos::from_usize(next_ch_len);
} else {
self.ch = None;
- self.pos = new_pos;
+ self.pos = self.next_pos;
}
}
pub fn nextch(&self) -> Option<char> {
- let offset = self.byte_offset(self.next_pos).to_usize();
- if offset < self.source_text.len() {
- Some(char_at(&self.source_text, offset))
+ let next_src_index = self.src_index(self.next_pos);
+ if next_src_index < self.end_src_index {
+ Some(char_at(&self.src, next_src_index))
} else {
None
}
}
pub fn nextnextch(&self) -> Option<char> {
- let offset = self.byte_offset(self.next_pos).to_usize();
- let s = &self.source_text[..];
- if offset >= s.len() {
- return None;
- }
- let next = offset + char_at(s, offset).len_utf8();
- if next < s.len() {
- Some(char_at(s, next))
- } else {
- None
+ let next_src_index = self.src_index(self.next_pos);
+ if next_src_index < self.end_src_index {
+ let next_next_src_index =
+ next_src_index + char_at(&self.src, next_src_index).len_utf8();
+ if next_next_src_index < self.end_src_index {
+ return Some(char_at(&self.src, next_next_src_index));
+ }
}
+ None
}
pub fn nextnextch_is(&self, c: char) -> bool {
return Ok(self.with_str_from(start, |string| {
// FIXME: perform NFKC normalization here. (Issue #2253)
let ident = self.mk_ident(string);
- if is_raw_ident && (token::is_path_segment_keyword(ident) ||
+ if is_raw_ident && (ident.is_path_segment_keyword() ||
ident.name == keywords::Underscore.name()) {
self.fatal_span_(raw_start, self.pos,
&format!("`r#{}` is not currently supported.", ident.name)
loop {
self.bump();
if self.ch_is('\'') {
- let start = self.byte_offset(start).to_usize();
- let end = self.byte_offset(self.pos).to_usize();
+ let start = self.src_index(start);
+ let end = self.src_index(self.pos);
self.bump();
let span = self.mk_sp(start_with_quote, self.pos);
self.sess.span_diagnostic
.span_suggestion(span,
"if you meant to write a `str` literal, \
use double quotes",
- format!("\"{}\"",
- &self.source_text[start..end]))
+ format!("\"{}\"", &self.src[start..end]))
.emit();
return Ok(token::Literal(token::Str_(Symbol::intern("??")), None))
}
].contains(&ident.name)
}
-pub fn is_path_segment_keyword(id: ast::Ident) -> bool {
- id.name == keywords::Super.name() ||
- id.name == keywords::SelfValue.name() ||
- id.name == keywords::SelfType.name() ||
- id.name == keywords::Extern.name() ||
- id.name == keywords::Crate.name() ||
- id.name == keywords::CrateRoot.name() ||
- id.name == keywords::DollarCrate.name()
-}
-
-// We see this identifier in a normal identifier position, like variable name or a type.
-// How was it written originally? Did it use the raw form? Let's try to guess.
-pub fn is_raw_guess(ident: ast::Ident) -> bool {
- ident.name != keywords::Invalid.name() &&
- is_reserved_ident(ident) && !is_path_segment_keyword(ident)
-}
-
-// Returns true for reserved identifiers used internally for elided lifetimes,
-// unnamed method parameters, crate root module, error recovery etc.
-pub fn is_special_ident(id: ast::Ident) -> bool {
- id.name <= keywords::Underscore.name()
-}
-
-/// Returns `true` if the token is a keyword used in the language.
-pub fn is_used_keyword(id: ast::Ident) -> bool {
- id.name >= keywords::As.name() && id.name <= keywords::While.name()
-}
-
-/// Returns `true` if the token is a keyword reserved for possible future use.
-pub fn is_unused_keyword(id: ast::Ident) -> bool {
- id.name >= keywords::Abstract.name() && id.name <= keywords::Yield.name()
-}
-
-/// Returns `true` if the token is either a special identifier or a keyword.
-pub fn is_reserved_ident(id: ast::Ident) -> bool {
- is_special_ident(id) || is_used_keyword(id) || is_unused_keyword(id)
-}
-
#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug)]
pub enum Token {
/* Expression-operator symbols. */
/// Recovers a `Token` from an `ast::Ident`. This creates a raw identifier if necessary.
pub fn from_ast_ident(ident: ast::Ident) -> Token {
- Ident(ident, is_raw_guess(ident))
+ Ident(ident, ident.is_raw_guess())
}
/// Returns `true` if the token starts with '>'.
pub fn is_path_segment_keyword(&self) -> bool {
match self.ident() {
- Some((id, false)) => is_path_segment_keyword(id),
+ Some((id, false)) => id.is_path_segment_keyword(),
_ => false,
}
}
// unnamed method parameters, crate root module, error recovery etc.
pub fn is_special_ident(&self) -> bool {
match self.ident() {
- Some((id, false)) => is_special_ident(id),
+ Some((id, false)) => id.is_special(),
_ => false,
}
}
/// Returns `true` if the token is a keyword used in the language.
pub fn is_used_keyword(&self) -> bool {
match self.ident() {
- Some((id, false)) => is_used_keyword(id),
+ Some((id, false)) => id.is_used_keyword(),
_ => false,
}
}
/// Returns `true` if the token is a keyword reserved for possible future use.
pub fn is_unused_keyword(&self) -> bool {
match self.ident() {
- Some((id, false)) => is_unused_keyword(id),
+ Some((id, false)) => id.is_unused_keyword(),
_ => false,
}
}
/// Returns `true` if the token is either a special identifier or a keyword.
pub fn is_reserved_ident(&self) -> bool {
match self.ident() {
- Some((id, false)) => is_reserved_ident(id),
+ Some((id, false)) => id.is_reserved(),
_ => false,
}
}
}
pub fn print_ident(&mut self, ident: ast::Ident) -> io::Result<()> {
- if token::is_raw_guess(ident) {
+ if ident.is_raw_guess() {
self.s.word(&format!("r#{}", ident))?;
} else {
self.s.word(&ident.name.as_str())?;
use ext::hygiene::{Mark, SyntaxContext};
use symbol::{Symbol, keywords};
use syntax_pos::{DUMMY_SP, Span};
-use codemap::{ExpnInfo, NameAndSpan, MacroAttribute, dummy_spanned, respan};
+use codemap::{ExpnInfo, NameAndSpan, MacroAttribute, dummy_spanned, hygiene, respan};
use ptr::P;
use tokenstream::TokenStream;
span: None,
allow_internal_unstable: true,
allow_internal_unsafe: false,
+ edition: hygiene::default_edition(),
}
});
sp.with_ctxt(SyntaxContext::empty().apply_mark(mark))
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+#[inline]
pub fn char_at(s: &str, byte: usize) -> char {
s[byte..].chars().next().unwrap()
}
use ext::base::{ExtCtxt, Resolver};
use ext::build::AstBuilder;
use ext::expand::ExpansionConfig;
-use ext::hygiene::{Mark, SyntaxContext};
+use ext::hygiene::{self, Mark, SyntaxContext};
use fold::Folder;
use feature_gate::Features;
use util::move_map::MoveMap;
span: None,
allow_internal_unstable: true,
allow_internal_unsafe: false,
+ edition: hygiene::default_edition(),
}
});
}
}
-macro_rules! span_err_if_not_stage0 {
- ($cx:expr, $sp:expr, $code:ident, $text:tt) => {
- #[cfg(not(stage0))] {
- span_err!($cx, $sp, $code, $text)
- }
- #[cfg(stage0)] {
- $cx.span_err($sp, $text)
- }
- }
-}
-
const OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"];
pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt,
if asm_str_style.is_some() {
// If we already have a string with instructions,
// ending up in Asm state again is an error.
- span_err_if_not_stage0!(cx, sp, E0660, "malformed inline assembly");
+ span_err!(cx, sp, E0660, "malformed inline assembly");
return DummyResult::expr(sp);
}
// Nested parser, stop before the first colon (see above).
Some(Symbol::intern(&format!("={}", ch.as_str())))
}
_ => {
- span_err_if_not_stage0!(cx, span, E0661,
+ span_err!(cx, span, E0661,
"output operand constraint lacks '=' or '+'");
None
}
let (constraint, _str_style) = panictry!(p.parse_str());
if constraint.as_str().starts_with("=") {
- span_err_if_not_stage0!(cx, p.prev_span, E0662,
+ span_err!(cx, p.prev_span, E0662,
"input operand constraint contains '='");
} else if constraint.as_str().starts_with("+") {
- span_err_if_not_stage0!(cx, p.prev_span, E0663,
+ span_err!(cx, p.prev_span, E0663,
"input operand constraint contains '+'");
}
if OPTIONS.iter().any(|&opt| s == opt) {
cx.span_warn(p.prev_span, "expected a clobber, found an option");
} else if s.as_str().starts_with("{") || s.as_str().ends_with("}") {
- span_err_if_not_stage0!(cx, p.prev_span, E0664,
+ span_err!(cx, p.prev_span, E0664,
"clobber should not be surrounded by braces");
}
}
if !parser.errors.is_empty() {
- let (err, note) = parser.errors.remove(0);
- let mut e = cx.ecx.struct_span_err(cx.fmtsp, &format!("invalid format string: {}", err));
- if let Some(note) = note {
+ let err = parser.errors.remove(0);
+ let sp = cx.fmtsp.from_inner_byte_pos(err.start, err.end);
+ let mut e = cx.ecx.struct_span_err(sp, &format!("invalid format string: {}",
+ err.description));
+ e.span_label(sp, err.label + " in format string");
+ if let Some(note) = err.note {
e.note(¬e);
}
e.emit();
#![feature(decl_macro)]
#![feature(str_escape)]
-#![cfg_attr(not(stage0), feature(rustc_diagnostic_macros))]
+#![feature(rustc_diagnostic_macros)]
extern crate fmt_macros;
#[macro_use]
extern crate rustc_errors as errors;
extern crate rustc_target;
-#[cfg(not(stage0))]
mod diagnostics;
mod assert;
use rustc_data_structures::sync::Lrc;
use syntax::ast;
use syntax::ext::base::{MacroExpanderFn, NormalTT, NamedSyntaxExtension};
+use syntax::ext::hygiene;
use syntax::symbol::Symbol;
pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver,
allow_internal_unstable: false,
allow_internal_unsafe: false,
unstable_feature: None,
+ edition: hygiene::default_edition(),
});
)* }
}
def_info: None,
allow_internal_unstable: true,
allow_internal_unsafe: false,
- unstable_feature: None
+ unstable_feature: None,
+ edition: hygiene::default_edition(),
});
for (name, ext) in user_exts {
use syntax::ast::{self, Ident, NodeId};
use syntax::attr;
-use syntax::codemap::{ExpnInfo, NameAndSpan, MacroAttribute, respan};
+use syntax::codemap::{ExpnInfo, NameAndSpan, MacroAttribute, hygiene, respan};
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::ext::expand::ExpansionConfig;
span: None,
allow_internal_unstable: true,
allow_internal_unsafe: false,
+ edition: hygiene::default_edition(),
}
});
let span = DUMMY_SP.apply_mark(mark);
--- /dev/null
+// Copyright 2018 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.
+
+use std::fmt;
+use std::str::FromStr;
+
+/// The edition of the compiler (RFC 2052)
+#[derive(Clone, Copy, Hash, PartialOrd, Ord, Eq, PartialEq, Debug, RustcEncodable, RustcDecodable)]
+#[non_exhaustive]
+pub enum Edition {
+ // editions must be kept in order, newest to oldest
+
+ /// The 2015 edition
+ Edition2015,
+ /// The 2018 edition
+ Edition2018,
+
+ // when adding new editions, be sure to update:
+ //
+ // - Update the `ALL_EDITIONS` const
+ // - Update the EDITION_NAME_LIST const
+ // - add a `rust_####()` function to the session
+ // - update the enum in Cargo's sources as well
+}
+
+// must be in order from oldest to newest
+pub const ALL_EDITIONS: &[Edition] = &[Edition::Edition2015, Edition::Edition2018];
+
+pub const EDITION_NAME_LIST: &'static str = "2015|2018";
+
+pub const DEFAULT_EDITION: Edition = Edition::Edition2015;
+
+impl fmt::Display for Edition {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let s = match *self {
+ Edition::Edition2015 => "2015",
+ Edition::Edition2018 => "2018",
+ };
+ write!(f, "{}", s)
+ }
+}
+
+impl Edition {
+ pub fn lint_name(&self) -> &'static str {
+ match *self {
+ Edition::Edition2015 => "rust_2015_compatibility",
+ Edition::Edition2018 => "rust_2018_compatibility",
+ }
+ }
+
+ pub fn feature_name(&self) -> &'static str {
+ match *self {
+ Edition::Edition2015 => "rust_2015_preview",
+ Edition::Edition2018 => "rust_2018_preview",
+ }
+ }
+
+ pub fn is_stable(&self) -> bool {
+ match *self {
+ Edition::Edition2015 => true,
+ Edition::Edition2018 => false,
+ }
+ }
+}
+
+impl FromStr for Edition {
+ type Err = ();
+ fn from_str(s: &str) -> Result<Self, ()> {
+ match s {
+ "2015" => Ok(Edition::Edition2015),
+ "2018" => Ok(Edition::Edition2018),
+ _ => Err(())
+ }
+ }
+}
use GLOBALS;
use Span;
+use edition::Edition;
use symbol::{Ident, Symbol};
use serialize::{Encodable, Decodable, Encoder, Decoder};
syntax_contexts: Vec<SyntaxContextData>,
markings: HashMap<(SyntaxContext, Mark), SyntaxContext>,
gensym_to_ctxt: HashMap<Symbol, Span>,
+ default_edition: Edition,
}
impl HygieneData {
}],
markings: HashMap::new(),
gensym_to_ctxt: HashMap::new(),
+ default_edition: Edition::Edition2015,
}
}
}
}
+pub fn default_edition() -> Edition {
+ HygieneData::with(|data| data.default_edition)
+}
+
+pub fn set_default_edition(edition: Edition) {
+ HygieneData::with(|data| data.default_edition = edition);
+}
+
pub fn clear_markings() {
HygieneData::with(|data| data.markings = HashMap::new());
}
/// Whether the macro is allowed to use `unsafe` internally
/// even if the user crate has `#![forbid(unsafe_code)]`.
pub allow_internal_unsafe: bool,
+ /// Edition of the crate in which the macro is defined.
+ pub edition: Edition,
/// The span of the macro definition itself. The macro may not
/// have a sensible definition span (e.g. something defined
/// completely inside libsyntax) in which case this is None.
#![feature(const_fn)]
#![feature(custom_attribute)]
+#![feature(non_exhaustive)]
#![feature(optin_builtin_traits)]
#![allow(unused_attributes)]
#![feature(specialization)]
extern crate unicode_width;
+pub mod edition;
pub mod hygiene;
pub use hygiene::{Mark, SyntaxContext, ExpnInfo, ExpnFormat, NameAndSpan, CompilerDesugaringKind};
self.ctxt().outer().expn_info().map(|i| i.call_site)
}
+ /// Edition of the crate from which this span came.
+ pub fn edition(self) -> edition::Edition {
+ self.ctxt().outer().expn_info().map_or_else(|| hygiene::default_edition(),
+ |einfo| einfo.callee.edition)
+ }
+
/// Return the source callee.
///
/// Returns None if the supplied span has no expansion trace,
)
}
+ pub fn from_inner_byte_pos(self, start: usize, end: usize) -> Span {
+ let span = self.data();
+ Span::new(span.lo + BytePos::from_usize(start),
+ span.lo + BytePos::from_usize(end),
+ span.ctxt)
+ }
+
#[inline]
pub fn apply_mark(self, mark: Mark) -> Span {
let span = self.data();
lines.push(pos);
}
+ /// Return the BytePos of the beginning of the current line.
+ pub fn line_begin_pos(&self) -> BytePos {
+ let lines = self.lines.borrow();
+ match lines.last() {
+ Some(&line_pos) => line_pos,
+ None => self.start_pos,
+ }
+ }
+
/// Add externally loaded source.
/// If the hash of the input doesn't match or no input is supplied via None,
/// it is interpreted as an error and the corresponding enum variant is set.
self.multibyte_chars.borrow_mut().push(mbc);
}
+ #[inline]
pub fn record_width(&self, pos: BytePos, ch: char) {
let width = match ch {
'\t' =>
//! allows bidirectional lookup; i.e. given a value, one can easily find the
//! type, and vice versa.
+use edition::Edition;
use hygiene::SyntaxContext;
use {Span, DUMMY_SP, GLOBALS};
// NB: leaving holes in the ident table is bad! a different ident will get
// interned with the id from the hole, but it will be between the min and max
// of the reserved words, and thus tagged as "reserved".
-// After modifying this list adjust `is_special_ident`, `is_used_keyword`/`is_unused_keyword`,
+// After modifying this list adjust `is_special`, `is_used_keyword`/`is_unused_keyword`,
// this should be rarely necessary though if the keywords are kept in alphabetic order.
declare_keywords! {
// Special reserved identifiers used internally for elided lifetimes,
(53, Virtual, "virtual")
(54, Yield, "yield")
+ // Edition-specific keywords reserved for future use.
+ (55, Async, "async") // >= 2018 Edition Only
+
// Special lifetime names
- (55, UnderscoreLifetime, "'_")
- (56, StaticLifetime, "'static")
+ (56, UnderscoreLifetime, "'_")
+ (57, StaticLifetime, "'static")
// Weak keywords, have special meaning only in specific contexts.
- (57, Auto, "auto")
- (58, Catch, "catch")
- (59, Default, "default")
- (60, Dyn, "dyn")
- (61, Union, "union")
+ (58, Auto, "auto")
+ (59, Catch, "catch")
+ (60, Default, "default")
+ (61, Dyn, "dyn")
+ (62, Union, "union")
+}
+
+impl Symbol {
+ fn is_unused_keyword_2018(self) -> bool {
+ self == keywords::Async.name()
+ }
+}
+
+impl Ident {
+ // Returns true for reserved identifiers used internally for elided lifetimes,
+ // unnamed method parameters, crate root module, error recovery etc.
+ pub fn is_special(self) -> bool {
+ self.name <= keywords::Underscore.name()
+ }
+
+ /// Returns `true` if the token is a keyword used in the language.
+ pub fn is_used_keyword(self) -> bool {
+ self.name >= keywords::As.name() && self.name <= keywords::While.name()
+ }
+
+ /// Returns `true` if the token is a keyword reserved for possible future use.
+ pub fn is_unused_keyword(self) -> bool {
+ // Note: `span.edition()` is relatively expensive, don't call it unless necessary.
+ self.name >= keywords::Abstract.name() && self.name <= keywords::Yield.name() ||
+ self.name.is_unused_keyword_2018() && self.span.edition() == Edition::Edition2018
+ }
+
+ /// Returns `true` if the token is either a special identifier or a keyword.
+ pub fn is_reserved(self) -> bool {
+ self.is_special() || self.is_used_keyword() || self.is_unused_keyword()
+ }
+
+ /// A keyword or reserved identifier that can be used as a path segment.
+ pub fn is_path_segment_keyword(self) -> bool {
+ self.name == keywords::Super.name() ||
+ self.name == keywords::SelfValue.name() ||
+ self.name == keywords::SelfType.name() ||
+ self.name == keywords::Extern.name() ||
+ self.name == keywords::Crate.name() ||
+ self.name == keywords::CrateRoot.name() ||
+ self.name == keywords::DollarCrate.name()
+ }
+
+ // We see this identifier in a normal identifier position, like variable name or a type.
+ // How was it written originally? Did it use the raw form? Let's try to guess.
+ pub fn is_raw_guess(self) -> bool {
+ self.name != keywords::Invalid.name() &&
+ self.is_reserved() && !self.is_path_segment_keyword()
+ }
}
// If an interner exists, return it. Otherwise, prepare a fresh one.
for &(ref f, ref stdout) in &state.not_failures {
successes.push(f.name.to_string());
if !stdout.is_empty() {
- stdouts.push_str(&format!("---- {} stdout ----\n\t", f.name));
+ stdouts.push_str(&format!("---- {} stdout ----\n", f.name));
let output = String::from_utf8_lossy(stdout);
stdouts.push_str(&output);
stdouts.push_str("\n");
for &(ref f, ref stdout) in &state.failures {
failures.push(f.name.to_string());
if !stdout.is_empty() {
- fail_out.push_str(&format!("---- {} stdout ----\n\t", f.name));
+ fail_out.push_str(&format!("---- {} stdout ----\n", f.name));
let output = String::from_utf8_lossy(stdout);
fail_out.push_str(&output);
fail_out.push_str("\n");
for &(ref f, ref stdout) in &state.not_failures {
successes.push(f.name.to_string());
if !stdout.is_empty() {
- stdouts.push_str(&format!("---- {} stdout ----\n\t", f.name));
+ stdouts.push_str(&format!("---- {} stdout ----\n", f.name));
let output = String::from_utf8_lossy(stdout);
stdouts.push_str(&output);
stdouts.push_str("\n");
for &(ref f, ref stdout) in &state.failures {
failures.push(f.name.to_string());
if !stdout.is_empty() {
- fail_out.push_str(&format!("---- {} stdout ----\n\t", f.name));
+ fail_out.push_str(&format!("---- {} stdout ----\n", f.name));
let output = String::from_utf8_lossy(stdout);
fail_out.push_str(&output);
fail_out.push_str("\n");
// of other runtime components (registered via yet another special image section).
#![feature(no_core, lang_items, optin_builtin_traits)]
-#![crate_type="rlib"]
+#![crate_type = "rlib"]
#![no_core]
#![allow(non_camel_case_types)]
drop_in_place(to_drop);
}
-#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))]
+#[cfg(all(target_os = "windows", target_arch = "x86", target_env = "gnu"))]
pub mod eh_frames {
#[no_mangle]
#[link_section = ".eh_frame"]
// This is defined as `struct object` in $GCC/libgcc/unwind-dw2-fde.h.
static mut OBJ: [isize; 6] = [0; 6];
+ macro_rules! impl_copy {
+ ($($t:ty)*) => {
+ $(
+ impl ::Copy for $t {}
+ )*
+ }
+ }
+
+ impl_copy! {
+ usize u8 u16 u32 u64 u128
+ isize i8 i16 i32 i64 i128
+ f32 f64
+ bool char
+ }
+
// Unwind info registration/deregistration routines.
// See the docs of `unwind` module in libstd.
extern "C" {
unsafe fn init() {
// register unwind info on module startup
- rust_eh_register_frames(&__EH_FRAME_BEGIN__ as *const u8,
- &mut OBJ as *mut _ as *mut u8);
+ rust_eh_register_frames(
+ &__EH_FRAME_BEGIN__ as *const u8,
+ &mut OBJ as *mut _ as *mut u8,
+ );
}
unsafe fn uninit() {
// unregister on shutdown
- rust_eh_unregister_frames(&__EH_FRAME_BEGIN__ as *const u8,
- &mut OBJ as *mut _ as *mut u8);
+ rust_eh_unregister_frames(
+ &__EH_FRAME_BEGIN__ as *const u8,
+ &mut OBJ as *mut _ as *mut u8,
+ );
}
// MSVC-specific init/uninit routine registration
version = "0.0.0"
[[bin]]
-name = "rustc"
+name = "rustc_binary"
path = "rustc.rs"
[dependencies]
# source tarball for a stable release you'll likely see `1.x.0` for rustc and
# `0.x.0` for Cargo where they were released on `date`.
-date: 2018-04-24
+date: 2018-05-10
rustc: beta
cargo: beta
### Editing and updating the reference files
If you have changed the compiler's output intentionally, or you are
-making a new test, you can use the script `ui/update-references.sh` to
-update the references. When you run the test framework, it will report
-various errors: in those errors is a command you can use to run the
-`ui/update-references.sh` script, which will then copy over the files
-from the build directory and use them as the new reference. You can
-also just run `ui/update-all-references.sh`. In both cases, you can run
-the script with `--help` to get a help message.
+making a new test, you can pass `--bless` to the command you used to
+run the tests. This will then copy over the files
+from the build directory and use them as the new reference.
### Normalization
const A: &'static [i32] = &[];
const B: i32 = (&A)[1];
-//~^ ERROR constant evaluation error
-//~| index out of bounds: the len is 0 but the index is 1
-//~| WARN this constant cannot be used
+//~^ index out of bounds: the len is 0 but the index is 1
+//~| ERROR this constant cannot be used
fn main() {
let _ = B;
const A: [i32; 0] = [];
const B: i32 = A[1];
-//~^ ERROR constant evaluation error
-//~| index out of bounds: the len is 0 but the index is 1
-//~| WARN this constant cannot be used
+//~^ index out of bounds: the len is 0 but the index is 1
+//~| ERROR this constant cannot be used
fn main() {
let _ = B;
const FOO: &'static[u32] = &[1, 2, 3];
const BAR: u32 = FOO[5];
-//~^ ERROR constant evaluation error [E0080]
-//~| index out of bounds: the len is 3 but the index is 5
-//~| WARN this constant cannot be used
+//~^ index out of bounds: the len is 3 but the index is 5
+//~| ERROR this constant cannot be used
fn main() {
let _ = BAR;
DivZero = 1/0,
//~^ attempt to divide by zero
//~| ERROR constant evaluation error
- //~| WARN constant evaluation error
+ //~| ERROR constant evaluation error
RemZero = 1%0,
//~^ attempt to calculate the remainder with a divisor of zero
//~| ERROR constant evaluation error
- //~| WARN constant evaluation error
+ //~| ERROR constant evaluation error
}
fn main() {}
// compile-pass
#![allow(warnings)]
+#![warn(const_err)]
fn main() {
255u8 + 1; //~ WARNING this expression will panic at run-time
// let mut _7: &'10s S1;
// let mut _8: &'10s S1;
// let mut _9: S1;
+// let mut _10: &'10s S1;
+// let mut _11: &'12ds S1;
//
// bb0: {
// StorageLive(_2);
// StorageLive(_3);
// StorageLive(_4);
// StorageLive(_5);
-// _5 = promoted[1];
+// _11 = promoted[1];
+// _5 = &'12ds (*_11);
// _4 = &'12ds (*_5);
// StorageLive(_7);
// StorageLive(_8);
-// _8 = promoted[0];
+// _10 = promoted[0];
+// _8 = &'10s (*_10);
// _7 = &'10s (*_8);
// _3 = D1<'12ds, '10s>::{{constructor}}(move _4, move _7);
// EndRegion('10s);
// }
// bb9: { // binding1 and guard
// StorageLive(_5);
-// _5 = &((_2 as Some).0: i32);
+// _11 = promoted[0];
+// _5 = &(((*_11) as Some).0: i32);
// StorageLive(_8);
// _8 = const guard() -> [return: bb10, unwind: bb1];
// }
// borrows in `&v[0]` and `&v[1]` each (in theory) have to outlive R3,
// but only at a particular point, and hence they wind up including
// distinct regions.
+//
+// FIXME(#43234) -- Well, this used to be true, but we modified NLL
+// for the time being to not take location into account.
// compile-flags:-Zborrowck=mir -Zverbose
// ^^^^^^^^^ force compiler to dump more region information
// END RUST SOURCE
// START rustc.main.nll.0.mir
-// | '_#2r | {bb2[0..=1], bb3[0..=1]}
+// | '_#2r | {bb2[0..=1], bb3[0..=1], bb8[2..=4]}
// ...
-// | '_#4r | {bb8[1..=4]}
+// | '_#4r | {bb2[1], bb3[0..=1], bb8[1..=4]}
// | '_#5r | {bb2[1], bb3[0..=1], bb8[2..=4]}
// ...
// let mut _2: &'_#5r usize;
// error-pattern:thread 'main' panicked at 'attempt to add with overflow'
// compile-flags: -C debug-assertions
+#![allow(const_err)]
+
fn main() {
let _x = 200u8 + 200u8 + 200u8;
}
// error-pattern:thread 'main' panicked at 'attempt to multiply with overflow'
// compile-flags: -C debug-assertions
+#![allow(const_err)]
+
fn main() {
let x = 200u8 * 4;
}
// error-pattern:thread 'main' panicked at 'attempt to negate with overflow'
// compile-flags: -C debug-assertions
+#![allow(const_err)]
+
fn main() {
let _x = -std::i8::MIN;
}
// error-pattern:thread 'main' panicked at 'attempt to subtract with overflow'
// compile-flags: -C debug-assertions
+#![allow(const_err)]
+
fn main() {
let _x = 42u8 - (42u8 + 1);
}
use std::borrow::ToOwned;
use syntax::ast;
+use syntax::ext::hygiene;
use syntax::ext::build::AstBuilder;
use syntax::ext::base::{TTMacroExpander, ExtCtxt, MacResult, MacEager, NormalTT};
use syntax::print::pprust;
allow_internal_unstable: false,
allow_internal_unsafe: false,
unstable_feature: None,
+ edition: hygiene::default_edition(),
});
}
+++ /dev/null
-// Copyright 2017 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.
-
-#![feature(align_offset)]
-
-fn main() {
- let x = 1 as *const u8;
- assert_eq!(x.align_offset(8), 7);
-}
--- /dev/null
+// Copyright 2018 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.
+
+// compile-flags: --edition=2015
+
+#![feature(raw_identifiers)]
+
+#[macro_export]
+macro_rules! produces_async {
+ () => (pub fn async() {})
+}
+
+#[macro_export]
+macro_rules! produces_async_raw {
+ () => (pub fn r#async() {})
+}
+
+#[macro_export]
+macro_rules! consumes_async {
+ (async) => (1)
+}
+
+#[macro_export]
+macro_rules! consumes_async_raw {
+ (r#async) => (1)
+}
+
+#[macro_export]
+macro_rules! passes_ident {
+ ($i: ident) => ($i)
+}
--- /dev/null
+// Copyright 2018 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.
+
+// compile-flags: --edition=2018
+
+#![feature(raw_identifiers)]
+
+#[macro_export]
+macro_rules! produces_async {
+ () => (pub fn async() {})
+}
+
+#[macro_export]
+macro_rules! produces_async_raw {
+ () => (pub fn r#async() {})
+}
+
+#[macro_export]
+macro_rules! consumes_async {
+ (async) => (1)
+}
+
+#[macro_export]
+macro_rules! consumes_async_raw {
+ (r#async) => (1)
+}
+
+#[macro_export]
+macro_rules! passes_ident {
+ ($i: ident) => ($i)
+}
--- /dev/null
+// Copyright 2018 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.
+
+// compile-flags: --edition=2015
+// aux-build:edition-kw-macro-2015.rs
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2015;
+
+pub fn check_async() {
+ let mut async = 1; // OK
+ let mut r#async = 1; // OK
+
+ r#async = consumes_async!(async); // OK
+ // r#async = consumes_async!(r#async); // ERROR, not a match
+ // r#async = consumes_async_raw!(async); // ERROR, not a match
+ r#async = consumes_async_raw!(r#async); // OK
+
+ if passes_ident!(async) == 1 {} // OK
+ if passes_ident!(r#async) == 1 {} // OK
+ one_async::async(); // OK
+ one_async::r#async(); // OK
+ two_async::async(); // OK
+ two_async::r#async(); // OK
+}
+
+mod one_async {
+ produces_async! {} // OK
+}
+mod two_async {
+ produces_async_raw! {} // OK
+}
+
+fn main() {}
--- /dev/null
+// Copyright 2018 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.
+
+// compile-flags: --edition=2015
+// aux-build:edition-kw-macro-2018.rs
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2018;
+
+pub fn check_async() {
+ let mut async = 1; // OK
+ let mut r#async = 1; // OK
+
+ r#async = consumes_async!(async); // OK
+ // r#async = consumes_async!(r#async); // ERROR, not a match
+ // r#async = consumes_async_raw!(async); // ERROR, not a match
+ r#async = consumes_async_raw!(r#async); // OK
+
+ if passes_ident!(async) == 1 {} // OK
+ if passes_ident!(r#async) == 1 {} // OK
+ // one_async::async(); // ERROR, unresolved name
+ // one_async::r#async(); // ERROR, unresolved name
+ two_async::async(); // OK
+ two_async::r#async(); // OK
+}
+
+mod one_async {
+ // produces_async! {} // ERROR, reserved
+}
+mod two_async {
+ produces_async_raw! {} // OK
+}
+
+fn main() {}
--- /dev/null
+// Copyright 2018 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.
+
+// compile-flags: --edition=2018
+// aux-build:edition-kw-macro-2015.rs
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2015;
+
+pub fn check_async() {
+ // let mut async = 1; // ERROR, reserved
+ let mut r#async = 1; // OK
+
+ r#async = consumes_async!(async); // OK
+ // r#async = consumes_async!(r#async); // ERROR, not a match
+ // r#async = consumes_async_raw!(async); // ERROR, not a match
+ r#async = consumes_async_raw!(r#async); // OK
+
+ // if passes_ident!(async) == 1 {} // ERROR, reserved
+ if passes_ident!(r#async) == 1 {} // OK
+ // one_async::async(); // ERROR, reserved
+ one_async::r#async(); // OK
+ // two_async::async(); // ERROR, reserved
+ two_async::r#async(); // OK
+}
+
+mod one_async {
+ produces_async! {} // OK
+}
+mod two_async {
+ produces_async_raw! {} // OK
+}
+
+fn main() {}
--- /dev/null
+// Copyright 2018 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.
+
+// compile-flags: --edition=2018
+// aux-build:edition-kw-macro-2018.rs
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2018;
+
+pub fn check_async() {
+ // let mut async = 1; // ERROR, reserved
+ let mut r#async = 1; // OK
+
+ r#async = consumes_async!(async); // OK
+ // r#async = consumes_async!(r#async); // ERROR, not a match
+ // r#async = consumes_async_raw!(async); // ERROR, not a match
+ r#async = consumes_async_raw!(r#async); // OK
+
+ // if passes_ident!(async) == 1 {} // ERROR, reserved
+ if passes_ident!(r#async) == 1 {} // OK
+ // one_async::async(); // ERROR, reserved
+ // one_async::r#async(); // ERROR, unresolved name
+ // two_async::async(); // ERROR, reserved
+ two_async::r#async(); // OK
+}
+
+mod one_async {
+ // produces_async! {} // ERROR, reserved
+}
+mod two_async {
+ produces_async_raw! {} // OK
+}
+
+fn main() {}
--- /dev/null
+// Copyright 2018 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.
+
+// compile-flags: -Z borrowck=mir
+
+use std::cell::Cell;
+
+#[inline(never)]
+fn tuple_field() -> &'static u32 {
+ // This test is MIR-borrowck-only because the old borrowck
+ // doesn't agree that borrows of "frozen" (i.e. without any
+ // interior mutability) fields of non-frozen temporaries,
+ // should be promoted, while MIR promotion does promote them.
+ &(Cell::new(5), 42).1
+}
+
+fn main() {
+ assert_eq!(tuple_field().to_string(), "42");
+}
--- /dev/null
+// Copyright 2018 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.
+
+// compile-flags: -Z borrowck=compare
+
+const ALL_THE_NUMS: [u32; 1] = [
+ 1
+];
+
+#[inline(never)]
+fn array(i: usize) -> &'static u32 {
+ return &ALL_THE_NUMS[i];
+}
+
+#[inline(never)]
+fn tuple_field() -> &'static u32 {
+ &(42,).0
+}
+
+fn main() {
+ assert_eq!(tuple_field().to_string(), "42");
+ assert_eq!(array(0).to_string(), "1");
+}
+++ /dev/null
-// 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.
-
-#![feature(nll)]
-
-use std::collections::HashMap;
-
-fn get_default(map: &mut HashMap<usize, String>, key: usize) -> &mut String {
- match map.get_mut(&key) {
- Some(value) => value,
- None => {
- map.insert(key, "".to_string());
- map.get_mut(&key).unwrap()
- }
- }
-}
-
-fn main() {
- let map = &mut HashMap::new();
- map.insert(22, format!("Hello, world"));
- map.insert(44, format!("Goodbye, world"));
- assert_eq!(&*get_default(map, 22), "Hello, world");
- assert_eq!(&*get_default(map, 66), "");
-}
--- /dev/null
+// 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.
+
+#![feature(nll)]
+#![deny(unused_mut)]
+
+struct Foo {
+ pub value: i32
+}
+
+fn use_foo_mut(mut foo: Foo) {
+ foo = foo;
+ println!("{}", foo.value);
+}
+
+fn main() {
+ use_foo_mut(Foo { value: 413 });
+}
--- /dev/null
+// Copyright 2018 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.
+
+#![crate_name = "foo"]
+pub use std::fs::File;
+
+// @has 'foo/primitive.i16.html' '//h2[@id="synthetic-implementations"]' 'Auto Trait Implementation'
+#[doc(primitive = "i16")]
+/// I love poneys!
+mod prim {}
--- /dev/null
+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`.
--- /dev/null
+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`.
--- /dev/null
+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`.
--- /dev/null
+// 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]
+}
--- /dev/null
+// Copyright 2018 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.
+
+// compile-flags: --edition=2015
+
+#![feature(raw_identifiers)]
+
+#[macro_export]
+macro_rules! produces_async {
+ () => (pub fn async() {})
+}
+
+#[macro_export]
+macro_rules! produces_async_raw {
+ () => (pub fn r#async() {})
+}
+
+#[macro_export]
+macro_rules! consumes_async {
+ (async) => (1)
+}
+
+#[macro_export]
+macro_rules! consumes_async_raw {
+ (r#async) => (1)
+}
+
+#[macro_export]
+macro_rules! passes_ident {
+ ($i: ident) => ($i)
+}
--- /dev/null
+// Copyright 2018 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.
+
+// compile-flags: --edition=2018
+
+#![feature(raw_identifiers)]
+
+#[macro_export]
+macro_rules! produces_async {
+ () => (pub fn async() {})
+}
+
+#[macro_export]
+macro_rules! produces_async_raw {
+ () => (pub fn r#async() {})
+}
+
+#[macro_export]
+macro_rules! consumes_async {
+ (async) => (1)
+}
+
+#[macro_export]
+macro_rules! consumes_async_raw {
+ (r#async) => (1)
+}
+
+#[macro_export]
+macro_rules! passes_ident {
+ ($i: ident) => ($i)
+}
--> $DIR/mut-borrow-in-loop.rs:20:25
|
LL | (self.func)(arg) //~ ERROR cannot borrow
- | ^^^ mutable borrow starts here in previous iteration of loop
+ | ------------^^^-
+ | | |
+ | | mutable borrow starts here in previous iteration of loop
+ | borrow later used here
error[E0499]: cannot borrow `*arg` as mutable more than once at a time
--> $DIR/mut-borrow-in-loop.rs:26:25
|
LL | (self.func)(arg) //~ ERROR cannot borrow
- | ^^^ mutable borrow starts here in previous iteration of loop
+ | ------------^^^-
+ | | |
+ | | mutable borrow starts here in previous iteration of loop
+ | borrow later used here
error[E0499]: cannot borrow `*arg` as mutable more than once at a time
--> $DIR/mut-borrow-in-loop.rs:33:25
|
LL | (self.func)(arg) //~ ERROR cannot borrow
- | ^^^ mutable borrow starts here in previous iteration of loop
+ | ------------^^^-
+ | | |
+ | | mutable borrow starts here in previous iteration of loop
+ | borrow later used here
error: aborting due to 3 previous errors
// Evaluation of constants in refutable patterns goes through
// different compiler control-flow paths.
-#![allow(unused_imports, warnings)]
+#![allow(unused_imports, warnings, const_err)]
use std::fmt;
use std::{i8, i16, i32, i64, isize};
const A_I8_T
: [u32; (i8::MAX as i8 + 1i8) as usize]
//~^ ERROR E0080
- //~| WARN attempt to add with overflow
+ //~| ERROR attempt to add with overflow
= [0; (i8::MAX as usize) + 1];
fn main() {
-warning: attempt to add with overflow
+error: attempt to add with overflow
--> $DIR/const-eval-overflow-4.rs:23:13
|
LL | : [u32; (i8::MAX as i8 + 1i8) as usize]
| ^^^^^^^^^^^^^^^^^^^^^
|
- = note: #[warn(const_err)] on by default
+ = note: #[deny(const_err)] on by default
error[E0080]: constant evaluation error
--> $DIR/const-eval-overflow-4.rs:23:13
LL | : [u32; (i8::MAX as i8 + 1i8) as usize]
| ^^^^^^^^^^^^^^^^^^^^^ attempt to add with overflow
-error: aborting due to previous error
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0080`.
// except according to those terms.
// compile-pass
+#![warn(const_err)]
const X: u32 = 5;
const Y: u32 = 6;
warning: attempt to subtract with overflow
- --> $DIR/conditional_array_execution.rs:15:19
+ --> $DIR/conditional_array_execution.rs:16:19
|
LL | const FOO: u32 = [X - Y, Y - X][(X < Y) as usize];
| ^^^^^
|
- = note: #[warn(const_err)] on by default
+note: lint level defined here
+ --> $DIR/conditional_array_execution.rs:12:9
+ |
+LL | #![warn(const_err)]
+ | ^^^^^^^^^
warning: this constant cannot be used
- --> $DIR/conditional_array_execution.rs:15:1
+ --> $DIR/conditional_array_execution.rs:16:1
|
LL | const FOO: u32 = [X - Y, Y - X][(X < Y) as usize];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to subtract with overflow
warning: constant evaluation error
- --> $DIR/conditional_array_execution.rs:20:20
+ --> $DIR/conditional_array_execution.rs:21:20
|
LL | println!("{}", FOO);
| ^^^ referenced constant has errors
// except according to those terms.
// compile-pass
+#![warn(const_err)]
#![feature(const_fn)]
warning: attempt to subtract with overflow
- --> $DIR/issue-43197.rs:20:20
+ --> $DIR/issue-43197.rs:21:20
|
LL | const X: u32 = 0-1;
| ^^^
|
- = note: #[warn(const_err)] on by default
+note: lint level defined here
+ --> $DIR/issue-43197.rs:12:9
+ |
+LL | #![warn(const_err)]
+ | ^^^^^^^^^
warning: this constant cannot be used
- --> $DIR/issue-43197.rs:20:5
+ --> $DIR/issue-43197.rs:21:5
|
LL | const X: u32 = 0-1;
| ^^^^^^^^^^^^^^^^^^^ attempt to subtract with overflow
warning: attempt to subtract with overflow
- --> $DIR/issue-43197.rs:23:24
+ --> $DIR/issue-43197.rs:24:24
|
LL | const Y: u32 = foo(0-1);
| ^^^
warning: this constant cannot be used
- --> $DIR/issue-43197.rs:23:5
+ --> $DIR/issue-43197.rs:24:5
|
LL | const Y: u32 = foo(0-1);
| ^^^^^^^^^^^^^^^^^^^^^^^^ attempt to subtract with overflow
warning: constant evaluation error
- --> $DIR/issue-43197.rs:26:23
+ --> $DIR/issue-43197.rs:27:23
|
LL | println!("{} {}", X, Y);
| ^ referenced constant has errors
warning: constant evaluation error
- --> $DIR/issue-43197.rs:26:26
+ --> $DIR/issue-43197.rs:27:26
|
LL | println!("{} {}", X, Y);
| ^ referenced constant has errors
// except according to those terms.
// compile-pass
+#![warn(const_err)]
trait Foo {
const AMT: usize;
warning: constant evaluation error
- --> $DIR/issue-44578.rs:35:20
+ --> $DIR/issue-44578.rs:36:20
|
LL | println!("{}", <Bar<u16, u8> as Foo>::AMT); //~ WARN const_err
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
|
- = note: #[warn(const_err)] on by default
+note: lint level defined here
+ --> $DIR/issue-44578.rs:12:9
+ |
+LL | #![warn(const_err)]
+ | ^^^^^^^^^
warning: constant evaluation error
- --> $DIR/issue-44578.rs:35:20
+ --> $DIR/issue-44578.rs:36:20
|
LL | println!("{}", <Bar<u16, u8> as Foo>::AMT); //~ WARN const_err
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+#![warn(const_err)]
+
// compile-pass
// compile-flags: -O
fn main() {
warning: constant evaluation error
- --> $DIR/promoted_errors.rs:14:20
+ --> $DIR/promoted_errors.rs:16:20
|
LL | println!("{}", 0u32 - 1);
| ^^^^^^^^ attempt to subtract with overflow
|
- = note: #[warn(const_err)] on by default
+note: lint level defined here
+ --> $DIR/promoted_errors.rs:11:9
+ |
+LL | #![warn(const_err)]
+ | ^^^^^^^^^
warning: constant evaluation error
- --> $DIR/promoted_errors.rs:14:20
+ --> $DIR/promoted_errors.rs:16:20
|
LL | println!("{}", 0u32 - 1);
| ^^^^^^^^ attempt to subtract with overflow
warning: constant evaluation error
- --> $DIR/promoted_errors.rs:17:14
+ --> $DIR/promoted_errors.rs:19:14
|
LL | let _x = 0u32 - 1;
| ^^^^^^^^ attempt to subtract with overflow
warning: attempt to divide by zero
- --> $DIR/promoted_errors.rs:19:20
+ --> $DIR/promoted_errors.rs:21:20
|
LL | println!("{}", 1/(1-1));
| ^^^^^^^
warning: constant evaluation error
- --> $DIR/promoted_errors.rs:19:20
+ --> $DIR/promoted_errors.rs:21:20
|
LL | println!("{}", 1/(1-1));
| ^^^^^^^ attempt to divide by zero
warning: attempt to divide by zero
- --> $DIR/promoted_errors.rs:22:14
+ --> $DIR/promoted_errors.rs:24:14
|
LL | let _x = 1/(1-1);
| ^^^^^^^
warning: constant evaluation error
- --> $DIR/promoted_errors.rs:22:14
+ --> $DIR/promoted_errors.rs:24:14
|
LL | let _x = 1/(1-1);
| ^^^^^^^ attempt to divide by zero
warning: constant evaluation error
- --> $DIR/promoted_errors.rs:25:20
+ --> $DIR/promoted_errors.rs:27:20
|
LL | println!("{}", 1/(false as u32));
| ^^^^^^^^^^^^^^^^ attempt to divide by zero
// except according to those terms.
// compile-pass
+#![warn(const_err)]
#![crate_type = "lib"]
warning: attempt to subtract with overflow
- --> $DIR/pub_const_err.rs:15:20
+ --> $DIR/pub_const_err.rs:16:20
|
LL | pub const Z: u32 = 0 - 1;
| ^^^^^
|
- = note: #[warn(const_err)] on by default
+note: lint level defined here
+ --> $DIR/pub_const_err.rs:12:9
+ |
+LL | #![warn(const_err)]
+ | ^^^^^^^^^
warning: this constant cannot be used
- --> $DIR/pub_const_err.rs:15:1
+ --> $DIR/pub_const_err.rs:16:1
|
LL | pub const Z: u32 = 0 - 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to subtract with overflow
warning: attempt to subtract with overflow
- --> $DIR/pub_const_err.rs:19:22
+ --> $DIR/pub_const_err.rs:20:22
|
LL | pub type Foo = [i32; 0 - 1];
| ^^^^^
warning: this array length cannot be used
- --> $DIR/pub_const_err.rs:19:22
+ --> $DIR/pub_const_err.rs:20:22
|
LL | pub type Foo = [i32; 0 - 1];
| ^^^^^ attempt to subtract with overflow
// except according to those terms.
// compile-pass
+#![warn(const_err)]
pub const Z: u32 = 0 - 1;
//~^ WARN attempt to subtract with overflow
warning: attempt to subtract with overflow
- --> $DIR/pub_const_err_bin.rs:13:20
+ --> $DIR/pub_const_err_bin.rs:14:20
|
LL | pub const Z: u32 = 0 - 1;
| ^^^^^
|
- = note: #[warn(const_err)] on by default
+note: lint level defined here
+ --> $DIR/pub_const_err_bin.rs:12:9
+ |
+LL | #![warn(const_err)]
+ | ^^^^^^^^^
warning: this constant cannot be used
- --> $DIR/pub_const_err_bin.rs:13:1
+ --> $DIR/pub_const_err_bin.rs:14:1
|
LL | pub const Z: u32 = 0 - 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to subtract with overflow
warning: attempt to subtract with overflow
- --> $DIR/pub_const_err_bin.rs:17:22
+ --> $DIR/pub_const_err_bin.rs:18:22
|
LL | pub type Foo = [i32; 0 - 1];
| ^^^^^
warning: this array length cannot be used
- --> $DIR/pub_const_err_bin.rs:17:22
+ --> $DIR/pub_const_err_bin.rs:18:22
|
LL | pub type Foo = [i32; 0 - 1];
| ^^^^^ attempt to subtract with overflow
const TWO: usize = 2;
const LEN: usize = ONE - TWO;
//~^ ERROR E0080
-//~| WARN attempt to subtract with overflow
+//~| ERROR attempt to subtract with overflow
fn main() {
let a: [i8; LEN] = unimplemented!();
-warning: attempt to subtract with overflow
+error: attempt to subtract with overflow
--> $DIR/const-len-underflow-separate-spans.rs:17:20
|
LL | const LEN: usize = ONE - TWO;
| ^^^^^^^^^
|
- = note: #[warn(const_err)] on by default
+ = note: #[deny(const_err)] on by default
error[E0080]: constant evaluation error
--> $DIR/const-len-underflow-separate-spans.rs:17:20
LL | let a: [i8; LEN] = unimplemented!();
| ^^^ referenced constant has errors
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0080`.
--- /dev/null
+// Copyright 2018 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.
+
+// compile-flags: --edition=2015
+// aux-build:edition-kw-macro-2015.rs
+// compile-pass
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2015;
+
+mod one_async {
+ produces_async! {} // OK
+}
+mod two_async {
+ produces_async_raw! {} // OK
+}
+
+fn main() {}
--- /dev/null
+// Copyright 2018 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.
+
+// compile-flags: --edition=2015
+// aux-build:edition-kw-macro-2015.rs
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2015;
+
+pub fn check_async() {
+ let mut async = 1; // OK
+ let mut r#async = 1; // OK
+
+ r#async = consumes_async!(async); // OK
+ r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async`
+ r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async`
+ r#async = consumes_async_raw!(r#async); // OK
+
+ if passes_ident!(async) == 1 {} // OK
+ if passes_ident!(r#async) == 1 {} // OK
+ module::async(); // OK
+ module::r#async(); // OK
+}
--- /dev/null
+error: no rules expected the token `r#async`
+ --> $DIR/edition-keywords-2015-2015-parsing.rs:24:31
+ |
+LL | r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async`
+ | ^^^^^^^
+
+error: no rules expected the token `async`
+ --> $DIR/edition-keywords-2015-2015-parsing.rs:25:35
+ |
+LL | r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async`
+ | ^^^^^
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+// Copyright 2018 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.
+
+// compile-flags: --edition=2015
+// aux-build:edition-kw-macro-2018.rs
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2018;
+
+mod one_async {
+ produces_async! {} // ERROR expected identifier, found reserved keyword
+}
+mod two_async {
+ produces_async_raw! {} // OK
+}
--- /dev/null
+error: expected identifier, found reserved keyword `async`
+ --> $DIR/edition-keywords-2015-2018-expansion.rs:20:5
+ |
+LL | produces_async! {} // ERROR expected identifier, found reserved keyword
+ | ^^^^^^^^^^^^^^^^^^ expected identifier, found reserved keyword
+ |
+ = 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 previous error
+
--- /dev/null
+// Copyright 2018 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.
+
+// compile-flags: --edition=2015
+// aux-build:edition-kw-macro-2018.rs
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2018;
+
+pub fn check_async() {
+ let mut async = 1; // OK
+ let mut r#async = 1; // OK
+
+ r#async = consumes_async!(async); // OK
+ r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async`
+ r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async`
+ r#async = consumes_async_raw!(r#async); // OK
+
+ if passes_ident!(async) == 1 {} // OK
+ if passes_ident!(r#async) == 1 {} // OK
+ module::async(); // OK
+ module::r#async(); // OK
+}
--- /dev/null
+error: no rules expected the token `r#async`
+ --> $DIR/edition-keywords-2015-2018-parsing.rs:24:31
+ |
+LL | r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async`
+ | ^^^^^^^
+
+error: no rules expected the token `async`
+ --> $DIR/edition-keywords-2015-2018-parsing.rs:25:35
+ |
+LL | r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async`
+ | ^^^^^
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+// Copyright 2018 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.
+
+// compile-flags: --edition=2018
+// aux-build:edition-kw-macro-2015.rs
+// compile-pass
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2015;
+
+mod one_async {
+ produces_async! {} // OK
+}
+mod two_async {
+ produces_async_raw! {} // OK
+}
+
+fn main() {}
--- /dev/null
+// Copyright 2018 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.
+
+// compile-flags: --edition=2018
+// aux-build:edition-kw-macro-2015.rs
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2015;
+
+pub fn check_async() {
+ let mut async = 1; //~ ERROR expected identifier, found reserved keyword `async`
+ let mut r#async = 1; // OK
+
+ r#async = consumes_async!(async); // OK
+ r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async`
+ r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async`
+ r#async = consumes_async_raw!(r#async); // OK
+
+ if passes_ident!(async) == 1 {} //~ ERROR expected expression, found reserved keyword `async`
+ if passes_ident!(r#async) == 1 {} // OK
+ module::async(); //~ ERROR expected identifier, found reserved keyword `async`
+ module::r#async(); // OK
+}
--- /dev/null
+error: expected identifier, found reserved keyword `async`
+ --> $DIR/edition-keywords-2018-2015-parsing.rs:20:13
+ |
+LL | let mut async = 1; //~ ERROR expected identifier, found reserved keyword `async`
+ | ^^^^^ expected identifier, found reserved keyword
+
+error: expected identifier, found reserved keyword `async`
+ --> $DIR/edition-keywords-2018-2015-parsing.rs:30:13
+ |
+LL | module::async(); //~ ERROR expected identifier, found reserved keyword `async`
+ | ^^^^^ expected identifier, found reserved keyword
+
+error: no rules expected the token `r#async`
+ --> $DIR/edition-keywords-2018-2015-parsing.rs:24:31
+ |
+LL | r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async`
+ | ^^^^^^^
+
+error: no rules expected the token `async`
+ --> $DIR/edition-keywords-2018-2015-parsing.rs:25:35
+ |
+LL | r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async`
+ | ^^^^^
+
+error: expected expression, found reserved keyword `async`
+ --> $DIR/edition-keywords-2018-2015-parsing.rs:28:22
+ |
+LL | if passes_ident!(async) == 1 {} //~ ERROR expected expression, found reserved keyword `async`
+ | ^^^^^ expected expression
+
+error: aborting due to 5 previous errors
+
--- /dev/null
+// Copyright 2018 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.
+
+// compile-flags: --edition=2018
+// aux-build:edition-kw-macro-2018.rs
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2018;
+
+mod one_async {
+ produces_async! {} // ERROR expected identifier, found reserved keyword `async`
+}
+mod two_async {
+ produces_async_raw! {} // OK
+}
--- /dev/null
+error: expected identifier, found reserved keyword `async`
+ --> $DIR/edition-keywords-2018-2018-expansion.rs:20:5
+ |
+LL | produces_async! {} // ERROR expected identifier, found reserved keyword `async`
+ | ^^^^^^^^^^^^^^^^^^ expected identifier, found reserved keyword
+ |
+ = 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 previous error
+
--- /dev/null
+// Copyright 2018 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.
+
+// compile-flags: --edition=2018
+// aux-build:edition-kw-macro-2018.rs
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2018;
+
+pub fn check_async() {
+ let mut async = 1; //~ ERROR expected identifier, found reserved keyword `async`
+ let mut r#async = 1; // OK
+
+ r#async = consumes_async!(async); // OK
+ r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async`
+ r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async`
+ r#async = consumes_async_raw!(r#async); // OK
+
+ if passes_ident!(async) == 1 {} //~ ERROR expected expression, found reserved keyword `async`
+ if passes_ident!(r#async) == 1 {} // OK
+ module::async(); //~ ERROR expected identifier, found reserved keyword `async`
+ module::r#async(); // OK
+}
--- /dev/null
+error: expected identifier, found reserved keyword `async`
+ --> $DIR/edition-keywords-2018-2018-parsing.rs:20:13
+ |
+LL | let mut async = 1; //~ ERROR expected identifier, found reserved keyword `async`
+ | ^^^^^ expected identifier, found reserved keyword
+
+error: expected identifier, found reserved keyword `async`
+ --> $DIR/edition-keywords-2018-2018-parsing.rs:30:13
+ |
+LL | module::async(); //~ ERROR expected identifier, found reserved keyword `async`
+ | ^^^^^ expected identifier, found reserved keyword
+
+error: no rules expected the token `r#async`
+ --> $DIR/edition-keywords-2018-2018-parsing.rs:24:31
+ |
+LL | r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async`
+ | ^^^^^^^
+
+error: no rules expected the token `async`
+ --> $DIR/edition-keywords-2018-2018-parsing.rs:25:35
+ |
+LL | r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async`
+ | ^^^^^
+
+error: expected expression, found reserved keyword `async`
+ --> $DIR/edition-keywords-2018-2018-parsing.rs:28:22
+ |
+LL | if passes_ident!(async) == 1 {} //~ ERROR expected expression, found reserved keyword `async`
+ | ^^^^^ expected expression
+
+error: aborting due to 5 previous errors
+
LL | X = (1 << 500), //~ ERROR E0080
| ^^^^^^^^^^ attempt to shift left with overflow
-warning: attempt to divide by zero
+error: attempt to divide by zero
--> $DIR/E0080.rs:14:9
|
LL | Y = (1 / 0) //~ ERROR E0080
| ^^^^^^^
|
- = note: #[warn(const_err)] on by default
+ = note: #[deny(const_err)] on by default
-warning: constant evaluation error
+error: constant evaluation error
--> $DIR/E0080.rs:14:9
|
LL | Y = (1 / 0) //~ ERROR E0080
LL | Y = (1 / 0) //~ ERROR E0080
| ^^^^^^^ attempt to divide by zero
-error: aborting due to 3 previous errors
+error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0080`.
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 `'\\'`
}
-
--> $DIR/format-string-error.rs:12:5
|
LL | println!("{");
- | ^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^ expected `'}'` in format string
|
= note: if you intended to print `{`, you can escape it using `{{`
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
--> $DIR/format-string-error.rs:14:5
|
LL | println!("}");
- | ^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^ unmatched `}` in format string
|
= note: if you intended to print `}`, you can escape it using `}}`
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
-error: aborting due to 2 previous errors
+error: invalid format string: invalid argument name `_foo`
+ --> $DIR/format-string-error.rs:15:23
+ |
+LL | let _ = format!("{_foo}", _foo = 6usize);
+ | ^^^^ invalid argument name in format string
+ |
+ = note: argument names cannot start with an underscore
+
+error: invalid format string: invalid argument name `_`
+ --> $DIR/format-string-error.rs:17:23
+ |
+LL | let _ = format!("{_}", _ = 6usize);
+ | ^ invalid argument name in format string
+ |
+ = note: argument names cannot start with an underscore
+
+error: invalid format string: expected `'}'` but string was terminated
+ --> $DIR/format-string-error.rs:19:23
+ |
+LL | let _ = format!("{");
+ | ^ expected `'}'` in format string
+ |
+ = note: if you intended to print `{`, you can escape it using `{{`
+
+error: invalid format string: unmatched `}` found
+ --> $DIR/format-string-error.rs:21:22
+ |
+LL | let _ = format!("}");
+ | ^ unmatched `}` in format string
+ |
+ = note: if you intended to print `}`, you can escape it using `}}`
+
+error: invalid format string: expected `'}'`, found `'/'`
+ --> $DIR/format-string-error.rs:23:23
+ |
+LL | let _ = format!("{/}");
+ | ^ expected `}` in format string
+
+error: aborting due to 7 previous errors
LL | match map.get() {
| --- immutable borrow occurs here
...
-LL | map.set(String::new()); // Just AST errors here
+LL | map.set(String::new()); // Ideally, this would not error.
| ^^^ mutable borrow occurs here
...
LL | }
| - immutable borrow ends here
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast)
- --> $DIR/get_default.rs:44:17
+ --> $DIR/get_default.rs:45:17
|
LL | match map.get() {
| --- immutable borrow occurs here
| - immutable borrow ends here
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast)
- --> $DIR/get_default.rs:50:17
+ --> $DIR/get_default.rs:51:17
|
LL | match map.get() {
| --- immutable borrow occurs here
...
-LL | map.set(String::new()); // Just AST errors here
+LL | map.set(String::new()); // Ideally, just AST would error here
| ^^^ mutable borrow occurs here
...
LL | }
| - immutable borrow ends here
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir)
- --> $DIR/get_default.rs:44:17
+ --> $DIR/get_default.rs:33:17
+ |
+LL | match map.get() {
+ | --- immutable borrow occurs here
+...
+LL | map.set(String::new()); // Ideally, this would not error.
+ | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
+ |
+note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 26:1...
+ --> $DIR/get_default.rs:26:1
+ |
+LL | / fn ok(map: &mut Map) -> &String {
+LL | | loop {
+LL | | match map.get() {
+LL | | Some(v) => {
+... |
+LL | | }
+LL | | }
+ | |_^
+
+error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir)
+ --> $DIR/get_default.rs:45:17
|
LL | match map.get() {
| --- immutable borrow occurs here
LL | return v;
| - borrow later used here
-error: aborting due to 4 previous errors
+error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir)
+ --> $DIR/get_default.rs:51:17
+ |
+LL | match map.get() {
+ | --- immutable borrow occurs here
+...
+LL | map.set(String::new()); // Ideally, just AST would error here
+ | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
+ |
+note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 41:1...
+ --> $DIR/get_default.rs:41:1
+ |
+LL | / fn err(map: &mut Map) -> &String {
+LL | | loop {
+LL | | match map.get() {
+LL | | Some(v) => {
+... |
+LL | | }
+LL | | }
+ | |_^
+
+error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0502`.
return v;
}
None => {
- map.set(String::new()); // Just AST errors here
+ map.set(String::new()); // Ideally, this would not error.
//~^ ERROR borrowed as immutable (Ast)
+ //~| ERROR borrowed as immutable (Mir)
}
}
}
return v;
}
None => {
- map.set(String::new()); // Just AST errors here
+ map.set(String::new()); // Ideally, just AST would error here
//~^ ERROR borrowed as immutable (Ast)
+ //~| ERROR borrowed as immutable (Mir)
}
}
}
LL | match map.get() {
| --- immutable borrow occurs here
...
-LL | map.set(String::new()); // Just AST errors here
+LL | map.set(String::new()); // Ideally, this would not error.
| ^^^ mutable borrow occurs here
...
LL | }
| - immutable borrow ends here
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast)
- --> $DIR/get_default.rs:44:17
+ --> $DIR/get_default.rs:45:17
|
LL | match map.get() {
| --- immutable borrow occurs here
| - immutable borrow ends here
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast)
- --> $DIR/get_default.rs:50:17
+ --> $DIR/get_default.rs:51:17
|
LL | match map.get() {
| --- immutable borrow occurs here
...
-LL | map.set(String::new()); // Just AST errors here
+LL | map.set(String::new()); // Ideally, just AST would error here
| ^^^ mutable borrow occurs here
...
LL | }
| - immutable borrow ends here
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir)
- --> $DIR/get_default.rs:44:17
+ --> $DIR/get_default.rs:33:17
+ |
+LL | match map.get() {
+ | --- immutable borrow occurs here
+...
+LL | map.set(String::new()); // Ideally, this would not error.
+ | ^^^ mutable borrow occurs here
+ |
+note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 26:1...
+ --> $DIR/get_default.rs:26:1
+ |
+LL | / fn ok(map: &mut Map) -> &String {
+LL | | loop {
+LL | | match map.get() {
+LL | | Some(v) => {
+... |
+LL | | }
+LL | | }
+ | |_^
+
+error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir)
+ --> $DIR/get_default.rs:51:17
+ |
+LL | match map.get() {
+ | --- immutable borrow occurs here
+...
+LL | map.set(String::new()); // Ideally, just AST would error here
+ | ^^^ mutable borrow occurs here
+ |
+note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 41:1...
+ --> $DIR/get_default.rs:41:1
+ |
+LL | / fn err(map: &mut Map) -> &String {
+LL | | loop {
+LL | | match map.get() {
+LL | | Some(v) => {
+... |
+LL | | }
+LL | | }
+ | |_^
+
+error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir)
+ --> $DIR/get_default.rs:45:17
|
LL | match map.get() {
| --- immutable borrow occurs here
LL | return v;
| - borrow later used here
-error: aborting due to 4 previous errors
+error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0502`.
#[derive(Clone)]
pub struct Config {
+ /// Whether to overwrite stderr/stdout files instead of complaining about changes in output
+ pub bless: bool,
+
/// The library paths required for running the compiler
pub compile_lib_path: PathBuf,
"FLAGS",
)
.optflag("", "verbose", "run tests verbosely, showing all output")
+ .optflag(
+ "",
+ "bless",
+ "overwrite stderr/stdout files instead of complaining about a mismatch",
+ )
.optflag(
"",
"quiet",
let src_base = opt_path(matches, "src-base");
let run_ignored = matches.opt_present("ignored");
Config {
+ bless: matches.opt_present("bless"),
compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
run_lib_path: make_absolute(opt_path(matches, "run-lib-path")),
rustc_path: opt_path(matches, "rustc-path"),
}
if errors > 0 {
- println!("To update references, run this command from build directory:");
+ println!("To update references, rerun the tests and pass the `--bless` flag");
let relative_path_to_file = self.testpaths
.relative_dir
.join(self.testpaths.file.file_name().unwrap());
println!(
- "{}/update-references.sh '{}' '{}'",
- self.config.src_base.display(),
- self.config.build_base.display(),
- relative_path_to_file.display()
+ "To only update this specific test, also pass `--test-args {}`",
+ relative_path_to_file.display(),
);
self.fatal_proc_rec(
&format!("{} errors occurred comparing output.", errors),
return 0;
}
- if expected.is_empty() {
- println!("normalized {}:\n{}\n", kind, actual);
- } else {
- println!("diff of {}:\n", kind);
- let diff_results = make_diff(expected, actual, 3);
- for result in diff_results {
- let mut line_number = result.line_number;
- for line in result.lines {
- match line {
- DiffLine::Expected(e) => {
- println!("-\t{}", e);
- line_number += 1;
- }
- DiffLine::Context(c) => {
- println!("{}\t{}", line_number, c);
- line_number += 1;
- }
- DiffLine::Resulting(r) => {
- println!("+\t{}", r);
+ if !self.config.bless {
+ if expected.is_empty() {
+ println!("normalized {}:\n{}\n", kind, actual);
+ } else {
+ println!("diff of {}:\n", kind);
+ let diff_results = make_diff(expected, actual, 3);
+ for result in diff_results {
+ let mut line_number = result.line_number;
+ for line in result.lines {
+ match line {
+ DiffLine::Expected(e) => {
+ println!("-\t{}", e);
+ line_number += 1;
+ }
+ DiffLine::Context(c) => {
+ println!("{}\t{}", line_number, c);
+ line_number += 1;
+ }
+ DiffLine::Resulting(r) => {
+ println!("+\t{}", r);
+ }
}
}
+ println!("");
}
- println!("");
}
}
.with_extra_extension(mode)
.with_extra_extension(kind);
- match File::create(&output_file).and_then(|mut f| f.write_all(actual.as_bytes())) {
- Ok(()) => {}
- Err(e) => self.fatal(&format!(
- "failed to write {} to `{}`: {}",
+ let mut files = vec![output_file];
+ if self.config.bless {
+ files.push(expected_output_path(
+ self.testpaths,
+ self.revision,
+ &self.config.compare_mode,
kind,
- output_file.display(),
- e
- )),
+ ));
+ }
+
+ for output_file in &files {
+ if actual.is_empty() {
+ if let Err(e) = ::std::fs::remove_file(output_file) {
+ self.fatal(&format!(
+ "failed to delete `{}`: {}",
+ output_file.display(),
+ e,
+ ));
+ }
+ } else {
+ match File::create(&output_file).and_then(|mut f| f.write_all(actual.as_bytes())) {
+ Ok(()) => {}
+ Err(e) => self.fatal(&format!(
+ "failed to write {} to `{}`: {}",
+ kind,
+ output_file.display(),
+ e
+ )),
+ }
+ }
}
println!("\nThe actual {0} differed from the expected {0}.", kind);
- println!("Actual {} saved to {}", kind, output_file.display());
- 1
+ for output_file in files {
+ println!("Actual {} saved to {}", kind, output_file.display());
+ }
+ if self.config.bless {
+ 0
+ } else {
+ 1
+ }
}
fn create_stamp(&self) {
-Subproject commit 3e3df0485004bc1343bc8200b68c67ac7c479b28
+Subproject commit cf0609d0af0b734d4b9ee9dce6df66f946fc763f
# the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool"
# rustdoc a different name.
[[bin]]
-name = "rustdoc-tool-binary"
+name = "rustdoc_tool_binary"
path = "main.rs"
[dependencies]
-Subproject commit db8cb0b8d6942d42a322b1d36b2504977404f362
+Subproject commit bf2581bf7709b91c4431ba7074de910f72283e1f