From fde4a22da21e9d9b1488f075eae0092a4d0f93d0 Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 21 May 2024 15:57:14 +0200 Subject: [PATCH 1/2] core: actually use TAIT instead of emulating it --- library/core/src/internal_macros.rs | 41 ----------- library/core/src/lib.rs | 1 + library/core/src/slice/ascii.rs | 9 +-- library/core/src/str/iter.rs | 6 +- library/core/src/str/mod.rs | 104 ++++++++++------------------ 5 files changed, 44 insertions(+), 117 deletions(-) diff --git a/library/core/src/internal_macros.rs b/library/core/src/internal_macros.rs index bf53b2245ac..d3a4d6aff2d 100644 --- a/library/core/src/internal_macros.rs +++ b/library/core/src/internal_macros.rs @@ -80,47 +80,6 @@ fn $method(&mut self, other: &$u) { } } -/// Create a zero-size type similar to a closure type, but named. -macro_rules! impl_fn_for_zst { - ($( - $( #[$attr: meta] )* - 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, )*)) - } - } - )+ - } -} - /// A macro for defining `#[cfg]` if-else statements. /// /// `cfg_if` is similar to the `if/elif` C preprocessor macro by allowing definition of a cascade diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index dcf68b36c7a..206d1ab8852 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -255,6 +255,7 @@ #![feature(trait_alias)] #![feature(transparent_unions)] #![feature(try_blocks)] +#![feature(type_alias_impl_trait)] #![feature(unboxed_closures)] #![feature(unsized_fn_params)] #![feature(with_negative_coherence)] diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs index 8ad045275ad..20f5e321d4f 100644 --- a/library/core/src/slice/ascii.rs +++ b/library/core/src/slice/ascii.rs @@ -108,7 +108,7 @@ pub fn make_ascii_lowercase(&mut self) { without modifying the original"] #[stable(feature = "inherent_ascii_escape", since = "1.60.0")] pub fn escape_ascii(&self) -> EscapeAscii<'_> { - EscapeAscii { inner: self.iter().flat_map(EscapeByte) } + EscapeAscii { inner: self.iter().flat_map(|byte| byte.escape_ascii()) } } /// Returns a byte slice with leading ASCII whitespace bytes removed. @@ -190,12 +190,7 @@ pub const fn trim_ascii(&self) -> &[u8] { } } -impl_fn_for_zst! { - #[derive(Clone)] - struct EscapeByte impl Fn = |byte: &u8| -> ascii::EscapeDefault { - ascii::escape_default(*byte) - }; -} +type EscapeByte = impl (Fn(&u8) -> ascii::EscapeDefault) + Clone; /// An iterator over the escaped version of a byte slice. /// diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index 19627f28e64..d61f04102e5 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -1274,8 +1274,10 @@ pub struct SplitWhitespace<'a> { #[stable(feature = "split_ascii_whitespace", since = "1.34.0")] #[derive(Clone, Debug)] pub struct SplitAsciiWhitespace<'a> { - pub(super) inner: - Map, BytesIsNotEmpty>, UnsafeBytesToStr>, + pub(super) inner: Map< + Filter, BytesIsNotEmpty<'a>>, + UnsafeBytesToStr<'a>, + >, } /// An iterator over the substrings of a string, diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 669cdc92e35..3e8cccd5c18 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -983,7 +983,7 @@ pub fn bytes(&self) -> Bytes<'_> { #[cfg_attr(not(test), rustc_diagnostic_item = "str_split_whitespace")] #[inline] pub fn split_whitespace(&self) -> SplitWhitespace<'_> { - SplitWhitespace { inner: self.split(IsWhitespace).filter(IsNotEmpty) } + SplitWhitespace { inner: self.split(char::is_whitespace).filter(|s| !s.is_empty()) } } /// Splits a string slice by ASCII whitespace. @@ -1032,8 +1032,13 @@ pub fn split_whitespace(&self) -> SplitWhitespace<'_> { #[stable(feature = "split_ascii_whitespace", since = "1.34.0")] #[inline] pub fn split_ascii_whitespace(&self) -> SplitAsciiWhitespace<'_> { - let inner = - self.as_bytes().split(IsAsciiWhitespace).filter(BytesIsNotEmpty).map(UnsafeBytesToStr); + let inner = self + .as_bytes() + .split(u8::is_ascii_whitespace) + .filter(|s| !s.is_empty()) + // SAFETY: the byte slice came from a string and was only split + // along character boundaries, so the resulting slices are strings. + .map(|bytes| unsafe { from_utf8_unchecked(bytes) }); SplitAsciiWhitespace { inner } } @@ -1085,7 +1090,11 @@ pub fn split_ascii_whitespace(&self) -> SplitAsciiWhitespace<'_> { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn lines(&self) -> Lines<'_> { - Lines(self.split_inclusive('\n').map(LinesMap)) + Lines(self.split_inclusive('\n').map(|line| { + let Some(line) = line.strip_suffix('\n') else { return line }; + let Some(line) = line.strip_suffix('\r') else { return line }; + line + })) } /// An iterator over the lines of a string. @@ -2636,14 +2645,19 @@ pub const fn trim_ascii(&self) -> &str { #[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(EscapeDebugExtArgs::ESCAPE_ALL)) - .into_iter() - .flatten() - .chain(chars.flat_map(CharEscapeDebugContinue)), - } + let first = chars + .next() + .map(|first| first.escape_debug_ext(EscapeDebugExtArgs::ESCAPE_ALL)) + .into_iter() + .flatten(); + let inner = first.chain(chars.flat_map(|c| { + c.escape_debug_ext(EscapeDebugExtArgs { + escape_grapheme_extended: false, + escape_single_quote: true, + escape_double_quote: true, + }) + })); + EscapeDebug { inner } } /// Return an iterator that escapes each char in `self` with [`char::escape_default`]. @@ -2681,7 +2695,7 @@ pub fn escape_debug(&self) -> EscapeDebug<'_> { without modifying the original"] #[stable(feature = "str_escape", since = "1.34.0")] pub fn escape_default(&self) -> EscapeDefault<'_> { - EscapeDefault { inner: self.chars().flat_map(CharEscapeDefault) } + EscapeDefault { inner: self.chars().flat_map(char::escape_default) } } /// Return an iterator that escapes each char in `self` with [`char::escape_unicode`]. @@ -2719,7 +2733,7 @@ pub fn escape_default(&self) -> EscapeDefault<'_> { without modifying the original"] #[stable(feature = "str_escape", since = "1.34.0")] pub fn escape_unicode(&self) -> EscapeUnicode<'_> { - EscapeUnicode { inner: self.chars().flat_map(CharEscapeUnicode) } + EscapeUnicode { inner: self.chars().flat_map(char::escape_unicode) } } } @@ -2750,59 +2764,15 @@ fn default() -> Self { } } -impl_fn_for_zst! { - /// A nameable, cloneable fn type - #[derive(Clone)] - struct LinesMap impl<'a> Fn = |line: &'a str| -> &'a str { - let Some(line) = line.strip_suffix('\n') else { return line }; - let Some(line) = line.strip_suffix('\r') else { return line }; - line - }; - - #[derive(Clone)] - struct CharEscapeDebugContinue impl Fn = |c: char| -> char::EscapeDebug { - c.escape_debug_ext(EscapeDebugExtArgs { - escape_grapheme_extended: false, - escape_single_quote: true, - escape_double_quote: true - }) - }; - - #[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() - }; - - #[derive(Clone)] - struct IsWhitespace impl Fn = |c: char| -> bool { - c.is_whitespace() - }; - - #[derive(Clone)] - struct IsAsciiWhitespace impl Fn = |byte: &u8| -> bool { - byte.is_ascii_whitespace() - }; - - #[derive(Clone)] - struct IsNotEmpty impl<'a, 'b> Fn = |s: &'a &'b str| -> bool { - !s.is_empty() - }; - - #[derive(Clone)] - struct BytesIsNotEmpty impl<'a, 'b> Fn = |s: &'a &'b [u8]| -> bool { - !s.is_empty() - }; - - #[derive(Clone)] - struct UnsafeBytesToStr impl<'a> Fn = |bytes: &'a [u8]| -> &'a str { - // SAFETY: not safe - unsafe { from_utf8_unchecked(bytes) } - }; -} +type LinesMap = impl (Fn(&str) -> &str) + Clone; +type CharEscapeDebugContinue = impl (FnMut(char) -> char::EscapeDebug) + Clone; +type CharEscapeUnicode = impl (Fn(char) -> char::EscapeUnicode) + Clone; +type CharEscapeDefault = impl (Fn(char) -> char::EscapeDefault) + Clone; +type IsWhitespace = impl (Fn(char) -> bool) + Clone; +type IsAsciiWhitespace = impl (Fn(&u8) -> bool) + Clone; +type IsNotEmpty = impl (Fn(&&str) -> bool) + Clone; +type BytesIsNotEmpty<'a> = impl (FnMut(&&'a [u8]) -> bool) + Clone; +type UnsafeBytesToStr<'a> = impl (FnMut(&'a [u8]) -> &'a str) + Clone; // This is required to make `impl From<&str> for Box` and `impl From for Box` not overlap. #[stable(feature = "rust1", since = "1.0.0")] From c398b2c1933f284977cdc4e3866d8c67649cd581 Mon Sep 17 00:00:00 2001 From: joboet Date: Thu, 23 May 2024 13:38:52 +0200 Subject: [PATCH 2/2] core: use `Copy` in TAIT to fix clippy lint --- library/core/src/slice/ascii.rs | 2 +- library/core/src/str/mod.rs | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs index 20f5e321d4f..19c91ba2eb9 100644 --- a/library/core/src/slice/ascii.rs +++ b/library/core/src/slice/ascii.rs @@ -190,7 +190,7 @@ pub const fn trim_ascii(&self) -> &[u8] { } } -type EscapeByte = impl (Fn(&u8) -> ascii::EscapeDefault) + Clone; +type EscapeByte = impl (Fn(&u8) -> ascii::EscapeDefault) + Copy; /// An iterator over the escaped version of a byte slice. /// diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 3e8cccd5c18..edda4d1b687 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -2764,15 +2764,15 @@ fn default() -> Self { } } -type LinesMap = impl (Fn(&str) -> &str) + Clone; -type CharEscapeDebugContinue = impl (FnMut(char) -> char::EscapeDebug) + Clone; -type CharEscapeUnicode = impl (Fn(char) -> char::EscapeUnicode) + Clone; -type CharEscapeDefault = impl (Fn(char) -> char::EscapeDefault) + Clone; -type IsWhitespace = impl (Fn(char) -> bool) + Clone; -type IsAsciiWhitespace = impl (Fn(&u8) -> bool) + Clone; -type IsNotEmpty = impl (Fn(&&str) -> bool) + Clone; -type BytesIsNotEmpty<'a> = impl (FnMut(&&'a [u8]) -> bool) + Clone; -type UnsafeBytesToStr<'a> = impl (FnMut(&'a [u8]) -> &'a str) + Clone; +type LinesMap = impl (Fn(&str) -> &str) + Copy; +type CharEscapeDebugContinue = impl (FnMut(char) -> char::EscapeDebug) + Copy; +type CharEscapeUnicode = impl (Fn(char) -> char::EscapeUnicode) + Copy; +type CharEscapeDefault = impl (Fn(char) -> char::EscapeDefault) + Copy; +type IsWhitespace = impl (Fn(char) -> bool) + Copy; +type IsAsciiWhitespace = impl (Fn(&u8) -> bool) + Copy; +type IsNotEmpty = impl (Fn(&&str) -> bool) + Copy; +type BytesIsNotEmpty<'a> = impl (FnMut(&&'a [u8]) -> bool) + Copy; +type UnsafeBytesToStr<'a> = impl (FnMut(&'a [u8]) -> &'a str) + Copy; // This is required to make `impl From<&str> for Box` and `impl From for Box` not overlap. #[stable(feature = "rust1", since = "1.0.0")]