diff --git a/.travis.yml b/.travis.yml index a776f1b1e1041..537849964ab41 100644 --- a/.travis.yml +++ b/.travis.yml @@ -65,6 +65,20 @@ matrix: osx_image: xcode8.2 install: *osx_install_sccache + # "alternate" deployments, these are "nightlies" but don't have assertions + # turned on, they're deployed to a different location primarily for projects + # which are stuck on nightly and don't want llvm assertions in the artifacts + # that they use. + - env: IMAGE=dist-x86-linux DEPLOY_ALT=1 + - env: > + RUST_CHECK_TARGET=dist + RUST_CONFIGURE_ARGS="--enable-extended" + SRC=. + DEPLOY_ALT=1 + os: osx + osx_image: xcode8.2 + install: *osx_install_sccache + env: global: - SCCACHE_BUCKET=rust-lang-ci-sccache @@ -125,3 +139,19 @@ deploy: on: branch: auto condition: $DEPLOY = 1 + + # this is the same as the above deployment provider except that it uploads to + # a slightly different directory and has a different trigger + - provider: s3 + bucket: rust-lang-ci + skip_cleanup: true + local_dir: deploy + upload_dir: rustc-builds-alt + acl: public_read + region: us-east-1 + access_key_id: AKIAIPQVNYF2T3DTYIWQ + secret_access_key: + secure: "FBqDqOTeIPMu6v/WYPf4CFSlh9rLRZGKVtpLa5KkyuOhXRTrnEzBduEtS8/FMIxdQImvurhSvxWvqRybMOi4qoVfjMqqpHAI7uBbidbrvAcJoHNsx6BgUNVCIoH6a0UsAjTUtm6/YPIpzbHoLZXPL0GrHPMk6Mu04qVSmcYNWn4=" + on: + branch: auto + condition: $DEPLOY_ALT = 1 diff --git a/appveyor.yml b/appveyor.yml index 2183d8da95f89..38781d281c8ec 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -61,6 +61,12 @@ environment: MINGW_DIR: mingw64 DEPLOY: 1 + # "alternate" deployment, see .travis.yml for more info + - MSYS_BITS: 64 + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-extended + SCRIPT: python x.py dist + DEPLOY_ALT: 1 + matrix: fast_finish: true @@ -146,6 +152,22 @@ deploy: branch: auto DEPLOY: 1 + # This provider is the same as the one above except that it has a slightly + # different upload directory and a slightly different trigger + - provider: S3 + skip_cleanup: true + access_key_id: AKIAIPQVNYF2T3DTYIWQ + secret_access_key: + secure: +11jsUNFTQ9dq5Ad1i2+PeUJaXluFJ0zIJAXESE1dFT3Kdjku4/eDdgyjgsB6GnV + bucket: rust-lang-ci + set_public: true + region: us-east-1 + artifact: /.*/ + folder: rustc-builds-alt + on: + branch: auto + DEPLOY_ALT: 1 + # init: # - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) # on_finish: diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh index 0ddab8c4160f4..892c5baa5c64b 100755 --- a/src/ci/docker/run.sh +++ b/src/ci/docker/run.sh @@ -49,6 +49,7 @@ exec docker \ $args \ --env CARGO_HOME=/cargo \ --env DEPLOY=$DEPLOY \ + --env DEPLOY_ALT=$DEPLOY_ALT \ --env LOCAL_USER_ID=`id -u` \ --volume "$HOME/.cargo:/cargo" \ --rm \ diff --git a/src/ci/run.sh b/src/ci/run.sh index 960acc4de7d87..41230aedbfa01 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -30,12 +30,14 @@ RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-manage-submodules" # # FIXME: need a scheme for changing this `nightly` value to `beta` and `stable` # either automatically or manually. -if [ "$DEPLOY" != "" ]; then +if [ "$DEPLOY$DEPLOY_ALT" != "" ]; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --release-channel=nightly" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-llvm-static-stdcpp" if [ "$NO_LLVM_ASSERTIONS" = "1" ]; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-llvm-assertions" + elif [ "$DEPLOY_ALT" != "" ]; then + RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-llvm-assertions" fi else RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-debug-assertions" diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index ac9439974a491..d0fce70612868 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -315,6 +315,14 @@ impl Default for Box<[T]> { } } +#[stable(feature = "default_box_extra", since = "1.17.0")] +impl Default for Box { + fn default() -> Box { + let default: Box<[u8]> = Default::default(); + unsafe { mem::transmute(default) } + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Clone for Box { /// Returns a new box with a `clone()` of this box's contents. diff --git a/src/libcollections/btree/map.rs b/src/libcollections/btree/map.rs index e1fabe2cc496b..7218d15ded5f8 100644 --- a/src/libcollections/btree/map.rs +++ b/src/libcollections/btree/map.rs @@ -714,6 +714,11 @@ impl BTreeMap { /// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive /// range from 4 to 10. /// + /// # Panics + /// + /// Panics if range `start > end`. + /// Panics if range `start == end` and both bounds are `Excluded`. + /// /// # Examples /// /// Basic usage: @@ -739,64 +744,11 @@ impl BTreeMap { pub fn range(&self, range: R) -> Range where T: Ord, K: Borrow, R: RangeArgument { - let min = range.start(); - let max = range.end(); - let front = match min { - Included(key) => { - match search::search_tree(self.root.as_ref(), key) { - Found(kv_handle) => { - match kv_handle.left_edge().force() { - Leaf(bottom) => bottom, - Internal(internal) => last_leaf_edge(internal.descend()), - } - } - GoDown(bottom) => bottom, - } - } - Excluded(key) => { - match search::search_tree(self.root.as_ref(), key) { - Found(kv_handle) => { - match kv_handle.right_edge().force() { - Leaf(bottom) => bottom, - Internal(internal) => first_leaf_edge(internal.descend()), - } - } - GoDown(bottom) => bottom, - } - } - Unbounded => first_leaf_edge(self.root.as_ref()), - }; + let root1 = self.root.as_ref(); + let root2 = self.root.as_ref(); + let (f, b) = range_search(root1, root2, range); - let back = match max { - Included(key) => { - match search::search_tree(self.root.as_ref(), key) { - Found(kv_handle) => { - match kv_handle.right_edge().force() { - Leaf(bottom) => bottom, - Internal(internal) => first_leaf_edge(internal.descend()), - } - } - GoDown(bottom) => bottom, - } - } - Excluded(key) => { - match search::search_tree(self.root.as_ref(), key) { - Found(kv_handle) => { - match kv_handle.left_edge().force() { - Leaf(bottom) => bottom, - Internal(internal) => last_leaf_edge(internal.descend()), - } - } - GoDown(bottom) => bottom, - } - } - Unbounded => last_leaf_edge(self.root.as_ref()), - }; - - Range { - front: front, - back: back, - } + Range { front: f, back: b} } /// Constructs a mutable double-ended iterator over a sub-range of elements in the map. @@ -806,6 +758,11 @@ impl BTreeMap { /// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive /// range from 4 to 10. /// + /// # Panics + /// + /// Panics if range `start > end`. + /// Panics if range `start == end` and both bounds are `Excluded`. + /// /// # Examples /// /// Basic usage: @@ -831,66 +788,13 @@ impl BTreeMap { pub fn range_mut(&mut self, range: R) -> RangeMut where T: Ord, K: Borrow, R: RangeArgument { - let min = range.start(); - let max = range.end(); let root1 = self.root.as_mut(); let root2 = unsafe { ptr::read(&root1) }; - - let front = match min { - Included(key) => { - match search::search_tree(root1, key) { - Found(kv_handle) => { - match kv_handle.left_edge().force() { - Leaf(bottom) => bottom, - Internal(internal) => last_leaf_edge(internal.descend()), - } - } - GoDown(bottom) => bottom, - } - } - Excluded(key) => { - match search::search_tree(root1, key) { - Found(kv_handle) => { - match kv_handle.right_edge().force() { - Leaf(bottom) => bottom, - Internal(internal) => first_leaf_edge(internal.descend()), - } - } - GoDown(bottom) => bottom, - } - } - Unbounded => first_leaf_edge(root1), - }; - - let back = match max { - Included(key) => { - match search::search_tree(root2, key) { - Found(kv_handle) => { - match kv_handle.right_edge().force() { - Leaf(bottom) => bottom, - Internal(internal) => first_leaf_edge(internal.descend()), - } - } - GoDown(bottom) => bottom, - } - } - Excluded(key) => { - match search::search_tree(root2, key) { - Found(kv_handle) => { - match kv_handle.left_edge().force() { - Leaf(bottom) => bottom, - Internal(internal) => last_leaf_edge(internal.descend()), - } - } - GoDown(bottom) => bottom, - } - } - Unbounded => last_leaf_edge(root2), - }; + let (f, b) = range_search(root1, root2, range); RangeMut { - front: front, - back: back, + front: f, + back: b, _marker: PhantomData, } } @@ -1827,6 +1731,80 @@ fn last_leaf_edge } } +fn range_search>( + root1: NodeRef, + root2: NodeRef, + range: R +)-> (Handle, marker::Edge>, + Handle, marker::Edge>) + where Q: Ord, K: Borrow +{ + match (range.start(), range.end()) { + (Excluded(s), Excluded(e)) if s==e => + panic!("range start and end are equal and excluded in BTreeMap"), + (Included(s), Included(e)) | + (Included(s), Excluded(e)) | + (Excluded(s), Included(e)) | + (Excluded(s), Excluded(e)) if s>e => + panic!("range start is greater than range end in BTreeMap"), + _ => {}, + }; + + let mut min_node = root1; + let mut max_node = root2; + let mut min_found = false; + let mut max_found = false; + let mut diverged = false; + + loop { + let min_edge = match (min_found, range.start()) { + (false, Included(key)) => match search::search_linear(&min_node, key) { + (i, true) => { min_found = true; i }, + (i, false) => i, + }, + (false, Excluded(key)) => match search::search_linear(&min_node, key) { + (i, true) => { min_found = true; i+1 }, + (i, false) => i, + }, + (_, Unbounded) => 0, + (true, Included(_)) => min_node.keys().len(), + (true, Excluded(_)) => 0, + }; + + let max_edge = match (max_found, range.end()) { + (false, Included(key)) => match search::search_linear(&max_node, key) { + (i, true) => { max_found = true; i+1 }, + (i, false) => i, + }, + (false, Excluded(key)) => match search::search_linear(&max_node, key) { + (i, true) => { max_found = true; i }, + (i, false) => i, + }, + (_, Unbounded) => max_node.keys().len(), + (true, Included(_)) => 0, + (true, Excluded(_)) => max_node.keys().len(), + }; + + if !diverged { + if max_edge < min_edge { panic!("Ord is ill-defined in BTreeMap range") } + if min_edge != max_edge { diverged = true; } + } + + let front = Handle::new_edge(min_node, min_edge); + let back = Handle::new_edge(max_node, max_edge); + match (front.force(), back.force()) { + (Leaf(f), Leaf(b)) => { + return (f, b); + }, + (Internal(min_int), Internal(max_int)) => { + min_node = min_int.descend(); + max_node = max_int.descend(); + }, + _ => unreachable!("BTreeMap has different depths"), + }; + } +} + #[inline(always)] unsafe fn unwrap_unchecked(val: Option) -> T { val.unwrap_or_else(|| { diff --git a/src/libcollections/btree/search.rs b/src/libcollections/btree/search.rs index c94b570bfed8b..bc1272fbc786e 100644 --- a/src/libcollections/btree/search.rs +++ b/src/libcollections/btree/search.rs @@ -58,7 +58,7 @@ pub fn search_node( } } -fn search_linear( +pub fn search_linear( node: &NodeRef, key: &Q ) -> (usize, bool) @@ -73,4 +73,3 @@ fn search_linear( } (node.keys().len(), false) } - diff --git a/src/libcollectionstest/btree/map.rs b/src/libcollectionstest/btree/map.rs index 11be13426e49c..f33923f996319 100644 --- a/src/libcollectionstest/btree/map.rs +++ b/src/libcollectionstest/btree/map.rs @@ -178,6 +178,48 @@ fn test_range_small() { assert_eq!(j, size - 2); } +#[test] +fn test_range_equal_empty_cases() { + let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); + assert_eq!(map.range((Included(2), Excluded(2))).next(), None); + assert_eq!(map.range((Excluded(2), Included(2))).next(), None); +} + +#[test] +#[should_panic] +fn test_range_equal_excluded() { + let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); + map.range((Excluded(2), Excluded(2))); +} + +#[test] +#[should_panic] +fn test_range_backwards_1() { + let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); + map.range((Included(3), Included(2))); +} + +#[test] +#[should_panic] +fn test_range_backwards_2() { + let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); + map.range((Included(3), Excluded(2))); +} + +#[test] +#[should_panic] +fn test_range_backwards_3() { + let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); + map.range((Excluded(3), Included(2))); +} + +#[test] +#[should_panic] +fn test_range_backwards_4() { + let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); + map.range((Excluded(3), Excluded(2))); +} + #[test] fn test_range_1000() { let size = 1000; diff --git a/src/libcore/any.rs b/src/libcore/any.rs index 78f3cd5576e1e..b8a4174766a78 100644 --- a/src/libcore/any.rs +++ b/src/libcore/any.rs @@ -338,7 +338,11 @@ impl Any+Send { /// /// A `TypeId` is currently only available for types which ascribe to `'static`, /// but this limitation may be removed in the future. -#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] +/// +/// While `TypeId` implements `Hash`, `PartialOrd`, and `Ord`, it is worth +/// noting that the hashes and ordering will vary between Rust releases. Beware +/// of relying on them outside of your code! +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] #[stable(feature = "rust1", since = "1.0.0")] pub struct TypeId { t: u64, diff --git a/src/libstd/ascii.rs b/src/libstd/ascii.rs index 35c388ba076ce..af21d6d906eb5 100644 --- a/src/libstd/ascii.rs +++ b/src/libstd/ascii.rs @@ -184,6 +184,368 @@ pub trait AsciiExt { /// [`to_ascii_lowercase`]: #tymethod.to_ascii_lowercase #[stable(feature = "ascii", since = "1.9.0")] fn make_ascii_lowercase(&mut self); + + /// Checks if the value is an ASCII alphabetic character: + /// U+0041 'A' ... U+005A 'Z' or U+0061 'a' ... U+007A 'z'. + /// For strings, true if all characters in the string are + /// ASCII alphabetic. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// # #![allow(non_snake_case)] + /// use std::ascii::AsciiExt; + /// let A = 'A'; + /// let G = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc = '\u{001b}'; + /// + /// assert!(A.is_ascii_alphabetic()); + /// assert!(G.is_ascii_alphabetic()); + /// assert!(a.is_ascii_alphabetic()); + /// assert!(g.is_ascii_alphabetic()); + /// assert!(!zero.is_ascii_alphabetic()); + /// assert!(!percent.is_ascii_alphabetic()); + /// assert!(!space.is_ascii_alphabetic()); + /// assert!(!lf.is_ascii_alphabetic()); + /// assert!(!esc.is_ascii_alphabetic()); + /// ``` + #[unstable(feature = "ascii_ctype", issue = "39658")] + fn is_ascii_alphabetic(&self) -> bool { unimplemented!(); } + + /// Checks if the value is an ASCII uppercase character: + /// U+0041 'A' ... U+005A 'Z'. + /// For strings, true if all characters in the string are + /// ASCII uppercase. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// # #![allow(non_snake_case)] + /// use std::ascii::AsciiExt; + /// let A = 'A'; + /// let G = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc = '\u{001b}'; + /// + /// assert!(A.is_ascii_uppercase()); + /// assert!(G.is_ascii_uppercase()); + /// assert!(!a.is_ascii_uppercase()); + /// assert!(!g.is_ascii_uppercase()); + /// assert!(!zero.is_ascii_uppercase()); + /// assert!(!percent.is_ascii_uppercase()); + /// assert!(!space.is_ascii_uppercase()); + /// assert!(!lf.is_ascii_uppercase()); + /// assert!(!esc.is_ascii_uppercase()); + /// ``` + #[unstable(feature = "ascii_ctype", issue = "39658")] + fn is_ascii_uppercase(&self) -> bool { unimplemented!(); } + + /// Checks if the value is an ASCII lowercase character: + /// U+0061 'a' ... U+007A 'z'. + /// For strings, true if all characters in the string are + /// ASCII lowercase. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// # #![allow(non_snake_case)] + /// use std::ascii::AsciiExt; + /// let A = 'A'; + /// let G = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc = '\u{001b}'; + /// + /// assert!(!A.is_ascii_lowercase()); + /// assert!(!G.is_ascii_lowercase()); + /// assert!(a.is_ascii_lowercase()); + /// assert!(g.is_ascii_lowercase()); + /// assert!(!zero.is_ascii_lowercase()); + /// assert!(!percent.is_ascii_lowercase()); + /// assert!(!space.is_ascii_lowercase()); + /// assert!(!lf.is_ascii_lowercase()); + /// assert!(!esc.is_ascii_lowercase()); + /// ``` + #[unstable(feature = "ascii_ctype", issue = "39658")] + fn is_ascii_lowercase(&self) -> bool { unimplemented!(); } + + /// Checks if the value is an ASCII alphanumeric character: + /// U+0041 'A' ... U+005A 'Z', U+0061 'a' ... U+007A 'z', or + /// U+0030 '0' ... U+0039 '9'. + /// For strings, true if all characters in the string are + /// ASCII alphanumeric. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// # #![allow(non_snake_case)] + /// use std::ascii::AsciiExt; + /// let A = 'A'; + /// let G = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc = '\u{001b}'; + /// + /// assert!(A.is_ascii_alphanumeric()); + /// assert!(G.is_ascii_alphanumeric()); + /// assert!(a.is_ascii_alphanumeric()); + /// assert!(g.is_ascii_alphanumeric()); + /// assert!(zero.is_ascii_alphanumeric()); + /// assert!(!percent.is_ascii_alphanumeric()); + /// assert!(!space.is_ascii_alphanumeric()); + /// assert!(!lf.is_ascii_alphanumeric()); + /// assert!(!esc.is_ascii_alphanumeric()); + /// ``` + #[unstable(feature = "ascii_ctype", issue = "39658")] + fn is_ascii_alphanumeric(&self) -> bool { unimplemented!(); } + + /// Checks if the value is an ASCII decimal digit: + /// U+0030 '0' ... U+0039 '9'. + /// For strings, true if all characters in the string are + /// ASCII digits. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// # #![allow(non_snake_case)] + /// use std::ascii::AsciiExt; + /// let A = 'A'; + /// let G = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc = '\u{001b}'; + /// + /// assert!(!A.is_ascii_digit()); + /// assert!(!G.is_ascii_digit()); + /// assert!(!a.is_ascii_digit()); + /// assert!(!g.is_ascii_digit()); + /// assert!(zero.is_ascii_digit()); + /// assert!(!percent.is_ascii_digit()); + /// assert!(!space.is_ascii_digit()); + /// assert!(!lf.is_ascii_digit()); + /// assert!(!esc.is_ascii_digit()); + /// ``` + #[unstable(feature = "ascii_ctype", issue = "39658")] + fn is_ascii_digit(&self) -> bool { unimplemented!(); } + + /// Checks if the value is an ASCII hexadecimal digit: + /// U+0030 '0' ... U+0039 '9', U+0041 'A' ... U+0046 'F', or + /// U+0061 'a' ... U+0066 'f'. + /// For strings, true if all characters in the string are + /// ASCII hex digits. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// # #![allow(non_snake_case)] + /// use std::ascii::AsciiExt; + /// let A = 'A'; + /// let G = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc = '\u{001b}'; + /// + /// assert!(A.is_ascii_hexdigit()); + /// assert!(!G.is_ascii_hexdigit()); + /// assert!(a.is_ascii_hexdigit()); + /// assert!(!g.is_ascii_hexdigit()); + /// assert!(zero.is_ascii_hexdigit()); + /// assert!(!percent.is_ascii_hexdigit()); + /// assert!(!space.is_ascii_hexdigit()); + /// assert!(!lf.is_ascii_hexdigit()); + /// assert!(!esc.is_ascii_hexdigit()); + /// ``` + #[unstable(feature = "ascii_ctype", issue = "39658")] + fn is_ascii_hexdigit(&self) -> bool { unimplemented!(); } + + /// Checks if the value is an ASCII punctuation character: + /// U+0021 ... U+002F `! " # $ % & ' ( ) * + , - . /` + /// U+003A ... U+0040 `: ; < = > ? @` + /// U+005B ... U+0060 `[ \\ ] ^ _ \`` + /// U+007B ... U+007E `{ | } ~` + /// For strings, true if all characters in the string are + /// ASCII punctuation. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// # #![allow(non_snake_case)] + /// use std::ascii::AsciiExt; + /// let A = 'A'; + /// let G = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc = '\u{001b}'; + /// + /// assert!(!A.is_ascii_punctuation()); + /// assert!(!G.is_ascii_punctuation()); + /// assert!(!a.is_ascii_punctuation()); + /// assert!(!g.is_ascii_punctuation()); + /// assert!(!zero.is_ascii_punctuation()); + /// assert!(percent.is_ascii_punctuation()); + /// assert!(!space.is_ascii_punctuation()); + /// assert!(!lf.is_ascii_punctuation()); + /// assert!(!esc.is_ascii_punctuation()); + /// ``` + #[unstable(feature = "ascii_ctype", issue = "39658")] + fn is_ascii_punctuation(&self) -> bool { unimplemented!(); } + + /// Checks if the value is an ASCII graphic character: + /// U+0021 '@' ... U+007E '~'. + /// For strings, true if all characters in the string are + /// ASCII punctuation. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// # #![allow(non_snake_case)] + /// use std::ascii::AsciiExt; + /// let A = 'A'; + /// let G = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc = '\u{001b}'; + /// + /// assert!(A.is_ascii_graphic()); + /// assert!(G.is_ascii_graphic()); + /// assert!(a.is_ascii_graphic()); + /// assert!(g.is_ascii_graphic()); + /// assert!(zero.is_ascii_graphic()); + /// assert!(percent.is_ascii_graphic()); + /// assert!(!space.is_ascii_graphic()); + /// assert!(!lf.is_ascii_graphic()); + /// assert!(!esc.is_ascii_graphic()); + /// ``` + #[unstable(feature = "ascii_ctype", issue = "39658")] + fn is_ascii_graphic(&self) -> bool { unimplemented!(); } + + /// Checks if the value is an ASCII whitespace character: + /// U+0020 SPACE, U+0009 HORIZONTAL TAB, U+000A LINE FEED, + /// U+000C FORM FEED, or U+000D CARRIAGE RETURN. + /// For strings, true if all characters in the string are + /// ASCII whitespace. + /// + /// Rust uses the WhatWG Infra Standard's [definition of ASCII + /// whitespace][infra-aw]. There are several other definitions in + /// wide use. For instance, [the POSIX locale][pct] includes + /// U+000B VERTICAL TAB as well as all the above characters, + /// but—from the very same specification—[the default rule for + /// "field splitting" in the Bourne shell][bfs] considers *only* + /// SPACE, HORIZONTAL TAB, and LINE FEED as whitespace. + /// + /// If you are writing a program that will process an existing + /// file format, check what that format's definition of whitespace is + /// before using this function. + /// + /// [infra-aw]: https://infra.spec.whatwg.org/#ascii-whitespace + /// [pct]: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html#tag_07_03_01 + /// [bfs]: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// # #![allow(non_snake_case)] + /// use std::ascii::AsciiExt; + /// let A = 'A'; + /// let G = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc = '\u{001b}'; + /// + /// assert!(!A.is_ascii_whitespace()); + /// assert!(!G.is_ascii_whitespace()); + /// assert!(!a.is_ascii_whitespace()); + /// assert!(!g.is_ascii_whitespace()); + /// assert!(!zero.is_ascii_whitespace()); + /// assert!(!percent.is_ascii_whitespace()); + /// assert!(space.is_ascii_whitespace()); + /// assert!(lf.is_ascii_whitespace()); + /// assert!(!esc.is_ascii_whitespace()); + /// ``` + #[unstable(feature = "ascii_ctype", issue = "39658")] + fn is_ascii_whitespace(&self) -> bool { unimplemented!(); } + + /// Checks if the value is an ASCII control character: + /// U+0000 NUL ... U+001F UNIT SEPARATOR, or U+007F DELETE. + /// Note that most ASCII whitespace characters are control + /// characters, but SPACE is not. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_ctype)] + /// # #![allow(non_snake_case)] + /// use std::ascii::AsciiExt; + /// let A = 'A'; + /// let G = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc = '\u{001b}'; + /// + /// assert!(!A.is_ascii_control()); + /// assert!(!G.is_ascii_control()); + /// assert!(!a.is_ascii_control()); + /// assert!(!g.is_ascii_control()); + /// assert!(!zero.is_ascii_control()); + /// assert!(!percent.is_ascii_control()); + /// assert!(!space.is_ascii_control()); + /// assert!(lf.is_ascii_control()); + /// assert!(esc.is_ascii_control()); + /// ``` + #[unstable(feature = "ascii_ctype", issue = "39658")] + fn is_ascii_control(&self) -> bool { unimplemented!(); } } #[stable(feature = "rust1", since = "1.0.0")] @@ -225,6 +587,56 @@ impl AsciiExt for str { let me: &mut [u8] = unsafe { mem::transmute(self) }; me.make_ascii_lowercase() } + + #[inline] + fn is_ascii_alphabetic(&self) -> bool { + self.bytes().all(|b| b.is_ascii_alphabetic()) + } + + #[inline] + fn is_ascii_uppercase(&self) -> bool { + self.bytes().all(|b| b.is_ascii_uppercase()) + } + + #[inline] + fn is_ascii_lowercase(&self) -> bool { + self.bytes().all(|b| b.is_ascii_lowercase()) + } + + #[inline] + fn is_ascii_alphanumeric(&self) -> bool { + self.bytes().all(|b| b.is_ascii_alphanumeric()) + } + + #[inline] + fn is_ascii_digit(&self) -> bool { + self.bytes().all(|b| b.is_ascii_digit()) + } + + #[inline] + fn is_ascii_hexdigit(&self) -> bool { + self.bytes().all(|b| b.is_ascii_hexdigit()) + } + + #[inline] + fn is_ascii_punctuation(&self) -> bool { + self.bytes().all(|b| b.is_ascii_punctuation()) + } + + #[inline] + fn is_ascii_graphic(&self) -> bool { + self.bytes().all(|b| b.is_ascii_graphic()) + } + + #[inline] + fn is_ascii_whitespace(&self) -> bool { + self.bytes().all(|b| b.is_ascii_whitespace()) + } + + #[inline] + fn is_ascii_control(&self) -> bool { + self.bytes().all(|b| b.is_ascii_control()) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -268,6 +680,56 @@ impl AsciiExt for [u8] { byte.make_ascii_lowercase(); } } + + #[inline] + fn is_ascii_alphabetic(&self) -> bool { + self.iter().all(|b| b.is_ascii_alphabetic()) + } + + #[inline] + fn is_ascii_uppercase(&self) -> bool { + self.iter().all(|b| b.is_ascii_uppercase()) + } + + #[inline] + fn is_ascii_lowercase(&self) -> bool { + self.iter().all(|b| b.is_ascii_lowercase()) + } + + #[inline] + fn is_ascii_alphanumeric(&self) -> bool { + self.iter().all(|b| b.is_ascii_alphanumeric()) + } + + #[inline] + fn is_ascii_digit(&self) -> bool { + self.iter().all(|b| b.is_ascii_digit()) + } + + #[inline] + fn is_ascii_hexdigit(&self) -> bool { + self.iter().all(|b| b.is_ascii_hexdigit()) + } + + #[inline] + fn is_ascii_punctuation(&self) -> bool { + self.iter().all(|b| b.is_ascii_punctuation()) + } + + #[inline] + fn is_ascii_graphic(&self) -> bool { + self.iter().all(|b| b.is_ascii_graphic()) + } + + #[inline] + fn is_ascii_whitespace(&self) -> bool { + self.iter().all(|b| b.is_ascii_whitespace()) + } + + #[inline] + fn is_ascii_control(&self) -> bool { + self.iter().all(|b| b.is_ascii_control()) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -287,6 +749,96 @@ impl AsciiExt for u8 { fn make_ascii_uppercase(&mut self) { *self = self.to_ascii_uppercase(); } #[inline] fn make_ascii_lowercase(&mut self) { *self = self.to_ascii_lowercase(); } + + #[inline] + fn is_ascii_alphabetic(&self) -> bool { + if *self >= 0x80 { return false; } + match ASCII_CHARACTER_CLASS[*self as usize] { + L|Lx|U|Ux => true, + _ => false + } + } + + #[inline] + fn is_ascii_uppercase(&self) -> bool { + if *self >= 0x80 { return false } + match ASCII_CHARACTER_CLASS[*self as usize] { + U|Ux => true, + _ => false + } + } + + #[inline] + fn is_ascii_lowercase(&self) -> bool { + if *self >= 0x80 { return false } + match ASCII_CHARACTER_CLASS[*self as usize] { + L|Lx => true, + _ => false + } + } + + #[inline] + fn is_ascii_alphanumeric(&self) -> bool { + if *self >= 0x80 { return false } + match ASCII_CHARACTER_CLASS[*self as usize] { + D|L|Lx|U|Ux => true, + _ => false + } + } + + #[inline] + fn is_ascii_digit(&self) -> bool { + if *self >= 0x80 { return false } + match ASCII_CHARACTER_CLASS[*self as usize] { + D => true, + _ => false + } + } + + #[inline] + fn is_ascii_hexdigit(&self) -> bool { + if *self >= 0x80 { return false } + match ASCII_CHARACTER_CLASS[*self as usize] { + D|Lx|Ux => true, + _ => false + } + } + + #[inline] + fn is_ascii_punctuation(&self) -> bool { + if *self >= 0x80 { return false } + match ASCII_CHARACTER_CLASS[*self as usize] { + P => true, + _ => false + } + } + + #[inline] + fn is_ascii_graphic(&self) -> bool { + if *self >= 0x80 { return false; } + match ASCII_CHARACTER_CLASS[*self as usize] { + Ux|U|Lx|L|D|P => true, + _ => false + } + } + + #[inline] + fn is_ascii_whitespace(&self) -> bool { + if *self >= 0x80 { return false; } + match ASCII_CHARACTER_CLASS[*self as usize] { + Cw|W => true, + _ => false + } + } + + #[inline] + fn is_ascii_control(&self) -> bool { + if *self >= 0x80 { return false; } + match ASCII_CHARACTER_CLASS[*self as usize] { + C|Cw => true, + _ => false + } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -324,6 +876,56 @@ impl AsciiExt for char { fn make_ascii_uppercase(&mut self) { *self = self.to_ascii_uppercase(); } #[inline] fn make_ascii_lowercase(&mut self) { *self = self.to_ascii_lowercase(); } + + #[inline] + fn is_ascii_alphabetic(&self) -> bool { + (*self as u32 <= 0x7f) && (*self as u8).is_ascii_alphabetic() + } + + #[inline] + fn is_ascii_uppercase(&self) -> bool { + (*self as u32 <= 0x7f) && (*self as u8).is_ascii_uppercase() + } + + #[inline] + fn is_ascii_lowercase(&self) -> bool { + (*self as u32 <= 0x7f) && (*self as u8).is_ascii_lowercase() + } + + #[inline] + fn is_ascii_alphanumeric(&self) -> bool { + (*self as u32 <= 0x7f) && (*self as u8).is_ascii_alphanumeric() + } + + #[inline] + fn is_ascii_digit(&self) -> bool { + (*self as u32 <= 0x7f) && (*self as u8).is_ascii_digit() + } + + #[inline] + fn is_ascii_hexdigit(&self) -> bool { + (*self as u32 <= 0x7f) && (*self as u8).is_ascii_hexdigit() + } + + #[inline] + fn is_ascii_punctuation(&self) -> bool { + (*self as u32 <= 0x7f) && (*self as u8).is_ascii_punctuation() + } + + #[inline] + fn is_ascii_graphic(&self) -> bool { + (*self as u32 <= 0x7f) && (*self as u8).is_ascii_graphic() + } + + #[inline] + fn is_ascii_whitespace(&self) -> bool { + (*self as u32 <= 0x7f) && (*self as u8).is_ascii_whitespace() + } + + #[inline] + fn is_ascii_control(&self) -> bool { + (*self as u32 <= 0x7f) && (*self as u8).is_ascii_control() + } } /// An iterator over the escaped version of a byte, constructed via @@ -485,6 +1087,30 @@ static ASCII_UPPERCASE_MAP: [u8; 256] = [ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, ]; +enum AsciiCharacterClass { + C, // control + Cw, // control whitespace + W, // whitespace + D, // digit + L, // lowercase + Lx, // lowercase hex digit + U, // uppercase + Ux, // uppercase hex digit + P, // punctuation +} +use self::AsciiCharacterClass::*; + +static ASCII_CHARACTER_CLASS: [AsciiCharacterClass; 128] = [ +// _0 _1 _2 _3 _4 _5 _6 _7 _8 _9 _a _b _c _d _e _f + C, C, C, C, C, C, C, C, C, Cw,Cw,C, Cw,Cw,C, C, // 0_ + C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, // 1_ + W, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, // 2_ + D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, P, // 3_ + P, Ux,Ux,Ux,Ux,Ux,Ux,U, U, U, U, U, U, U, U, U, // 4_ + U, U, U, U, U, U, U, U, U, U, U, P, P, P, P, P, // 5_ + P, Lx,Lx,Lx,Lx,Lx,Lx,L, L, L, L, L, L, L, L, L, // 6_ + L, L, L, L, L, L, L, L, L, L, L, P, P, P, P, C, // 7_ +]; #[cfg(test)] mod tests { @@ -606,4 +1232,236 @@ mod tests { let x = "a".to_string(); x.eq_ignore_ascii_case("A"); } + + // Shorthands used by the is_ascii_* tests. + macro_rules! assert_all { + ($what:ident, $($str:tt),+) => {{ + $( + for b in $str.chars() { + if !b.$what() { + panic!("expected {}({}) but it isn't", + stringify!($what), b); + } + } + for b in $str.as_bytes().iter() { + if !b.$what() { + panic!("expected {}(0x{:02x})) but it isn't", + stringify!($what), b); + } + } + assert!($str.$what()); + assert!($str.as_bytes().$what()); + )+ + }}; + ($what:ident, $($str:tt),+,) => (assert_all!($what,$($str),+)) + } + macro_rules! assert_none { + ($what:ident, $($str:tt),+) => {{ + $( + for b in $str.chars() { + if b.$what() { + panic!("expected not-{}({}) but it is", + stringify!($what), b); + } + } + for b in $str.as_bytes().iter() { + if b.$what() { + panic!("expected not-{}(0x{:02x})) but it is", + stringify!($what), b); + } + } + )* + }}; + ($what:ident, $($str:tt),+,) => (assert_none!($what,$($str),+)) + } + + #[test] + fn test_is_ascii_alphabetic() { + assert_all!(is_ascii_alphabetic, + "", + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + ); + assert_none!(is_ascii_alphabetic, + "0123456789", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); + } + + #[test] + fn test_is_ascii_uppercase() { + assert_all!(is_ascii_uppercase, + "", + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + ); + assert_none!(is_ascii_uppercase, + "abcdefghijklmnopqrstuvwxyz", + "0123456789", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); + } + + #[test] + fn test_is_ascii_lowercase() { + assert_all!(is_ascii_lowercase, + "abcdefghijklmnopqrstuvwxyz", + ); + assert_none!(is_ascii_lowercase, + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + "0123456789", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); + } + + #[test] + fn test_is_ascii_alphanumeric() { + assert_all!(is_ascii_alphanumeric, + "", + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + "0123456789", + ); + assert_none!(is_ascii_alphanumeric, + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); + } + + #[test] + fn test_is_ascii_digit() { + assert_all!(is_ascii_digit, + "", + "0123456789", + ); + assert_none!(is_ascii_digit, + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); + } + + #[test] + fn test_is_ascii_hexdigit() { + assert_all!(is_ascii_hexdigit, + "", + "0123456789", + "abcdefABCDEF", + ); + assert_none!(is_ascii_hexdigit, + "ghijklmnopqrstuvwxyz", + "GHIJKLMNOQPRSTUVWXYZ", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); + } + + #[test] + fn test_is_ascii_punctuation() { + assert_all!(is_ascii_punctuation, + "", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + ); + assert_none!(is_ascii_punctuation, + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + "0123456789", + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); + } + + #[test] + fn test_is_ascii_graphic() { + assert_all!(is_ascii_graphic, + "", + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + "0123456789", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + ); + assert_none!(is_ascii_graphic, + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); + } + + #[test] + fn test_is_ascii_whitespace() { + assert_all!(is_ascii_whitespace, + "", + " \t\n\x0c\r", + ); + assert_none!(is_ascii_whitespace, + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + "0123456789", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x0b\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); + } + + #[test] + fn test_is_ascii_control() { + assert_all!(is_ascii_control, + "", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); + assert_none!(is_ascii_control, + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + "0123456789", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + " ", + ); + } } diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index ffb7a1a1fc15f..ca9bdcfe2c26f 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -416,22 +416,26 @@ fn search_hashed(table: M, hash: SafeHash, mut is_match: F) -> Inter } } -fn pop_internal(starting_bucket: FullBucketMut) -> (K, V) { +fn pop_internal(starting_bucket: FullBucketMut) + -> (K, V, &mut RawTable) +{ let (empty, retkey, retval) = starting_bucket.take(); let mut gap = match empty.gap_peek() { - Some(b) => b, - None => return (retkey, retval), + Ok(b) => b, + Err(b) => return (retkey, retval, b.into_table()), }; while gap.full().displacement() != 0 { gap = match gap.shift() { - Some(b) => b, - None => break, + Ok(b) => b, + Err(b) => { + return (retkey, retval, b.into_table()); + }, }; } // Now we've done all our shifting. Return the value we grabbed earlier. - (retkey, retval) + (retkey, retval, gap.into_bucket().into_table()) } /// Perform robin hood bucket stealing at the given `bucket`. You must @@ -721,38 +725,7 @@ impl HashMap return; } - // Grow the table. - // Specialization of the other branch. - let mut bucket = Bucket::first(&mut old_table); - - // "So a few of the first shall be last: for many be called, - // but few chosen." - // - // We'll most likely encounter a few buckets at the beginning that - // have their initial buckets near the end of the table. They were - // placed at the beginning as the probe wrapped around the table - // during insertion. We must skip forward to a bucket that won't - // get reinserted too early and won't unfairly steal others spot. - // This eliminates the need for robin hood. - loop { - bucket = match bucket.peek() { - Full(full) => { - if full.displacement() == 0 { - // This bucket occupies its ideal spot. - // It indicates the start of another "cluster". - bucket = full.into_bucket(); - break; - } - // Leaving this bucket in the last cluster for later. - full.into_bucket() - } - Empty(b) => { - // Encountered a hole between clusters. - b.into_bucket() - } - }; - bucket.next(); - } + let mut bucket = Bucket::head_bucket(&mut old_table); // This is how the buckets might be laid out in memory: // ($ marks an initialized bucket) @@ -1208,6 +1181,57 @@ impl HashMap self.search_mut(k).into_occupied_bucket().map(|bucket| pop_internal(bucket).1) } + + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all pairs `(k, v)` such that `f(&k,&mut v)` returns `false`. + /// + /// # Examples + /// + /// ``` + /// #![feature(retain_hash_collection)] + /// use std::collections::HashMap; + /// + /// let mut map: HashMap = (0..8).map(|x|(x, x*10)).collect(); + /// map.retain(|&k, _| k % 2 == 0); + /// assert_eq!(map.len(), 4); + /// ``` + #[unstable(feature = "retain_hash_collection", issue = "36648")] + pub fn retain(&mut self, mut f: F) + where F: FnMut(&K, &mut V) -> bool + { + if self.table.capacity() == 0 || self.table.size() == 0 { + return; + } + let mut bucket = Bucket::head_bucket(&mut self.table); + bucket.prev(); + let tail = bucket.index(); + loop { + bucket = match bucket.peek() { + Full(mut full) => { + let should_remove = { + let (k, v) = full.read_mut(); + !f(k, v) + }; + if should_remove { + let prev_idx = full.index(); + let prev_raw = full.raw(); + let (_, _, t) = pop_internal(full); + Bucket::new_from(prev_raw, prev_idx, t) + } else { + full.into_bucket() + } + }, + Empty(b) => { + b.into_bucket() + } + }; + bucket.prev(); // reverse iteration + if bucket.index() == tail { + break; + } + } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1862,7 +1886,8 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// ``` #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] pub fn remove_entry(self) -> (K, V) { - pop_internal(self.elem) + let (k, v, _) = pop_internal(self.elem); + (k, v) } /// Gets a reference to the value in the entry. @@ -3156,4 +3181,15 @@ mod test_map { assert_eq!(a.len(), 1); assert_eq!(a[key], value); } + + #[test] + fn test_retain() { + let mut map: HashMap = (0..100).map(|x|(x, x*10)).collect(); + + map.retain(|&k, _| k % 2 == 0); + assert_eq!(map.len(), 50); + assert_eq!(map[&2], 20); + assert_eq!(map[&4], 40); + assert_eq!(map[&6], 60); + } } diff --git a/src/libstd/collections/hash/set.rs b/src/libstd/collections/hash/set.rs index d438aa8b3ac9c..ac0d15472c1bf 100644 --- a/src/libstd/collections/hash/set.rs +++ b/src/libstd/collections/hash/set.rs @@ -630,6 +630,28 @@ impl HashSet { Recover::take(&mut self.map, value) } + + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all elements `e` such that `f(&e)` returns `false`. + /// + /// # Examples + /// + /// ``` + /// #![feature(retain_hash_collection)] + /// use std::collections::HashSet; + /// + /// let xs = [1,2,3,4,5,6]; + /// let mut set: HashSet = xs.iter().cloned().collect(); + /// set.retain(|&k| k % 2 == 0); + /// assert_eq!(set.len(), 3); + /// ``` + #[unstable(feature = "retain_hash_collection", issue = "36648")] + pub fn retain(&mut self, mut f: F) + where F: FnMut(&T) -> bool + { + self.map.retain(|k, _| f(k)); + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1611,4 +1633,15 @@ mod test_set { assert!(a.contains(&5)); assert!(a.contains(&6)); } + + #[test] + fn test_retain() { + let xs = [1,2,3,4,5,6]; + let mut set: HashSet = xs.iter().cloned().collect(); + set.retain(|&k| k % 2 == 0); + assert_eq!(set.len(), 3); + assert!(set.contains(&2)); + assert!(set.contains(&4)); + assert!(set.contains(&6)); + } } diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 1ab62130cd3dd..9e92b4750145e 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -85,7 +85,7 @@ pub struct RawTable { unsafe impl Send for RawTable {} unsafe impl Sync for RawTable {} -struct RawBucket { +pub struct RawBucket { hash: *mut HashUint, // We use *const to ensure covariance with respect to K and V pair: *const (K, V), @@ -216,6 +216,10 @@ impl FullBucket { pub fn index(&self) -> usize { self.idx } + /// Get the raw bucket. + pub fn raw(&self) -> RawBucket { + self.raw + } } impl EmptyBucket { @@ -230,6 +234,10 @@ impl Bucket { pub fn index(&self) -> usize { self.idx } + /// get the table. + pub fn into_table(self) -> M { + self.table + } } impl Deref for FullBucket @@ -275,6 +283,16 @@ impl>> Bucket { Bucket::at_index(table, hash.inspect() as usize) } + pub fn new_from(r: RawBucket, i: usize, t: M) + -> Bucket + { + Bucket { + raw: r, + idx: i, + table: t, + } + } + pub fn at_index(table: M, ib_index: usize) -> Bucket { // if capacity is 0, then the RawBucket will be populated with bogus pointers. // This is an uncommon case though, so avoid it in release builds. @@ -296,6 +314,40 @@ impl>> Bucket { } } + // "So a few of the first shall be last: for many be called, + // but few chosen." + // + // We'll most likely encounter a few buckets at the beginning that + // have their initial buckets near the end of the table. They were + // placed at the beginning as the probe wrapped around the table + // during insertion. We must skip forward to a bucket that won't + // get reinserted too early and won't unfairly steal others spot. + // This eliminates the need for robin hood. + pub fn head_bucket(table: M) -> Bucket { + let mut bucket = Bucket::first(table); + + loop { + bucket = match bucket.peek() { + Full(full) => { + if full.displacement() == 0 { + // This bucket occupies its ideal spot. + // It indicates the start of another "cluster". + bucket = full.into_bucket(); + break; + } + // Leaving this bucket in the last cluster for later. + full.into_bucket() + } + Empty(b) => { + // Encountered a hole between clusters. + b.into_bucket() + } + }; + bucket.next(); + } + bucket + } + /// Reads a bucket at a given index, returning an enum indicating whether /// it's initialized or not. You need to match on this enum to get /// the appropriate types to call most of the other functions in @@ -333,6 +385,17 @@ impl>> Bucket { self.raw = self.raw.offset(dist); } } + + /// Modifies the bucket pointer in place to make it point to the previous slot. + pub fn prev(&mut self) { + let range = self.table.capacity(); + let new_idx = self.idx.wrapping_sub(1) & (range - 1); + let dist = (new_idx as isize).wrapping_sub(self.idx as isize); + self.idx = new_idx; + unsafe { + self.raw = self.raw.offset(dist); + } + } } impl>> EmptyBucket { @@ -352,7 +415,7 @@ impl>> EmptyBucket { } } - pub fn gap_peek(self) -> Option> { + pub fn gap_peek(self) -> Result, Bucket> { let gap = EmptyBucket { raw: self.raw, idx: self.idx, @@ -361,12 +424,12 @@ impl>> EmptyBucket { match self.next().peek() { Full(bucket) => { - Some(GapThenFull { + Ok(GapThenFull { gap: gap, full: bucket, }) } - Empty(..) => None, + Empty(e) => Err(e.into_bucket()), } } } @@ -529,7 +592,11 @@ impl GapThenFull &self.full } - pub fn shift(mut self) -> Option> { + pub fn into_bucket(self) -> Bucket { + self.full.into_bucket() + } + + pub fn shift(mut self) -> Result, Bucket> { unsafe { *self.gap.raw.hash = mem::replace(&mut *self.full.raw.hash, EMPTY_BUCKET); ptr::copy_nonoverlapping(self.full.raw.pair, self.gap.raw.pair as *mut (K, V), 1); @@ -544,9 +611,9 @@ impl GapThenFull self.full = bucket; - Some(self) + Ok(self) } - Empty(..) => None, + Empty(b) => Err(b.into_bucket()), } } } diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index 7a676c041ad89..9ae30a6d71768 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -15,6 +15,9 @@ #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] +#[allow(unused_imports)] +use sys; + #[cfg(not(test))] use core::num; #[cfg(not(test))] @@ -40,6 +43,8 @@ pub use core::f32::consts; mod cmath { use libc::{c_float, c_int}; + pub use sys::f32::cmath::*; + extern { pub fn cbrtf(n: c_float) -> c_float; pub fn erff(n: c_float) -> c_float; @@ -55,88 +60,6 @@ mod cmath { pub fn modff(n: c_float, iptr: &mut c_float) -> c_float; pub fn nextafterf(x: c_float, y: c_float) -> c_float; pub fn tgammaf(n: c_float) -> c_float; - - #[cfg_attr(all(windows, target_env = "msvc"), link_name = "__lgammaf_r")] - pub fn lgammaf_r(n: c_float, sign: &mut c_int) -> c_float; - #[cfg_attr(all(windows, target_env = "msvc"), link_name = "_hypotf")] - pub fn hypotf(x: c_float, y: c_float) -> c_float; - } - - // See the comments in the `floor` function for why MSVC is special - // here. - #[cfg(not(target_env = "msvc"))] - extern { - pub fn acosf(n: c_float) -> c_float; - pub fn asinf(n: c_float) -> c_float; - pub fn atan2f(a: c_float, b: c_float) -> c_float; - pub fn atanf(n: c_float) -> c_float; - pub fn coshf(n: c_float) -> c_float; - pub fn frexpf(n: c_float, value: &mut c_int) -> c_float; - pub fn ldexpf(x: c_float, n: c_int) -> c_float; - pub fn sinhf(n: c_float) -> c_float; - pub fn tanf(n: c_float) -> c_float; - pub fn tanhf(n: c_float) -> c_float; - } - - #[cfg(target_env = "msvc")] - pub use self::shims::*; - #[cfg(target_env = "msvc")] - mod shims { - use libc::{c_float, c_int}; - - #[inline] - pub unsafe fn acosf(n: c_float) -> c_float { - f64::acos(n as f64) as c_float - } - - #[inline] - pub unsafe fn asinf(n: c_float) -> c_float { - f64::asin(n as f64) as c_float - } - - #[inline] - pub unsafe fn atan2f(n: c_float, b: c_float) -> c_float { - f64::atan2(n as f64, b as f64) as c_float - } - - #[inline] - pub unsafe fn atanf(n: c_float) -> c_float { - f64::atan(n as f64) as c_float - } - - #[inline] - pub unsafe fn coshf(n: c_float) -> c_float { - f64::cosh(n as f64) as c_float - } - - #[inline] - #[allow(deprecated)] - pub unsafe fn frexpf(x: c_float, value: &mut c_int) -> c_float { - let (a, b) = f64::frexp(x as f64); - *value = b as c_int; - a as c_float - } - - #[inline] - #[allow(deprecated)] - pub unsafe fn ldexpf(x: c_float, n: c_int) -> c_float { - f64::ldexp(x as f64, n as isize) as c_float - } - - #[inline] - pub unsafe fn sinhf(n: c_float) -> c_float { - f64::sinh(n as f64) as c_float - } - - #[inline] - pub unsafe fn tanf(n: c_float) -> c_float { - f64::tan(n as f64) as c_float - } - - #[inline] - pub unsafe fn tanhf(n: c_float) -> c_float { - f64::tanh(n as f64) as c_float - } } } @@ -301,10 +224,7 @@ impl f32 { // Note that there are many MSVC-specific float operations which // redirect to this comment, so `floorf` is just one case of a missing // function on MSVC, but there are many others elsewhere. - #[cfg(target_env = "msvc")] - return (self as f64).floor() as f32; - #[cfg(not(target_env = "msvc"))] - return unsafe { intrinsics::floorf32(self) }; + sys::f32::floor(self) } /// Returns the smallest integer greater than or equal to a number. @@ -320,10 +240,7 @@ impl f32 { #[inline] pub fn ceil(self) -> f32 { // see notes above in `floor` - #[cfg(target_env = "msvc")] - return (self as f64).ceil() as f32; - #[cfg(not(target_env = "msvc"))] - return unsafe { intrinsics::ceilf32(self) }; + sys::f32::ceil(self) } /// Returns the nearest integer to a number. Round half-way cases away from @@ -519,10 +436,7 @@ impl f32 { #[inline] pub fn powf(self, n: f32) -> f32 { // see notes above in `floor` - #[cfg(target_env = "msvc")] - return (self as f64).powf(n as f64) as f32; - #[cfg(not(target_env = "msvc"))] - return unsafe { intrinsics::powf32(self, n) }; + sys::f32::powf(self, n) } /// Takes the square root of a number. @@ -568,10 +482,7 @@ impl f32 { #[inline] pub fn exp(self) -> f32 { // see notes above in `floor` - #[cfg(target_env = "msvc")] - return (self as f64).exp() as f32; - #[cfg(not(target_env = "msvc"))] - return unsafe { intrinsics::expf32(self) }; + sys::f32::exp(self) } /// Returns `2^(self)`. @@ -610,10 +521,7 @@ impl f32 { #[inline] pub fn ln(self) -> f32 { // see notes above in `floor` - #[cfg(target_env = "msvc")] - return (self as f64).ln() as f32; - #[cfg(not(target_env = "msvc"))] - return unsafe { intrinsics::logf32(self) }; + sys::f32::ln(self) } /// Returns the logarithm of the number with respect to an arbitrary base. @@ -652,10 +560,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn log2(self) -> f32 { - #[cfg(target_os = "android")] - return ::sys::android::log2f32(self); - #[cfg(not(target_os = "android"))] - return unsafe { intrinsics::log2f32(self) }; + sys::f32::log2(self) } /// Returns the base 10 logarithm of the number. @@ -674,10 +579,7 @@ impl f32 { #[inline] pub fn log10(self) -> f32 { // see notes above in `floor` - #[cfg(target_env = "msvc")] - return (self as f64).log10() as f32; - #[cfg(not(target_env = "msvc"))] - return unsafe { intrinsics::log10f32(self) }; + sys::f32::log10(self) } /// Converts radians to degrees. @@ -908,10 +810,7 @@ impl f32 { #[inline] pub fn sin(self) -> f32 { // see notes in `core::f32::Float::floor` - #[cfg(target_env = "msvc")] - return (self as f64).sin() as f32; - #[cfg(not(target_env = "msvc"))] - return unsafe { intrinsics::sinf32(self) }; + sys::f32::sin(self) } /// Computes the cosine of a number (in radians). @@ -929,10 +828,7 @@ impl f32 { #[inline] pub fn cos(self) -> f32 { // see notes in `core::f32::Float::floor` - #[cfg(target_env = "msvc")] - return (self as f64).cos() as f32; - #[cfg(not(target_env = "msvc"))] - return unsafe { intrinsics::cosf32(self) }; + sys::f32::cos(self) } /// Computes the tangent of a number (in radians). diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index 67a1c302483d2..0b40358526224 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -35,10 +35,15 @@ pub use core::f64::{MIN, MIN_POSITIVE, MAX}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::f64::consts; +#[allow(unused_imports)] +use sys; + #[allow(dead_code)] mod cmath { use libc::{c_double, c_int}; + pub use sys::f64::cmath::*; + #[link_name = "m"] extern { pub fn acos(n: c_double) -> c_double; @@ -75,12 +80,6 @@ mod cmath { pub fn y0(n: c_double) -> c_double; pub fn y1(n: c_double) -> c_double; pub fn yn(i: c_int, n: c_double) -> c_double; - - #[cfg_attr(all(windows, target_env = "msvc"), link_name = "__lgamma_r")] - pub fn lgamma_r(n: c_double, sign: &mut c_int) -> c_double; - - #[cfg_attr(all(windows, target_env = "msvc"), link_name = "_hypot")] - pub fn hypot(x: c_double, y: c_double) -> c_double; } } @@ -515,7 +514,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn ln(self) -> f64 { - self.log_wrapper(|n| { unsafe { intrinsics::logf64(n) } }) + sys::f64::ln(self) } /// Returns the logarithm of the number with respect to an arbitrary base. @@ -550,12 +549,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn log2(self) -> f64 { - self.log_wrapper(|n| { - #[cfg(target_os = "android")] - return ::sys::android::log2f64(n); - #[cfg(not(target_os = "android"))] - return unsafe { intrinsics::log2f64(n) }; - }) + sys::f64::log2(self) } /// Returns the base 10 logarithm of the number. @@ -571,7 +565,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn log10(self) -> f64 { - self.log_wrapper(|n| { unsafe { intrinsics::log10f64(n) } }) + sys::f64::log10(self) } /// Converts radians to degrees. @@ -1091,31 +1085,6 @@ impl f64 { pub fn atanh(self) -> f64 { 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() } - - // Solaris/Illumos requires a wrapper around log, log2, and log10 functions - // because of their non-standard behavior (e.g. log(-n) returns -Inf instead - // of expected NaN). - fn log_wrapper f64>(self, log_fn: F) -> f64 { - if !cfg!(target_os = "solaris") { - log_fn(self) - } else { - if self.is_finite() { - if self > 0.0 { - log_fn(self) - } else if self == 0.0 { - NEG_INFINITY // log(0) = -Inf - } else { - NAN // log(-n) = NaN - } - } else if self.is_nan() { - self // log(NaN) = NaN - } else if self > 0.0 { - self // log(Inf) = Inf - } else { - NAN // log(-Inf) = NaN - } - } - } } #[cfg(test)] diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs index d1b8fcd744003..dc3855367ae27 100644 --- a/src/libstd/ffi/c_str.rs +++ b/src/libstd/ffi/c_str.rs @@ -303,6 +303,12 @@ impl CString { &self.inner } + /// Converts this `CString` into a boxed `CStr`. + #[unstable(feature = "into_boxed_c_str", issue = "0")] + pub fn into_boxed_c_str(self) -> Box { + unsafe { mem::transmute(self.into_inner()) } + } + // Bypass "move out of struct which implements `Drop` trait" restriction. fn into_inner(self) -> Box<[u8]> { unsafe { @@ -380,6 +386,22 @@ impl Borrow for CString { fn borrow(&self) -> &CStr { self } } +#[stable(feature = "box_from_c_str", since = "1.17.0")] +impl<'a> From<&'a CStr> for Box { + fn from(s: &'a CStr) -> Box { + let boxed: Box<[u8]> = Box::from(s.to_bytes_with_nul()); + unsafe { mem::transmute(boxed) } + } +} + +#[stable(feature = "default_box_extra", since = "1.17.0")] +impl Default for Box { + fn default() -> Box { + let boxed: Box<[u8]> = Box::from([0]); + unsafe { mem::transmute(boxed) } + } +} + impl NulError { /// Returns the position of the nul byte in the slice that was provided to /// `CString::new`. @@ -686,7 +708,7 @@ impl ToOwned for CStr { type Owned = CString; fn to_owned(&self) -> CString { - CString { inner: self.to_bytes_with_nul().to_vec().into_boxed_slice() } + CString { inner: self.to_bytes_with_nul().into() } } } @@ -847,4 +869,22 @@ mod tests { let cstr = CStr::from_bytes_with_nul(data); assert!(cstr.is_err()); } + + #[test] + fn into_boxed() { + let orig: &[u8] = b"Hello, world!\0"; + let cstr = CStr::from_bytes_with_nul(orig).unwrap(); + let cstring = cstr.to_owned(); + let box1: Box = Box::from(cstr); + let box2 = cstring.into_boxed_c_str(); + assert_eq!(cstr, &*box1); + assert_eq!(box1, box2); + assert_eq!(&*box2, cstr); + } + + #[test] + fn boxed_default() { + let boxed = >::default(); + assert_eq!(boxed.to_bytes_with_nul(), &[0]); + } } diff --git a/src/libstd/ffi/os_str.rs b/src/libstd/ffi/os_str.rs index 273b717f4678b..7b8bf42e0a74a 100644 --- a/src/libstd/ffi/os_str.rs +++ b/src/libstd/ffi/os_str.rs @@ -204,6 +204,12 @@ impl OsString { pub fn reserve_exact(&mut self, additional: usize) { self.inner.reserve_exact(additional) } + + /// Converts this `OsString` into a boxed `OsStr`. + #[unstable(feature = "into_boxed_os_str", issue = "0")] + pub fn into_boxed_os_str(self) -> Box { + unsafe { mem::transmute(self.inner.into_box()) } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -445,6 +451,20 @@ impl OsStr { } } +#[stable(feature = "box_from_os_str", since = "1.17.0")] +impl<'a> From<&'a OsStr> for Box { + fn from(s: &'a OsStr) -> Box { + unsafe { mem::transmute(s.inner.into_box()) } + } +} + +#[stable(feature = "box_default_extra", since = "1.17.0")] +impl Default for Box { + fn default() -> Box { + unsafe { mem::transmute(Slice::empty_box()) } + } +} + #[stable(feature = "osstring_default", since = "1.9.0")] impl<'a> Default for &'a OsStr { /// Creates an empty `OsStr`. @@ -741,4 +761,22 @@ mod tests { let os_str: &OsStr = Default::default(); assert_eq!("", os_str); } + + #[test] + fn into_boxed() { + let orig = "Hello, world!"; + let os_str = OsStr::new(orig); + let os_string = os_str.to_owned(); + let box1: Box = Box::from(os_str); + let box2 = os_string.into_boxed_os_str(); + assert_eq!(os_str, &*box1); + assert_eq!(box1, box2); + assert_eq!(&*box2, os_str); + } + + #[test] + fn boxed_default() { + let boxed = >::default(); + assert!(boxed.is_empty()); + } } diff --git a/src/libstd/path.rs b/src/libstd/path.rs index 07b43cd89ac71..463d0200db20b 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -127,7 +127,7 @@ use ops::{self, Deref}; use ffi::{OsStr, OsString}; -use sys::path::{is_sep_byte, is_verbatim_sep, MAIN_SEP_STR, parse_prefix}; +use sys::path::{ABSOLUTE_NEEDS_PREFIX, is_sep_byte, is_verbatim_sep, MAIN_SEP_STR, parse_prefix}; //////////////////////////////////////////////////////////////////////////////// // GENERAL NOTES @@ -1194,6 +1194,28 @@ impl PathBuf { pub fn into_os_string(self) -> OsString { self.inner } + + /// Converts this `PathBuf` into a boxed `Path`. + #[unstable(feature = "into_boxed_path", issue = "0")] + pub fn into_boxed_path(self) -> Box { + unsafe { mem::transmute(self.inner.into_boxed_os_str()) } + } +} + +#[stable(feature = "box_from_path", since = "1.17.0")] +impl<'a> From<&'a Path> for Box { + fn from(path: &'a Path) -> Box { + let boxed: Box = path.inner.into(); + unsafe { mem::transmute(boxed) } + } +} + +#[stable(feature = "box_default_extra", since = "1.17.0")] +impl Default for Box { + fn default() -> Box { + let boxed: Box = Default::default(); + unsafe { mem::transmute(boxed) } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1516,7 +1538,7 @@ impl Path { #[allow(deprecated)] pub fn is_absolute(&self) -> bool { // FIXME: Remove target_os = "redox" and allow Redox prefixes - self.has_root() && (cfg!(unix) || cfg!(target_os = "redox") || self.prefix().is_some()) + self.has_root() && (!ABSOLUTE_NEEDS_PREFIX || self.prefix().is_some()) } /// A path is *relative* if it is not absolute. @@ -3676,4 +3698,22 @@ mod tests { let actual = format!("{:?}", iter); assert_eq!(expected, actual); } + + #[test] + fn into_boxed() { + let orig: &str = "some/sort/of/path"; + let path = Path::new(orig); + let path_buf = path.to_owned(); + let box1: Box = Box::from(path); + let box2 = path_buf.into_boxed_path(); + assert_eq!(path, &*box1); + assert_eq!(box1, box2); + assert_eq!(&*box2, path); + } + + #[test] + fn boxed_default() { + let boxed = >::default(); + assert!(boxed.as_os_str().is_empty()); + } } diff --git a/src/libstd/sys/redox/f32.rs b/src/libstd/sys/redox/f32.rs new file mode 100644 index 0000000000000..6fd9ce3cc6bdb --- /dev/null +++ b/src/libstd/sys/redox/f32.rs @@ -0,0 +1,78 @@ +// Copyright 2012-2015 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. + +#![allow(dead_code)] + +use intrinsics; + +pub mod cmath { + use libc::{c_float, c_int}; + + #[link_name = "m"] + extern { + pub fn lgammaf_r(n: c_float, sign: &mut c_int) -> c_float; + pub fn hypotf(x: c_float, y: c_float) -> c_float; + pub fn acosf(n: c_float) -> c_float; + pub fn asinf(n: c_float) -> c_float; + pub fn atan2f(a: c_float, b: c_float) -> c_float; + pub fn atanf(n: c_float) -> c_float; + pub fn coshf(n: c_float) -> c_float; + pub fn frexpf(n: c_float, value: &mut c_int) -> c_float; + pub fn ldexpf(x: c_float, n: c_int) -> c_float; + pub fn sinhf(n: c_float) -> c_float; + pub fn tanf(n: c_float) -> c_float; + pub fn tanhf(n: c_float) -> c_float; + } +} + +#[inline] +pub fn floor(x: f32) -> f32 { + unsafe { intrinsics::floorf32(x) } +} + +#[inline] +pub fn ceil(x: f32) -> f32 { + unsafe { intrinsics::ceilf32(x) } +} + +#[inline] +pub fn powf(x: f32, n: f32) -> f32 { + unsafe { intrinsics::powf32(x, n) } +} + +#[inline] +pub fn exp(x: f32) -> f32 { + unsafe { intrinsics::expf32(x) } +} + +#[inline] +pub fn ln(x: f32) -> f32 { + unsafe { intrinsics::logf32(x) } +} + +#[inline] +pub fn log2(x: f32) -> f32 { + unsafe { intrinsics::log2f32(x) } +} + +#[inline] +pub fn log10(x: f32) -> f32 { + unsafe { intrinsics::log10f32(x) } +} + +#[inline] +pub fn sin(x: f32) -> f32 { + unsafe { intrinsics::sinf32(x) } +} + +#[inline] +pub fn cos(x: f32) -> f32 { + unsafe { intrinsics::cosf32(x) } +} diff --git a/src/libstd/sys/redox/f64.rs b/src/libstd/sys/redox/f64.rs new file mode 100644 index 0000000000000..4935a813f240e --- /dev/null +++ b/src/libstd/sys/redox/f64.rs @@ -0,0 +1,33 @@ +// Copyright 2012-2015 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. + +#![allow(dead_code)] + +pub mod cmath { + use libc::{c_double, c_int}; + + #[link_name = "m"] + extern { + pub fn lgamma_r(n: c_double, sign: &mut c_int) -> c_double; + pub fn hypot(x: c_double, y: c_double) -> c_double; + } +} + +pub fn ln(x: f64) -> f64 { + unsafe { ::intrinsics::logf64(x) } +} + +pub fn log2(x: f64) -> f64 { + unsafe { ::intrinsics::log2f64(x) } +} + +pub fn log10(x: f64) -> f64 { + unsafe { ::intrinsics::log10f64(x) } +} diff --git a/src/libstd/sys/redox/mod.rs b/src/libstd/sys/redox/mod.rs index 5982bdd6549ca..09880b74adfb5 100644 --- a/src/libstd/sys/redox/mod.rs +++ b/src/libstd/sys/redox/mod.rs @@ -18,6 +18,8 @@ pub mod backtrace; pub mod condvar; pub mod env; pub mod ext; +pub mod f32; +pub mod f64; pub mod fast_thread_local; pub mod fd; pub mod fs; diff --git a/src/libstd/sys/redox/os_str.rs b/src/libstd/sys/redox/os_str.rs index 8922bf04f56da..0f967863899cb 100644 --- a/src/libstd/sys/redox/os_str.rs +++ b/src/libstd/sys/redox/os_str.rs @@ -94,6 +94,11 @@ impl Buf { pub fn push_slice(&mut self, s: &Slice) { self.inner.extend_from_slice(&s.inner) } + + #[inline] + pub fn into_box(self) -> Box { + unsafe { mem::transmute(self.inner.into_boxed_slice()) } + } } impl Slice { @@ -116,4 +121,15 @@ impl Slice { pub fn to_owned(&self) -> Buf { Buf { inner: self.inner.to_vec() } } + + #[inline] + pub fn into_box(&self) -> Box { + let boxed: Box<[u8]> = self.inner.into(); + unsafe { mem::transmute(boxed) } + } + + pub fn empty_box() -> Box { + let boxed: Box<[u8]> = Default::default(); + unsafe { mem::transmute(boxed) } + } } diff --git a/src/libstd/sys/redox/path.rs b/src/libstd/sys/redox/path.rs index e6a267dd5d913..8863937794bbe 100644 --- a/src/libstd/sys/redox/path.rs +++ b/src/libstd/sys/redox/path.rs @@ -37,3 +37,5 @@ pub fn parse_prefix(path: &OsStr) -> Option { pub const MAIN_SEP_STR: &'static str = "/"; pub const MAIN_SEP: char = '/'; +// FIXME: Change to 'true' and allow Redox prefixes +pub const ABSOLUTE_NEEDS_PREFIX: bool = false; diff --git a/src/libstd/sys/unix/f32.rs b/src/libstd/sys/unix/f32.rs new file mode 100644 index 0000000000000..23b73a84b4192 --- /dev/null +++ b/src/libstd/sys/unix/f32.rs @@ -0,0 +1,81 @@ +// Copyright 2012-2015 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. + +#![allow(dead_code)] + +use intrinsics; + +pub mod cmath { + use libc::{c_float, c_int}; + + #[link_name = "m"] + extern { + pub fn lgammaf_r(n: c_float, sign: &mut c_int) -> c_float; + pub fn hypotf(x: c_float, y: c_float) -> c_float; + pub fn acosf(n: c_float) -> c_float; + pub fn asinf(n: c_float) -> c_float; + pub fn atan2f(a: c_float, b: c_float) -> c_float; + pub fn atanf(n: c_float) -> c_float; + pub fn coshf(n: c_float) -> c_float; + pub fn frexpf(n: c_float, value: &mut c_int) -> c_float; + pub fn ldexpf(x: c_float, n: c_int) -> c_float; + pub fn sinhf(n: c_float) -> c_float; + pub fn tanf(n: c_float) -> c_float; + pub fn tanhf(n: c_float) -> c_float; + } +} + +#[inline] +pub fn floor(x: f32) -> f32 { + unsafe { intrinsics::floorf32(x) } +} + +#[inline] +pub fn ceil(x: f32) -> f32 { + unsafe { intrinsics::ceilf32(x) } +} + +#[inline] +pub fn powf(x: f32, n: f32) -> f32 { + unsafe { intrinsics::powf32(x, n) } +} + +#[inline] +pub fn exp(x: f32) -> f32 { + unsafe { intrinsics::expf32(x) } +} + +#[inline] +pub fn ln(x: f32) -> f32 { + unsafe { intrinsics::logf32(x) } +} + +#[inline] +pub fn log2(x: f32) -> f32 { + #[cfg(target_os = "android")] + return ::sys::android::log2f32(x); + #[cfg(not(target_os = "android"))] + return unsafe { intrinsics::log2f32(x) }; +} + +#[inline] +pub fn log10(x: f32) -> f32 { + unsafe { intrinsics::log10f32(x) } +} + +#[inline] +pub fn sin(x: f32) -> f32 { + unsafe { intrinsics::sinf32(x) } +} + +#[inline] +pub fn cos(x: f32) -> f32 { + unsafe { intrinsics::cosf32(x) } +} diff --git a/src/libstd/sys/unix/f64.rs b/src/libstd/sys/unix/f64.rs new file mode 100644 index 0000000000000..cce9d7a619f0e --- /dev/null +++ b/src/libstd/sys/unix/f64.rs @@ -0,0 +1,66 @@ +// Copyright 2012-2015 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. + +#![allow(dead_code)] + +use core::f64::{NAN, NEG_INFINITY}; + +pub mod cmath { + use libc::{c_double, c_int}; + + #[link_name = "m"] + extern { + pub fn lgamma_r(n: c_double, sign: &mut c_int) -> c_double; + pub fn hypot(x: c_double, y: c_double) -> c_double; + } +} + +pub fn ln(x: f64) -> f64 { + log_wrapper(x, |n| { unsafe { ::intrinsics::logf64(n) } }) +} + +pub fn log2(x: f64) -> f64 { + log_wrapper(x, + |n| { + #[cfg(target_os = "android")] + return ::sys::android::log2f64(n); + #[cfg(not(target_os = "android"))] + return unsafe { ::intrinsics::log2f64(n) }; + }) +} + +pub fn log10(x: f64) -> f64 { + log_wrapper(x, |n| { unsafe { ::intrinsics::log10f64(n) } }) +} + +// Solaris/Illumos requires a wrapper around log, log2, and log10 functions +// because of their non-standard behavior (e.g. log(-n) returns -Inf instead +// of expected NaN). +fn log_wrapper f64>(x: f64, log_fn: F) -> f64 { + if !cfg!(target_os = "solaris") { + log_fn(x) + } else { + if x.is_finite() { + if x > 0.0 { + log_fn(x) + } else if x == 0.0 { + NEG_INFINITY // log(0) = -Inf + } else { + NAN // log(-n) = NaN + } + } else if x.is_nan() { + x // log(NaN) = NaN + } else if x > 0.0 { + x // log(Inf) = Inf + } else { + NAN // log(-Inf) = NaN + } + } +} diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index c57751a01d7c1..8314d5502851a 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -38,6 +38,8 @@ pub mod backtrace; pub mod condvar; pub mod env; pub mod ext; +pub mod f32; +pub mod f64; pub mod fast_thread_local; pub mod fd; pub mod fs; diff --git a/src/libstd/sys/unix/os_str.rs b/src/libstd/sys/unix/os_str.rs index 5a733c0cb8763..938bcfc6d162e 100644 --- a/src/libstd/sys/unix/os_str.rs +++ b/src/libstd/sys/unix/os_str.rs @@ -94,6 +94,11 @@ impl Buf { pub fn push_slice(&mut self, s: &Slice) { self.inner.extend_from_slice(&s.inner) } + + #[inline] + pub fn into_box(self) -> Box { + unsafe { mem::transmute(self.inner.into_boxed_slice()) } + } } impl Slice { @@ -116,4 +121,15 @@ impl Slice { pub fn to_owned(&self) -> Buf { Buf { inner: self.inner.to_vec() } } + + #[inline] + pub fn into_box(&self) -> Box { + let boxed: Box<[u8]> = self.inner.into(); + unsafe { mem::transmute(boxed) } + } + + pub fn empty_box() -> Box { + let boxed: Box<[u8]> = Default::default(); + unsafe { mem::transmute(boxed) } + } } diff --git a/src/libstd/sys/unix/path.rs b/src/libstd/sys/unix/path.rs index bf9af7a4353a8..5a06058534632 100644 --- a/src/libstd/sys/unix/path.rs +++ b/src/libstd/sys/unix/path.rs @@ -27,3 +27,4 @@ pub fn parse_prefix(_: &OsStr) -> Option { pub const MAIN_SEP_STR: &'static str = "/"; pub const MAIN_SEP: char = '/'; +pub const ABSOLUTE_NEEDS_PREFIX: bool = false; diff --git a/src/libstd/sys/windows/f32.rs b/src/libstd/sys/windows/f32.rs new file mode 100644 index 0000000000000..4721858ab7bf5 --- /dev/null +++ b/src/libstd/sys/windows/f32.rs @@ -0,0 +1,190 @@ +// Copyright 2012-2015 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. + +#![allow(dead_code)] + +use intrinsics; + +pub mod cmath { + use libc::{c_float, c_int}; + + extern { + #[cfg_attr(target_env = "msvc", link_name = "__lgammaf_r")] + pub fn lgammaf_r(n: c_float, sign: &mut c_int) -> c_float; + + #[cfg_attr(target_env = "msvc", link_name = "_hypotf")] + pub fn hypotf(x: c_float, y: c_float) -> c_float; + } + + // See the comments in the `floor` function for why MSVC is special + // here. + #[cfg(not(target_env = "msvc"))] + extern { + pub fn acosf(n: c_float) -> c_float; + pub fn asinf(n: c_float) -> c_float; + pub fn atan2f(a: c_float, b: c_float) -> c_float; + pub fn atanf(n: c_float) -> c_float; + pub fn coshf(n: c_float) -> c_float; + pub fn frexpf(n: c_float, value: &mut c_int) -> c_float; + pub fn ldexpf(x: c_float, n: c_int) -> c_float; + pub fn sinhf(n: c_float) -> c_float; + pub fn tanf(n: c_float) -> c_float; + pub fn tanhf(n: c_float) -> c_float; + } + + #[cfg(target_env = "msvc")] + pub use self::shims::*; + #[cfg(target_env = "msvc")] + mod shims { + use libc::{c_float, c_int}; + + #[inline] + pub unsafe fn acosf(n: c_float) -> c_float { + f64::acos(n as f64) as c_float + } + + #[inline] + pub unsafe fn asinf(n: c_float) -> c_float { + f64::asin(n as f64) as c_float + } + + #[inline] + pub unsafe fn atan2f(n: c_float, b: c_float) -> c_float { + f64::atan2(n as f64, b as f64) as c_float + } + + #[inline] + pub unsafe fn atanf(n: c_float) -> c_float { + f64::atan(n as f64) as c_float + } + + #[inline] + pub unsafe fn coshf(n: c_float) -> c_float { + f64::cosh(n as f64) as c_float + } + + #[inline] + #[allow(deprecated)] + pub unsafe fn frexpf(x: c_float, value: &mut c_int) -> c_float { + let (a, b) = f64::frexp(x as f64); + *value = b as c_int; + a as c_float + } + + #[inline] + #[allow(deprecated)] + pub unsafe fn ldexpf(x: c_float, n: c_int) -> c_float { + f64::ldexp(x as f64, n as isize) as c_float + } + + #[inline] + pub unsafe fn sinhf(n: c_float) -> c_float { + f64::sinh(n as f64) as c_float + } + + #[inline] + pub unsafe fn tanf(n: c_float) -> c_float { + f64::tan(n as f64) as c_float + } + + #[inline] + pub unsafe fn tanhf(n: c_float) -> c_float { + f64::tanh(n as f64) as c_float + } + } +} + +pub fn floor(x: f32) -> f32 { + // On MSVC LLVM will lower many math intrinsics to a call to the + // corresponding function. On MSVC, however, many of these functions + // aren't actually available as symbols to call, but rather they are all + // `static inline` functions in header files. This means that from a C + // perspective it's "compatible", but not so much from an ABI + // perspective (which we're worried about). + // + // The inline header functions always just cast to a f64 and do their + // operation, so we do that here as well, but only for MSVC targets. + // + // Note that there are many MSVC-specific float operations which + // redirect to this comment, so `floorf` is just one case of a missing + // function on MSVC, but there are many others elsewhere. + #[cfg(target_env = "msvc")] + return (x as f64).floor() as f32; + #[cfg(not(target_env = "msvc"))] + return unsafe { intrinsics::floorf32(x) }; +} + +#[inline] +pub fn ceil(x: f32) -> f32 { + // see notes above in `floor` + #[cfg(target_env = "msvc")] + return (x as f64).ceil() as f32; + #[cfg(not(target_env = "msvc"))] + return unsafe { intrinsics::ceilf32(x) }; +} + +#[inline] +pub fn powf(x: f32, n: f32) -> f32 { + // see notes above in `floor` + #[cfg(target_env = "msvc")] + return (x as f64).powf(n as f64) as f32; + #[cfg(not(target_env = "msvc"))] + return unsafe { intrinsics::powf32(x, n) }; +} + +#[inline] +pub fn exp(x: f32) -> f32 { + // see notes above in `floor` + #[cfg(target_env = "msvc")] + return (x as f64).exp() as f32; + #[cfg(not(target_env = "msvc"))] + return unsafe { intrinsics::expf32(x) }; +} + +#[inline] +pub fn ln(x: f32) -> f32 { + // see notes above in `floor` + #[cfg(target_env = "msvc")] + return (x as f64).ln() as f32; + #[cfg(not(target_env = "msvc"))] + return unsafe { intrinsics::logf32(x) }; +} + +#[inline] +pub fn log2(x: f32) -> f32 { + unsafe { intrinsics::log2f32(x) } +} + +#[inline] +pub fn log10(x: f32) -> f32 { + // see notes above in `floor` + #[cfg(target_env = "msvc")] + return (x as f64).log10() as f32; + #[cfg(not(target_env = "msvc"))] + return unsafe { intrinsics::log10f32(x) }; +} + +#[inline] +pub fn sin(x: f32) -> f32 { + // see notes in `core::f32::Float::floor` + #[cfg(target_env = "msvc")] + return (x as f64).sin() as f32; + #[cfg(not(target_env = "msvc"))] + return unsafe { intrinsics::sinf32(x) }; +} + +#[inline] +pub fn cos(x: f32) -> f32 { + // see notes in `core::f32::Float::floor` + #[cfg(target_env = "msvc")] + return (x as f64).cos() as f32; + #[cfg(not(target_env = "msvc"))] + return unsafe { intrinsics::cosf32(x) }; +} diff --git a/src/libstd/sys/windows/f64.rs b/src/libstd/sys/windows/f64.rs new file mode 100644 index 0000000000000..3e8504f86dc95 --- /dev/null +++ b/src/libstd/sys/windows/f64.rs @@ -0,0 +1,36 @@ +// Copyright 2012-2015 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. + +#![allow(dead_code)] + +pub mod cmath { + use libc::{c_double, c_int}; + + #[link_name = "m"] + extern { + #[cfg_attr(target_env = "msvc", link_name = "__lgamma_r")] + pub fn lgamma_r(n: c_double, sign: &mut c_int) -> c_double; + + #[cfg_attr(target_env = "msvc", link_name = "_hypot")] + pub fn hypot(x: c_double, y: c_double) -> c_double; + } +} + +pub fn ln(x: f64) -> f64 { + unsafe { ::intrinsics::logf64(x) } +} + +pub fn log2(x: f64) -> f64 { + unsafe { ::intrinsics::log2f64(x) } +} + +pub fn log10(x: f64) -> f64 { + unsafe { ::intrinsics::log10f64(x) } +} diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 4424c6c6136c5..915cb9a080275 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -26,6 +26,8 @@ pub mod condvar; pub mod dynamic_lib; pub mod env; pub mod ext; +pub mod f32; +pub mod f64; pub mod fs; pub mod handle; pub mod memchr; diff --git a/src/libstd/sys/windows/os_str.rs b/src/libstd/sys/windows/os_str.rs index a065c7a7fd013..b27317208384b 100644 --- a/src/libstd/sys/windows/os_str.rs +++ b/src/libstd/sys/windows/os_str.rs @@ -88,6 +88,11 @@ impl Buf { pub fn reserve_exact(&mut self, additional: usize) { self.inner.reserve_exact(additional) } + + #[inline] + pub fn into_box(self) -> Box { + unsafe { mem::transmute(self.inner.into_box()) } + } } impl Slice { @@ -108,4 +113,13 @@ impl Slice { buf.push_wtf8(&self.inner); Buf { inner: buf } } + + #[inline] + pub fn into_box(&self) -> Box { + unsafe { mem::transmute(self.inner.into_box()) } + } + + pub fn empty_box() -> Box { + unsafe { mem::transmute(self.inner.empty_box()) } + } } diff --git a/src/libstd/sys/windows/path.rs b/src/libstd/sys/windows/path.rs index 2b47808451bc2..900946438d5d2 100644 --- a/src/libstd/sys/windows/path.rs +++ b/src/libstd/sys/windows/path.rs @@ -106,3 +106,4 @@ pub fn parse_prefix<'a>(path: &'a OsStr) -> Option { pub const MAIN_SEP_STR: &'static str = "\\"; pub const MAIN_SEP: char = '\\'; +pub const ABSOLUTE_NEEDS_PREFIX: bool = true; diff --git a/src/libstd/sys_common/wtf8.rs b/src/libstd/sys_common/wtf8.rs index 0a94ff1e95823..1d61181a4ee0f 100644 --- a/src/libstd/sys_common/wtf8.rs +++ b/src/libstd/sys_common/wtf8.rs @@ -340,6 +340,12 @@ impl Wtf8Buf { } } } + + /// Converts this `Wtf8Buf` into a boxed `Wtf8`. + #[inline] + pub fn into_box(self) -> Box { + unsafe { mem::transmute(self.bytes.into_boxed_slice()) } + } } /// Create a new WTF-8 string from an iterator of code points. @@ -583,6 +589,19 @@ impl Wtf8 { _ => None } } + + /// Boxes this `Wtf8`. + #[inline] + pub fn into_box(&self) -> Box { + let boxed: Box<[u8]> = self.bytes.into(); + unsafe { mem::transmute(boxed) } + } + + /// Creates a boxed, empty `Wtf8`. + pub fn empty_box() -> Box { + let boxed: Box<[u8]> = Default::default(); + unsafe { mem::transmute(boxed) } + } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index b051928ff9d3c..2c4fa8e15edf2 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -302,11 +302,20 @@ impl<'a> Parser<'a> { if i + 1 < tts.len() { self.tts.push((tts, i + 1)); } - if let TokenTree::Token(sp, tok) = tt { - TokenAndSpan { tok: tok, sp: sp } - } else { - self.tts.push((tt, 0)); - continue + // FIXME(jseyfried): remove after fixing #39390 in #39419. + if self.quote_depth > 0 { + if let TokenTree::Sequence(sp, _) = tt { + self.span_err(sp, "attempted to repeat an expression containing no \ + syntax variables matched as repeating at this depth"); + } + } + match tt { + TokenTree::Token(sp, tok) => TokenAndSpan { tok: tok, sp: sp }, + _ if tt.len() > 0 => { + self.tts.push((tt, 0)); + continue + } + _ => continue, } } else { TokenAndSpan { tok: token::Eof, sp: self.span } diff --git a/src/test/run-make/sanitizer-thread/racy.rs b/src/test/compile-fail/issue-39709.rs similarity index 74% rename from src/test/run-make/sanitizer-thread/racy.rs rename to src/test/compile-fail/issue-39709.rs index dc929e004a479..0f66fe8439336 100644 --- a/src/test/run-make/sanitizer-thread/racy.rs +++ b/src/test/compile-fail/issue-39709.rs @@ -8,14 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::thread; - -static mut ANSWER: i32 = 0; - fn main() { - let t1 = thread::spawn(|| unsafe { ANSWER = 42 }); - unsafe { - ANSWER = 24; - } - t1.join().ok(); + println!("{}", { macro_rules! x { ($()*) => {} } 33 }); + //~^ ERROR no syntax variables matched as repeating at this depth } + diff --git a/src/test/run-make/sanitizer-thread/Makefile b/src/test/run-make/sanitizer-thread/Makefile deleted file mode 100644 index 8bb89a241cb05..0000000000000 --- a/src/test/run-make/sanitizer-thread/Makefile +++ /dev/null @@ -1,10 +0,0 @@ --include ../tools.mk - -ifdef SANITIZER_SUPPORT -all: - $(RUSTC) -g -Z sanitizer=thread -Z print-link-args racy.rs | grep -q librustc_tsan - $(TMPDIR)/racy 2>&1 | grep -q 'data race' -else -all: - -endif diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs index 3808c05c6b939..82fd68652b2d6 100644 --- a/src/tools/tidy/src/pal.rs +++ b/src/tools/tidy/src/pal.rs @@ -66,9 +66,6 @@ const EXCEPTION_PATHS: &'static [&'static str] = &[ // temporary exceptions "src/libstd/rtdeps.rs", // Until rustbuild replaces make - "src/libstd/path.rs", - "src/libstd/f32.rs", - "src/libstd/f64.rs", "src/libstd/sys_common/mod.rs", "src/libstd/sys_common/net.rs", "src/libterm", // Not sure how to make this crate portable, but test needs it diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index c722eb690b8c3..2233f8c352974 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -38,6 +38,60 @@ http://www.apache.org/licenses/LICENSE-2.0> or the MIT license option. This file may not be copied, modified, or distributed except according to those terms."; +/// Parser states for line_is_url. +#[derive(PartialEq)] +#[allow(non_camel_case_types)] +enum LIUState { EXP_COMMENT_START, + EXP_LINK_LABEL_OR_URL, + EXP_URL, + EXP_END } + +/// True if LINE appears to be a line comment containing an URL, +/// possibly with a Markdown link label in front, and nothing else. +/// The Markdown link label, if present, may not contain whitespace. +/// Lines of this form are allowed to be overlength, because Markdown +/// offers no way to split a line in the middle of a URL, and the lengths +/// of URLs to external references are beyond our control. +fn line_is_url(line: &str) -> bool { + use self::LIUState::*; + let mut state: LIUState = EXP_COMMENT_START; + + for tok in line.split_whitespace() { + match (state, tok) { + (EXP_COMMENT_START, "//") => state = EXP_LINK_LABEL_OR_URL, + (EXP_COMMENT_START, "///") => state = EXP_LINK_LABEL_OR_URL, + (EXP_COMMENT_START, "//!") => state = EXP_LINK_LABEL_OR_URL, + + (EXP_LINK_LABEL_OR_URL, w) + if w.len() >= 4 && w.starts_with("[") && w.ends_with("]:") + => state = EXP_URL, + + (EXP_LINK_LABEL_OR_URL, w) + if w.starts_with("http://") || w.starts_with("https://") + => state = EXP_END, + + (EXP_URL, w) + if w.starts_with("http://") || w.starts_with("https://") + => state = EXP_END, + + (_, _) => return false, + } + } + + state == EXP_END +} + +/// True if LINE is allowed to be longer than the normal limit. +/// Currently there is only one exception, for long URLs, but more +/// may be added in the future. +fn long_line_is_ok(line: &str) -> bool { + if line_is_url(line) { + return true; + } + + false +} + pub fn check(path: &Path, bad: &mut bool) { let mut contents = String::new(); super::walk(path, &mut super::filter_dirs, &mut |file| { @@ -61,8 +115,9 @@ pub fn check(path: &Path, bad: &mut bool) { println!("{}:{}: {}", file.display(), i + 1, msg); *bad = true; }; - if line.chars().count() > COLS && !skip_length { - err(&format!("line longer than {} chars", COLS)); + if !skip_length && line.chars().count() > COLS + && !long_line_is_ok(line) { + err(&format!("line longer than {} chars", COLS)); } if line.contains("\t") && !skip_tab { err("tab character");