Skip to content

Commit

Permalink
Rollup merge of rust-lang#125362 - joboet:tait_hack, r=Nilstrieb
Browse files Browse the repository at this point in the history
Actually use TAIT instead of emulating it

`core`'s `impl_fn_for_zst` macro is just a hacky way of emulating TAIT. TAIT has become stable enough to be used [in other places](https://github.com/rust-lang/rust/blob/e8fbd991287f637f95016a71ddc13438415bbe59/library/std/src/backtrace.rs#L431) inside the standard library, so let's use it in `core` as well.
  • Loading branch information
GuillaumeGomez committed May 23, 2024
2 parents 4ee97fc + c398b2c commit 1e4bde1
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 117 deletions.
41 changes: 0 additions & 41 deletions library/core/src/internal_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,47 +80,6 @@ macro_rules! forward_ref_op_assign {
}
}

/// 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
Expand Down
1 change: 1 addition & 0 deletions library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
9 changes: 2 additions & 7 deletions library/core/src/slice/ascii.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ impl [u8] {
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.
Expand Down Expand Up @@ -190,12 +190,7 @@ impl [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) + Copy;

/// An iterator over the escaped version of a byte slice.
///
Expand Down
6 changes: 4 additions & 2 deletions library/core/src/str/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Filter<SliceSplit<'a, u8, IsAsciiWhitespace>, BytesIsNotEmpty>, UnsafeBytesToStr>,
pub(super) inner: Map<
Filter<SliceSplit<'a, u8, IsAsciiWhitespace>, BytesIsNotEmpty<'a>>,
UnsafeBytesToStr<'a>,
>,
}

/// An iterator over the substrings of a string,
Expand Down
104 changes: 37 additions & 67 deletions library/core/src/str/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -983,7 +983,7 @@ impl str {
#[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.
Expand Down Expand Up @@ -1032,8 +1032,13 @@ impl str {
#[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 }
}

Expand Down Expand Up @@ -1085,7 +1090,11 @@ impl str {
#[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.
Expand Down Expand Up @@ -2636,14 +2645,19 @@ impl 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`].
Expand Down Expand Up @@ -2681,7 +2695,7 @@ impl str {
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`].
Expand Down Expand Up @@ -2719,7 +2733,7 @@ impl str {
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) }
}
}

Expand Down Expand Up @@ -2750,59 +2764,15 @@ impl Default for &mut str {
}
}

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) + 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<dyn Error>` and `impl<E> From<E> for Box<dyn Error>` not overlap.
#[stable(feature = "rust1", since = "1.0.0")]
Expand Down

0 comments on commit 1e4bde1

Please sign in to comment.