diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 9b52638e612..62e22127b77 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -27,6 +27,8 @@ #![feature(thread_id_value)] #![feature(vec_into_raw_parts)] #![feature(get_mut_unchecked)] +#![feature(lint_reasons)] +#![feature(unwrap_infallible)] #![allow(rustc::default_hash_types)] #![allow(rustc::potential_query_instability)] #![deny(rustc::untranslatable_diagnostic)] @@ -82,6 +84,7 @@ pub mod vec_linked_list; pub mod work_queue; pub use atomic_ref::AtomicRef; pub mod frozen; +pub mod owned_slice; pub mod sso; pub mod steal; pub mod tagged_ptr; diff --git a/compiler/rustc_data_structures/src/owned_slice.rs b/compiler/rustc_data_structures/src/owned_slice.rs new file mode 100644 index 00000000000..dce209adfaa --- /dev/null +++ b/compiler/rustc_data_structures/src/owned_slice.rs @@ -0,0 +1,113 @@ +use std::{borrow::Borrow, ops::Deref}; + +// Use our fake Send/Sync traits when on not parallel compiler, +// so that `OwnedSlice` only implements/requires Send/Sync +// for parallel compiler builds. +use crate::sync::{Send, Sync}; + +/// An owned slice. +/// +/// This is similar to `Box<[u8]>` but allows slicing and using anything as the +/// backing buffer. +/// +/// See [`slice_owned`] for `OwnedSlice` construction and examples. +/// +/// --------------------------------------------------------------------------- +/// +/// This is essentially a replacement for `owning_ref` which is a lot simpler +/// and even sound! 🌸 +pub struct OwnedSlice { + /// This is conceptually a `&'self.owner [u8]`. + bytes: *const [u8], + + // +---------------------------------------+ + // | We expect `dead_code` lint here, | + // | because we don't want to accidentally | + // | touch the owner — otherwise the owner | + // | could invalidate out `bytes` pointer | + // | | + // | so be quite | + // +----+ +-------------------------------+ + // \/ + // ⊂(´・◡・⊂ )∘˚˳° + #[expect(dead_code)] + owner: Box, +} + +/// Makes an [`OwnedSlice`] out of an `owner` and a `slicer` function. +/// +/// ## Examples +/// +/// ```rust +/// # use rustc_data_structures::owned_slice::{OwnedSlice, slice_owned}; +/// let vec = vec![1, 2, 3, 4]; +/// +/// // Identical to slicing via `&v[1..3]` but produces an owned slice +/// let slice: OwnedSlice = slice_owned(vec, |v| &v[1..3]); +/// assert_eq!(&*slice, [2, 3]); +/// ``` +/// +/// ```rust +/// # use rustc_data_structures::owned_slice::{OwnedSlice, slice_owned}; +/// # use std::ops::Deref; +/// let vec = vec![1, 2, 3, 4]; +/// +/// // Identical to slicing via `&v[..]` but produces an owned slice +/// let slice: OwnedSlice = slice_owned(vec, Deref::deref); +/// assert_eq!(&*slice, [1, 2, 3, 4]); +/// ``` +pub fn slice_owned(owner: O, slicer: F) -> OwnedSlice +where + O: Send + Sync + 'static, + F: Fn(&O) -> &[u8], +{ + try_slice_owned(owner, |x| Ok::<_, !>(slicer(x))).into_ok() +} + +/// Makes an [`OwnedSlice`] out of an `owner` and a `slicer` function that can fail. +/// +/// See [`slice_owned`] for the infallible version. +pub fn try_slice_owned(owner: O, slicer: F) -> Result +where + O: Send + Sync + 'static, + F: Fn(&O) -> Result<&[u8], E>, +{ + // We box the owner of the bytes, so it doesn't move. + // + // Since the owner does not move and we don't access it in any way + // before drop, there is nothing that can invalidate the bytes pointer. + // + // Thus, "extending" the lifetime of the reference returned from `F` is fine. + // We pretend that we pass it a reference that lives as long as the returned slice. + // + // N.B. the HRTB on the `slicer` is important — without it the caller could provide + // a short lived slice, unrelated to the owner. + + let owner = Box::new(owner); + let bytes = slicer(&*owner)?; + + Ok(OwnedSlice { bytes, owner }) +} + +impl Deref for OwnedSlice { + type Target = [u8]; + + fn deref(&self) -> &[u8] { + // Safety: + // `self.bytes` is valid per the construction in `slice_owned` + // (which is the only constructor) + unsafe { &*self.bytes } + } +} + +impl Borrow<[u8]> for OwnedSlice { + fn borrow(&self) -> &[u8] { + self + } +} + +// Safety: `OwnedSlice` is conceptually `(&'self.1 [u8], Box)`, which is `Send` +unsafe impl Send for OwnedSlice {} + +// Safety: `OwnedSlice` is conceptually `(&'self.1 [u8], Box)`, which is `Sync` +unsafe impl Sync for OwnedSlice {} diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 4e2126fff7b..8a53e28034b 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -57,11 +57,8 @@ mod vec; cfg_if! { if #[cfg(not(parallel_compiler))] { - pub auto trait Send {} - pub auto trait Sync {} - - impl Send for T {} - impl Sync for T {} + pub unsafe auto trait Send {} + pub unsafe auto trait Sync {} #[macro_export] macro_rules! rustc_erase_owner { @@ -69,6 +66,8 @@ cfg_if! { $v.erase_owner() } } + unsafe impl Send for T {} + unsafe impl Sync for T {} use std::ops::Add;