From 92dcae47425a9084b97680bc0327ac88353b8a68 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 1 Feb 2019 12:43:29 +0100 Subject: [PATCH 1/6] Add internal impl_fn_for_zst macro for "named closure types" --- src/libcore/lib.rs | 1 + src/libcore/macros.rs | 46 ++++++++++++++ src/libcore/str/mod.rs | 141 ++++++++--------------------------------- 3 files changed, 75 insertions(+), 113 deletions(-) diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 8b728c9414d4a..419980225c335 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -95,6 +95,7 @@ #![feature(simd_ffi)] #![feature(specialization)] #![feature(staged_api)] +#![feature(std_internals)] #![feature(stmt_expr_attributes)] #![feature(unboxed_closures)] #![feature(unsized_locals)] diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index 664490c1997ef..bc5d35f719114 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -749,3 +749,49 @@ mod builtin { ($cond:expr, $($arg:tt)+) => ({ /* compiler built-in */ }); } } + +/// Create a named zero-size type similar to a closure. +#[doc(hidden)] +#[macro_export] +#[unstable(feature = "std_internals", issue = "0")] +macro_rules! impl_fn_for_zst { + ($( + $( #[$attr: meta] )* + // FIXME: when libcore is in the 2018 edition, use `?` repetition in + // $( <$( $li : lifetime ),+> )? + struct $Name: ident impl$( <$( $lifetime : lifetime ),+> )* Fn = + |$( $arg: ident: $ArgTy: ty ),*| -> $ReturnTy: ty + $body: block; + )+) => { + $( + $( #[$attr] )* + struct $Name; + + impl $( <$( $lifetime ),+> )* Fn<($( $ArgTy, )*)> for $Name { + #[inline] + extern "rust-call" fn call(&self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { + $body + } + } + + impl $( <$( $lifetime ),+> )* FnMut<($( $ArgTy, )*)> for $Name { + #[inline] + extern "rust-call" fn call_mut( + &mut self, + ($( $arg, )*): ($( $ArgTy, )*) + ) -> $ReturnTy { + Fn::call(&*self, ($( $arg, )*)) + } + } + + impl $( <$( $lifetime ),+> )* FnOnce<($( $ArgTy, )*)> for $Name { + type Output = $ReturnTy; + + #[inline] + extern "rust-call" fn call_once(self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { + Fn::call(&self, ($( $arg, )*)) + } + } + )+ + } +} diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index e9190cc3ddf1b..eefed51733001 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -1345,33 +1345,14 @@ impl FusedIterator for Lines<'_> {} #[allow(deprecated)] pub struct LinesAny<'a>(Lines<'a>); -/// A nameable, cloneable fn type -#[derive(Clone)] -struct LinesAnyMap; - -impl<'a> Fn<(&'a str,)> for LinesAnyMap { - #[inline] - extern "rust-call" fn call(&self, (line,): (&'a str,)) -> &'a str { +impl_fn_for_zst! { + /// A nameable, cloneable fn type + #[derive(Clone)] + struct LinesAnyMap impl<'a> Fn = |line: &'a str| -> &'a str { let l = line.len(); if l > 0 && line.as_bytes()[l - 1] == b'\r' { &line[0 .. l - 1] } else { line } - } -} - -impl<'a> FnMut<(&'a str,)> for LinesAnyMap { - #[inline] - extern "rust-call" fn call_mut(&mut self, (line,): (&'a str,)) -> &'a str { - Fn::call(&*self, (line,)) - } -} - -impl<'a> FnOnce<(&'a str,)> for LinesAnyMap { - type Output = &'a str; - - #[inline] - extern "rust-call" fn call_once(self, (line,): (&'a str,)) -> &'a str { - Fn::call(&self, (line,)) - } + }; } #[stable(feature = "rust1", since = "1.0.0")] @@ -2727,7 +2708,7 @@ impl str { let inner = self .as_bytes() .split(IsAsciiWhitespace) - .filter(IsNotEmpty) + .filter(BytesIsNotEmpty) .map(UnsafeBytesToStr); SplitAsciiWhitespace { inner } } @@ -4011,102 +3992,36 @@ pub struct SplitWhitespace<'a> { #[stable(feature = "split_ascii_whitespace", since = "1.34.0")] #[derive(Clone, Debug)] pub struct SplitAsciiWhitespace<'a> { - inner: Map, IsNotEmpty>, UnsafeBytesToStr>, -} - -#[derive(Clone)] -struct IsWhitespace; - -impl FnOnce<(char, )> for IsWhitespace { - type Output = bool; - - #[inline] - extern "rust-call" fn call_once(mut self, arg: (char, )) -> bool { - self.call_mut(arg) - } + inner: Map, BytesIsNotEmpty>, UnsafeBytesToStr>, } -impl FnMut<(char, )> for IsWhitespace { - #[inline] - extern "rust-call" fn call_mut(&mut self, arg: (char, )) -> bool { - arg.0.is_whitespace() - } -} - -#[derive(Clone)] -struct IsAsciiWhitespace; - -impl<'a> FnOnce<(&'a u8, )> for IsAsciiWhitespace { - type Output = bool; - - #[inline] - extern "rust-call" fn call_once(mut self, arg: (&u8, )) -> bool { - self.call_mut(arg) - } -} - -impl<'a> FnMut<(&'a u8, )> for IsAsciiWhitespace { - #[inline] - extern "rust-call" fn call_mut(&mut self, arg: (&u8, )) -> bool { - arg.0.is_ascii_whitespace() - } -} - -#[derive(Clone)] -struct IsNotEmpty; - -impl<'a, 'b> FnOnce<(&'a &'b str, )> for IsNotEmpty { - type Output = bool; - - #[inline] - extern "rust-call" fn call_once(mut self, arg: (&'a &'b str, )) -> bool { - self.call_mut(arg) - } -} - -impl<'a, 'b> FnMut<(&'a &'b str, )> for IsNotEmpty { - #[inline] - extern "rust-call" fn call_mut(&mut self, arg: (&'a &'b str, )) -> bool { - !arg.0.is_empty() - } -} - -impl<'a, 'b> FnOnce<(&'a &'b [u8], )> for IsNotEmpty { - type Output = bool; - - #[inline] - extern "rust-call" fn call_once(mut self, arg: (&'a &'b [u8], )) -> bool { - self.call_mut(arg) - } -} - -impl<'a, 'b> FnMut<(&'a &'b [u8], )> for IsNotEmpty { - #[inline] - extern "rust-call" fn call_mut(&mut self, arg: (&'a &'b [u8], )) -> bool { - !arg.0.is_empty() - } -} +impl_fn_for_zst! { + #[derive(Clone)] + struct IsWhitespace impl Fn = |c: char| -> bool { + c.is_whitespace() + }; -#[derive(Clone)] -struct UnsafeBytesToStr; + #[derive(Clone)] + struct IsAsciiWhitespace impl Fn = |byte: &u8| -> bool { + byte.is_ascii_whitespace() + }; -impl<'a> FnOnce<(&'a [u8], )> for UnsafeBytesToStr { - type Output = &'a str; + #[derive(Clone)] + struct IsNotEmpty impl<'a, 'b> Fn = |s: &'a &'b str| -> bool { + !s.is_empty() + }; - #[inline] - extern "rust-call" fn call_once(mut self, arg: (&'a [u8], )) -> &'a str { - self.call_mut(arg) - } -} + #[derive(Clone)] + struct BytesIsNotEmpty impl<'a, 'b> Fn = |s: &'a &'b [u8]| -> bool { + !s.is_empty() + }; -impl<'a> FnMut<(&'a [u8], )> for UnsafeBytesToStr { - #[inline] - extern "rust-call" fn call_mut(&mut self, arg: (&'a [u8], )) -> &'a str { - unsafe { from_utf8_unchecked(arg.0) } - } + #[derive(Clone)] + struct UnsafeBytesToStr impl<'a> Fn = |bytes: &'a [u8]| -> &'a str { + unsafe { from_utf8_unchecked(bytes) } + }; } - #[stable(feature = "split_whitespace", since = "1.1.0")] impl<'a> Iterator for SplitWhitespace<'a> { type Item = &'a str; From 7a077804a3a58c93f690e382c86ccd4f1a8850af Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 1 Feb 2019 13:31:24 +0100 Subject: [PATCH 2/6] New return types for str::escape_* that impl Display and Iterator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As FCP’ed in the tracking issue: https://github.com/rust-lang/rust/issues/27791#issuecomment-376864727 --- src/liballoc/lib.rs | 3 +- src/liballoc/str.rs | 108 ++++++++++++++++++++++++++++++---- src/liballoc/tests/str.rs | 61 +++++++++---------- src/libgraphviz/lib.rs | 2 +- src/libsyntax/attr/mod.rs | 2 +- src/libsyntax/print/pprust.rs | 2 +- 6 files changed, 132 insertions(+), 46 deletions(-) diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 189ba84eeed1d..d4ee428a3b5c7 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -68,7 +68,6 @@ #![warn(intra_doc_link_resolution_failure)] #![warn(missing_debug_implementations)] -#![cfg_attr(not(test), feature(fn_traits))] #![cfg_attr(not(test), feature(generator_trait))] #![cfg_attr(test, feature(test))] @@ -86,6 +85,7 @@ #![feature(dropck_eyepatch)] #![feature(exact_size_is_empty)] #![feature(fmt_internals)] +#![feature(fn_traits)] #![feature(fundamental)] #![feature(futures_api)] #![feature(lang_items)] @@ -100,6 +100,7 @@ #![feature(receiver_trait)] #![feature(specialization)] #![feature(staged_api)] +#![feature(std_internals)] #![feature(str_internals)] #![feature(trusted_len)] #![feature(try_reserve)] diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 1fd4c9978a608..1705c80d5f50c 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -29,10 +29,13 @@ #![allow(unused_imports)] use core::borrow::Borrow; -use core::fmt; -use core::str as core_str; +use core::fmt::{self, Write}; +use core::char; +use core::iter::{Chain, Flatten, FlatMap}; use core::str::pattern::{Pattern, Searcher, ReverseSearcher, DoubleEndedSearcher}; use core::mem; +use core::ops::Try; +use core::option; use core::ptr; use core::iter::FusedIterator; use core::unicode::conversions; @@ -452,14 +455,15 @@ impl str { #[unstable(feature = "str_escape", reason = "return type may change to be an iterator", issue = "27791")] - pub fn escape_debug(&self) -> String { - let mut string = String::with_capacity(self.len()); + pub fn escape_debug(&self) -> EscapeDebug { let mut chars = self.chars(); - if let Some(first) = chars.next() { - string.extend(first.escape_debug_ext(true)) + EscapeDebug { + inner: chars.next() + .map(|first| first.escape_debug_ext(true)) + .into_iter() + .flatten() + .chain(chars.flat_map(CharEscapeDebugContinue)) } - string.extend(chars.flat_map(|c| c.escape_debug_ext(false))); - string } /// Escapes each char in `s` with [`char::escape_default`]. @@ -468,8 +472,8 @@ impl str { #[unstable(feature = "str_escape", reason = "return type may change to be an iterator", issue = "27791")] - pub fn escape_default(&self) -> String { - self.chars().flat_map(|c| c.escape_default()).collect() + pub fn escape_default(&self) -> EscapeDefault { + EscapeDefault { inner: self.chars().flat_map(CharEscapeDefault) } } /// Escapes each char in `s` with [`char::escape_unicode`]. @@ -478,8 +482,8 @@ impl str { #[unstable(feature = "str_escape", reason = "return type may change to be an iterator", issue = "27791")] - pub fn escape_unicode(&self) -> String { - self.chars().flat_map(|c| c.escape_unicode()).collect() + pub fn escape_unicode(&self) -> EscapeUnicode { + EscapeUnicode { inner: self.chars().flat_map(CharEscapeUnicode) } } /// Converts a [`Box`] into a [`String`] without copying or allocating. @@ -612,3 +616,83 @@ impl str { pub unsafe fn from_boxed_utf8_unchecked(v: Box<[u8]>) -> Box { Box::from_raw(Box::into_raw(v) as *mut str) } + +impl_fn_for_zst! { + #[derive(Clone)] + struct CharEscapeDebugContinue impl Fn = |c: char| -> char::EscapeDebug { + c.escape_debug_ext(false) + }; + + #[derive(Clone)] + struct CharEscapeUnicode impl Fn = |c: char| -> char::EscapeUnicode { + c.escape_unicode() + }; + #[derive(Clone)] + struct CharEscapeDefault impl Fn = |c: char| -> char::EscapeDefault { + c.escape_default() + }; +} + +macro_rules! escape_types { + ($( + struct $Name: ident<'a> { + inner: $Inner: ty, + } + )+) => {$( + #[unstable(feature = "str_escape", issue = "27791")] + #[derive(Clone, Debug)] + pub struct $Name<'a> { + inner: $Inner, + } + + #[unstable(feature = "str_escape", issue = "27791")] + impl<'a> fmt::Display for $Name<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.clone().try_for_each(|c| f.write_char(c)) + } + } + + #[unstable(feature = "str_escape", issue = "27791")] + impl<'a> Iterator for $Name<'a> { + type Item = char; + + #[inline] + fn next(&mut self) -> Option { self.inner.next() } + + #[inline] + fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } + + #[inline] + fn try_fold(&mut self, init: Acc, fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + self.inner.try_fold(init, fold) + } + + #[inline] + fn fold(self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.inner.fold(init, fold) + } + } + + #[unstable(feature = "str_escape", issue = "27791")] + impl<'a> FusedIterator for $Name<'a> {} + )+} +} + +escape_types! { + struct EscapeDebug<'a> { + inner: Chain< + Flatten>, + FlatMap, char::EscapeDebug, CharEscapeDebugContinue> + >, + } + struct EscapeUnicode<'a> { + inner: FlatMap, char::EscapeUnicode, CharEscapeUnicode>, + } + struct EscapeDefault<'a> { + inner: FlatMap, char::EscapeDefault, CharEscapeDefault>, + } +} diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index f4e6bc9ea31d5..a1dc763f6d8ff 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -990,15 +990,15 @@ fn test_split_at_boundscheck() { #[test] fn test_escape_unicode() { - assert_eq!("abc".escape_unicode(), "\\u{61}\\u{62}\\u{63}"); - assert_eq!("a c".escape_unicode(), "\\u{61}\\u{20}\\u{63}"); - assert_eq!("\r\n\t".escape_unicode(), "\\u{d}\\u{a}\\u{9}"); - assert_eq!("'\"\\".escape_unicode(), "\\u{27}\\u{22}\\u{5c}"); - assert_eq!("\x00\x01\u{fe}\u{ff}".escape_unicode(), "\\u{0}\\u{1}\\u{fe}\\u{ff}"); - assert_eq!("\u{100}\u{ffff}".escape_unicode(), "\\u{100}\\u{ffff}"); - assert_eq!("\u{10000}\u{10ffff}".escape_unicode(), "\\u{10000}\\u{10ffff}"); - assert_eq!("ab\u{fb00}".escape_unicode(), "\\u{61}\\u{62}\\u{fb00}"); - assert_eq!("\u{1d4ea}\r".escape_unicode(), "\\u{1d4ea}\\u{d}"); + assert_eq!("abc".escape_unicode().to_string(), "\\u{61}\\u{62}\\u{63}"); + assert_eq!("a c".escape_unicode().to_string(), "\\u{61}\\u{20}\\u{63}"); + assert_eq!("\r\n\t".escape_unicode().to_string(), "\\u{d}\\u{a}\\u{9}"); + assert_eq!("'\"\\".escape_unicode().to_string(), "\\u{27}\\u{22}\\u{5c}"); + assert_eq!("\x00\x01\u{fe}\u{ff}".escape_unicode().to_string(), "\\u{0}\\u{1}\\u{fe}\\u{ff}"); + assert_eq!("\u{100}\u{ffff}".escape_unicode().to_string(), "\\u{100}\\u{ffff}"); + assert_eq!("\u{10000}\u{10ffff}".escape_unicode().to_string(), "\\u{10000}\\u{10ffff}"); + assert_eq!("ab\u{fb00}".escape_unicode().to_string(), "\\u{61}\\u{62}\\u{fb00}"); + assert_eq!("\u{1d4ea}\r".escape_unicode().to_string(), "\\u{1d4ea}\\u{d}"); } #[test] @@ -1009,31 +1009,32 @@ fn test_escape_debug() { // they are escaped. However, when the character is unescaped (e.g., for // printable characters), only a single backslash appears (as the character // itself appears in the debug string). - assert_eq!("abc".escape_debug(), "abc"); - assert_eq!("a c".escape_debug(), "a c"); - assert_eq!("éèê".escape_debug(), "éèê"); - assert_eq!("\r\n\t".escape_debug(), "\\r\\n\\t"); - assert_eq!("'\"\\".escape_debug(), "\\'\\\"\\\\"); - assert_eq!("\u{7f}\u{ff}".escape_debug(), "\\u{7f}\u{ff}"); - assert_eq!("\u{100}\u{ffff}".escape_debug(), "\u{100}\\u{ffff}"); - assert_eq!("\u{10000}\u{10ffff}".escape_debug(), "\u{10000}\\u{10ffff}"); - assert_eq!("ab\u{200b}".escape_debug(), "ab\\u{200b}"); - assert_eq!("\u{10d4ea}\r".escape_debug(), "\\u{10d4ea}\\r"); - assert_eq!("\u{301}a\u{301}bé\u{e000}".escape_debug(), "\\u{301}a\u{301}bé\\u{e000}"); + assert_eq!("abc".escape_debug().to_string(), "abc"); + assert_eq!("a c".escape_debug().to_string(), "a c"); + assert_eq!("éèê".escape_debug().to_string(), "éèê"); + assert_eq!("\r\n\t".escape_debug().to_string(), "\\r\\n\\t"); + assert_eq!("'\"\\".escape_debug().to_string(), "\\'\\\"\\\\"); + assert_eq!("\u{7f}\u{ff}".escape_debug().to_string(), "\\u{7f}\u{ff}"); + assert_eq!("\u{100}\u{ffff}".escape_debug().to_string(), "\u{100}\\u{ffff}"); + assert_eq!("\u{10000}\u{10ffff}".escape_debug().to_string(), "\u{10000}\\u{10ffff}"); + assert_eq!("ab\u{200b}".escape_debug().to_string(), "ab\\u{200b}"); + assert_eq!("\u{10d4ea}\r".escape_debug().to_string(), "\\u{10d4ea}\\r"); + assert_eq!("\u{301}a\u{301}bé\u{e000}".escape_debug().to_string(), + "\\u{301}a\u{301}bé\\u{e000}"); } #[test] fn test_escape_default() { - assert_eq!("abc".escape_default(), "abc"); - assert_eq!("a c".escape_default(), "a c"); - assert_eq!("éèê".escape_default(), "\\u{e9}\\u{e8}\\u{ea}"); - assert_eq!("\r\n\t".escape_default(), "\\r\\n\\t"); - assert_eq!("'\"\\".escape_default(), "\\'\\\"\\\\"); - assert_eq!("\u{7f}\u{ff}".escape_default(), "\\u{7f}\\u{ff}"); - assert_eq!("\u{100}\u{ffff}".escape_default(), "\\u{100}\\u{ffff}"); - assert_eq!("\u{10000}\u{10ffff}".escape_default(), "\\u{10000}\\u{10ffff}"); - assert_eq!("ab\u{200b}".escape_default(), "ab\\u{200b}"); - assert_eq!("\u{10d4ea}\r".escape_default(), "\\u{10d4ea}\\r"); + assert_eq!("abc".escape_default().to_string(), "abc"); + assert_eq!("a c".escape_default().to_string(), "a c"); + assert_eq!("éèê".escape_default().to_string(), "\\u{e9}\\u{e8}\\u{ea}"); + assert_eq!("\r\n\t".escape_default().to_string(), "\\r\\n\\t"); + assert_eq!("'\"\\".escape_default().to_string(), "\\'\\\"\\\\"); + assert_eq!("\u{7f}\u{ff}".escape_default().to_string(), "\\u{7f}\\u{ff}"); + assert_eq!("\u{100}\u{ffff}".escape_default().to_string(), "\\u{100}\\u{ffff}"); + assert_eq!("\u{10000}\u{10ffff}".escape_default().to_string(), "\\u{10000}\\u{10ffff}"); + assert_eq!("ab\u{200b}".escape_default().to_string(), "ab\\u{200b}"); + assert_eq!("\u{10d4ea}\r".escape_default().to_string(), "\\u{10d4ea}\\r"); } #[test] diff --git a/src/libgraphviz/lib.rs b/src/libgraphviz/lib.rs index fadcfaec4b268..6ee151a6484d9 100644 --- a/src/libgraphviz/lib.rs +++ b/src/libgraphviz/lib.rs @@ -538,7 +538,7 @@ impl<'a> LabelText<'a> { EscStr(s) => s, LabelStr(s) => { if s.contains('\\') { - (&*s).escape_default().into() + (&*s).escape_default().to_string().into() } else { s } diff --git a/src/libsyntax/attr/mod.rs b/src/libsyntax/attr/mod.rs index 0c3aedae71598..6c3f6c173edb3 100644 --- a/src/libsyntax/attr/mod.rs +++ b/src/libsyntax/attr/mod.rs @@ -634,7 +634,7 @@ impl LitKind { match *self { LitKind::Str(string, ast::StrStyle::Cooked) => { - let escaped = string.as_str().escape_default(); + let escaped = string.as_str().escape_default().to_string(); Token::Literal(token::Lit::Str_(Symbol::intern(&escaped)), None) } LitKind::Str(string, ast::StrStyle::Raw(n)) => { diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index c670f47b597da..cdf805176a293 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -606,7 +606,7 @@ pub trait PrintState<'a> { match lit.node { ast::LitKind::Str(st, style) => self.print_string(&st.as_str(), style), ast::LitKind::Err(st) => { - let st = st.as_str().escape_debug(); + let st = st.as_str().escape_debug().to_string(); let mut res = String::with_capacity(st.len() + 2); res.push('\''); res.push_str(&st); From 55216f82a69092fdb22fb779d3f64d3d756c7ed8 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 1 Feb 2019 13:34:07 +0100 Subject: [PATCH 3/6] Stabilize str::escape_* methods FCP: https://github.com/rust-lang/rust/issues/27791#issuecomment-376864727 --- src/liballoc/str.rs | 20 +++++++------------ src/liballoc/tests/lib.rs | 1 - src/libgraphviz/lib.rs | 1 - src/libsyntax/lib.rs | 1 - src/libsyntax_ext/lib.rs | 1 - .../sync-send-iterators-in-libcore.rs | 3 --- 6 files changed, 7 insertions(+), 20 deletions(-) diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 1705c80d5f50c..8daa088a21a26 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -452,9 +452,7 @@ impl str { /// escaped. /// /// [`char::escape_debug`]: primitive.char.html#method.escape_debug - #[unstable(feature = "str_escape", - reason = "return type may change to be an iterator", - issue = "27791")] + #[stable(feature = "str_escape", since = "1.34.0")] pub fn escape_debug(&self) -> EscapeDebug { let mut chars = self.chars(); EscapeDebug { @@ -469,9 +467,7 @@ impl str { /// Escapes each char in `s` with [`char::escape_default`]. /// /// [`char::escape_default`]: primitive.char.html#method.escape_default - #[unstable(feature = "str_escape", - reason = "return type may change to be an iterator", - issue = "27791")] + #[stable(feature = "str_escape", since = "1.34.0")] pub fn escape_default(&self) -> EscapeDefault { EscapeDefault { inner: self.chars().flat_map(CharEscapeDefault) } } @@ -479,9 +475,7 @@ impl str { /// Escapes each char in `s` with [`char::escape_unicode`]. /// /// [`char::escape_unicode`]: primitive.char.html#method.escape_unicode - #[unstable(feature = "str_escape", - reason = "return type may change to be an iterator", - issue = "27791")] + #[stable(feature = "str_escape", since = "1.34.0")] pub fn escape_unicode(&self) -> EscapeUnicode { EscapeUnicode { inner: self.chars().flat_map(CharEscapeUnicode) } } @@ -639,20 +633,20 @@ macro_rules! escape_types { inner: $Inner: ty, } )+) => {$( - #[unstable(feature = "str_escape", issue = "27791")] + #[stable(feature = "str_escape", since = "1.34.0")] #[derive(Clone, Debug)] pub struct $Name<'a> { inner: $Inner, } - #[unstable(feature = "str_escape", issue = "27791")] + #[stable(feature = "str_escape", since = "1.34.0")] impl<'a> fmt::Display for $Name<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.clone().try_for_each(|c| f.write_char(c)) } } - #[unstable(feature = "str_escape", issue = "27791")] + #[stable(feature = "str_escape", since = "1.34.0")] impl<'a> Iterator for $Name<'a> { type Item = char; @@ -677,7 +671,7 @@ macro_rules! escape_types { } } - #[unstable(feature = "str_escape", issue = "27791")] + #[stable(feature = "str_escape", since = "1.34.0")] impl<'a> FusedIterator for $Name<'a> {} )+} } diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index a76fd87a1a92d..2b63ac5c7d2f9 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -5,7 +5,6 @@ #![feature(pattern)] #![feature(repeat_generic_slice)] #![feature(slice_sort_by_cached_key)] -#![feature(str_escape)] #![feature(try_reserve)] #![feature(unboxed_closures)] #![feature(vecdeque_rotate)] diff --git a/src/libgraphviz/lib.rs b/src/libgraphviz/lib.rs index 6ee151a6484d9..e54ebae42cf4c 100644 --- a/src/libgraphviz/lib.rs +++ b/src/libgraphviz/lib.rs @@ -277,7 +277,6 @@ #![deny(rust_2018_idioms)] #![feature(nll)] -#![feature(str_escape)] use LabelText::*; diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index c844f9e2a91ee..a56cdf623bf43 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -15,7 +15,6 @@ #![feature(rustc_attrs)] #![feature(rustc_diagnostic_macros)] #![feature(slice_sort_by_cached_key)] -#![feature(str_escape)] #![feature(step_trait)] #![feature(try_trait)] #![feature(unicode_internals)] diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index 7d7fd03085912..0232c19e896ea 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -10,7 +10,6 @@ #![feature(proc_macro_span)] #![feature(decl_macro)] #![feature(nll)] -#![feature(str_escape)] #![feature(rustc_diagnostic_macros)] #![recursion_limit="256"] diff --git a/src/test/run-pass/threads-sendsync/sync-send-iterators-in-libcore.rs b/src/test/run-pass/threads-sendsync/sync-send-iterators-in-libcore.rs index 903bbf5163443..44beb9dc1e534 100644 --- a/src/test/run-pass/threads-sendsync/sync-send-iterators-in-libcore.rs +++ b/src/test/run-pass/threads-sendsync/sync-send-iterators-in-libcore.rs @@ -2,9 +2,6 @@ // pretty-expanded FIXME #23616 #![allow(warnings)] -#![feature(iter_empty)] -#![feature(iter_once)] -#![feature(str_escape)] use std::iter::{empty, once, repeat}; From 92cce78d066676f49524c07f72c7cdbf49cc0de8 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 2 Feb 2019 10:34:36 +0100 Subject: [PATCH 4/6] Move str::escape_* to libcore --- src/liballoc/str.rs | 118 ------------------------------ src/libcore/internal_macros.rs | 44 ++++++++++++ src/libcore/macros.rs | 46 ------------ src/libcore/str/mod.rs | 126 ++++++++++++++++++++++++++++++++- 4 files changed, 169 insertions(+), 165 deletions(-) diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 8daa088a21a26..a36804bddff32 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -29,13 +29,8 @@ #![allow(unused_imports)] use core::borrow::Borrow; -use core::fmt::{self, Write}; -use core::char; -use core::iter::{Chain, Flatten, FlatMap}; use core::str::pattern::{Pattern, Searcher, ReverseSearcher, DoubleEndedSearcher}; use core::mem; -use core::ops::Try; -use core::option; use core::ptr; use core::iter::FusedIterator; use core::unicode::conversions; @@ -446,40 +441,6 @@ impl str { return s; } - /// Escapes each char in `s` with [`char::escape_debug`]. - /// - /// Note: only extended grapheme codepoints that begin the string will be - /// escaped. - /// - /// [`char::escape_debug`]: primitive.char.html#method.escape_debug - #[stable(feature = "str_escape", since = "1.34.0")] - pub fn escape_debug(&self) -> EscapeDebug { - let mut chars = self.chars(); - EscapeDebug { - inner: chars.next() - .map(|first| first.escape_debug_ext(true)) - .into_iter() - .flatten() - .chain(chars.flat_map(CharEscapeDebugContinue)) - } - } - - /// Escapes each char in `s` with [`char::escape_default`]. - /// - /// [`char::escape_default`]: primitive.char.html#method.escape_default - #[stable(feature = "str_escape", since = "1.34.0")] - pub fn escape_default(&self) -> EscapeDefault { - EscapeDefault { inner: self.chars().flat_map(CharEscapeDefault) } - } - - /// Escapes each char in `s` with [`char::escape_unicode`]. - /// - /// [`char::escape_unicode`]: primitive.char.html#method.escape_unicode - #[stable(feature = "str_escape", since = "1.34.0")] - pub fn escape_unicode(&self) -> EscapeUnicode { - EscapeUnicode { inner: self.chars().flat_map(CharEscapeUnicode) } - } - /// Converts a [`Box`] into a [`String`] without copying or allocating. /// /// [`String`]: string/struct.String.html @@ -611,82 +572,3 @@ pub unsafe fn from_boxed_utf8_unchecked(v: Box<[u8]>) -> Box { Box::from_raw(Box::into_raw(v) as *mut str) } -impl_fn_for_zst! { - #[derive(Clone)] - struct CharEscapeDebugContinue impl Fn = |c: char| -> char::EscapeDebug { - c.escape_debug_ext(false) - }; - - #[derive(Clone)] - struct CharEscapeUnicode impl Fn = |c: char| -> char::EscapeUnicode { - c.escape_unicode() - }; - #[derive(Clone)] - struct CharEscapeDefault impl Fn = |c: char| -> char::EscapeDefault { - c.escape_default() - }; -} - -macro_rules! escape_types { - ($( - struct $Name: ident<'a> { - inner: $Inner: ty, - } - )+) => {$( - #[stable(feature = "str_escape", since = "1.34.0")] - #[derive(Clone, Debug)] - pub struct $Name<'a> { - inner: $Inner, - } - - #[stable(feature = "str_escape", since = "1.34.0")] - impl<'a> fmt::Display for $Name<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.clone().try_for_each(|c| f.write_char(c)) - } - } - - #[stable(feature = "str_escape", since = "1.34.0")] - impl<'a> Iterator for $Name<'a> { - type Item = char; - - #[inline] - fn next(&mut self) -> Option { self.inner.next() } - - #[inline] - fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } - - #[inline] - fn try_fold(&mut self, init: Acc, fold: Fold) -> R where - Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try - { - self.inner.try_fold(init, fold) - } - - #[inline] - fn fold(self, init: Acc, fold: Fold) -> Acc - where Fold: FnMut(Acc, Self::Item) -> Acc, - { - self.inner.fold(init, fold) - } - } - - #[stable(feature = "str_escape", since = "1.34.0")] - impl<'a> FusedIterator for $Name<'a> {} - )+} -} - -escape_types! { - struct EscapeDebug<'a> { - inner: Chain< - Flatten>, - FlatMap, char::EscapeDebug, CharEscapeDebugContinue> - >, - } - struct EscapeUnicode<'a> { - inner: FlatMap, char::EscapeUnicode, CharEscapeUnicode>, - } - struct EscapeDefault<'a> { - inner: FlatMap, char::EscapeDefault, CharEscapeDefault>, - } -} diff --git a/src/libcore/internal_macros.rs b/src/libcore/internal_macros.rs index d12800f712483..9f0f6fd49fba1 100644 --- a/src/libcore/internal_macros.rs +++ b/src/libcore/internal_macros.rs @@ -75,3 +75,47 @@ macro_rules! forward_ref_op_assign { } } } + +/// Create a zero-size type similar to a closure type, but named. +#[unstable(feature = "std_internals", issue = "0")] +macro_rules! impl_fn_for_zst { + ($( + $( #[$attr: meta] )* + // FIXME: when libcore is in the 2018 edition, use `?` repetition in + // $( <$( $li : lifetime ),+> )? + struct $Name: ident impl$( <$( $lifetime : lifetime ),+> )* Fn = + |$( $arg: ident: $ArgTy: ty ),*| -> $ReturnTy: ty + $body: block; + )+) => { + $( + $( #[$attr] )* + struct $Name; + + impl $( <$( $lifetime ),+> )* Fn<($( $ArgTy, )*)> for $Name { + #[inline] + extern "rust-call" fn call(&self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { + $body + } + } + + impl $( <$( $lifetime ),+> )* FnMut<($( $ArgTy, )*)> for $Name { + #[inline] + extern "rust-call" fn call_mut( + &mut self, + ($( $arg, )*): ($( $ArgTy, )*) + ) -> $ReturnTy { + Fn::call(&*self, ($( $arg, )*)) + } + } + + impl $( <$( $lifetime ),+> )* FnOnce<($( $ArgTy, )*)> for $Name { + type Output = $ReturnTy; + + #[inline] + extern "rust-call" fn call_once(self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { + Fn::call(&self, ($( $arg, )*)) + } + } + )+ + } +} diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index bc5d35f719114..664490c1997ef 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -749,49 +749,3 @@ mod builtin { ($cond:expr, $($arg:tt)+) => ({ /* compiler built-in */ }); } } - -/// Create a named zero-size type similar to a closure. -#[doc(hidden)] -#[macro_export] -#[unstable(feature = "std_internals", issue = "0")] -macro_rules! impl_fn_for_zst { - ($( - $( #[$attr: meta] )* - // FIXME: when libcore is in the 2018 edition, use `?` repetition in - // $( <$( $li : lifetime ),+> )? - struct $Name: ident impl$( <$( $lifetime : lifetime ),+> )* Fn = - |$( $arg: ident: $ArgTy: ty ),*| -> $ReturnTy: ty - $body: block; - )+) => { - $( - $( #[$attr] )* - struct $Name; - - impl $( <$( $lifetime ),+> )* Fn<($( $ArgTy, )*)> for $Name { - #[inline] - extern "rust-call" fn call(&self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { - $body - } - } - - impl $( <$( $lifetime ),+> )* FnMut<($( $ArgTy, )*)> for $Name { - #[inline] - extern "rust-call" fn call_mut( - &mut self, - ($( $arg, )*): ($( $ArgTy, )*) - ) -> $ReturnTy { - Fn::call(&*self, ($( $arg, )*)) - } - } - - impl $( <$( $lifetime ),+> )* FnOnce<($( $ArgTy, )*)> for $Name { - type Output = $ReturnTy; - - #[inline] - extern "rust-call" fn call_once(self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { - Fn::call(&self, ($( $arg, )*)) - } - } - )+ - } -} diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index eefed51733001..5a20e32a41916 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -8,10 +8,13 @@ use self::pattern::Pattern; use self::pattern::{Searcher, ReverseSearcher, DoubleEndedSearcher}; use char; -use fmt; +use fmt::{self, Write}; use iter::{Map, Cloned, FusedIterator, TrustedLen, TrustedRandomAccess, Filter}; +use iter::{Flatten, FlatMap, Chain}; use slice::{self, SliceIndex, Split as SliceSplit}; use mem; +use ops::Try; +use option; pub mod pattern; @@ -3945,6 +3948,56 @@ impl str { let me = unsafe { self.as_bytes_mut() }; me.make_ascii_lowercase() } + + /// Escapes each char in `s` with [`char::escape_debug`]. + /// + /// Note: only extended grapheme codepoints that begin the string will be + /// escaped. + /// + /// [`char::escape_debug`]: ../std/primitive.char.html#method.escape_debug + #[stable(feature = "str_escape", since = "1.34.0")] + pub fn escape_debug(&self) -> EscapeDebug { + let mut chars = self.chars(); + EscapeDebug { + inner: chars.next() + .map(|first| first.escape_debug_ext(true)) + .into_iter() + .flatten() + .chain(chars.flat_map(CharEscapeDebugContinue)) + } + } + + /// Escapes each char in `s` with [`char::escape_default`]. + /// + /// [`char::escape_default`]: ../std/primitive.char.html#method.escape_default + #[stable(feature = "str_escape", since = "1.34.0")] + pub fn escape_default(&self) -> EscapeDefault { + EscapeDefault { inner: self.chars().flat_map(CharEscapeDefault) } + } + + /// Escapes each char in `s` with [`char::escape_unicode`]. + /// + /// [`char::escape_unicode`]: ../std/primitive.char.html#method.escape_unicode + #[stable(feature = "str_escape", since = "1.34.0")] + pub fn escape_unicode(&self) -> EscapeUnicode { + EscapeUnicode { inner: self.chars().flat_map(CharEscapeUnicode) } + } +} + +impl_fn_for_zst! { + #[derive(Clone)] + struct CharEscapeDebugContinue impl Fn = |c: char| -> char::EscapeDebug { + c.escape_debug_ext(false) + }; + + #[derive(Clone)] + struct CharEscapeUnicode impl Fn = |c: char| -> char::EscapeUnicode { + c.escape_unicode() + }; + #[derive(Clone)] + struct CharEscapeDefault impl Fn = |c: char| -> char::EscapeDefault { + c.escape_default() + }; } #[stable(feature = "rust1", since = "1.0.0")] @@ -4131,3 +4184,74 @@ impl<'a> Iterator for EncodeUtf16<'a> { #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for EncodeUtf16<'_> {} + +/// The return type of [`str::escape_debug`]. +/// +/// [`str::escape_debug`]: ../../std/primitive.str.html#method.escape_debug +#[stable(feature = "str_escape", since = "1.34.0")] +#[derive(Clone, Debug)] +pub struct EscapeDebug<'a> { + inner: Chain< + Flatten>, + FlatMap, char::EscapeDebug, CharEscapeDebugContinue> + >, +} + +/// The return type of [`str::escape_default`]. +/// +/// [`str::escape_default`]: ../../std/primitive.str.html#method.escape_default +#[stable(feature = "str_escape", since = "1.34.0")] +#[derive(Clone, Debug)] +pub struct EscapeDefault<'a> { + inner: FlatMap, char::EscapeDefault, CharEscapeDefault>, +} + +/// The return type of [`str::escape_unicode`]. +/// +/// [`str::escape_unicode`]: ../../std/primitive.str.html#method.escape_unicode +#[stable(feature = "str_escape", since = "1.34.0")] +#[derive(Clone, Debug)] +pub struct EscapeUnicode<'a> { + inner: FlatMap, char::EscapeUnicode, CharEscapeUnicode>, +} + +macro_rules! escape_types_impls { + ($( $Name: ident ),+) => {$( + #[stable(feature = "str_escape", since = "1.34.0")] + impl<'a> fmt::Display for $Name<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.clone().try_for_each(|c| f.write_char(c)) + } + } + + #[stable(feature = "str_escape", since = "1.34.0")] + impl<'a> Iterator for $Name<'a> { + type Item = char; + + #[inline] + fn next(&mut self) -> Option { self.inner.next() } + + #[inline] + fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } + + #[inline] + fn try_fold(&mut self, init: Acc, fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + self.inner.try_fold(init, fold) + } + + #[inline] + fn fold(self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.inner.fold(init, fold) + } + } + + #[stable(feature = "str_escape", since = "1.34.0")] + impl<'a> FusedIterator for $Name<'a> {} + )+} +} + +escape_types_impls!(EscapeDebug, EscapeDefault, EscapeUnicode); From 114593d638c55f1754d675a6f2ff38a973a0d8ae Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 2 Feb 2019 10:37:40 +0100 Subject: [PATCH 5/6] Make the prema-unstable char::escape_debug_ext method crate-private --- src/libcore/char/methods.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libcore/char/methods.rs b/src/libcore/char/methods.rs index fbc9a4a6b8efa..3c600d9ad4347 100644 --- a/src/libcore/char/methods.rs +++ b/src/libcore/char/methods.rs @@ -189,10 +189,8 @@ impl char { /// An extended version of `escape_debug` that optionally permits escaping /// Extended Grapheme codepoints. This allows us to format characters like /// nonspacing marks better when they're at the start of a string. - #[doc(hidden)] - #[unstable(feature = "str_internals", issue = "0")] #[inline] - pub fn escape_debug_ext(self, escape_grapheme_extended: bool) -> EscapeDebug { + pub(crate) fn escape_debug_ext(self, escape_grapheme_extended: bool) -> EscapeDebug { let init_state = match self { '\t' => EscapeDefaultState::Backslash('t'), '\r' => EscapeDefaultState::Backslash('r'), From eb158f93505dbc265787ac671cbc728c80f8a39d Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 2 Feb 2019 11:25:46 +0100 Subject: [PATCH 6/6] Add doctests for str::escape_* --- src/libcore/str/mod.rs | 96 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 3 deletions(-) diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 5a20e32a41916..8a88fca131609 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -3949,12 +3949,42 @@ impl str { me.make_ascii_lowercase() } - /// Escapes each char in `s` with [`char::escape_debug`]. + /// Return an iterator that escapes each char in `s` with [`char::escape_debug`]. /// /// Note: only extended grapheme codepoints that begin the string will be /// escaped. /// /// [`char::escape_debug`]: ../std/primitive.char.html#method.escape_debug + /// + /// # Examples + /// + /// As an iterator: + /// + /// ``` + /// for c in "❤\n!".escape_debug() { + /// print!("{}", c); + /// } + /// println!(); + /// ``` + /// + /// Using `println!` directly: + /// + /// ``` + /// println!("{}", "❤\n!".escape_debug()); + /// ``` + /// + /// + /// Both are equivalent to: + /// + /// ``` + /// println!("❤\\n!"); + /// ``` + /// + /// Using `to_string`: + /// + /// ``` + /// assert_eq!("❤\n!".escape_debug().to_string(), "❤\\n!"); + /// ``` #[stable(feature = "str_escape", since = "1.34.0")] pub fn escape_debug(&self) -> EscapeDebug { let mut chars = self.chars(); @@ -3967,17 +3997,77 @@ impl str { } } - /// Escapes each char in `s` with [`char::escape_default`]. + /// Return an iterator that escapes each char in `s` with [`char::escape_default`]. /// /// [`char::escape_default`]: ../std/primitive.char.html#method.escape_default + /// + /// # Examples + /// + /// As an iterator: + /// + /// ``` + /// for c in "❤\n!".escape_default() { + /// print!("{}", c); + /// } + /// println!(); + /// ``` + /// + /// Using `println!` directly: + /// + /// ``` + /// println!("{}", "❤\n!".escape_default()); + /// ``` + /// + /// + /// Both are equivalent to: + /// + /// ``` + /// println!("\\u{{2764}}\n!"); + /// ``` + /// + /// Using `to_string`: + /// + /// ``` + /// assert_eq!("❤\n!".escape_default().to_string(), "\\u{2764}\\n!"); + /// ``` #[stable(feature = "str_escape", since = "1.34.0")] pub fn escape_default(&self) -> EscapeDefault { EscapeDefault { inner: self.chars().flat_map(CharEscapeDefault) } } - /// Escapes each char in `s` with [`char::escape_unicode`]. + /// Return an iterator that escapes each char in `s` with [`char::escape_unicode`]. /// /// [`char::escape_unicode`]: ../std/primitive.char.html#method.escape_unicode + /// + /// # Examples + /// + /// As an iterator: + /// + /// ``` + /// for c in "❤\n!".escape_unicode() { + /// print!("{}", c); + /// } + /// println!(); + /// ``` + /// + /// Using `println!` directly: + /// + /// ``` + /// println!("{}", "❤\n!".escape_unicode()); + /// ``` + /// + /// + /// Both are equivalent to: + /// + /// ``` + /// println!("\\u{{2764}}\\u{{a}}\\u{{21}}"); + /// ``` + /// + /// Using `to_string`: + /// + /// ``` + /// assert_eq!("❤\n!".escape_unicode().to_string(), "\\u{2764}\\u{a}\\u{21}"); + /// ``` #[stable(feature = "str_escape", since = "1.34.0")] pub fn escape_unicode(&self) -> EscapeUnicode { EscapeUnicode { inner: self.chars().flat_map(CharEscapeUnicode) }