From 828289610bece3d2208d34147ae2a419a9c524c4 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sun, 16 Sep 2018 23:04:11 +0200 Subject: [PATCH 01/21] remove (more) CAS API from Atomic* types where not natively supported closes #54276 --- src/libcore/sync/atomic.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/libcore/sync/atomic.rs b/src/libcore/sync/atomic.rs index 602f8d4ee718e..69c524925fc54 100644 --- a/src/libcore/sync/atomic.rs +++ b/src/libcore/sync/atomic.rs @@ -558,6 +558,7 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "extended_compare_and_swap", since = "1.10.0")] + #[cfg(target_has_atomic = "cas")] pub fn compare_exchange_weak(&self, current: bool, new: bool, @@ -1041,6 +1042,7 @@ impl AtomicPtr { /// ``` #[inline] #[stable(feature = "extended_compare_and_swap", since = "1.10.0")] + #[cfg(target_has_atomic = "cas")] pub fn compare_exchange_weak(&self, current: *mut T, new: *mut T, @@ -1434,6 +1436,7 @@ loop { ```"), #[inline] #[$stable_cxchg] + #[cfg(target_has_atomic = "cas")] pub fn compare_exchange_weak(&self, current: $int_type, new: $int_type, @@ -1471,6 +1474,7 @@ assert_eq!(foo.load(Ordering::SeqCst), 10); ```"), #[inline] #[$stable] + #[cfg(target_has_atomic = "cas")] pub fn fetch_add(&self, val: $int_type, order: Ordering) -> $int_type { unsafe { atomic_add(self.v.get(), val, order) } } @@ -1502,6 +1506,7 @@ assert_eq!(foo.load(Ordering::SeqCst), 10); ```"), #[inline] #[$stable] + #[cfg(target_has_atomic = "cas")] pub fn fetch_sub(&self, val: $int_type, order: Ordering) -> $int_type { unsafe { atomic_sub(self.v.get(), val, order) } } @@ -1536,6 +1541,7 @@ assert_eq!(foo.load(Ordering::SeqCst), 0b100001); ```"), #[inline] #[$stable] + #[cfg(target_has_atomic = "cas")] pub fn fetch_and(&self, val: $int_type, order: Ordering) -> $int_type { unsafe { atomic_and(self.v.get(), val, order) } } @@ -1571,6 +1577,7 @@ assert_eq!(foo.load(Ordering::SeqCst), !(0x13 & 0x31)); ```"), #[inline] #[$stable_nand] + #[cfg(target_has_atomic = "cas")] pub fn fetch_nand(&self, val: $int_type, order: Ordering) -> $int_type { unsafe { atomic_nand(self.v.get(), val, order) } } @@ -1605,6 +1612,7 @@ assert_eq!(foo.load(Ordering::SeqCst), 0b111111); ```"), #[inline] #[$stable] + #[cfg(target_has_atomic = "cas")] pub fn fetch_or(&self, val: $int_type, order: Ordering) -> $int_type { unsafe { atomic_or(self.v.get(), val, order) } } @@ -1639,6 +1647,7 @@ assert_eq!(foo.load(Ordering::SeqCst), 0b011110); ```"), #[inline] #[$stable] + #[cfg(target_has_atomic = "cas")] pub fn fetch_xor(&self, val: $int_type, order: Ordering) -> $int_type { unsafe { atomic_xor(self.v.get(), val, order) } } @@ -1688,6 +1697,7 @@ assert_eq!(x.load(Ordering::SeqCst), 9); #[unstable(feature = "no_more_cas", reason = "no more CAS loops in user code", issue = "48655")] + #[cfg(target_has_atomic = "cas")] pub fn fetch_update(&self, mut f: F, fetch_order: Ordering, @@ -1748,6 +1758,7 @@ assert!(max_foo == 42); #[unstable(feature = "atomic_min_max", reason = "easier and faster min/max than writing manual CAS loop", issue = "48655")] + #[cfg(target_has_atomic = "cas")] pub fn fetch_max(&self, val: $int_type, order: Ordering) -> $int_type { unsafe { $max_fn(self.v.get(), val, order) } } @@ -1799,6 +1810,7 @@ assert_eq!(min_foo, 12); #[unstable(feature = "atomic_min_max", reason = "easier and faster min/max than writing manual CAS loop", issue = "48655")] + #[cfg(target_has_atomic = "cas")] pub fn fetch_min(&self, val: $int_type, order: Ordering) -> $int_type { unsafe { $min_fn(self.v.get(), val, order) } } @@ -1987,6 +1999,7 @@ unsafe fn atomic_swap(dst: *mut T, val: T, order: Ordering) -> T { /// Returns the previous value (like __sync_fetch_and_add). #[inline] +#[cfg(target_has_atomic = "cas")] unsafe fn atomic_add(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_xadd_acq(dst, val), @@ -1999,6 +2012,7 @@ unsafe fn atomic_add(dst: *mut T, val: T, order: Ordering) -> T { /// Returns the previous value (like __sync_fetch_and_sub). #[inline] +#[cfg(target_has_atomic = "cas")] unsafe fn atomic_sub(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_xsub_acq(dst, val), @@ -2035,6 +2049,7 @@ unsafe fn atomic_compare_exchange(dst: *mut T, } #[inline] +#[cfg(target_has_atomic = "cas")] unsafe fn atomic_compare_exchange_weak(dst: *mut T, old: T, new: T, @@ -2059,6 +2074,7 @@ unsafe fn atomic_compare_exchange_weak(dst: *mut T, } #[inline] +#[cfg(target_has_atomic = "cas")] unsafe fn atomic_and(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_and_acq(dst, val), @@ -2070,6 +2086,7 @@ unsafe fn atomic_and(dst: *mut T, val: T, order: Ordering) -> T { } #[inline] +#[cfg(target_has_atomic = "cas")] unsafe fn atomic_nand(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_nand_acq(dst, val), @@ -2081,6 +2098,7 @@ unsafe fn atomic_nand(dst: *mut T, val: T, order: Ordering) -> T { } #[inline] +#[cfg(target_has_atomic = "cas")] unsafe fn atomic_or(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_or_acq(dst, val), @@ -2092,6 +2110,7 @@ unsafe fn atomic_or(dst: *mut T, val: T, order: Ordering) -> T { } #[inline] +#[cfg(target_has_atomic = "cas")] unsafe fn atomic_xor(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_xor_acq(dst, val), @@ -2104,6 +2123,7 @@ unsafe fn atomic_xor(dst: *mut T, val: T, order: Ordering) -> T { /// returns the max value (signed comparison) #[inline] +#[cfg(target_has_atomic = "cas")] unsafe fn atomic_max(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_max_acq(dst, val), @@ -2116,6 +2136,7 @@ unsafe fn atomic_max(dst: *mut T, val: T, order: Ordering) -> T { /// returns the min value (signed comparison) #[inline] +#[cfg(target_has_atomic = "cas")] unsafe fn atomic_min(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_min_acq(dst, val), @@ -2128,6 +2149,7 @@ unsafe fn atomic_min(dst: *mut T, val: T, order: Ordering) -> T { /// returns the max value (signed comparison) #[inline] +#[cfg(target_has_atomic = "cas")] unsafe fn atomic_umax(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_umax_acq(dst, val), @@ -2140,6 +2162,7 @@ unsafe fn atomic_umax(dst: *mut T, val: T, order: Ordering) -> T { /// returns the min value (signed comparison) #[inline] +#[cfg(target_has_atomic = "cas")] unsafe fn atomic_umin(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_umin_acq(dst, val), From cb594cf3730c35fd6c514e98d9c7a0d78a00a02d Mon Sep 17 00:00:00 2001 From: varkor Date: Sat, 15 Sep 2018 18:18:49 +0100 Subject: [PATCH 02/21] Treat `dyn` as a keyword in the 2018 edition --- src/librustc_lint/builtin.rs | 3 +-- src/libsyntax/parse/parser.rs | 5 +++-- src/libsyntax/parse/token.rs | 1 + src/libsyntax_pos/symbol.rs | 19 +++++++++---------- src/test/ui/rust-2018/dyn-keyword.fixed | 10 ++++++++++ src/test/ui/rust-2018/dyn-keyword.rs | 10 ++++++++++ src/test/ui/rust-2018/dyn-keyword.stderr | 16 ++++++++++++++++ .../ui/rust-2018/dyn-trait-compatibility.rs | 8 ++++++++ .../rust-2018/dyn-trait-compatibility.stderr | 14 ++++++++++++++ 9 files changed, 72 insertions(+), 14 deletions(-) create mode 100644 src/test/ui/rust-2018/dyn-keyword.fixed create mode 100644 src/test/ui/rust-2018/dyn-keyword.rs create mode 100644 src/test/ui/rust-2018/dyn-keyword.stderr create mode 100644 src/test/ui/rust-2018/dyn-trait-compatibility.rs create mode 100644 src/test/ui/rust-2018/dyn-trait-compatibility.stderr diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index b662b82501393..7da84ccd28dd2 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1937,8 +1937,7 @@ impl EarlyLintPass for KeywordIdents { let next_edition = match cx.sess.edition() { Edition::Edition2015 => { match &ident.as_str()[..] { - "async" | - "try" => Edition::Edition2018, + "async" | "try" | "dyn" => Edition::Edition2018, _ => return, } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index f57fca2cfcf60..ab1f46c4bb921 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1562,8 +1562,9 @@ impl<'a> Parser<'a> { impl_dyn_multi = bounds.len() > 1 || self.prev_token_kind == PrevTokenKind::Plus; TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds) } else if self.check_keyword(keywords::Dyn) && - self.look_ahead(1, |t| t.can_begin_bound() && - !can_continue_type_after_non_fn_ident(t)) { + (self.span.edition() == Edition::Edition2018 || + self.look_ahead(1, |t| t.can_begin_bound() && + !can_continue_type_after_non_fn_ident(t))) { self.bump(); // `dyn` // Always parse bounds greedily for better error recovery. let bounds = self.parse_generic_bounds()?; diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 6e8014284ec40..01bc7f6ad302b 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -136,6 +136,7 @@ fn ident_can_begin_type(ident: ast::Ident, is_raw: bool) -> bool { keywords::Unsafe.name(), keywords::Extern.name(), keywords::Typeof.name(), + keywords::Dyn.name(), ].contains(&ident.name) } diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index defdca9abd15a..d412412fc655e 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -414,26 +414,25 @@ declare_keywords! { (50, Yield, "yield") // Edition-specific keywords reserved for future use. - (51, Async, "async") // >= 2018 Edition Only - (52, Try, "try") // >= 2018 Edition Only + (51, Async, "async") // >= 2018 Edition only + (52, Dyn, "dyn") // >= 2018 Edition only + (53, Try, "try") // >= 2018 Edition only // Special lifetime names - (53, UnderscoreLifetime, "'_") - (54, StaticLifetime, "'static") + (54, UnderscoreLifetime, "'_") + (55, StaticLifetime, "'static") // Weak keywords, have special meaning only in specific contexts. - (55, Auto, "auto") - (56, Catch, "catch") - (57, Default, "default") - (58, Dyn, "dyn") + (56, Auto, "auto") + (57, Catch, "catch") + (58, Default, "default") (59, Union, "union") (60, Existential, "existential") } impl Symbol { fn is_unused_keyword_2018(self) -> bool { - self >= keywords::Async.name() && - self <= keywords::Try.name() + self >= keywords::Async.name() && self <= keywords::Try.name() } } diff --git a/src/test/ui/rust-2018/dyn-keyword.fixed b/src/test/ui/rust-2018/dyn-keyword.fixed new file mode 100644 index 0000000000000..e9cda1af93932 --- /dev/null +++ b/src/test/ui/rust-2018/dyn-keyword.fixed @@ -0,0 +1,10 @@ +// edition:2015 +// run-rustfix + +#![allow(unused_variables)] +#![deny(keyword_idents)] + +fn main() { + let r#dyn = (); //~ ERROR dyn + //~^ WARN hard error in the 2018 edition +} diff --git a/src/test/ui/rust-2018/dyn-keyword.rs b/src/test/ui/rust-2018/dyn-keyword.rs new file mode 100644 index 0000000000000..bdd3a90cab9ec --- /dev/null +++ b/src/test/ui/rust-2018/dyn-keyword.rs @@ -0,0 +1,10 @@ +// edition:2015 +// run-rustfix + +#![allow(unused_variables)] +#![deny(keyword_idents)] + +fn main() { + let dyn = (); //~ ERROR dyn + //~^ WARN hard error in the 2018 edition +} diff --git a/src/test/ui/rust-2018/dyn-keyword.stderr b/src/test/ui/rust-2018/dyn-keyword.stderr new file mode 100644 index 0000000000000..5a3e00ab1d96a --- /dev/null +++ b/src/test/ui/rust-2018/dyn-keyword.stderr @@ -0,0 +1,16 @@ +error: `dyn` is a keyword in the 2018 edition + --> $DIR/dyn-keyword.rs:8:9 + | +LL | let dyn = (); //~ ERROR dyn + | ^^^ help: you can use a raw identifier to stay compatible: `r#dyn` + | +note: lint level defined here + --> $DIR/dyn-keyword.rs:5:9 + | +LL | #![deny(keyword_idents)] + | ^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! + = note: for more information, see issue #49716 + +error: aborting due to previous error + diff --git a/src/test/ui/rust-2018/dyn-trait-compatibility.rs b/src/test/ui/rust-2018/dyn-trait-compatibility.rs new file mode 100644 index 0000000000000..9548df5959bd4 --- /dev/null +++ b/src/test/ui/rust-2018/dyn-trait-compatibility.rs @@ -0,0 +1,8 @@ +// edition:2018 + +type A0 = dyn; +type A1 = dyn::dyn; //~ERROR expected identifier, found reserved keyword +type A2 = dyn; //~ERROR expected identifier, found `<` +type A3 = dyn<::dyn>; + +fn main() {} diff --git a/src/test/ui/rust-2018/dyn-trait-compatibility.stderr b/src/test/ui/rust-2018/dyn-trait-compatibility.stderr new file mode 100644 index 0000000000000..ea0483394b5ea --- /dev/null +++ b/src/test/ui/rust-2018/dyn-trait-compatibility.stderr @@ -0,0 +1,14 @@ +error: expected identifier, found reserved keyword `dyn` + --> $DIR/dyn-trait-compatibility.rs:4:16 + | +LL | type A1 = dyn::dyn; //~ERROR expected identifier, found reserved keyword + | ^^^ expected identifier, found reserved keyword + +error: expected identifier, found `<` + --> $DIR/dyn-trait-compatibility.rs:5:14 + | +LL | type A2 = dyn; //~ERROR expected identifier, found `<` + | ^ expected identifier + +error: aborting due to 2 previous errors + From 2a45057e17082559b4c3a365d9b29cc30105f740 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Tue, 18 Sep 2018 14:58:11 +0200 Subject: [PATCH 03/21] rustbuild: drop color handling Let cargo handle that for us Signed-off-by: Marc-Antoine Perennou --- src/bootstrap/bin/rustc.rs | 9 --------- src/bootstrap/compile.rs | 34 +--------------------------------- 2 files changed, 1 insertion(+), 42 deletions(-) diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs index f30f34acf5c6c..b89976eca26c4 100644 --- a/src/bootstrap/bin/rustc.rs +++ b/src/bootstrap/bin/rustc.rs @@ -291,15 +291,6 @@ fn main() { cmd.arg("-Z").arg("verify-llvm-ir"); } - let color = match env::var("RUSTC_COLOR") { - Ok(s) => usize::from_str(&s).expect("RUSTC_COLOR should be an integer"), - Err(_) => 0, - }; - - if color != 0 { - cmd.arg("--color=always"); - } - if env::var_os("RUSTC_DENY_WARNINGS").is_some() && env::var_os("RUSTC_EXTERNAL_TOOL").is_none() { cmd.arg("-Dwarnings"); diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 4205d5aebffa1..608f2c982c2a5 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -29,7 +29,7 @@ use build_helper::{output, mtime, up_to_date}; use filetime::FileTime; use serde_json; -use util::{exe, libdir, is_dylib, CiEnv}; +use util::{exe, libdir, is_dylib}; use {Compiler, Mode, GitRepo}; use native; @@ -1034,29 +1034,6 @@ pub fn add_to_sysroot(builder: &Builder, sysroot_dst: &Path, stamp: &Path) { } } -// Avoiding a dependency on winapi to keep compile times down -#[cfg(unix)] -fn stderr_isatty() -> bool { - use libc; - unsafe { libc::isatty(libc::STDERR_FILENO) != 0 } -} -#[cfg(windows)] -fn stderr_isatty() -> bool { - type DWORD = u32; - type BOOL = i32; - type HANDLE = *mut u8; - const STD_ERROR_HANDLE: DWORD = -12i32 as DWORD; - extern "system" { - fn GetStdHandle(which: DWORD) -> HANDLE; - fn GetConsoleMode(hConsoleHandle: HANDLE, lpMode: *mut DWORD) -> BOOL; - } - unsafe { - let handle = GetStdHandle(STD_ERROR_HANDLE); - let mut out = 0; - GetConsoleMode(handle, &mut out) != 0 - } -} - pub fn run_cargo(builder: &Builder, cargo: &mut Command, tail_args: Vec, @@ -1218,15 +1195,6 @@ pub fn stream_cargo( cargo.arg("--message-format").arg("json") .stdout(Stdio::piped()); - if stderr_isatty() && builder.ci_env == CiEnv::None && - // if the terminal is reported as dumb, then we don't want to enable color for rustc - env::var_os("TERM").map(|t| t != *"dumb").unwrap_or(true) { - // since we pass message-format=json to cargo, we need to tell the rustc - // wrapper to give us colored output if necessary. This is because we - // only want Cargo's JSON output, not rustcs. - cargo.env("RUSTC_COLOR", "1"); - } - for arg in tail_args { cargo.arg(arg); } From c996c4d316ce9023c3ef296a583e0c8bf7ce5880 Mon Sep 17 00:00:00 2001 From: Philip Munksgaard Date: Tue, 18 Sep 2018 08:55:15 +0200 Subject: [PATCH 04/21] Add support for running doc test in specific edition --- src/librustdoc/html/markdown.rs | 50 +++++++++++++++++++++------------ src/librustdoc/test.rs | 2 +- src/libsyntax_pos/edition.rs | 2 +- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 18ad862c11bb0..173db236f7cd5 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -37,6 +37,7 @@ use std::fmt::{self, Write}; use std::borrow::Cow; use std::ops::Range; use std::str; +use syntax::edition::Edition; use html::toc::TocBuilder; use html::highlight; @@ -577,6 +578,7 @@ pub struct LangString { pub compile_fail: bool, pub error_codes: Vec, pub allow_fail: bool, + pub edition: Option } impl LangString { @@ -591,6 +593,7 @@ impl LangString { compile_fail: false, error_codes: Vec::new(), allow_fail: false, + edition: None, } } @@ -625,6 +628,11 @@ impl LangString { seen_rust_tags = !seen_other_tags || seen_rust_tags; data.no_run = true; } + x if allow_error_code_check && x.starts_with("edition") => { + // allow_error_code_check is true if we're on nightly, which + // is needed for edition support + data.edition = x[7..].parse::().ok(); + } x if allow_error_code_check && x.starts_with("E") && x.len() == 5 => { if x[1..].parse::().is_ok() { data.error_codes.push(x.to_owned()); @@ -925,12 +933,14 @@ mod tests { use super::{ErrorCodes, LangString, Markdown, MarkdownHtml, IdMap}; use super::plain_summary_line; use std::cell::RefCell; + use syntax::edition::Edition; #[test] fn test_lang_string_parse() { fn t(s: &str, should_panic: bool, no_run: bool, ignore: bool, rust: bool, test_harness: bool, - compile_fail: bool, allow_fail: bool, error_codes: Vec) { + compile_fail: bool, allow_fail: bool, error_codes: Vec, + edition: Option) { assert_eq!(LangString::parse(s, ErrorCodes::Yes), LangString { should_panic, no_run, @@ -941,6 +951,7 @@ mod tests { error_codes, original: s.to_owned(), allow_fail, + edition, }) } @@ -948,23 +959,26 @@ mod tests { Vec::new() } - // marker | should_panic| no_run| ignore| rust | test_harness| compile_fail - // | allow_fail | error_codes - t("", false, false, false, true, false, false, false, v()); - t("rust", false, false, false, true, false, false, false, v()); - t("sh", false, false, false, false, false, false, false, v()); - t("ignore", false, false, true, true, false, false, false, v()); - t("should_panic", true, false, false, true, false, false, false, v()); - t("no_run", false, true, false, true, false, false, false, v()); - t("test_harness", false, false, false, true, true, false, false, v()); - t("compile_fail", false, true, false, true, false, true, false, v()); - t("allow_fail", false, false, false, true, false, false, true, v()); - t("{.no_run .example}", false, true, false, true, false, false, false, v()); - t("{.sh .should_panic}", true, false, false, false, false, false, false, v()); - t("{.example .rust}", false, false, false, true, false, false, false, v()); - t("{.test_harness .rust}", false, false, false, true, true, false, false, v()); - t("text, no_run", false, true, false, false, false, false, false, v()); - t("text,no_run", false, true, false, false, false, false, false, v()); + // ignore-tidy-linelength + // marker | should_panic | no_run | ignore | rust | test_harness + // | compile_fail | allow_fail | error_codes | edition + t("", false, false, false, true, false, false, false, v(), None); + t("rust", false, false, false, true, false, false, false, v(), None); + t("sh", false, false, false, false, false, false, false, v(), None); + t("ignore", false, false, true, true, false, false, false, v(), None); + t("should_panic", true, false, false, true, false, false, false, v(), None); + t("no_run", false, true, false, true, false, false, false, v(), None); + t("test_harness", false, false, false, true, true, false, false, v(), None); + t("compile_fail", false, true, false, true, false, true, false, v(), None); + t("allow_fail", false, false, false, true, false, false, true, v(), None); + t("{.no_run .example}", false, true, false, true, false, false, false, v(), None); + t("{.sh .should_panic}", true, false, false, false, false, false, false, v(), None); + t("{.example .rust}", false, false, false, true, false, false, false, v(), None); + t("{.test_harness .rust}", false, false, false, true, true, false, false, v(), None); + t("text, no_run", false, true, false, false, false, false, false, v(), None); + t("text,no_run", false, true, false, false, false, false, false, v(), None); + t("edition2015", false, false, false, true, false, false, false, v(), Some(Edition::Edition2015)); + t("edition2018", false, false, false, true, false, false, false, v(), Some(Edition::Edition2018)); } #[test] diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 3b07a2ccdde09..c33888d1a49e5 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -545,7 +545,7 @@ impl Collector { let opts = self.opts.clone(); let maybe_sysroot = self.maybe_sysroot.clone(); let linker = self.linker.clone(); - let edition = self.edition; + let edition = config.edition.unwrap_or(self.edition); debug!("Creating test {}: {}", name, test); self.tests.push(testing::TestDescAndFn { desc: testing::TestDesc { diff --git a/src/libsyntax_pos/edition.rs b/src/libsyntax_pos/edition.rs index 7709db72a02fb..5819cd7f480a0 100644 --- a/src/libsyntax_pos/edition.rs +++ b/src/libsyntax_pos/edition.rs @@ -12,7 +12,7 @@ use std::fmt; use std::str::FromStr; /// The edition of the compiler (RFC 2052) -#[derive(Clone, Copy, Hash, PartialEq, PartialOrd, Debug, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, Hash, PartialEq, PartialOrd, Debug, RustcEncodable, RustcDecodable, Eq)] #[non_exhaustive] pub enum Edition { // editions must be kept in order, oldest to newest From 99f05e800ed806b6612f334d0e1d00343a911724 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 18 Sep 2018 17:03:24 +1000 Subject: [PATCH 05/21] Improve handling of type bounds in `bit_set.rs`. Currently, `BitSet` doesn't actually know its own domain size; it just knows how many words it contains. To improve things, this commit makes the following changes. - It changes `BitSet` and `SparseBitSet` to store their own domain size, and do more precise bounds and same-size checks with it. It also changes the signature of `BitSet::to_string()` (and puts it within `impl ToString`) now that the domain size need not be passed in from outside. - It uses `derive(RustcDecodable, RustcEncodable)` for `BitSet`. This required adding code to handle `PhantomData` in `libserialize`. - As a result, it removes the domain size from `HybridBitSet`, making a lot of that code nicer. - Both set_up_to() and clear_above() were overly general, working with arbitrary sizes when they are only needed for the domain size. The commit removes the former, degeneralizes the latter, and removes the (overly general) tests. - Changes `GrowableBitSet::grow()` to `ensure()`, fixing a bug where a (1-based) domain size was confused with a (0-based) element index. - Changes `BitMatrix` to store its row count, and do more precise bounds checks with it. - Changes `ty_params` in `select.rs` from a `BitSet` to a `GrowableBitSet` because it repeatedly failed the new, more precise bounds checks. (Changing the type was simpler than computing an accurate domain size.) - Various other minor improvements. --- src/librustc/traits/select.rs | 4 +- src/librustc_data_structures/bit_set.rs | 386 ++++++++++----------- src/librustc_mir/dataflow/graphviz.rs | 3 +- src/librustc_mir/dataflow/impls/mod.rs | 3 +- src/librustc_mir/monomorphize/collector.rs | 2 +- src/libserialize/serialize.rs | 14 + 6 files changed, 210 insertions(+), 202 deletions(-) diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 85e7368bfdd10..366cf2b77ea58 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -44,7 +44,7 @@ use ty::relate::TypeRelation; use middle::lang_items; use mir::interpret::{GlobalId}; -use rustc_data_structures::bit_set::BitSet; +use rustc_data_structures::bit_set::GrowableBitSet; use rustc_data_structures::sync::Lock; use std::iter; use std::cmp; @@ -3069,7 +3069,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { } else { return Err(Unimplemented); }; - let mut ty_params = BitSet::new_empty(substs_a.types().count()); + let mut ty_params = GrowableBitSet::new_empty(); let mut found = false; for ty in field.walk() { if let ty::Param(p) = ty.sty { diff --git a/src/librustc_data_structures/bit_set.rs b/src/librustc_data_structures/bit_set.rs index 9eb8d0afd46ec..1fba57fa541f0 100644 --- a/src/librustc_data_structures/bit_set.rs +++ b/src/librustc_data_structures/bit_set.rs @@ -9,7 +9,6 @@ // except according to those terms. use indexed_vec::{Idx, IndexVec}; -use rustc_serialize; use smallvec::SmallVec; use std::fmt; use std::iter; @@ -26,33 +25,48 @@ pub const WORD_BITS: usize = WORD_BYTES * 8; /// /// `T` is an index type, typically a newtyped `usize` wrapper, but it can also /// just be `usize`. -#[derive(Clone, Eq, PartialEq)] +/// +/// All operations that involve an element will panic if the element is equal +/// to or greater than the domain size. All operations that involve two bitsets +/// will panic if the bitsets have differing domain sizes. +#[derive(Clone, Eq, PartialEq, RustcDecodable, RustcEncodable)] pub struct BitSet { + domain_size: usize, words: Vec, marker: PhantomData, } impl BitSet { + /// Create a new, empty bitset with a given `domain_size`. #[inline] pub fn new_empty(domain_size: usize) -> BitSet { let num_words = num_words(domain_size); BitSet { + domain_size, words: vec![0; num_words], marker: PhantomData, } } + /// Create a new, filled bitset with a given `domain_size`. #[inline] pub fn new_filled(domain_size: usize) -> BitSet { let num_words = num_words(domain_size); let mut result = BitSet { + domain_size, words: vec![!0; num_words], marker: PhantomData, }; - result.clear_above(domain_size); + result.clear_excess_bits(); result } + /// Get the domain size. + pub fn domain_size(&self) -> usize { + self.domain_size + } + + /// Clear all elements. #[inline] pub fn clear(&mut self) { for word in &mut self.words { @@ -60,34 +74,19 @@ impl BitSet { } } - /// Sets all elements up to and including `size`. - pub fn set_up_to(&mut self, elem: usize) { - for word in &mut self.words { - *word = !0; + /// Clear excess bits in the final word. + fn clear_excess_bits(&mut self) { + let num_bits_in_final_word = self.domain_size % WORD_BITS; + if num_bits_in_final_word > 0 { + let mask = (1 << num_bits_in_final_word) - 1; + let final_word_idx = self.words.len() - 1; + self.words[final_word_idx] &= mask; } - self.clear_above(elem); } - /// Clear all elements above `elem`. - fn clear_above(&mut self, elem: usize) { - let first_clear_block = elem / WORD_BITS; - - if first_clear_block < self.words.len() { - // Within `first_clear_block`, the `elem % WORD_BITS` LSBs should - // remain. - let mask = (1 << (elem % WORD_BITS)) - 1; - self.words[first_clear_block] &= mask; - - // All the blocks above `first_clear_block` are fully cleared. - for word in &mut self.words[first_clear_block + 1..] { - *word = 0; - } - } - } - - /// Efficiently overwrite `self` with `other`. Panics if `self` and `other` - /// don't have the same length. + /// Efficiently overwrite `self` with `other`. pub fn overwrite(&mut self, other: &BitSet) { + assert!(self.domain_size == other.domain_size); self.words.clone_from_slice(&other.words); } @@ -99,16 +98,15 @@ impl BitSet { /// True if `self` contains `elem`. #[inline] pub fn contains(&self, elem: T) -> bool { + assert!(elem.index() < self.domain_size); let (word_index, mask) = word_index_and_mask(elem); (self.words[word_index] & mask) != 0 } - /// True if `self` is a (non-strict) superset of `other`. - /// - /// The two sets must have the same domain_size. + /// Is `self` is a (non-strict) superset of `other`? #[inline] pub fn superset(&self, other: &BitSet) -> bool { - assert_eq!(self.words.len(), other.words.len()); + assert_eq!(self.domain_size, other.domain_size); self.words.iter().zip(&other.words).all(|(a, b)| (a & b) == *b) } @@ -121,6 +119,7 @@ impl BitSet { /// Insert `elem`. Returns true if the set has changed. #[inline] pub fn insert(&mut self, elem: T) -> bool { + assert!(elem.index() < self.domain_size); let (word_index, mask) = word_index_and_mask(elem); let word_ref = &mut self.words[word_index]; let word = *word_ref; @@ -134,11 +133,13 @@ impl BitSet { for word in &mut self.words { *word = !0; } + self.clear_excess_bits(); } /// Returns true if the set has changed. #[inline] pub fn remove(&mut self, elem: T) -> bool { + assert!(elem.index() < self.domain_size); let (word_index, mask) = word_index_and_mask(elem); let word_ref = &mut self.words[word_index]; let word = *word_ref; @@ -162,6 +163,7 @@ impl BitSet { /// Set `self = self & other` and return true if `self` changed. /// (i.e., if any bits were removed). pub fn intersect(&mut self, other: &BitSet) -> bool { + assert_eq!(self.domain_size, other.domain_size); bitwise(&mut self.words, &other.words, |a, b| { a & b }) } @@ -182,43 +184,8 @@ impl BitSet { /// Duplicates the set as a hybrid set. pub fn to_hybrid(&self) -> HybridBitSet { - // This domain_size may be slightly larger than the one specified - // upon creation, due to rounding up to a whole word. That's ok. - let domain_size = self.words.len() * WORD_BITS; - // Note: we currently don't bother trying to make a Sparse set. - HybridBitSet::Dense(self.to_owned(), domain_size) - } - - pub fn to_string(&self, bits: usize) -> String { - let mut result = String::new(); - let mut sep = '['; - - // Note: this is a little endian printout of bytes. - - // i tracks how many bits we have printed so far. - let mut i = 0; - for word in &self.words { - let mut word = *word; - for _ in 0..WORD_BYTES { // for each byte in `word`: - let remain = bits - i; - // If less than a byte remains, then mask just that many bits. - let mask = if remain <= 8 { (1 << remain) - 1 } else { 0xFF }; - assert!(mask <= 0xFF); - let byte = word & mask; - - result.push_str(&format!("{}{:02x}", sep, byte)); - - if remain <= 8 { break; } - word >>= 8; - i += 8; - sep = '-'; - } - sep = '|'; - } - result.push(']'); - - result + HybridBitSet::Dense(self.to_owned()) } } @@ -238,12 +205,14 @@ pub trait SubtractFromBitSet { impl UnionIntoBitSet for BitSet { fn union_into(&self, other: &mut BitSet) -> bool { + assert_eq!(self.domain_size, other.domain_size); bitwise(&mut other.words, &self.words, |a, b| { a | b }) } } impl SubtractFromBitSet for BitSet { fn subtract_from(&self, other: &mut BitSet) -> bool { + assert_eq!(self.domain_size, other.domain_size); bitwise(&mut other.words, &self.words, |a, b| { a & !b }) } } @@ -256,19 +225,36 @@ impl fmt::Debug for BitSet { } } -impl rustc_serialize::Encodable for BitSet { - fn encode(&self, encoder: &mut E) -> Result<(), E::Error> { - self.words.encode(encoder) - } -} +impl ToString for BitSet { + fn to_string(&self) -> String { + let mut result = String::new(); + let mut sep = '['; -impl rustc_serialize::Decodable for BitSet { - fn decode(d: &mut D) -> Result, D::Error> { - let words: Vec = rustc_serialize::Decodable::decode(d)?; - Ok(BitSet { - words, - marker: PhantomData, - }) + // Note: this is a little endian printout of bytes. + + // i tracks how many bits we have printed so far. + let mut i = 0; + for word in &self.words { + let mut word = *word; + for _ in 0..WORD_BYTES { // for each byte in `word`: + let remain = self.domain_size - i; + // If less than a byte remains, then mask just that many bits. + let mask = if remain <= 8 { (1 << remain) - 1 } else { 0xFF }; + assert!(mask <= 0xFF); + let byte = word & mask; + + result.push_str(&format!("{}{:02x}", sep, byte)); + + if remain <= 8 { break; } + word >>= 8; + i += 8; + sep = '-'; + } + sep = '|'; + } + result.push(']'); + + result } } @@ -326,67 +312,78 @@ const SPARSE_MAX: usize = 8; /// /// This type is used by `HybridBitSet`; do not use directly. #[derive(Clone, Debug)] -pub struct SparseBitSet(SmallVec<[T; SPARSE_MAX]>); +pub struct SparseBitSet { + domain_size: usize, + elems: SmallVec<[T; SPARSE_MAX]>, +} impl SparseBitSet { - fn new_empty() -> Self { - SparseBitSet(SmallVec::new()) + fn new_empty(domain_size: usize) -> Self { + SparseBitSet { + domain_size, + elems: SmallVec::new() + } } fn len(&self) -> usize { - self.0.len() + self.elems.len() } fn is_empty(&self) -> bool { - self.0.len() == 0 + self.elems.len() == 0 } fn contains(&self, elem: T) -> bool { - self.0.contains(&elem) + assert!(elem.index() < self.domain_size); + self.elems.contains(&elem) } fn insert(&mut self, elem: T) -> bool { - assert!(self.len() < SPARSE_MAX); - if let Some(i) = self.0.iter().position(|&e| e >= elem) { - if self.0[i] == elem { + assert!(elem.index() < self.domain_size); + let changed = if let Some(i) = self.elems.iter().position(|&e| e >= elem) { + if self.elems[i] == elem { // `elem` is already in the set. false } else { // `elem` is smaller than one or more existing elements. - self.0.insert(i, elem); + self.elems.insert(i, elem); true } } else { // `elem` is larger than all existing elements. - self.0.push(elem); + self.elems.push(elem); true - } + }; + assert!(self.len() <= SPARSE_MAX); + changed } fn remove(&mut self, elem: T) -> bool { - if let Some(i) = self.0.iter().position(|&e| e == elem) { - self.0.remove(i); + assert!(elem.index() < self.domain_size); + if let Some(i) = self.elems.iter().position(|&e| e == elem) { + self.elems.remove(i); true } else { false } } - fn to_dense(&self, domain_size: usize) -> BitSet { - let mut dense = BitSet::new_empty(domain_size); - for elem in self.0.iter() { + fn to_dense(&self) -> BitSet { + let mut dense = BitSet::new_empty(self.domain_size); + for elem in self.elems.iter() { dense.insert(*elem); } dense } fn iter(&self) -> slice::Iter { - self.0.iter() + self.elems.iter() } } impl UnionIntoBitSet for SparseBitSet { fn union_into(&self, other: &mut BitSet) -> bool { + assert_eq!(self.domain_size, other.domain_size); let mut changed = false; for elem in self.iter() { changed |= other.insert(*elem); @@ -397,6 +394,7 @@ impl UnionIntoBitSet for SparseBitSet { impl SubtractFromBitSet for SparseBitSet { fn subtract_from(&self, other: &mut BitSet) -> bool { + assert_eq!(self.domain_size, other.domain_size); let mut changed = false; for elem in self.iter() { changed |= other.remove(*elem); @@ -414,10 +412,14 @@ impl SubtractFromBitSet for SparseBitSet { /// /// `T` is an index type, typically a newtyped `usize` wrapper, but it can also /// just be `usize`. +/// +/// All operations that involve an element will panic if the element is equal +/// to or greater than the domain size. All operations that involve two bitsets +/// will panic if the bitsets have differing domain sizes. #[derive(Clone, Debug)] pub enum HybridBitSet { - Sparse(SparseBitSet, usize), - Dense(BitSet, usize), + Sparse(SparseBitSet), + Dense(BitSet), } impl HybridBitSet { @@ -427,17 +429,17 @@ impl HybridBitSet { fn dummy() -> Self { // The cheapest HybridBitSet to construct, which is only used to get // around the borrow checker. - HybridBitSet::Sparse(SparseBitSet::new_empty(), 0) + HybridBitSet::Sparse(SparseBitSet::new_empty(0)) } pub fn new_empty(domain_size: usize) -> Self { - HybridBitSet::Sparse(SparseBitSet::new_empty(), domain_size) + HybridBitSet::Sparse(SparseBitSet::new_empty(domain_size)) } - pub fn domain_size(&self) -> usize { - match *self { - HybridBitSet::Sparse(_, size) => size, - HybridBitSet::Dense(_, size) => size, + fn domain_size(&self) -> usize { + match self { + HybridBitSet::Sparse(sparse) => sparse.domain_size, + HybridBitSet::Dense(dense) => dense.domain_size, } } @@ -448,83 +450,88 @@ impl HybridBitSet { pub fn contains(&self, elem: T) -> bool { match self { - HybridBitSet::Sparse(sparse, _) => sparse.contains(elem), - HybridBitSet::Dense(dense, _) => dense.contains(elem), + HybridBitSet::Sparse(sparse) => sparse.contains(elem), + HybridBitSet::Dense(dense) => dense.contains(elem), } } pub fn superset(&self, other: &HybridBitSet) -> bool { match (self, other) { - (HybridBitSet::Dense(self_dense, _), HybridBitSet::Dense(other_dense, _)) => { + (HybridBitSet::Dense(self_dense), HybridBitSet::Dense(other_dense)) => { self_dense.superset(other_dense) } - _ => other.iter().all(|elem| self.contains(elem)), + _ => { + assert!(self.domain_size() == other.domain_size()); + other.iter().all(|elem| self.contains(elem)) + } } } pub fn is_empty(&self) -> bool { match self { - HybridBitSet::Sparse(sparse, _) => sparse.is_empty(), - HybridBitSet::Dense(dense, _) => dense.is_empty(), + HybridBitSet::Sparse(sparse) => sparse.is_empty(), + HybridBitSet::Dense(dense) => dense.is_empty(), } } pub fn insert(&mut self, elem: T) -> bool { + // No need to check `elem` against `self.domain_size` here because all + // the match cases check it, one way or another. match self { - HybridBitSet::Sparse(sparse, _) if sparse.len() < SPARSE_MAX => { + HybridBitSet::Sparse(sparse) if sparse.len() < SPARSE_MAX => { // The set is sparse and has space for `elem`. sparse.insert(elem) } - HybridBitSet::Sparse(sparse, _) if sparse.contains(elem) => { + HybridBitSet::Sparse(sparse) if sparse.contains(elem) => { // The set is sparse and does not have space for `elem`, but // that doesn't matter because `elem` is already present. false } - HybridBitSet::Sparse(_, _) => { + HybridBitSet::Sparse(_) => { // The set is sparse and full. Convert to a dense set. match mem::replace(self, HybridBitSet::dummy()) { - HybridBitSet::Sparse(sparse, domain_size) => { - let mut dense = sparse.to_dense(domain_size); + HybridBitSet::Sparse(sparse) => { + let mut dense = sparse.to_dense(); let changed = dense.insert(elem); assert!(changed); - *self = HybridBitSet::Dense(dense, domain_size); + *self = HybridBitSet::Dense(dense); changed } _ => unreachable!() } } - HybridBitSet::Dense(dense, _) => dense.insert(elem), + HybridBitSet::Dense(dense) => dense.insert(elem), } } pub fn insert_all(&mut self) { let domain_size = self.domain_size(); match self { - HybridBitSet::Sparse(_, _) => { - let dense = BitSet::new_filled(domain_size); - *self = HybridBitSet::Dense(dense, domain_size); + HybridBitSet::Sparse(_) => { + *self = HybridBitSet::Dense(BitSet::new_filled(domain_size)); } - HybridBitSet::Dense(dense, _) => dense.insert_all(), + HybridBitSet::Dense(dense) => dense.insert_all(), } } pub fn remove(&mut self, elem: T) -> bool { // Note: we currently don't bother going from Dense back to Sparse. match self { - HybridBitSet::Sparse(sparse, _) => sparse.remove(elem), - HybridBitSet::Dense(dense, _) => dense.remove(elem), + HybridBitSet::Sparse(sparse) => sparse.remove(elem), + HybridBitSet::Dense(dense) => dense.remove(elem), } } pub fn union(&mut self, other: &HybridBitSet) -> bool { match self { - HybridBitSet::Sparse(_, _) => { + HybridBitSet::Sparse(_) => { match other { - HybridBitSet::Sparse(other_sparse, _) => { + HybridBitSet::Sparse(other_sparse) => { // Both sets are sparse. Add the elements in // `other_sparse` to `self_hybrid` one at a time. This // may or may not cause `self_hybrid` to be densified. + assert_eq!(self.domain_size(), other.domain_size()); let mut self_hybrid = mem::replace(self, HybridBitSet::dummy()); let mut changed = false; for elem in other_sparse.iter() { @@ -533,14 +540,14 @@ impl HybridBitSet { *self = self_hybrid; changed } - HybridBitSet::Dense(other_dense, _) => { + HybridBitSet::Dense(other_dense) => { // `self` is sparse and `other` is dense. Densify // `self` and then do the bitwise union. match mem::replace(self, HybridBitSet::dummy()) { - HybridBitSet::Sparse(self_sparse, self_domain_size) => { - let mut new_dense = self_sparse.to_dense(self_domain_size); + HybridBitSet::Sparse(self_sparse) => { + let mut new_dense = self_sparse.to_dense(); let changed = new_dense.union(other_dense); - *self = HybridBitSet::Dense(new_dense, self_domain_size); + *self = HybridBitSet::Dense(new_dense); changed } _ => unreachable!() @@ -549,22 +556,22 @@ impl HybridBitSet { } } - HybridBitSet::Dense(self_dense, _) => self_dense.union(other), + HybridBitSet::Dense(self_dense) => self_dense.union(other), } } /// Converts to a dense set, consuming itself in the process. pub fn to_dense(self) -> BitSet { match self { - HybridBitSet::Sparse(sparse, domain_size) => sparse.to_dense(domain_size), - HybridBitSet::Dense(dense, _) => dense, + HybridBitSet::Sparse(sparse) => sparse.to_dense(), + HybridBitSet::Dense(dense) => dense, } } pub fn iter(&self) -> HybridIter { match self { - HybridBitSet::Sparse(sparse, _) => HybridIter::Sparse(sparse.iter()), - HybridBitSet::Dense(dense, _) => HybridIter::Dense(dense.iter()), + HybridBitSet::Sparse(sparse) => HybridIter::Sparse(sparse.iter()), + HybridBitSet::Dense(dense) => HybridIter::Dense(dense.iter()), } } } @@ -572,8 +579,8 @@ impl HybridBitSet { impl UnionIntoBitSet for HybridBitSet { fn union_into(&self, other: &mut BitSet) -> bool { match self { - HybridBitSet::Sparse(sparse, _) => sparse.union_into(other), - HybridBitSet::Dense(dense, _) => dense.union_into(other), + HybridBitSet::Sparse(sparse) => sparse.union_into(other), + HybridBitSet::Dense(dense) => dense.union_into(other), } } } @@ -581,8 +588,8 @@ impl UnionIntoBitSet for HybridBitSet { impl SubtractFromBitSet for HybridBitSet { fn subtract_from(&self, other: &mut BitSet) -> bool { match self { - HybridBitSet::Sparse(sparse, _) => sparse.subtract_from(other), - HybridBitSet::Dense(dense, _) => dense.subtract_from(other), + HybridBitSet::Sparse(sparse) => sparse.subtract_from(other), + HybridBitSet::Dense(dense) => dense.subtract_from(other), } } } @@ -607,16 +614,24 @@ impl<'a, T: Idx> Iterator for HybridIter<'a, T> { /// /// `T` is an index type, typically a newtyped `usize` wrapper, but it can also /// just be `usize`. +/// +/// All operations that involve an element will panic if the element is equal +/// to or greater than the domain size. #[derive(Clone, Debug, PartialEq)] pub struct GrowableBitSet { bit_set: BitSet, } impl GrowableBitSet { - pub fn grow(&mut self, domain_size: T) { - let num_words = num_words(domain_size); - if self.bit_set.words.len() <= num_words { - self.bit_set.words.resize(num_words + 1, 0) + /// Ensure that the set can hold at least `min_domain_size` elements. + pub fn ensure(&mut self, min_domain_size: usize) { + if self.bit_set.domain_size < min_domain_size { + self.bit_set.domain_size = min_domain_size; + } + + let min_num_words = num_words(min_domain_size); + if self.bit_set.words.len() < min_num_words { + self.bit_set.words.resize(min_num_words, 0) } } @@ -631,7 +646,7 @@ impl GrowableBitSet { /// Returns true if the set has changed. #[inline] pub fn insert(&mut self, elem: T) -> bool { - self.grow(elem); + self.ensure(elem.index() + 1); self.bit_set.insert(elem) } @@ -651,31 +666,34 @@ impl GrowableBitSet { /// `R` and `C` are index types used to identify rows and columns respectively; /// typically newtyped `usize` wrappers, but they can also just be `usize`. /// +/// All operations that involve a row and/or column index will panic if the +/// index exceeds the relevant bound. #[derive(Clone, Debug)] pub struct BitMatrix { - columns: usize, + num_rows: usize, + num_columns: usize, words: Vec, marker: PhantomData<(R, C)>, } impl BitMatrix { /// Create a new `rows x columns` matrix, initially empty. - pub fn new(rows: usize, columns: usize) -> BitMatrix { + pub fn new(num_rows: usize, num_columns: usize) -> BitMatrix { // For every element, we need one bit for every other // element. Round up to an even number of words. - let words_per_row = num_words(columns); + let words_per_row = num_words(num_columns); BitMatrix { - columns, - words: vec![0; rows * words_per_row], + num_rows, + num_columns, + words: vec![0; num_rows * words_per_row], marker: PhantomData, } } /// The range of bits for a given row. fn range(&self, row: R) -> (usize, usize) { - let row = row.index(); - let words_per_row = num_words(self.columns); - let start = row * words_per_row; + let words_per_row = num_words(self.num_columns); + let start = row.index() * words_per_row; (start, start + words_per_row) } @@ -683,7 +701,8 @@ impl BitMatrix { /// `column` to the bitset for `row`. /// /// Returns true if this changed the matrix, and false otherwise. - pub fn insert(&mut self, row: R, column: R) -> bool { + pub fn insert(&mut self, row: R, column: C) -> bool { + assert!(row.index() < self.num_rows && column.index() < self.num_columns); let (start, _) = self.range(row); let (word_index, mask) = word_index_and_mask(column); let words = &mut self.words[..]; @@ -697,7 +716,8 @@ impl BitMatrix { /// the matrix cell at `(row, column)` true? Put yet another way, /// if the matrix represents (transitive) reachability, can /// `row` reach `column`? - pub fn contains(&self, row: R, column: R) -> bool { + pub fn contains(&self, row: R, column: C) -> bool { + assert!(row.index() < self.num_rows && column.index() < self.num_columns); let (start, _) = self.range(row); let (word_index, mask) = word_index_and_mask(column); (self.words[start + word_index] & mask) != 0 @@ -707,11 +727,12 @@ impl BitMatrix { /// is an O(n) operation where `n` is the number of elements /// (somewhat independent from the actual size of the /// intersection, in particular). - pub fn intersect_rows(&self, a: R, b: R) -> Vec { - let (a_start, a_end) = self.range(a); - let (b_start, b_end) = self.range(b); - let mut result = Vec::with_capacity(self.columns); - for (base, (i, j)) in (a_start..a_end).zip(b_start..b_end).enumerate() { + pub fn intersect_rows(&self, row1: R, row2: R) -> Vec { + assert!(row1.index() < self.num_rows && row2.index() < self.num_rows); + let (row1_start, row1_end) = self.range(row1); + let (row2_start, row2_end) = self.range(row2); + let mut result = Vec::with_capacity(self.num_columns); + for (base, (i, j)) in (row1_start..row1_end).zip(row2_start..row2_end).enumerate() { let mut v = self.words[i] & self.words[j]; for bit in 0..WORD_BITS { if v == 0 { @@ -734,6 +755,7 @@ impl BitMatrix { /// `write` can reach everything that `read` can (and /// potentially more). pub fn union_rows(&mut self, read: R, write: R) -> bool { + assert!(read.index() < self.num_rows && write.index() < self.num_rows); let (read_start, read_end) = self.range(read); let (write_start, write_end) = self.range(write); let words = &mut self.words[..]; @@ -750,6 +772,7 @@ impl BitMatrix { /// Iterates through all the columns set to true in a given row of /// the matrix. pub fn iter<'a>(&'a self, row: R) -> BitIter<'a, C> { + assert!(row.index() < self.num_rows); let (start, end) = self.range(row); BitIter { cur: None, @@ -865,47 +888,18 @@ impl SparseBitMatrix { } #[inline] -fn num_words(elements: T) -> usize { - (elements.index() + WORD_BITS - 1) / WORD_BITS +fn num_words(domain_size: T) -> usize { + (domain_size.index() + WORD_BITS - 1) / WORD_BITS } #[inline] -fn word_index_and_mask(index: T) -> (usize, Word) { - let index = index.index(); - let word_index = index / WORD_BITS; - let mask = 1 << (index % WORD_BITS); +fn word_index_and_mask(elem: T) -> (usize, Word) { + let elem = elem.index(); + let word_index = elem / WORD_BITS; + let mask = 1 << (elem % WORD_BITS); (word_index, mask) } -#[test] -fn test_clear_above() { - use std::cmp; - - for i in 0..256 { - let mut idx_buf: BitSet = BitSet::new_filled(128); - idx_buf.clear_above(i); - - let elems: Vec = idx_buf.iter().collect(); - let expected: Vec = (0..cmp::min(i, 128)).collect(); - assert_eq!(elems, expected); - } -} - -#[test] -fn test_set_up_to() { - for i in 0..128 { - for mut idx_buf in - vec![BitSet::new_empty(128), BitSet::new_filled(128)].into_iter() - { - idx_buf.set_up_to(i); - - let elems: Vec = idx_buf.iter().collect(); - let expected: Vec = (0..i).collect(); - assert_eq!(elems, expected); - } - } -} - #[test] fn test_new_filled() { for i in 0..128 { @@ -936,7 +930,7 @@ fn bitset_iter_works() { #[test] fn bitset_iter_works_2() { - let mut bitset: BitSet = BitSet::new_empty(319); + let mut bitset: BitSet = BitSet::new_empty(320); bitset.insert(0); bitset.insert(127); bitset.insert(191); @@ -1037,7 +1031,7 @@ fn grow() { assert!(set.insert(index)); assert!(!set.insert(index)); } - set.grow(128); + set.ensure(128); // Check if the bits set before growing are still set for index in 0..65 { diff --git a/src/librustc_mir/dataflow/graphviz.rs b/src/librustc_mir/dataflow/graphviz.rs index 45baab844abc3..1fbeb66be304f 100644 --- a/src/librustc_mir/dataflow/graphviz.rs +++ b/src/librustc_mir/dataflow/graphviz.rs @@ -216,13 +216,12 @@ where MWF: MirWithFlowState<'tcx>, let i = n.index(); let flow = self.mbcx.flow_state(); - let bits_per_block = flow.sets.bits_per_block(); write!(w, "")?; // Entry let set = flow.sets.on_entry_set_for(i); - write!(w, "{:?}", dot::escape_html(&set.to_string(bits_per_block)))?; + write!(w, "{:?}", dot::escape_html(&set.to_string()))?; // Terminator write!(w, "")?; diff --git a/src/librustc_mir/dataflow/impls/mod.rs b/src/librustc_mir/dataflow/impls/mod.rs index 984d1f686d987..efdf9c3302374 100644 --- a/src/librustc_mir/dataflow/impls/mod.rs +++ b/src/librustc_mir/dataflow/impls/mod.rs @@ -354,7 +354,8 @@ impl<'a, 'gcx, 'tcx> BitDenotation for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> // sets on_entry bits for Arg places fn start_block_effect(&self, entry_set: &mut BitSet) { // set all bits to 1 (uninit) before gathering counterevidence - entry_set.set_up_to(self.bits_per_block()); + assert!(self.bits_per_block() == entry_set.domain_size()); + entry_set.insert_all(); drop_flag_effects_for_function_entry( self.tcx, self.mir, self.mdpe, diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index d50ef2242faee..ea01d17ac1341 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -256,7 +256,7 @@ impl<'tcx> InliningMap<'tcx> { let new_items_count_total = new_items_count + self.targets.len(); self.targets.reserve(new_items_count); - self.inlines.grow(new_items_count_total); + self.inlines.ensure(new_items_count_total); for (i, (target, inline)) in new_targets.enumerate() { self.targets.push(target); diff --git a/src/libserialize/serialize.rs b/src/libserialize/serialize.rs index f0b49c3d9bc8f..6f1652cdee06f 100644 --- a/src/libserialize/serialize.rs +++ b/src/libserialize/serialize.rs @@ -16,6 +16,7 @@ Core encoding and decoding interfaces. use std::borrow::Cow; use std::intrinsics; +use std::marker::PhantomData; use std::path; use std::rc::Rc; use std::cell::{Cell, RefCell}; @@ -547,6 +548,19 @@ impl Decodable for () { } } +impl Encodable for PhantomData { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_unit() + } +} + +impl Decodable for PhantomData { + fn decode(d: &mut D) -> Result, D::Error> { + d.read_nil()?; + Ok(PhantomData) + } +} + impl<'a, T: ?Sized + Encodable> Encodable for &'a T { fn encode(&self, s: &mut S) -> Result<(), S::Error> { (**self).encode(s) From 93321671caf0bf04613db660bfa6228579fb2bbe Mon Sep 17 00:00:00 2001 From: QuietMisdreavus Date: Wed, 19 Sep 2018 18:39:39 -0500 Subject: [PATCH 06/21] add -Zui-testing to rustdoc --- src/librustdoc/core.rs | 11 +++-- src/librustdoc/lib.rs | 12 +++-- .../deny-intra-link-resolution-failure.stderr | 4 +- src/test/rustdoc-ui/deprecated-attrs.stderr | 10 ++--- .../rustdoc-ui/intra-doc-alias-ice.stderr | 4 +- .../rustdoc-ui/intra-links-warning.stderr | 44 +++++++++---------- 6 files changed, 48 insertions(+), 37 deletions(-) diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 2feeecb388f38..004db37bbfe48 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -263,6 +263,7 @@ impl DocAccessLevels for AccessLevels { pub fn new_handler(error_format: ErrorOutputType, source_map: Option>, treat_err_as_bug: bool, + ui_testing: bool, ) -> errors::Handler { // rustdoc doesn't override (or allow to override) anything from this that is relevant here, so // stick to the defaults @@ -274,7 +275,7 @@ pub fn new_handler(error_format: ErrorOutputType, source_map.map(|cm| cm as _), false, sessopts.debugging_opts.teach, - ).ui_testing(sessopts.debugging_opts.ui_testing) + ).ui_testing(ui_testing) ), ErrorOutputType::Json(pretty) => { let source_map = source_map.unwrap_or_else( @@ -284,7 +285,7 @@ pub fn new_handler(error_format: ErrorOutputType, None, source_map, pretty, - ).ui_testing(sessopts.debugging_opts.ui_testing) + ).ui_testing(ui_testing) ) }, ErrorOutputType::Short(color_config) => Box::new( @@ -326,6 +327,7 @@ pub fn run_core(search_paths: SearchPaths, mut manual_passes: Vec, mut default_passes: passes::DefaultPassOption, treat_err_as_bug: bool, + ui_testing: bool, ) -> (clean::Crate, RenderInfo, Vec) { // Parse, resolve, and typecheck the given crate. @@ -380,6 +382,8 @@ pub fn run_core(search_paths: SearchPaths, actually_rustdoc: true, debugging_opts: config::DebuggingOptions { force_unstable_if_unmarked, + treat_err_as_bug, + ui_testing, ..config::basic_debugging_options() }, error_format, @@ -391,7 +395,8 @@ pub fn run_core(search_paths: SearchPaths, let source_map = Lrc::new(source_map::SourceMap::new(sessopts.file_path_mapping())); let diagnostic_handler = new_handler(error_format, Some(source_map.clone()), - treat_err_as_bug); + treat_err_as_bug, + ui_testing); let mut sess = session::build_session_( sessopts, cpath, diagnostic_handler, source_map, diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 5f373a635dd7a..bbb1b69c3fd47 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -407,8 +407,11 @@ fn main_args(args: &[String]) -> isize { let treat_err_as_bug = matches.opt_strs("Z").iter().any(|x| { *x == "treat-err-as-bug" }); + let ui_testing = matches.opt_strs("Z").iter().any(|x| { + *x == "ui-testing" + }); - let diag = core::new_handler(error_format, None, treat_err_as_bug); + let diag = core::new_handler(error_format, None, treat_err_as_bug, ui_testing); // check for deprecated options check_deprecated_options(&matches, &diag); @@ -563,7 +566,7 @@ fn main_args(args: &[String]) -> isize { let res = acquire_input(PathBuf::from(input), externs, edition, cg, &matches, error_format, move |out| { let Output { krate, passes, renderinfo } = out; - let diag = core::new_handler(error_format, None, treat_err_as_bug); + let diag = core::new_handler(error_format, None, treat_err_as_bug, ui_testing); info!("going to format"); match output_format.as_ref().map(|s| &**s) { Some("html") | None => { @@ -700,6 +703,9 @@ where R: 'static + Send, let treat_err_as_bug = matches.opt_strs("Z").iter().any(|x| { *x == "treat-err-as-bug" }); + let ui_testing = matches.opt_strs("Z").iter().any(|x| { + *x == "ui-testing" + }); let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format); @@ -713,7 +719,7 @@ where R: 'static + Send, display_warnings, crate_name.clone(), force_unstable_if_unmarked, edition, cg, error_format, lint_opts, lint_cap, describe_lints, manual_passes, default_passes, - treat_err_as_bug); + treat_err_as_bug, ui_testing); info!("finished with rustc"); diff --git a/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr b/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr index 66ee48ed4c9c0..b82cbc1ab36fd 100644 --- a/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr +++ b/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr @@ -1,13 +1,13 @@ error: `[v2]` cannot be resolved, ignoring it... --> $DIR/deny-intra-link-resolution-failure.rs:13:6 | -13 | /// [v2] //~ ERROR +LL | /// [v2] //~ ERROR | ^^ cannot be resolved, ignoring | note: lint level defined here --> $DIR/deny-intra-link-resolution-failure.rs:11:9 | -11 | #![deny(intra_doc_link_resolution_failure)] +LL | #![deny(intra_doc_link_resolution_failure)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: to escape `[` and `]` characters, just add '/' before them like `/[` or `/]` diff --git a/src/test/rustdoc-ui/deprecated-attrs.stderr b/src/test/rustdoc-ui/deprecated-attrs.stderr index 77ba4b2a2b67e..5bd62d60b48f3 100644 --- a/src/test/rustdoc-ui/deprecated-attrs.stderr +++ b/src/test/rustdoc-ui/deprecated-attrs.stderr @@ -1,9 +1,9 @@ warning: the `#![doc(no_default_passes)]` attribute is considered deprecated - | - = warning: please see https://github.com/rust-lang/rust/issues/44136 - = help: you may want to use `#![doc(document_private_items)]` + | + = warning: please see https://github.com/rust-lang/rust/issues/44136 + = help: you may want to use `#![doc(document_private_items)]` warning: the `#![doc(passes = "...")]` attribute is considered deprecated - | - = warning: please see https://github.com/rust-lang/rust/issues/44136 + | + = warning: please see https://github.com/rust-lang/rust/issues/44136 diff --git a/src/test/rustdoc-ui/intra-doc-alias-ice.stderr b/src/test/rustdoc-ui/intra-doc-alias-ice.stderr index 231963976ea75..498d02a7d1caf 100644 --- a/src/test/rustdoc-ui/intra-doc-alias-ice.stderr +++ b/src/test/rustdoc-ui/intra-doc-alias-ice.stderr @@ -1,13 +1,13 @@ error: `[TypeAlias::hoge]` cannot be resolved, ignoring it... --> $DIR/intra-doc-alias-ice.rs:15:30 | -15 | /// [broken cross-reference](TypeAlias::hoge) //~ ERROR +LL | /// [broken cross-reference](TypeAlias::hoge) //~ ERROR | ^^^^^^^^^^^^^^^ cannot be resolved, ignoring | note: lint level defined here --> $DIR/intra-doc-alias-ice.rs:11:9 | -11 | #![deny(intra_doc_link_resolution_failure)] +LL | #![deny(intra_doc_link_resolution_failure)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: to escape `[` and `]` characters, just add '/' before them like `/[` or `/]` diff --git a/src/test/rustdoc-ui/intra-links-warning.stderr b/src/test/rustdoc-ui/intra-links-warning.stderr index 2a51e94b1f5d7..c05f99fadc9e4 100644 --- a/src/test/rustdoc-ui/intra-links-warning.stderr +++ b/src/test/rustdoc-ui/intra-links-warning.stderr @@ -1,7 +1,7 @@ warning: `[Foo::baz]` cannot be resolved, ignoring it... --> $DIR/intra-links-warning.rs:13:23 | -13 | //! Test with [Foo::baz], [Bar::foo], ... +LL | //! Test with [Foo::baz], [Bar::foo], ... | ^^^^^^^^ cannot be resolved, ignoring | = note: #[warn(intra_doc_link_resolution_failure)] on by default @@ -10,7 +10,7 @@ warning: `[Foo::baz]` cannot be resolved, ignoring it... warning: `[Bar::foo]` cannot be resolved, ignoring it... --> $DIR/intra-links-warning.rs:13:35 | -13 | //! Test with [Foo::baz], [Bar::foo], ... +LL | //! Test with [Foo::baz], [Bar::foo], ... | ^^^^^^^^ cannot be resolved, ignoring | = help: to escape `[` and `]` characters, just add '/' before them like `/[` or `/]` @@ -18,7 +18,7 @@ warning: `[Bar::foo]` cannot be resolved, ignoring it... warning: `[Uniooon::X]` cannot be resolved, ignoring it... --> $DIR/intra-links-warning.rs:14:13 | -14 | //! , [Uniooon::X] and [Qux::Z]. +LL | //! , [Uniooon::X] and [Qux::Z]. | ^^^^^^^^^^ cannot be resolved, ignoring | = help: to escape `[` and `]` characters, just add '/' before them like `/[` or `/]` @@ -26,7 +26,7 @@ warning: `[Uniooon::X]` cannot be resolved, ignoring it... warning: `[Qux::Z]` cannot be resolved, ignoring it... --> $DIR/intra-links-warning.rs:14:30 | -14 | //! , [Uniooon::X] and [Qux::Z]. +LL | //! , [Uniooon::X] and [Qux::Z]. | ^^^^^^ cannot be resolved, ignoring | = help: to escape `[` and `]` characters, just add '/' before them like `/[` or `/]` @@ -34,7 +34,7 @@ warning: `[Qux::Z]` cannot be resolved, ignoring it... warning: `[Uniooon::X]` cannot be resolved, ignoring it... --> $DIR/intra-links-warning.rs:16:14 | -16 | //! , [Uniooon::X] and [Qux::Z]. +LL | //! , [Uniooon::X] and [Qux::Z]. | ^^^^^^^^^^ cannot be resolved, ignoring | = help: to escape `[` and `]` characters, just add '/' before them like `/[` or `/]` @@ -42,7 +42,7 @@ warning: `[Uniooon::X]` cannot be resolved, ignoring it... warning: `[Qux::Z]` cannot be resolved, ignoring it... --> $DIR/intra-links-warning.rs:16:31 | -16 | //! , [Uniooon::X] and [Qux::Z]. +LL | //! , [Uniooon::X] and [Qux::Z]. | ^^^^^^ cannot be resolved, ignoring | = help: to escape `[` and `]` characters, just add '/' before them like `/[` or `/]` @@ -50,7 +50,7 @@ warning: `[Qux::Z]` cannot be resolved, ignoring it... warning: `[Qux:Y]` cannot be resolved, ignoring it... --> $DIR/intra-links-warning.rs:18:13 | -18 | /// [Qux:Y] +LL | /// [Qux:Y] | ^^^^^ cannot be resolved, ignoring | = help: to escape `[` and `]` characters, just add '/' before them like `/[` or `/]` @@ -58,7 +58,7 @@ warning: `[Qux:Y]` cannot be resolved, ignoring it... warning: `[BarA]` cannot be resolved, ignoring it... --> $DIR/intra-links-warning.rs:24:10 | -24 | /// bar [BarA] bar +LL | /// bar [BarA] bar | ^^^^ cannot be resolved, ignoring | = help: to escape `[` and `]` characters, just add '/' before them like `/[` or `/]` @@ -66,11 +66,11 @@ warning: `[BarA]` cannot be resolved, ignoring it... warning: `[BarB]` cannot be resolved, ignoring it... --> $DIR/intra-links-warning.rs:28:1 | -28 | / /** -29 | | * Foo -30 | | * bar [BarB] bar -31 | | * baz -32 | | */ +LL | / /** +LL | | * Foo +LL | | * bar [BarB] bar +LL | | * baz +LL | | */ | |___^ | = note: the link appears in this line: @@ -82,13 +82,13 @@ warning: `[BarB]` cannot be resolved, ignoring it... warning: `[BarC]` cannot be resolved, ignoring it... --> $DIR/intra-links-warning.rs:35:1 | -35 | / /** Foo -36 | | -37 | | bar [BarC] bar -38 | | baz +LL | / /** Foo +LL | | +LL | | bar [BarC] bar +LL | | baz ... | -44 | | -45 | | */ +LL | | +LL | | */ | |__^ | = note: the link appears in this line: @@ -100,7 +100,7 @@ warning: `[BarC]` cannot be resolved, ignoring it... warning: `[BarD]` cannot be resolved, ignoring it... --> $DIR/intra-links-warning.rs:48:1 | -48 | #[doc = "Foo/nbar [BarD] bar/nbaz"] +LL | #[doc = "Foo/nbar [BarD] bar/nbaz"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the link appears in this line: @@ -112,10 +112,10 @@ warning: `[BarD]` cannot be resolved, ignoring it... warning: `[BarF]` cannot be resolved, ignoring it... --> $DIR/intra-links-warning.rs:53:9 | -53 | #[doc = $f] +LL | #[doc = $f] | ^^^^^^^^^^^ ... -57 | f!("Foo/nbar [BarF] bar/nbaz"); +LL | f!("Foo/nbar [BarF] bar/nbaz"); | ------------------------------- in this macro invocation | = note: the link appears in this line: From adc2c04543b9bb18766688e55e23082b0d9ac66d Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 19 Sep 2018 19:22:21 -0700 Subject: [PATCH 07/21] Make 'proc_macro::MultiSpan' public. --- src/libproc_macro/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index 1a0dde3ccd7c3..d885f9b406f59 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -48,7 +48,7 @@ pub mod rustc; mod diagnostic; #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] -pub use diagnostic::{Diagnostic, Level}; +pub use diagnostic::{Diagnostic, Level, MultiSpan}; use std::{ascii, fmt, iter}; use std::path::PathBuf; From b3ffd3344e3ac5e2468c7255c2bbecac13daab82 Mon Sep 17 00:00:00 2001 From: Jack O'Connor Date: Thu, 23 Aug 2018 18:22:53 -0400 Subject: [PATCH 08/21] define copy_within on slices This is a safe wrapper around ptr::copy, for regions within a single slice. Previously, safe in-place copying was only available as a side effect of Vec::drain. --- src/libcore/slice/mod.rs | 57 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index da4a56cfecd6e..e4ac79a26d860 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -1618,6 +1618,63 @@ impl [T] { } } + /// Copies elements from one part of the slice to another part of itself, + /// using a memmove. + /// + /// `src` is the range within `self` to copy from. `dest` is the starting + /// index of the range within `self` to copy to, which will have the same + /// length as `src`. The two ranges may overlap. The ends of the two ranges + /// must be less than or equal to `self.len()`. + /// + /// # Panics + /// + /// This function will panic if either range exceeds the end of the slice, + /// or if the end of `src` is before the start. + /// + /// # Examples + /// + /// Copying four bytes within a slice: + /// + /// ``` + /// # #![feature(copy_within)] + /// let mut bytes = *b"Hello, World!"; + /// + /// bytes.copy_within(1..5, 8); + /// + /// assert_eq!(&bytes, b"Hello, Wello!"); + /// ``` + #[unstable(feature = "copy_within", issue = "54236")] + pub fn copy_within>(&mut self, src: R, dest: usize) + where + T: Copy, + { + let src_start = match src.start_bound() { + ops::Bound::Included(&n) => n, + ops::Bound::Excluded(&n) => n + .checked_add(1) + .unwrap_or_else(|| slice_index_overflow_fail()), + ops::Bound::Unbounded => 0, + }; + let src_end = match src.end_bound() { + ops::Bound::Included(&n) => n + .checked_add(1) + .unwrap_or_else(|| slice_index_overflow_fail()), + ops::Bound::Excluded(&n) => n, + ops::Bound::Unbounded => self.len(), + }; + assert!(src_start <= src_end, "src end is before src start"); + assert!(src_end <= self.len(), "src is out of bounds"); + let count = src_end - src_start; + assert!(dest <= self.len() - count, "dest is out of bounds"); + unsafe { + ptr::copy( + self.get_unchecked(src_start), + self.get_unchecked_mut(dest), + count, + ); + } + } + /// Swaps all elements in `self` with those in `other`. /// /// The length of `other` must be the same as `self`. From d0e59f563d11a0d8efbe9a59e7b20526bc42adce Mon Sep 17 00:00:00 2001 From: Jack O'Connor Date: Wed, 19 Sep 2018 12:03:16 -0400 Subject: [PATCH 09/21] add tests for copy_within --- src/libcore/tests/lib.rs | 1 + src/libcore/tests/slice.rs | 46 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 4f3086575c0cf..8fc32f40b9920 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -39,6 +39,7 @@ #![feature(inner_deref)] #![feature(slice_internals)] #![feature(option_replace)] +#![feature(copy_within)] extern crate core; extern crate test; diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs index 012dc9bf5e0b9..d46a35ab82cfc 100644 --- a/src/libcore/tests/slice.rs +++ b/src/libcore/tests/slice.rs @@ -1000,3 +1000,49 @@ fn test_align_to_empty_mid() { assert_eq!(mid.as_ptr() as usize % mem::align_of::(), 0); } } + +#[test] +fn test_copy_within() { + // Start to end, with a RangeTo. + let mut bytes = *b"Hello, World!"; + bytes.copy_within(..3, 10); + assert_eq!(&bytes, b"Hello, WorHel"); + + // End to start, with a RangeFrom. + let mut bytes = *b"Hello, World!"; + bytes.copy_within(10.., 0); + assert_eq!(&bytes, b"ld!lo, World!"); + + // Overlapping, with a RangeInclusive. + let mut bytes = *b"Hello, World!"; + bytes.copy_within(0..=11, 1); + assert_eq!(&bytes, b"HHello, World"); + + // Whole slice, with a RangeFull. + let mut bytes = *b"Hello, World!"; + bytes.copy_within(.., 0); + assert_eq!(&bytes, b"Hello, World!"); +} + +#[test] +#[should_panic(expected = "src is out of bounds")] +fn test_copy_within_panics_src_too_long() { + let mut bytes = *b"Hello, World!"; + // The length is only 13, so 14 is out of bounds. + bytes.copy_within(10..14, 0); +} + +#[test] +#[should_panic(expected = "dest is out of bounds")] +fn test_copy_within_panics_dest_too_long() { + let mut bytes = *b"Hello, World!"; + // The length is only 13, so a slice of length 4 starting at index 10 is out of bounds. + bytes.copy_within(0..4, 10); +} +#[test] +#[should_panic(expected = "src end is before src start")] +fn test_copy_within_panics_src_inverted() { + let mut bytes = *b"Hello, World!"; + // 2 is greater than 1, so this range is invalid. + bytes.copy_within(2..1, 0); +} From ac8d8d71fac1225a35d4895f1f97dd14bc6a4ea2 Mon Sep 17 00:00:00 2001 From: Philip Munksgaard Date: Tue, 18 Sep 2018 11:44:28 +0200 Subject: [PATCH 10/21] Add test for doctest edition support --- .../src/language-features/try-blocks.md | 4 +- src/test/rustdoc/edition-doctest.rs | 54 +++++++++++++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 src/test/rustdoc/edition-doctest.rs diff --git a/src/doc/unstable-book/src/language-features/try-blocks.md b/src/doc/unstable-book/src/language-features/try-blocks.md index 866b37a39a781..e342c260a739b 100644 --- a/src/doc/unstable-book/src/language-features/try-blocks.md +++ b/src/doc/unstable-book/src/language-features/try-blocks.md @@ -9,9 +9,7 @@ The tracking issue for this feature is: [#31436] The `try_blocks` feature adds support for `try` blocks. A `try` block creates a new scope one can use the `?` operator in. -```rust,ignore -// This code needs the 2018 edition - +```rust,edition2018 #![feature(try_blocks)] use std::num::ParseIntError; diff --git a/src/test/rustdoc/edition-doctest.rs b/src/test/rustdoc/edition-doctest.rs new file mode 100644 index 0000000000000..322d461f854e9 --- /dev/null +++ b/src/test/rustdoc/edition-doctest.rs @@ -0,0 +1,54 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:--test + +/// ```rust,edition2018 +/// #![feature(try_blocks)] +/// +/// use std::num::ParseIntError; +/// +/// let result: Result = try { +/// "1".parse::()? +/// + "2".parse::()? +/// + "3".parse::()? +/// }; +/// assert_eq!(result, Ok(6)); +/// +/// let result: Result = try { +/// "1".parse::()? +/// + "foo".parse::()? +/// + "3".parse::()? +/// }; +/// assert!(result.is_err()); +/// ``` + + +/// ```rust,edition2015,compile_fail,E0574 +/// #![feature(try_blocks)] +/// +/// use std::num::ParseIntError; +/// +/// let result: Result = try { +/// "1".parse::()? +/// + "2".parse::()? +/// + "3".parse::()? +/// }; +/// assert_eq!(result, Ok(6)); +/// +/// let result: Result = try { +/// "1".parse::()? +/// + "foo".parse::()? +/// + "3".parse::()? +/// }; +/// assert!(result.is_err()); +/// ``` + +pub fn foo() {} From b01e0e43d4c9d301226c168d131f26f55263f71b Mon Sep 17 00:00:00 2001 From: Philip Munksgaard Date: Wed, 19 Sep 2018 11:27:36 +0200 Subject: [PATCH 11/21] Add a documentation banner for edition specific code --- src/librustdoc/html/markdown.rs | 57 +++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 173db236f7cd5..d14275aeb6bf5 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -171,6 +171,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'a, I> { let event = self.inner.next(); let compile_fail; let ignore; + let edition; if let Some(Event::Start(Tag::CodeBlock(lang))) = event { let parse_result = LangString::parse(&lang, self.check_error_codes); if !parse_result.rust { @@ -178,6 +179,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'a, I> { } compile_fail = parse_result.compile_fail; ignore = parse_result.ignore; + edition = parse_result.edition; } else { return event; } @@ -213,6 +215,17 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'a, I> { } else { "" }; + + let edition_string = if let Some(e @ Edition::Edition2018) = edition { + format!("&edition={}{}", e, + if channel == "&version=nightly" { "" } + else { "&version=nightly" }) + } else if let Some(e) = edition { + format!("&edition={}", e) + } else { + "".to_owned() + }; + // These characters don't need to be escaped in a URI. // FIXME: use a library function for percent encoding. fn dont_escape(c: u8) -> bool { @@ -232,26 +245,44 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'a, I> { } } Some(format!( - r#"Run"#, - url, test_escaped, channel + r#"Run"#, + url, test_escaped, channel, edition_string )) }); + let tooltip = if ignore { - Some(("This example is not tested", "ignore")) + Some(("This example is not tested".to_owned(), "ignore")) } else if compile_fail { - Some(("This example deliberately fails to compile", "compile_fail")) + Some(("This example deliberately fails to compile".to_owned(), "compile_fail")) + } else if let Some(e) = edition { + Some((format!("This code runs with edition {}", e), "edition")) } else { None }; - s.push_str(&highlight::render_with_highlighting( - &text, - Some(&format!("rust-example-rendered{}", - if ignore { " ignore" } - else if compile_fail { " compile_fail" } - else { "" })), - playground_button.as_ref().map(String::as_str), - tooltip)); - Some(Event::Html(s.into())) + + if let Some((s1, s2)) = tooltip { + s.push_str(&highlight::render_with_highlighting( + &text, + Some(&format!("rust-example-rendered{}", + if ignore { " ignore" } + else if compile_fail { " compile_fail" } + else if edition.is_some() { " edition " } + else { "" })), + playground_button.as_ref().map(String::as_str), + Some((s1.as_str(), s2)))); + Some(Event::Html(s.into())) + } else { + s.push_str(&highlight::render_with_highlighting( + &text, + Some(&format!("rust-example-rendered{}", + if ignore { " ignore" } + else if compile_fail { " compile_fail" } + else if edition.is_some() { " edition " } + else { "" })), + playground_button.as_ref().map(String::as_str), + None)); + Some(Event::Html(s.into())) + } }) } } From 06b197582e1974e925925d28196b1ed867208ebf Mon Sep 17 00:00:00 2001 From: Philip Munksgaard Date: Wed, 19 Sep 2018 16:35:25 +0200 Subject: [PATCH 12/21] Add documentation about the edition flag --- src/doc/rustdoc/src/documentation-tests.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/doc/rustdoc/src/documentation-tests.md b/src/doc/rustdoc/src/documentation-tests.md index e4af122d0cb98..dd8dcb7ff9bd2 100644 --- a/src/doc/rustdoc/src/documentation-tests.md +++ b/src/doc/rustdoc/src/documentation-tests.md @@ -323,6 +323,22 @@ compiles, then the test will fail. However please note that code failing with the current Rust release may work in a future release, as new features are added. +```text +/// Only runs on the 2018 edition. +/// +/// ```edition2018 +/// let result: Result = try { +/// "1".parse::()? +/// + "2".parse::()? +/// + "3".parse::()? +/// }; +/// ``` +``` + +`edition2018` tells `rustdoc` that the code sample should be compiled the 2018 +edition of Rust. Similarly, you can specify `edition2015` to compile the code +with the 2015 edition. + ## Syntax reference The *exact* syntax for code blocks, including the edge cases, can be found From 36d562ff2305242d528d0d51b666e4a15e6bf811 Mon Sep 17 00:00:00 2001 From: bgermann Date: Thu, 20 Sep 2018 20:00:02 +0200 Subject: [PATCH 13/21] Use no_default_libraries for all NetBSD flavors The no_default_libraries was introduced in #28578 because the NetBSD-based rumprun needed to disable the link flag. This moves the definition to be used by all NetBSD linker flavors to close #49627. A different solution would be adding -lc but as there is no platform with explicit -lc, this approach is used. --- src/librustc_target/spec/netbsd_base.rs | 1 + src/librustc_target/spec/x86_64_rumprun_netbsd.rs | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_target/spec/netbsd_base.rs b/src/librustc_target/spec/netbsd_base.rs index 8b6bb5dec9138..8cb5a33cdb5f8 100644 --- a/src/librustc_target/spec/netbsd_base.rs +++ b/src/librustc_target/spec/netbsd_base.rs @@ -29,6 +29,7 @@ pub fn opts() -> TargetOptions { executables: true, target_family: Some("unix".to_string()), linker_is_gnu: true, + no_default_libraries: false, has_rpath: true, pre_link_args: args, position_independent_executables: true, diff --git a/src/librustc_target/spec/x86_64_rumprun_netbsd.rs b/src/librustc_target/spec/x86_64_rumprun_netbsd.rs index af846653af727..684bf5a6c1026 100644 --- a/src/librustc_target/spec/x86_64_rumprun_netbsd.rs +++ b/src/librustc_target/spec/x86_64_rumprun_netbsd.rs @@ -21,7 +21,6 @@ pub fn target() -> TargetResult { base.has_rpath = false; base.position_independent_executables = false; base.disable_redzone = true; - base.no_default_libraries = false; base.exe_allocation_crate = None; base.stack_probes = true; From d341b177ae47305d2204efe6e16654e4f45892c3 Mon Sep 17 00:00:00 2001 From: memoryruins Date: Thu, 20 Sep 2018 21:14:05 -0400 Subject: [PATCH 14/21] Add test for deref recursion limit printing twice --- src/test/ui/issues/issue-38940.rs | 51 +++++++++++++++++++++++++++ src/test/ui/issues/issue-38940.stderr | 21 +++++++++++ 2 files changed, 72 insertions(+) create mode 100644 src/test/ui/issues/issue-38940.rs create mode 100644 src/test/ui/issues/issue-38940.stderr diff --git a/src/test/ui/issues/issue-38940.rs b/src/test/ui/issues/issue-38940.rs new file mode 100644 index 0000000000000..22d7d1cd916f6 --- /dev/null +++ b/src/test/ui/issues/issue-38940.rs @@ -0,0 +1,51 @@ +// issue-38940: error printed twice for deref recursion limit exceeded +// Test that the recursion limit can be changed. In this case, we have +// deeply nested types that will fail the `Send` check by overflow +// when the recursion limit is set very low. +#![allow(dead_code)] +#![recursion_limit="10"] +macro_rules! link { + ($outer:ident, $inner:ident) => { + struct $outer($inner); + impl $outer { + fn new() -> $outer { + $outer($inner::new()) + } + } + impl std::ops::Deref for $outer { + type Target = $inner; + fn deref(&self) -> &$inner { + &self.0 + } + } + } +} +struct Bottom; +impl Bottom { + fn new() -> Bottom { + Bottom + } +} +link!(Top, A); +link!(A, B); +link!(B, C); +link!(C, D); +link!(D, E); +link!(E, F); +link!(F, G); +link!(G, H); +link!(H, I); +link!(I, J); +link!(J, K); +link!(K, Bottom); +fn main() { + let t = Top::new(); + let x: &Bottom = &t; + //~^ ERROR mismatched types + //~| NOTE expected type `&Bottom` + //~| NOTE found type `&Top` + //~| NOTE expected struct `Bottom`, found struct `Top` + //~| ERROR reached the recursion limit while auto-dereferencing I + //~| NOTE deref recursion limit reached + //~| NOTE consider adding a `#![recursion_limit="20"]` attribute to your crate +} diff --git a/src/test/ui/issues/issue-38940.stderr b/src/test/ui/issues/issue-38940.stderr new file mode 100644 index 0000000000000..2d3cfda9a5f72 --- /dev/null +++ b/src/test/ui/issues/issue-38940.stderr @@ -0,0 +1,21 @@ +error[E0055]: reached the recursion limit while auto-dereferencing I + --> $DIR/issue-38940.rs:43:22 + | +LL | let x: &Bottom = &t; + | ^^ deref recursion limit reached + | + = help: consider adding a `#![recursion_limit="20"]` attribute to your crate + +error[E0308]: mismatched types + --> $DIR/issue-38940.rs:43:22 + | +LL | let x: &Bottom = &t; + | ^^ expected struct `Bottom`, found struct `Top` + | + = note: expected type `&Bottom` + found type `&Top` + +error: aborting due to 2 previous errors + +Some errors occurred: E0055, E0308. +For more information about an error, try `rustc --explain E0055`. From 9784d3543f524f142aae52da9d2a6ce1eb9d702a Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 21 Sep 2018 04:26:36 +0300 Subject: [PATCH 15/21] parser: Tweak function parameter parsing to avoid rollback on succesfull path --- src/libsyntax/parse/parser.rs | 62 +++++++++++++++-------------------- 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 6ec1ad969ee70..963a7206bd61e 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1780,27 +1780,32 @@ impl<'a> Parser<'a> { (pat, self.parse_ty()?) } else { debug!("parse_arg_general ident_to_pat"); + let parser_snapshot_before_ty = self.clone(); + let mut ty = self.parse_ty(); + if ty.is_ok() && self.token == token::Colon { + // This wasn't actually a type, but a pattern looking like a type, + // so we are going to rollback and re-parse for recovery. + ty = self.unexpected(); + } + match ty { + Ok(ty) => { + let ident = Ident::new(keywords::Invalid.name(), self.prev_span); + let pat = P(Pat { + id: ast::DUMMY_NODE_ID, + node: PatKind::Ident( + BindingMode::ByValue(Mutability::Immutable), ident, None), + span: ty.span, + }); + (pat, ty) + } + Err(mut err) => { + // Recover from attempting to parse the argument as a type without pattern. + err.cancel(); + mem::replace(self, parser_snapshot_before_ty); + let pat = self.parse_pat()?; + self.expect(&token::Colon)?; + let ty = self.parse_ty()?; - let parser_snapshot_before_pat = self.clone(); - - // Once we can use edition 2018 in the compiler, - // replace this with real try blocks. - macro_rules! try_block { - ($($inside:tt)*) => ( - (||{ ::std::ops::Try::from_ok({ $($inside)* }) })() - ) - } - - // We're going to try parsing the argument as a pattern (even though it's not - // allowed). This way we can provide better errors to the user. - let pat_arg: PResult<'a, _> = try_block! { - let pat = self.parse_pat()?; - self.expect(&token::Colon)?; - (pat, self.parse_ty()?) - }; - - match pat_arg { - Ok((pat, ty)) => { let mut err = self.diagnostic().struct_span_err_with_code( pat.span, "patterns aren't allowed in methods without bodies", @@ -1813,6 +1818,7 @@ impl<'a> Parser<'a> { Applicability::MachineApplicable, ); err.emit(); + // Pretend the pattern is `_`, to avoid duplicate errors from AST validation. let pat = P(Pat { node: PatKind::Wild, @@ -1821,22 +1827,6 @@ impl<'a> Parser<'a> { }); (pat, ty) } - Err(mut err) => { - err.cancel(); - // Recover from attempting to parse the argument as a pattern. This means - // the type is alone, with no name, e.g. `fn foo(u32)`. - mem::replace(self, parser_snapshot_before_pat); - debug!("parse_arg_general ident_to_pat"); - let ident = Ident::new(keywords::Invalid.name(), self.prev_span); - let ty = self.parse_ty()?; - let pat = P(Pat { - id: ast::DUMMY_NODE_ID, - node: PatKind::Ident( - BindingMode::ByValue(Mutability::Immutable), ident, None), - span: ty.span, - }); - (pat, ty) - } } }; From 70da203259360b12d871c043d9eab327034785d6 Mon Sep 17 00:00:00 2001 From: memoryruins Date: Thu, 20 Sep 2018 22:07:57 -0400 Subject: [PATCH 16/21] Remove incidental notes --- src/test/ui/issues/issue-38940.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/test/ui/issues/issue-38940.rs b/src/test/ui/issues/issue-38940.rs index 22d7d1cd916f6..7f9b141e02e3c 100644 --- a/src/test/ui/issues/issue-38940.rs +++ b/src/test/ui/issues/issue-38940.rs @@ -42,10 +42,5 @@ fn main() { let t = Top::new(); let x: &Bottom = &t; //~^ ERROR mismatched types - //~| NOTE expected type `&Bottom` - //~| NOTE found type `&Top` - //~| NOTE expected struct `Bottom`, found struct `Top` //~| ERROR reached the recursion limit while auto-dereferencing I - //~| NOTE deref recursion limit reached - //~| NOTE consider adding a `#![recursion_limit="20"]` attribute to your crate } From 7f9a259d3f05ad49bc7ebec462e6f134da6bb3ed Mon Sep 17 00:00:00 2001 From: Joseph Post Date: Thu, 20 Sep 2018 20:06:03 -0500 Subject: [PATCH 17/21] add applicability to span suggestion call --- .../borrow_check/nll/region_infer/error_reporting/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs index dfed41cb1defd..67e4394c9aaa0 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs @@ -22,6 +22,7 @@ use std::collections::VecDeque; use std::fmt; use syntax::symbol::keywords; use syntax_pos::Span; +use syntax::errors::Applicability; mod region_name; mod var_name; @@ -540,7 +541,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { RegionName::Named(name) => format!("{}", name), RegionName::Synthesized(_) => "'_".to_string(), }; - diag.span_suggestion( + diag.span_suggestion_with_applicability( span, &format!( "to allow this impl Trait to capture borrowed data with lifetime \ @@ -548,6 +549,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { fr_name, suggestable_fr_name, ), format!("{} + {}", snippet, suggestable_fr_name), + Applicability::MachineApplicable, ); } } From 06d577d8b25b5f2e131005a6bcc2e142748016e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 19 Sep 2018 16:23:21 -0700 Subject: [PATCH 18/21] Detect `for _ in in bar {}` typo --- src/libsyntax/parse/parser.rs | 34 ++++++++++++++----- ...sue-54109-and_instead_of_ampersands.stderr | 12 ++++--- src/test/ui/parser/if-in-in.rs | 5 +++ src/test/ui/parser/if-in-in.stderr | 13 +++++++ 4 files changed, 51 insertions(+), 13 deletions(-) create mode 100644 src/test/ui/parser/if-in-in.rs create mode 100644 src/test/ui/parser/if-in-in.stderr diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 48e034b117f18..daf1f0857d9bb 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2702,8 +2702,8 @@ impl<'a> Parser<'a> { token::Literal(token::Float(n), _suf) => { self.bump(); let fstr = n.as_str(); - let mut err = self.diagnostic().struct_span_err(self.prev_span, - &format!("unexpected token: `{}`", n)); + let mut err = self.diagnostic() + .struct_span_err(self.prev_span, &format!("unexpected token: `{}`", n)); err.span_label(self.prev_span, "unexpected token"); if fstr.chars().all(|x| "0123456789.".contains(x)) { let float = match fstr.parse::().ok() { @@ -2863,8 +2863,8 @@ impl<'a> Parser<'a> { let e = self.parse_prefix_expr(None); let (span, e) = self.interpolated_or_expr_span(e)?; let span_of_tilde = lo; - let mut err = self.diagnostic().struct_span_err(span_of_tilde, - "`~` cannot be used as a unary operator"); + let mut err = self.diagnostic() + .struct_span_err(span_of_tilde, "`~` cannot be used as a unary operator"); err.span_suggestion_short_with_applicability( span_of_tilde, "use `!` to perform bitwise negation", @@ -3422,6 +3422,24 @@ impl<'a> Parser<'a> { ); err.emit(); } + let in_span = self.prev_span; + if self.eat_keyword(keywords::In) { + // a common typo: `for _ in in bar {}` + let mut err = self.sess.span_diagnostic.struct_span_err( + self.prev_span, + "expected iterable, found keyword `in`", + ); + err.span_suggestion_short_with_applicability( + in_span.until(self.prev_span), + "remove the duplicated `in`", + String::new(), + Applicability::MachineApplicable, + ); + err.note("if you meant to use emplacement syntax, it is obsolete (for now, anyway)"); + err.note("for more information on the status of emplacement syntax, see <\ + https://github.com/rust-lang/rust/issues/27779#issuecomment-378416911>"); + err.emit(); + } let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?; let (iattrs, loop_block) = self.parse_inner_attrs_and_block()?; attrs.extend(iattrs); @@ -4760,12 +4778,9 @@ impl<'a> Parser<'a> { if !self.eat(&token::OpenDelim(token::Brace)) { let sp = self.span; let tok = self.this_token_to_string(); - let mut do_not_suggest_help = false; let mut e = self.span_fatal(sp, &format!("expected `{{`, found `{}`", tok)); - if self.token.is_keyword(keywords::In) || self.token == token::Colon { - do_not_suggest_help = true; - e.span_label(sp, "expected `{`"); - } + let do_not_suggest_help = + self.token.is_keyword(keywords::In) || self.token == token::Colon; if self.token.is_ident_named("and") { e.span_suggestion_short_with_applicability( @@ -4796,6 +4811,7 @@ impl<'a> Parser<'a> { || do_not_suggest_help { // if the next token is an open brace (e.g., `if a b {`), the place- // inside-a-block suggestion would be more likely wrong than right + e.span_label(sp, "expected `{`"); return Err(e); } let mut stmt_span = stmt.span; diff --git a/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.stderr b/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.stderr index 22845775aed13..aa54425efa342 100644 --- a/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.stderr +++ b/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.stderr @@ -2,16 +2,20 @@ error: expected `{`, found `and` --> $DIR/issue-54109-and_instead_of_ampersands.rs:14:10 | LL | if a and b { - | -- ^^^ help: use `&&` instead of `and` for the boolean operator - | | + | -- ^^^ + | | | + | | expected `{` + | | help: use `&&` instead of `and` for the boolean operator | this `if` statement has a condition, but no block error: expected `{`, found `or` --> $DIR/issue-54109-and_instead_of_ampersands.rs:23:10 | LL | if a or b { - | -- ^^ help: use `||` instead of `or` for the boolean operator - | | + | -- ^^ + | | | + | | expected `{` + | | help: use `||` instead of `or` for the boolean operator | this `if` statement has a condition, but no block error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `{`, or an operator, found `and` diff --git a/src/test/ui/parser/if-in-in.rs b/src/test/ui/parser/if-in-in.rs new file mode 100644 index 0000000000000..9bc9aaff298db --- /dev/null +++ b/src/test/ui/parser/if-in-in.rs @@ -0,0 +1,5 @@ +fn main() { + for i in in 1..2 { + println!("{}", i); + } +} diff --git a/src/test/ui/parser/if-in-in.stderr b/src/test/ui/parser/if-in-in.stderr new file mode 100644 index 0000000000000..9926fcc0858e5 --- /dev/null +++ b/src/test/ui/parser/if-in-in.stderr @@ -0,0 +1,13 @@ +error: expected iterable, found keyword `in` + --> $DIR/if-in-in.rs:2:14 + | +LL | for i in in 1..2 { + | ---^^ + | | + | help: remove the duplicated `in` + | + = note: if you meant to use emplacement syntax, it is obsolete (for now, anyway) + = note: for more information on the status of emplacement syntax, see + +error: aborting due to previous error + From b2f25e3c38ff29eebe6c8ce69b8c69243faa440d Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 21 Sep 2018 20:42:49 +1000 Subject: [PATCH 19/21] Compress `Liveness` data some more. Profiling shows that the `(reader, writer, used)` triples used by liveness analysis almost always have invalid `reader` and `writer` fields. We can take advantage of this knowledge to use a compressed representation for them, falling back to a secondary table for the uncommon cases. This change reduces instruction counts on numerous benchmarks, the best by 16%. It also reduces max-rss on numerous benchmarks, the best by 38%. The patch also renames these triples from `Users` to `RWU`, because it's confusing having a type whose name is plural and then used within vectors whose names are also plural. --- src/librustc/middle/liveness.rs | 207 +++++++++++++++++++++++--------- 1 file changed, 149 insertions(+), 58 deletions(-) diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 13847fb48cee9..0d70a64123bf3 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -64,10 +64,10 @@ //! methods. It effectively does a reverse walk of the AST; whenever we //! reach a loop node, we iterate until a fixed point is reached. //! -//! ## The `users_*` fields +//! ## The `RWU` struct //! //! At each live node `N`, we track three pieces of information for each -//! variable `V` (these are in the `users_*` fields): +//! variable `V` (these are encapsulated in the `RWU` struct): //! //! - `reader`: the `LiveNode` ID of some node which will read the value //! that `V` holds on entry to `N`. Formally: a node `M` such @@ -536,6 +536,112 @@ fn visit_expr<'a, 'tcx>(ir: &mut IrMaps<'a, 'tcx>, expr: &'tcx Expr) { // Actually we compute just a bit more than just liveness, but we use // the same basic propagation framework in all cases. +#[derive(Clone, Copy)] +struct RWU { + reader: LiveNode, + writer: LiveNode, + used: bool +} + +/// Conceptually, this is like a `Vec`. But the number of `RWU`s can get +/// very large, so it uses a more compact representation that takes advantage +/// of the fact that when the number of `RWU`s is large, most of them have an +/// invalid reader and an invalid writer. +struct RWUTable { + /// Each entry in `packed_rwus` is either INV_INV_FALSE, INV_INV_TRUE, or + /// an index into `unpacked_rwus`. In the common cases, this compacts the + /// 65 bits of data into 32; in the uncommon cases, it expands the 65 bits + /// in 96. + /// + /// More compact representations are possible -- e.g. use only 2 bits per + /// packed `RWU` and make the secondary table a HashMap that maps from + /// indices to `RWU`s -- but this one strikes a good balance between size + /// and speed. + packed_rwus: Vec, + unpacked_rwus: Vec, +} + +// A constant representing `RWU { reader: invalid_node(); writer: invalid_node(); used: false }`. +const INV_INV_FALSE: u32 = u32::MAX; + +// A constant representing `RWU { reader: invalid_node(); writer: invalid_node(); used: true }`. +const INV_INV_TRUE: u32 = u32::MAX - 1; + +impl RWUTable { + fn new(num_rwus: usize) -> RWUTable { + Self { + packed_rwus: vec![INV_INV_FALSE; num_rwus], + unpacked_rwus: vec![], + } + } + + fn get(&self, idx: usize) -> RWU { + let packed_rwu = self.packed_rwus[idx]; + match packed_rwu { + INV_INV_FALSE => RWU { reader: invalid_node(), writer: invalid_node(), used: false }, + INV_INV_TRUE => RWU { reader: invalid_node(), writer: invalid_node(), used: true }, + _ => self.unpacked_rwus[packed_rwu as usize], + } + } + + fn get_reader(&self, idx: usize) -> LiveNode { + let packed_rwu = self.packed_rwus[idx]; + match packed_rwu { + INV_INV_FALSE | INV_INV_TRUE => invalid_node(), + _ => self.unpacked_rwus[packed_rwu as usize].reader, + } + } + + fn get_writer(&self, idx: usize) -> LiveNode { + let packed_rwu = self.packed_rwus[idx]; + match packed_rwu { + INV_INV_FALSE | INV_INV_TRUE => invalid_node(), + _ => self.unpacked_rwus[packed_rwu as usize].writer, + } + } + + fn get_used(&self, idx: usize) -> bool { + let packed_rwu = self.packed_rwus[idx]; + match packed_rwu { + INV_INV_FALSE => false, + INV_INV_TRUE => true, + _ => self.unpacked_rwus[packed_rwu as usize].used, + } + } + + #[inline] + fn copy_packed(&mut self, dst_idx: usize, src_idx: usize) { + self.packed_rwus[dst_idx] = self.packed_rwus[src_idx]; + } + + fn assign_unpacked(&mut self, idx: usize, rwu: RWU) { + if rwu.reader == invalid_node() && rwu.writer == invalid_node() { + // When we overwrite an indexing entry in `self.packed_rwus` with + // `INV_INV_{TRUE,FALSE}` we don't remove the corresponding entry + // from `self.unpacked_rwus`; it's not worth the effort, and we + // can't have entries shifting around anyway. + self.packed_rwus[idx] = if rwu.used { + INV_INV_TRUE + } else { + INV_INV_FALSE + } + } else { + // Add a new RWU to `unpacked_rwus` and make `packed_rwus[idx]` + // point to it. + self.packed_rwus[idx] = self.unpacked_rwus.len() as u32; + self.unpacked_rwus.push(rwu); + } + } + + fn assign_inv_inv(&mut self, idx: usize) { + self.packed_rwus[idx] = if self.get_used(idx) { + INV_INV_TRUE + } else { + INV_INV_FALSE + }; + } +} + #[derive(Copy, Clone)] struct Specials { exit_ln: LiveNode, @@ -552,14 +658,7 @@ struct Liveness<'a, 'tcx: 'a> { tables: &'a ty::TypeckTables<'tcx>, s: Specials, successors: Vec, - - // We used to have a single `users: Vec` field here, where `Users` - // had `reader`, `writer` and `used` fields. But the number of users can - // get very large, and it's more compact to store the data in three - // separate `Vec`s so that no space is wasted for padding. - users_reader: Vec, - users_writer: Vec, - users_used: Vec, + rwu_table: RWUTable, // mappings from loop node ID to LiveNode // ("break" label should map to loop node ID, @@ -584,16 +683,13 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { let num_live_nodes = ir.num_live_nodes; let num_vars = ir.num_vars; - let num_users = num_live_nodes * num_vars; Liveness { ir, tables, s: specials, successors: vec![invalid_node(); num_live_nodes], - users_reader: vec![invalid_node(); num_users], - users_writer: vec![invalid_node(); num_users], - users_used: vec![false; num_users], + rwu_table: RWUTable::new(num_live_nodes * num_vars), break_ln: NodeMap(), cont_ln: NodeMap(), } @@ -657,16 +753,13 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { ln.get() * self.ir.num_vars + var.get() } - fn live_on_entry(&self, ln: LiveNode, var: Variable) - -> Option { + fn live_on_entry(&self, ln: LiveNode, var: Variable) -> Option { assert!(ln.is_valid()); - let reader = self.users_reader[self.idx(ln, var)]; - if reader.is_valid() {Some(self.ir.lnk(reader))} else {None} + let reader = self.rwu_table.get_reader(self.idx(ln, var)); + if reader.is_valid() { Some(self.ir.lnk(reader)) } else { None } } - /* - Is this variable live on entry to any of its successor nodes? - */ + // Is this variable live on entry to any of its successor nodes? fn live_on_exit(&self, ln: LiveNode, var: Variable) -> Option { let successor = self.successors[ln.get()]; @@ -675,14 +768,14 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { fn used_on_entry(&self, ln: LiveNode, var: Variable) -> bool { assert!(ln.is_valid()); - self.users_used[self.idx(ln, var)] + self.rwu_table.get_used(self.idx(ln, var)) } fn assigned_on_entry(&self, ln: LiveNode, var: Variable) -> Option { assert!(ln.is_valid()); - let writer = self.users_writer[self.idx(ln, var)]; - if writer.is_valid() {Some(self.ir.lnk(writer))} else {None} + let writer = self.rwu_table.get_writer(self.idx(ln, var)); + if writer.is_valid() { Some(self.ir.lnk(writer)) } else { None } } fn assigned_on_exit(&self, ln: LiveNode, var: Variable) @@ -725,9 +818,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { { let wr = &mut wr as &mut dyn Write; write!(wr, "[ln({:?}) of kind {:?} reads", ln.get(), self.ir.lnk(ln)); - self.write_vars(wr, ln, |idx| self.users_reader[idx]); + self.write_vars(wr, ln, |idx| self.rwu_table.get_reader(idx)); write!(wr, " writes"); - self.write_vars(wr, ln, |idx| self.users_writer[idx]); + self.write_vars(wr, ln, |idx| self.rwu_table.get_writer(idx)); write!(wr, " precedes {:?}]", self.successors[ln.get()]); } String::from_utf8(wr).unwrap() @@ -736,16 +829,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { fn init_empty(&mut self, ln: LiveNode, succ_ln: LiveNode) { self.successors[ln.get()] = succ_ln; - // It is not necessary to initialize the - // values to empty because this is the value - // they have when they are created, and the sets - // only grow during iterations. - // - // self.indices(ln) { |idx| - // self.users_reader[idx] = invalid_node(); - // self.users_writer[idx] = invalid_node(); - // self.users_used[idx] = false; - // } + // It is not necessary to initialize the RWUs here because they are all + // set to INV_INV_FALSE when they are created, and the sets only grow + // during iterations. } fn init_from_succ(&mut self, ln: LiveNode, succ_ln: LiveNode) { @@ -753,9 +839,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { self.successors[ln.get()] = succ_ln; self.indices2(ln, succ_ln, |this, idx, succ_idx| { - this.users_reader[idx] = this.users_reader[succ_idx]; - this.users_writer[idx] = this.users_writer[succ_idx]; - this.users_used[idx] = this.users_used[succ_idx]; + this.rwu_table.copy_packed(idx, succ_idx); }); debug!("init_from_succ(ln={}, succ={})", self.ln_str(ln), self.ln_str(succ_ln)); @@ -770,26 +854,31 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { let mut changed = false; self.indices2(ln, succ_ln, |this, idx, succ_idx| { - changed |= copy_if_invalid(this.users_reader[succ_idx], &mut this.users_reader[idx]); - changed |= copy_if_invalid(this.users_writer[succ_idx], &mut this.users_writer[idx]); - if this.users_used[succ_idx] && !this.users_used[idx] { - this.users_used[idx] = true; + let mut rwu = this.rwu_table.get(idx); + let succ_rwu = this.rwu_table.get(succ_idx); + if succ_rwu.reader.is_valid() && !rwu.reader.is_valid() { + rwu.reader = succ_rwu.reader; + changed = true + } + + if succ_rwu.writer.is_valid() && !rwu.writer.is_valid() { + rwu.writer = succ_rwu.writer; + changed = true + } + + if succ_rwu.used && !rwu.used { + rwu.used = true; changed = true; } + + if changed { + this.rwu_table.assign_unpacked(idx, rwu); + } }); debug!("merge_from_succ(ln={:?}, succ={}, first_merge={}, changed={})", ln, self.ln_str(succ_ln), first_merge, changed); return changed; - - fn copy_if_invalid(src: LiveNode, dst: &mut LiveNode) -> bool { - if src.is_valid() && !dst.is_valid() { - *dst = src; - true - } else { - false - } - } } // Indicates that a local variable was *defined*; we know that no @@ -797,8 +886,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // this) so we just clear out all the data. fn define(&mut self, writer: LiveNode, var: Variable) { let idx = self.idx(writer, var); - self.users_reader[idx] = invalid_node(); - self.users_writer[idx] = invalid_node(); + self.rwu_table.assign_inv_inv(idx); debug!("{:?} defines {:?} (idx={}): {}", writer, var, idx, self.ln_str(writer)); @@ -810,21 +898,24 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { ln, acc, var, self.ln_str(ln)); let idx = self.idx(ln, var); + let mut rwu = self.rwu_table.get(idx); if (acc & ACC_WRITE) != 0 { - self.users_reader[idx] = invalid_node(); - self.users_writer[idx] = ln; + rwu.reader = invalid_node(); + rwu.writer = ln; } // Important: if we both read/write, must do read second // or else the write will override. if (acc & ACC_READ) != 0 { - self.users_reader[idx] = ln; + rwu.reader = ln; } if (acc & ACC_USE) != 0 { - self.users_used[idx] = true; + rwu.used = true; } + + self.rwu_table.assign_unpacked(idx, rwu); } // _______________________________________________________________________ From 48f46056b7604acd1fb328e41792eb25d1d37163 Mon Sep 17 00:00:00 2001 From: ljedrz Date: Fri, 21 Sep 2018 13:06:44 +0200 Subject: [PATCH 20/21] Simplify slice's first(_mut) and last(_mut) with get --- src/libcore/slice/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index da4a56cfecd6e..59f7130320081 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -119,7 +119,7 @@ impl [T] { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn first(&self) -> Option<&T> { - if self.is_empty() { None } else { Some(&self[0]) } + self.get(0) } /// Returns a mutable pointer to the first element of the slice, or `None` if it is empty. @@ -137,7 +137,7 @@ impl [T] { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn first_mut(&mut self) -> Option<&mut T> { - if self.is_empty() { None } else { Some(&mut self[0]) } + self.get_mut(0) } /// Returns the first and all the rest of the elements of the slice, or `None` if it is empty. @@ -239,7 +239,8 @@ impl [T] { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn last(&self) -> Option<&T> { - if self.is_empty() { None } else { Some(&self[self.len() - 1]) } + let last_idx = self.len().checked_sub(1)?; + self.get(last_idx) } /// Returns a mutable pointer to the last item in the slice. @@ -257,9 +258,8 @@ impl [T] { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn last_mut(&mut self) -> Option<&mut T> { - let len = self.len(); - if len == 0 { return None; } - Some(&mut self[len - 1]) + let last_idx = self.len().checked_sub(1)?; + self.get_mut(last_idx) } /// Returns a reference to an element or subslice depending on the type of From ad9bf1717749da9aa75cfbc199c9bf02946ac89c Mon Sep 17 00:00:00 2001 From: Christian Poveda <31802960+christianpoveda@users.noreply.github.com> Date: Fri, 21 Sep 2018 14:02:45 -0500 Subject: [PATCH 21/21] Update .mailmap --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index 8f4287a438580..f63b299ffd57e 100644 --- a/.mailmap +++ b/.mailmap @@ -52,6 +52,7 @@ Chris C Cerami Chris C Cerami Chris Thorn Chris Thorn Chris Vittal Christopher Vittal +Christian Poveda Clark Gaebel Clinton Ryan Corey Richardson Elaine "See More" Nemo