Skip to content

Commit 689beda

Browse files
committed
Implement OwnedSlice
1 parent be8e5ba commit 689beda

File tree

3 files changed

+120
-5
lines changed

3 files changed

+120
-5
lines changed

compiler/rustc_data_structures/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
#![feature(thread_id_value)]
2828
#![feature(vec_into_raw_parts)]
2929
#![feature(get_mut_unchecked)]
30+
#![feature(lint_reasons)]
31+
#![feature(unwrap_infallible)]
3032
#![allow(rustc::default_hash_types)]
3133
#![allow(rustc::potential_query_instability)]
3234
#![deny(rustc::untranslatable_diagnostic)]
@@ -82,6 +84,7 @@ pub mod vec_linked_list;
8284
pub mod work_queue;
8385
pub use atomic_ref::AtomicRef;
8486
pub mod frozen;
87+
pub mod owned_slice;
8588
pub mod sso;
8689
pub mod steal;
8790
pub mod tagged_ptr;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use std::{borrow::Borrow, ops::Deref};
2+
3+
// Use our fake Send/Sync traits when on not parallel compiler,
4+
// so that `OwnedSlice` only implements/requires Send/Sync
5+
// for parallel compiler builds.
6+
use crate::sync::{Send, Sync};
7+
8+
/// An owned slice.
9+
///
10+
/// This is similar to `Box<[u8]>` but allows slicing and using anything as the
11+
/// backing buffer.
12+
///
13+
/// See [`slice_owned`] for `OwnedSlice` construction and examples.
14+
///
15+
/// ---------------------------------------------------------------------------
16+
///
17+
/// This is essentially a replacement for `owning_ref` which is a lot simpler
18+
/// and even sound! 🌸
19+
pub struct OwnedSlice {
20+
/// This is conceptually a `&'self.owner [u8]`.
21+
bytes: *const [u8],
22+
23+
// +---------------------------------------+
24+
// | We expect `dead_code` lint here, |
25+
// | because we don't want to accidentally |
26+
// | touch the owner — otherwise the owner |
27+
// | could invalidate out `bytes` pointer |
28+
// | |
29+
// | so be quite |
30+
// +----+ +-------------------------------+
31+
// \/
32+
// ⊂(´・◡・⊂ )∘˚˳°
33+
#[expect(dead_code)]
34+
owner: Box<dyn Send + Sync>,
35+
}
36+
37+
/// Makes an [`OwnedSlice`] out of an `owner` and a `slicer` function.
38+
///
39+
/// ## Examples
40+
///
41+
/// ```rust
42+
/// # use rustc_data_structures::owned_slice::{OwnedSlice, slice_owned};
43+
/// let vec = vec![1, 2, 3, 4];
44+
///
45+
/// // Identical to slicing via `&v[1..3]` but produces an owned slice
46+
/// let slice: OwnedSlice = slice_owned(vec, |v| &v[1..3]);
47+
/// assert_eq!(&*slice, [2, 3]);
48+
/// ```
49+
///
50+
/// ```rust
51+
/// # use rustc_data_structures::owned_slice::{OwnedSlice, slice_owned};
52+
/// # use std::ops::Deref;
53+
/// let vec = vec![1, 2, 3, 4];
54+
///
55+
/// // Identical to slicing via `&v[..]` but produces an owned slice
56+
/// let slice: OwnedSlice = slice_owned(vec, Deref::deref);
57+
/// assert_eq!(&*slice, [1, 2, 3, 4]);
58+
/// ```
59+
pub fn slice_owned<O, F>(owner: O, slicer: F) -> OwnedSlice
60+
where
61+
O: Send + Sync + 'static,
62+
F: Fn(&O) -> &[u8],
63+
{
64+
try_slice_owned(owner, |x| Ok::<_, !>(slicer(x))).into_ok()
65+
}
66+
67+
/// Makes an [`OwnedSlice`] out of an `owner` and a `slicer` function that can fail.
68+
///
69+
/// See [`slice_owned`] for the infallible version.
70+
pub fn try_slice_owned<O, F, E>(owner: O, slicer: F) -> Result<OwnedSlice, E>
71+
where
72+
O: Send + Sync + 'static,
73+
F: Fn(&O) -> Result<&[u8], E>,
74+
{
75+
// We box the owner of the bytes, so it doesn't move.
76+
//
77+
// Since the owner does not move and we don't access it in any way
78+
// before drop, there is nothing that can invalidate the bytes pointer.
79+
//
80+
// Thus, "extending" the lifetime of the reference returned from `F` is fine.
81+
// We pretend that we pass it a reference that lives as long as the returned slice.
82+
//
83+
// N.B. the HRTB on the `slicer` is important — without it the caller could provide
84+
// a short lived slice, unrelated to the owner.
85+
86+
let owner = Box::new(owner);
87+
let bytes = slicer(&*owner)?;
88+
89+
Ok(OwnedSlice { bytes, owner })
90+
}
91+
92+
impl Deref for OwnedSlice {
93+
type Target = [u8];
94+
95+
fn deref(&self) -> &[u8] {
96+
// Safety:
97+
// `self.bytes` is valid per the construction in `slice_owned`
98+
// (which is the only constructor)
99+
unsafe { &*self.bytes }
100+
}
101+
}
102+
103+
impl Borrow<[u8]> for OwnedSlice {
104+
fn borrow(&self) -> &[u8] {
105+
self
106+
}
107+
}
108+
109+
// Safety: `OwnedSlice` is conceptually `(&'self.1 [u8], Box<dyn Send + Sync>)`, which is `Send`
110+
unsafe impl Send for OwnedSlice {}
111+
112+
// Safety: `OwnedSlice` is conceptually `(&'self.1 [u8], Box<dyn Send + Sync>)`, which is `Sync`
113+
unsafe impl Sync for OwnedSlice {}

compiler/rustc_data_structures/src/sync.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -57,18 +57,17 @@ mod vec;
5757

5858
cfg_if! {
5959
if #[cfg(not(parallel_compiler))] {
60-
pub auto trait Send {}
61-
pub auto trait Sync {}
62-
63-
impl<T> Send for T {}
64-
impl<T> Sync for T {}
60+
pub unsafe auto trait Send {}
61+
pub unsafe auto trait Sync {}
6562

6663
#[macro_export]
6764
macro_rules! rustc_erase_owner {
6865
($v:expr) => {
6966
$v.erase_owner()
7067
}
7168
}
69+
unsafe impl<T> Send for T {}
70+
unsafe impl<T> Sync for T {}
7271

7372
use std::ops::Add;
7473

0 commit comments

Comments
 (0)