diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index db7cace49ae8f..5e82d4acbdb82 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -181,8 +181,7 @@ pub fn join_path_syms(path: impl IntoIterator>) -> St for sym in iter { let sym = *sym.borrow(); debug_assert_ne!(sym, kw::PathRoot); - s.push_str("::"); - s.push_str(sym.as_str()); + s.extend(["::", sym.as_str()]); } s } @@ -201,8 +200,7 @@ pub fn join_path_idents(path: impl IntoIterator>) -> S for ident in iter { let ident = *ident.borrow(); debug_assert_ne!(ident.name, kw::PathRoot); - s.push_str("::"); - s.push_str(&ident.to_string()); + s.extend(["::", &ident.to_string()]); } s } diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index 31855cbd4e6cc..96013e72186d0 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -334,9 +334,7 @@ impl<'cx, 'a> Context<'cx, 'a> { if self.paths.contains(&path_ident) { return; } else { - self.fmt_string.push_str(" "); - self.fmt_string.push_str(path_ident.as_str()); - self.fmt_string.push_str(" = {:?}\n"); + self.fmt_string.extend([" ", path_ident.as_str(), " = {:?}\n"]); let _ = self.paths.insert(path_ident); } let curr_capture_idx = self.capture_decls.len(); diff --git a/library/alloc/src/borrow.rs b/library/alloc/src/borrow.rs index aa973e0bb0240..c993df1b5bdaf 100644 --- a/library/alloc/src/borrow.rs +++ b/library/alloc/src/borrow.rs @@ -13,8 +13,6 @@ use core::ops::{Deref, DerefPure}; use Cow::*; use crate::fmt; -#[cfg(not(no_global_oom_handling))] -use crate::string::String; // FIXME(inference): const bounds removed due to inference regressions found by crater; // see https://github.com/rust-lang/rust/issues/147964 @@ -496,12 +494,14 @@ impl<'a> AddAssign<&'a str> for Cow<'a, str> { if self.is_empty() { *self = Cow::Borrowed(rhs) } else if !rhs.is_empty() { - if let Cow::Borrowed(lhs) = *self { - let mut s = String::with_capacity(lhs.len() + rhs.len()); - s.push_str(lhs); - *self = Cow::Owned(s); + match self { + Self::Borrowed(lhs) => { + *self = Cow::Owned([lhs, rhs].into_iter().collect()); + } + Self::Owned(lhs) => { + lhs.push_str(&rhs); + } } - self.to_mut().push_str(rhs); } } } @@ -513,12 +513,14 @@ impl<'a> AddAssign> for Cow<'a, str> { if self.is_empty() { *self = rhs } else if !rhs.is_empty() { - if let Cow::Borrowed(lhs) = *self { - let mut s = String::with_capacity(lhs.len() + rhs.len()); - s.push_str(lhs); - *self = Cow::Owned(s); + match self { + Self::Borrowed(lhs) => { + *self = Cow::Owned([&*lhs, &*rhs].into_iter().collect()); + } + Self::Owned(lhs) => { + lhs.push_str(&rhs); + } } - self.to_mut().push_str(&rhs); } } } diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index f5ba71c288334..81aa6773f8d99 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -54,7 +54,7 @@ use core::ops::AddAssign; use core::ops::Bound::{Excluded, Included, Unbounded}; use core::ops::{self, Range, RangeBounds}; use core::str::pattern::{Pattern, Utf8Pattern}; -use core::{fmt, hash, ptr, slice}; +use core::{array, fmt, hash, ptr, slice}; #[cfg(not(no_global_oom_handling))] use crate::alloc::Allocator; @@ -2203,6 +2203,35 @@ impl String { let slice = self.vec.leak(); unsafe { from_utf8_unchecked_mut(slice) } } + + /// SAFETY: `impl AsRef for S` must be stable + #[cfg(not(no_global_oom_handling))] + unsafe fn extend_many>(&mut self, vals: &[S]) { + let additional = vals.iter().fold(0usize, |a, s| a.saturating_add(s.as_ref().len())); + self.reserve(additional); + + let mut spare = self.vec.spare_capacity_mut().as_mut_ptr().cast_init(); + for val in vals { + let val = val.as_ref(); + // SAFETY: + // - `val` is a valid &str, so `val.as_ptr()` is valid + // for `val.len()` bytes and properly initialized. + // - `spare` points to valid spare capacity in the Vec + // with enough space for `val.len()` bytes. + // This is guaranteed because the caller ensures + // that `.as_ref()` is stable, and the saturating + // addition stops undercounting by overflow. + // - Both pointers are byte-aligned and the regions cannot overlap. + unsafe { + ptr::copy_nonoverlapping(val.as_ptr(), spare, val.len()); + spare = spare.add(val.len()); + } + } + + let new_len = self.vec.len() + additional; + // SAFETY: the elements have just been initialized + unsafe { self.vec.set_len(new_len) } + } } impl FromUtf8Error { @@ -2502,7 +2531,8 @@ impl<'a> Extend<&'a char> for String { #[stable(feature = "rust1", since = "1.0.0")] impl<'a> Extend<&'a str> for String { fn extend>(&mut self, iter: I) { - iter.into_iter().for_each(move |s| self.push_str(s)); + // SAFETY: `impl AsRef for &str` is stable + unsafe { self.extend_many_chunked(iter.into_iter()) } } #[inline] @@ -2515,7 +2545,8 @@ impl<'a> Extend<&'a str> for String { #[stable(feature = "box_str2", since = "1.45.0")] impl Extend> for String { fn extend>>(&mut self, iter: I) { - iter.into_iter().for_each(move |s| self.push_str(&s)); + // SAFETY: `impl AsRef for Box` is stable + unsafe { self.extend_many_chunked(iter.into_iter()) } } } @@ -2523,7 +2554,8 @@ impl Extend> for String { #[stable(feature = "extend_string", since = "1.4.0")] impl Extend for String { fn extend>(&mut self, iter: I) { - iter.into_iter().for_each(move |s| self.push_str(&s)); + // SAFETY: `impl AsRef for String` is stable + unsafe { self.extend_many_chunked(iter.into_iter()) } } #[inline] @@ -2536,7 +2568,8 @@ impl Extend for String { #[stable(feature = "herd_cows", since = "1.19.0")] impl<'a> Extend> for String { fn extend>>(&mut self, iter: I) { - iter.into_iter().for_each(move |s| self.push_str(&s)); + // SAFETY: `impl AsRef for Cow<'a, str>` is stable + unsafe { self.extend_many_chunked(iter.into_iter()) } } #[inline] @@ -2573,6 +2606,71 @@ impl<'a> Extend<&'a core::ascii::Char> for String { } } +#[cfg(not(no_global_oom_handling))] +trait ExtendManySpec { + /// SAFETY: `impl AsRef for S` must be stable + unsafe fn extend_many_chunked(&mut self, iter: I); +} + +#[cfg(not(no_global_oom_handling))] +impl ExtendManySpec for String +where + S: AsRef, + I: Iterator, +{ + default unsafe fn extend_many_chunked(&mut self, mut iter: I) { + let mut repeat = true; + while repeat { + let chunk = match iter.next_chunk::<8>() { + Ok(chunk) => chunk.into_iter(), + Err(partial_chunk) => { + repeat = false; + partial_chunk + } + }; + + // SAFETY: the caller ensures that `impl AsRef for S` is stable + unsafe { self.extend_many(chunk.as_slice()) } + } + } +} + +// TODO: specialization thinks that this is a conflicting implementation +/* +#[cfg(not(no_global_oom_handling))] +impl<'a, S> ExtendManySpec> for String +where + S: AsRef, +{ + unsafe fn extend_many_chunked(&mut self, iter: slice::Iter<'a, S>) { + // SAFETY: the caller ensures that `impl AsRef for S` is stable + unsafe { self.extend_many(iter.as_slice()) } + } +} +*/ + +#[cfg(not(no_global_oom_handling))] +impl ExtendManySpec> for String +where + S: AsRef, +{ + unsafe fn extend_many_chunked(&mut self, iter: array::IntoIter) { + // SAFETY: the caller ensures that `impl AsRef for S` is stable + unsafe { self.extend_many(iter.as_slice()) } + } +} + +#[cfg(not(no_global_oom_handling))] +impl ExtendManySpec> for String +where + S: AsRef, +{ + unsafe fn extend_many_chunked(&mut self, iter: vec::IntoIter) { + // SAFETY: the caller ensures that `impl AsRef for S` is stable + unsafe { self.extend_many(iter.as_slice()) } + } +} + /// A convenience impl that delegates to the impl for `&str`. /// /// # Examples diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index a4d377432c914..d623c94ff622a 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -1645,9 +1645,7 @@ pub(crate) fn plain_text_from_events<'a>( match &event { Event::Text(text) => s.push_str(text), Event::Code(code) => { - s.push('`'); - s.push_str(code); - s.push('`'); + s.extend(["`", code, "`"]); } Event::HardBreak | Event::SoftBreak => s.push(' '), Event::Start(Tag::CodeBlock(..)) => break, diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 0c511738d7c8a..69a335fe678e2 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -2257,8 +2257,7 @@ pub(crate) fn compare_names(left: &str, right: &str) -> Ordering { pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String { let mut s = join_path_syms(&cx.current); - s.push_str("::"); - s.push_str(item.name.unwrap().as_str()); + s.extend(["::", item.name.unwrap().as_str()]); s }