Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Yeet owning_ref #109971

Merged
merged 9 commits into from
Apr 8, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions compiler/rustc_data_structures/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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;
Expand Down
113 changes: 113 additions & 0 deletions compiler/rustc_data_structures/src/owned_slice.rs
Original file line number Diff line number Diff line change
@@ -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<dyn Send + Sync>,
}

/// 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<O, F>(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<O, F, E>(owner: O, slicer: F) -> Result<OwnedSlice, E>
where
O: Send + Sync + 'static,
F: Fn(&O) -> Result<&[u8], E>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FnOnce, this avoids having to change it in other places and is simply more correct.

{
// 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] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be nice to check, that this (and other short fns around) call will be inlined, as rust sometimes forget about that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great call, these two functions should all have #[inline] attributes. I'm not entirely sure where they are all used but given that it's used in metadata loading which is very hot this could be really bad on platforms that aren't compiled with LTO (like our neutral perf run was).

self
}
}

// Safety: `OwnedSlice` is conceptually `(&'self.1 [u8], Box<dyn Send + Sync>)`, which is `Send`
unsafe impl Send for OwnedSlice {}

// Safety: `OwnedSlice` is conceptually `(&'self.1 [u8], Box<dyn Send + Sync>)`, which is `Sync`
unsafe impl Sync for OwnedSlice {}
9 changes: 4 additions & 5 deletions compiler/rustc_data_structures/src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,17 @@ mod vec;

cfg_if! {
if #[cfg(not(parallel_compiler))] {
pub auto trait Send {}
pub auto trait Sync {}

impl<T> Send for T {}
impl<T> Sync for T {}
pub unsafe auto trait Send {}
pub unsafe auto trait Sync {}

#[macro_export]
macro_rules! rustc_erase_owner {
($v:expr) => {
$v.erase_owner()
}
}
unsafe impl<T> Send for T {}
unsafe impl<T> Sync for T {}

use std::ops::Add;

Expand Down