From e0f657c7d072e77cca5119ba214052a960ec0c6f Mon Sep 17 00:00:00 2001 From: Max Dexheimer Date: Sun, 27 Sep 2026 13:14:10 +0200 Subject: [PATCH 1/3] Clarify some things in the docs of `Global` --- library/alloc/src/alloc.rs | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index a900ed91d720c..de65820cf9a75 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -43,30 +43,38 @@ unsafe extern "Rust" { fn __rust_no_alloc_shim_is_unstable_v2(); } -/// The global memory allocator. +/// A wrapper for the global allocator. /// /// This type implements the [`Allocator`] trait by forwarding calls /// to the allocator registered with the `#[global_allocator]` attribute /// if there is one, or the `std` crate’s default. /// +/// `Global` is not "the global allocator", but a wrapper for it. +/// If the global allocator used `Global`, it would call itself recursively. +/// To avoid this, `Global` does not implement [`GlobalAlloc`]. +/// /// Similar to [`alloc`], [`dealloc`], and the other global allocation functions, -/// the way in which calls are forwarded to the allocator registered with +/// when and how calls are forwarded to the allocator registered with /// `#[global_allocator]` is unspecified. See their safety docs for more information. /// -/// This allocator must be treated like an opaque allocator that only guarantees -/// the contract described in the docs of [`Allocator`], as well as the following -/// things: +/// In particular, even if you know which allocator was registered +/// as the global allocator, calling it directly is not the same as calling +/// `Global`. You cannot deallocate memory from one using the other. +/// +/// `Global` must be treated like an opaque allocator that only guarantees the contract +/// described in the docs of [`Allocator`], as well as the following: /// * All instances of `Global` are [*equivalent*]. /// * Allocations from `Global` are only invalidated by calls to de-/reallocating functions. /// If no such call is made, then the allocation will live for the rest of the program. -/// * The global allocation functions are equivalent to the methods on `Global`, except -/// that they disallow zero-sized allocations, and implicitly ignore any returned -/// excess size. +/// * The global allocation functions in the [`alloc`](self) module are equivalent to the +/// methods on `Global`, except that they disallow zero-sized allocations, and implicitly +/// ignore any returned excess size. +/// In particular, you may deallocate memory from [`alloc::alloc`](self::alloc) using +/// [`Global.deallocate`](Global::deallocate) and vice-versa. /// /// Note that the current implementation of `Global` does not take advantage of -/// some features of [`Allocator`], such as zero-sized allocations (which currently always +/// some features of [`Allocator`], such as zero-sized allocations (which currently /// return a dangling pointer) and overallocating. -/// /// This may change in the future. You must not rely on it for correctness! /// /// [*equivalent*]: Allocator#equivalent-allocators From 2ccceb061620c801600b1c5bb31ee6f2e739ce63 Mon Sep 17 00:00:00 2001 From: Yilin Chen <1479826151@qq.com> Date: Tue, 29 Sep 2026 16:34:25 +0800 Subject: [PATCH 2/3] Add safety comments for alloc::string --- library/alloc/src/string.rs | 41 ++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index b363b6862f7e5..03c07b15712bb 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -802,7 +802,7 @@ impl String { let (chunks, []) = v.as_chunks::<2>() else { return Err(FromUtf16Error { kind: FromUtf16ErrorKind::OddBytes }); }; - // ignore-tidy-undocumented-unsafe + // SAFETY: The aligned part of `v` can be transmuted into a valid u16 slice. match (cfg!(target_endian = "little"), unsafe { v.align_to::() }) { (true, ([], v, [])) => Self::from_utf16(v), _ => { @@ -838,7 +838,7 @@ impl String { #[cfg(not(no_global_oom_handling))] #[stable(feature = "str_from_utf16_endian", since = "1.98.0")] pub fn from_utf16le_lossy(v: &[u8]) -> String { - // ignore-tidy-undocumented-unsafe + // SAFETY: The aligned part of `v` can be transmuted into a valid u16 slice. match (cfg!(target_endian = "little"), unsafe { v.align_to::() }) { (true, ([], v, [])) => Self::from_utf16_lossy(v), (true, ([], v, [_remainder])) => Self::from_utf16_lossy(v) + "\u{FFFD}", @@ -877,7 +877,7 @@ impl String { let (chunks, []) = v.as_chunks::<2>() else { return Err(FromUtf16Error { kind: FromUtf16ErrorKind::OddBytes }); }; - // ignore-tidy-undocumented-unsafe + // SAFETY: The aligned part of `v` can be transmuted into a valid u16 slice. match (cfg!(target_endian = "big"), unsafe { v.align_to::() }) { (true, ([], v, [])) => Self::from_utf16(v), _ => { @@ -913,7 +913,7 @@ impl String { #[cfg(not(no_global_oom_handling))] #[stable(feature = "str_from_utf16_endian", since = "1.98.0")] pub fn from_utf16be_lossy(v: &[u8]) -> String { - // ignore-tidy-undocumented-unsafe + // SAFETY: The aligned part of `v` can be transmuted into a valid u16 slice. match (cfg!(target_endian = "big"), unsafe { v.align_to::() }) { (true, ([], v, [])) => Self::from_utf16_lossy(v), (true, ([], v, [_remainder])) => Self::from_utf16_lossy(v) + "\u{FFFD}", @@ -1143,7 +1143,8 @@ impl String { let additional: Saturating = slice.iter().map(|x| Saturating(x.len())).sum(); self.reserve(additional.0); let (ptr, len, cap) = core::mem::take(self).into_raw_parts(); - // ignore-tidy-undocumented-unsafe + // SAFETY: `self` have reserved enough space for strs in `slice`, and we are copying from valid UTF-8 slices. + // Therefore all unsafe operations are safe. unsafe { let mut dst = ptr.add(len); for new in slice { @@ -1532,7 +1533,7 @@ impl String { pub fn pop(&mut self) -> Option { let ch = self.chars().rev().next()?; let newlen = self.len() - ch.len_utf8(); - // ignore-tidy-undocumented-unsafe + // SAFETY: `newlen` is less than old length. unsafe { self.vec.set_len(newlen); } @@ -1571,7 +1572,11 @@ impl String { let next = idx + ch.len_utf8(); let len = self.len(); - // ignore-tidy-undocumented-unsafe + // SAFETY: + // * Since `next` is not bigger than `len`, `self.vec.as_ptr().add(next)` + // is valid for reads and `self.vec.as_mut_ptr().add(idx)` is valid for writes. + // Both pointers are valid for `len - next` bytes. + // * new length is less than old length. unsafe { ptr::copy(self.vec.as_ptr().add(next), self.vec.as_mut_ptr().add(idx), len - next); self.vec.set_len(len - (next - idx)); @@ -1645,7 +1650,7 @@ impl String { len += count; } - // ignore-tidy-undocumented-unsafe + // SAFETY: `len` is less than or equal to the original length of the string. unsafe { self.vec.set_len(len); } @@ -1954,7 +1959,7 @@ impl String { pub fn split_off(&mut self, at: usize) -> String { assert!(self.is_char_boundary(at)); let other = self.vec.split_off(at); - // ignore-tidy-undocumented-unsafe + // SAFETY: `other` contains only valid UTF-8 because `at` is on a `UTF-8` code point boundary. unsafe { String::from_utf8_unchecked(other) } } @@ -2224,7 +2229,7 @@ impl String { #[inline] pub fn into_boxed_str(self) -> Box { let slice = self.vec.into_boxed_slice(); - // ignore-tidy-undocumented-unsafe + // SAFETY: `slice` contains only valid UTF-8 because `String` is guaranteed to be valid UTF-8. unsafe { from_boxed_utf8_unchecked(slice) } } @@ -2256,7 +2261,7 @@ impl String { #[inline] pub fn leak<'a>(self) -> &'a mut str { let slice = self.vec.leak(); - // ignore-tidy-undocumented-unsafe + // SAFETY: `slice` contains only valid UTF-8 because `String` is guaranteed to be valid UTF-8. unsafe { from_utf8_unchecked_mut(slice) } } } @@ -3586,14 +3591,12 @@ unsafe impl Send for Drain<'_> {} #[stable(feature = "drain", since = "1.6.0")] impl Drop for Drain<'_> { fn drop(&mut self) { - // ignore-tidy-undocumented-unsafe - unsafe { - // Use Vec::drain. "Reaffirm" the bounds checks to avoid - // panic code being inserted again. - let self_vec = (*self.string).as_mut_vec(); - if self.start <= self.end && self.end <= self_vec.len() { - self_vec.drain(self.start..self.end); - } + // Use Vec::drain. "Reaffirm" the bounds checks to avoid + // panic code being inserted again. + // SAFETY: We only use the returned Vec to call drain. + let self_vec = unsafe { &mut *(*self.string).as_mut_vec() }; + if self.start <= self.end && self.end <= self_vec.len() { + self_vec.drain(self.start..self.end); } } } From e490bd5975e2f98bdf9b638bcae352c6b2ae1796 Mon Sep 17 00:00:00 2001 From: b1yd <2156864690@qq.com> Date: Tue, 29 Sep 2026 16:35:22 +0800 Subject: [PATCH 3/3] Fix ICE when use tree appears in docs. --- compiler/rustc_resolve/src/check_unused.rs | 7 +++++++ tests/ui/attributes/use-tree-in-docs.rs | 7 +++++++ tests/ui/attributes/use-tree-in-docs.stderr | 8 ++++++++ 3 files changed, 22 insertions(+) create mode 100644 tests/ui/attributes/use-tree-in-docs.rs create mode 100644 tests/ui/attributes/use-tree-in-docs.stderr diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index aac244ad6cf22..91e6188a8c63c 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -250,6 +250,13 @@ impl<'a, 'ra, 'tcx> AsMut> for UnusedImportCheckVisitor<'a, } impl<'a, 'ra, 'tcx> Visitor<'a> for UnusedImportCheckVisitor<'a, 'ra, 'tcx> { + // Don't walk into attributes because imports can appear + // inside (e.g #[doc = {use std::{io,fmt};}]). They have + // a different owner, so they will cause an ICE if we visit + // them. Note that this doc attribute is not valid and will + // be rejected during AST lowering. + fn visit_attribute(&mut self, _: &'a rustc_ast::Attribute) {} + fn visit_item(&mut self, item: &'a ast::Item) { self.item_span = item.span_with_attributes(); match &item.kind { diff --git a/tests/ui/attributes/use-tree-in-docs.rs b/tests/ui/attributes/use-tree-in-docs.rs new file mode 100644 index 0000000000000..69e1121464558 --- /dev/null +++ b/tests/ui/attributes/use-tree-in-docs.rs @@ -0,0 +1,7 @@ +// Don't ICE when use tree appears in docs. (#163355) +#![feature(generic_assert)] +#[doc = assert !(b)] +//~^ ERROR attribute value must be a literal +use std as x; + +fn main(){} diff --git a/tests/ui/attributes/use-tree-in-docs.stderr b/tests/ui/attributes/use-tree-in-docs.stderr new file mode 100644 index 0000000000000..fb03874a714c7 --- /dev/null +++ b/tests/ui/attributes/use-tree-in-docs.stderr @@ -0,0 +1,8 @@ +error: attribute value must be a literal + --> $DIR/use-tree-in-docs.rs:3:9 + | +LL | #[doc = assert !(b)] + | ^^^^^^^^^^^ + +error: aborting due to 1 previous error +