From 689beda166719ba423faa8ceaab009b85cf03c4a Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 5 Apr 2023 13:25:04 +0000 Subject: [PATCH 1/9] Implement `OwnedSlice` --- compiler/rustc_data_structures/src/lib.rs | 3 + .../rustc_data_structures/src/owned_slice.rs | 113 ++++++++++++++++++ compiler/rustc_data_structures/src/sync.rs | 9 +- 3 files changed, 120 insertions(+), 5 deletions(-) create mode 100644 compiler/rustc_data_structures/src/owned_slice.rs 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; From c0ceefdfaf9a1e131bfbf4849bbaaac23af621a4 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 5 Apr 2023 13:26:26 +0000 Subject: [PATCH 2/9] Use `OwnedSlice` instead of `owning_ref` --- .../rustc_codegen_ssa/src/back/metadata.rs | 13 +++++------ compiler/rustc_data_structures/src/sync.rs | 6 ++--- compiler/rustc_metadata/src/lib.rs | 2 -- compiler/rustc_metadata/src/locator.rs | 22 +++++++++---------- compiler/rustc_metadata/src/rmeta/decoder.rs | 6 ----- .../src/rmeta/def_path_hash_map.rs | 14 ++++++------ 6 files changed, 27 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 4ab56699922..c0f263d1478 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -13,8 +13,7 @@ use object::{ use snap::write::FrameEncoder; use rustc_data_structures::memmap::Mmap; -use rustc_data_structures::owning_ref::OwningRef; -use rustc_data_structures::rustc_erase_owner; +use rustc_data_structures::owned_slice::try_slice_owned; use rustc_data_structures::sync::MetadataRef; use rustc_metadata::fs::METADATA_FILENAME; use rustc_metadata::EncodedMetadata; @@ -38,14 +37,14 @@ pub struct DefaultMetadataLoader; fn load_metadata_with( path: &Path, - f: impl for<'a> FnOnce(&'a [u8]) -> Result<&'a [u8], String>, + f: impl for<'a> Fn(&'a [u8]) -> Result<&'a [u8], String>, ) -> Result { let file = File::open(path).map_err(|e| format!("failed to open file '{}': {}", path.display(), e))?; - let data = unsafe { Mmap::map(file) } - .map_err(|e| format!("failed to mmap file '{}': {}", path.display(), e))?; - let metadata = OwningRef::new(data).try_map(f)?; - return Ok(rustc_erase_owner!(metadata.map_owner_box())); + + unsafe { Mmap::map(file) } + .map_err(|e| format!("failed to mmap file '{}': {}", path.display(), e)) + .and_then(|mmap| try_slice_owned(mmap, |mmap| f(mmap))) } impl MetadataLoader for DefaultMetadataLoader { diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 8a53e28034b..855a7eb331f 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -42,7 +42,7 @@ //! //! [^2] `MTLockRef` is a typedef. -use crate::owning_ref::{Erased, OwningRef}; +use crate::owned_slice::OwnedSlice; use std::collections::HashMap; use std::hash::{BuildHasher, Hash}; use std::ops::{Deref, DerefMut}; @@ -188,7 +188,7 @@ cfg_if! { } } - pub type MetadataRef = OwningRef, [u8]>; + pub type MetadataRef = OwnedSlice; pub use std::rc::Rc as Lrc; pub use std::rc::Weak as Weak; @@ -371,7 +371,7 @@ cfg_if! { }); } - pub type MetadataRef = OwningRef, [u8]>; + pub type MetadataRef = OwnedSlice; /// This makes locks panic if they are already held. /// It is only useful when you are running in a single thread diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 880da5ca593..81e62eccb8a 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -22,8 +22,6 @@ extern crate proc_macro; extern crate rustc_macros; #[macro_use] extern crate rustc_middle; -#[macro_use] -extern crate rustc_data_structures; #[macro_use] extern crate tracing; diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 79c42a128e7..2515269ea2f 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -218,7 +218,7 @@ use crate::rmeta::{rustc_version, MetadataBlob, METADATA_HEADER}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::memmap::Mmap; -use rustc_data_structures::owning_ref::OwningRef; +use rustc_data_structures::owned_slice::slice_owned; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::MetadataRef; use rustc_errors::{DiagnosticArgValue, FatalError, IntoDiagnosticArg}; @@ -236,6 +236,7 @@ use rustc_target::spec::{Target, TargetTriple}; use snap::read::FrameDecoder; use std::borrow::Cow; use std::io::{Read, Result as IoResult, Write}; +use std::ops::Deref; use std::path::{Path, PathBuf}; use std::{cmp, fmt}; @@ -814,15 +815,14 @@ fn get_metadata_section<'p>( // Assume the decompressed data will be at least the size of the compressed data, so we // don't have to grow the buffer as much. let mut inflated = Vec::with_capacity(compressed_bytes.len()); - match FrameDecoder::new(compressed_bytes).read_to_end(&mut inflated) { - Ok(_) => rustc_erase_owner!(OwningRef::new(inflated).map_owner_box()), - Err(_) => { - return Err(MetadataError::LoadFailure(format!( - "failed to decompress metadata: {}", - filename.display() - ))); - } - } + FrameDecoder::new(compressed_bytes).read_to_end(&mut inflated).map_err(|_| { + MetadataError::LoadFailure(format!( + "failed to decompress metadata: {}", + filename.display() + )) + })?; + + slice_owned(inflated, Deref::deref) } CrateFlavor::Rmeta => { // mmap the file, because only a small fraction of it is read. @@ -840,7 +840,7 @@ fn get_metadata_section<'p>( )) })?; - rustc_erase_owner!(OwningRef::new(mmap).map_owner_box()) + slice_owned(mmap, Deref::deref) } }; let blob = MetadataBlob::new(raw_bytes); diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 43e5946f313..b5390e52c85 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -51,12 +51,6 @@ mod cstore_impl; #[derive(Clone)] pub(crate) struct MetadataBlob(Lrc); -// This is needed so we can create an OwningRef into the blob. -// The data behind a `MetadataBlob` has a stable address because it is -// contained within an Rc/Arc. -unsafe impl rustc_data_structures::owning_ref::StableAddress for MetadataBlob {} - -// This is needed so we can create an OwningRef into the blob. impl std::ops::Deref for MetadataBlob { type Target = [u8]; diff --git a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs index a6133f1b417..02cab561b8f 100644 --- a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs +++ b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs @@ -1,14 +1,14 @@ use crate::rmeta::DecodeContext; use crate::rmeta::EncodeContext; -use crate::rmeta::MetadataBlob; -use rustc_data_structures::owning_ref::OwningRef; +use rustc_data_structures::owned_slice::slice_owned; +use rustc_data_structures::owned_slice::OwnedSlice; use rustc_hir::def_path_hash_map::{Config as HashMapConfig, DefPathHashMap}; use rustc_middle::parameterized_over_tcx; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_span::def_id::{DefIndex, DefPathHash}; pub(crate) enum DefPathHashMapRef<'tcx> { - OwnedFromMetadata(odht::HashTable>), + OwnedFromMetadata(odht::HashTable), BorrowedFromTcx(&'tcx DefPathHashMap), } @@ -50,11 +50,11 @@ impl<'a, 'tcx> Decodable> for DefPathHashMapRef<'static> let len = d.read_usize(); let pos = d.position(); - let o = OwningRef::new(d.blob().clone()).map(|x| &x[pos..pos + len]); + let o = slice_owned(d.blob().clone(), |blob| &blob[pos..pos + len]); - // Although we already have the data we need via the OwningRef, we still need - // to advance the DecodeContext's position so it's in a valid state after - // the method. We use read_raw_bytes() for that. + // Although we already have the data we need via the `OwnedSlice`, we still need + // to advance the `DecodeContext`'s position so it's in a valid state after + // the method. We use `read_raw_bytes()` for that. let _ = d.read_raw_bytes(len); let inner = odht::HashTable::from_raw_bytes(o).unwrap_or_else(|e| { From d7056548f9dd51d44d9d29fd0376ac509605076b Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 5 Apr 2023 13:28:25 +0000 Subject: [PATCH 3/9] Yeet `owning_ref` Turns out - `owning_ref` is unsound due to `Box` aliasing stuff - `rustc` doesn't need 99% of the `owning_ref` API - `rustc` can use a far simpler abstraction that is `OwnedSlice` --- compiler/rustc_data_structures/src/lib.rs | 1 - compiler/rustc_data_structures/src/memmap.rs | 10 +- .../src/owning_ref/LICENSE | 21 - .../src/owning_ref/mod.rs | 1211 ----------------- .../src/owning_ref/tests.rs | 711 ---------- compiler/rustc_data_structures/src/sync.rs | 18 - 6 files changed, 1 insertion(+), 1971 deletions(-) delete mode 100644 compiler/rustc_data_structures/src/owning_ref/LICENSE delete mode 100644 compiler/rustc_data_structures/src/owning_ref/mod.rs delete mode 100644 compiler/rustc_data_structures/src/owning_ref/tests.rs diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 62e22127b77..47d8c84ba20 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -61,7 +61,6 @@ pub mod intern; pub mod jobserver; pub mod macros; pub mod obligation_forest; -pub mod owning_ref; pub mod sip128; pub mod small_c_str; pub mod small_str; diff --git a/compiler/rustc_data_structures/src/memmap.rs b/compiler/rustc_data_structures/src/memmap.rs index 3d44e17f31d..1556873f597 100644 --- a/compiler/rustc_data_structures/src/memmap.rs +++ b/compiler/rustc_data_structures/src/memmap.rs @@ -2,9 +2,7 @@ use std::fs::File; use std::io; use std::ops::{Deref, DerefMut}; -use crate::owning_ref::StableAddress; - -/// A trivial wrapper for [`memmap2::Mmap`] that implements [`StableAddress`]. +/// A trivial wrapper for [`memmap2::Mmap`] (or `Vec` on WASM). #[cfg(not(target_arch = "wasm32"))] pub struct Mmap(memmap2::Mmap); @@ -46,12 +44,6 @@ impl AsRef<[u8]> for Mmap { } } -// SAFETY: On architectures other than WASM, mmap is used as backing storage. The address of this -// memory map is stable. On WASM, `Vec` is used as backing storage. The `Mmap` type doesn't -// export any function that can cause the `Vec` to be re-allocated. As such the address of the -// bytes inside this `Vec` is stable. -unsafe impl StableAddress for Mmap {} - #[cfg(not(target_arch = "wasm32"))] pub struct MmapMut(memmap2::MmapMut); diff --git a/compiler/rustc_data_structures/src/owning_ref/LICENSE b/compiler/rustc_data_structures/src/owning_ref/LICENSE deleted file mode 100644 index dff72d1e432..00000000000 --- a/compiler/rustc_data_structures/src/owning_ref/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Marvin Löbel - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/compiler/rustc_data_structures/src/owning_ref/mod.rs b/compiler/rustc_data_structures/src/owning_ref/mod.rs deleted file mode 100644 index d1d92b905b8..00000000000 --- a/compiler/rustc_data_structures/src/owning_ref/mod.rs +++ /dev/null @@ -1,1211 +0,0 @@ -#![warn(missing_docs)] - -/*! -# An owning reference. - -This crate provides the _owning reference_ types `OwningRef` and `OwningRefMut` -that enables it to bundle a reference together with the owner of the data it points to. -This allows moving and dropping of an `OwningRef` without needing to recreate the reference. - -This can sometimes be useful because Rust borrowing rules normally prevent -moving a type that has been moved from. For example, this kind of code gets rejected: - -```compile_fail,E0515 -fn return_owned_and_referenced<'a>() -> (Vec, &'a [u8]) { - let v = vec![1, 2, 3, 4]; - let s = &v[1..3]; - (v, s) -} -``` - -Even though, from a memory-layout point of view, this can be entirely safe -if the new location of the vector still lives longer than the lifetime `'a` -of the reference because the backing allocation of the vector does not change. - -This library enables this safe usage by keeping the owner and the reference -bundled together in a wrapper type that ensure that lifetime constraint: - -``` -# use rustc_data_structures::owning_ref::OwningRef; -# fn main() { -fn return_owned_and_referenced() -> OwningRef, [u8]> { - let v = vec![1, 2, 3, 4]; - let or = OwningRef::new(v); - let or = or.map(|v| &v[1..3]); - or -} -# } -``` - -It works by requiring owner types to dereference to stable memory locations -and preventing mutable access to root containers, which in practice requires heap allocation -as provided by `Box`, `Rc`, etc. - -Also provided are typedefs for common owner type combinations, -which allow for less verbose type signatures. -For example, `BoxRef` instead of `OwningRef, T>`. - -The crate also provides the more advanced `OwningHandle` type, -which allows more freedom in bundling a dependent handle object -along with the data it depends on, at the cost of some unsafe needed in the API. -See the documentation around `OwningHandle` for more details. - -# Examples - -## Basics - -``` -use rustc_data_structures::owning_ref::BoxRef; - -fn main() { - // Create an array owned by a Box. - let arr = Box::new([1, 2, 3, 4]) as Box<[i32]>; - - // Transfer into a BoxRef. - let arr: BoxRef<[i32]> = BoxRef::new(arr); - assert_eq!(&*arr, &[1, 2, 3, 4]); - - // We can slice the array without losing ownership or changing type. - let arr: BoxRef<[i32]> = arr.map(|arr| &arr[1..3]); - assert_eq!(&*arr, &[2, 3]); - - // Also works for Arc, Rc, String and Vec! -} -``` - -## Caching a reference to a struct field - -``` -use rustc_data_structures::owning_ref::BoxRef; - -fn main() { - struct Foo { - tag: u32, - x: u16, - y: u16, - z: u16, - } - let foo = Foo { tag: 1, x: 100, y: 200, z: 300 }; - - let or = BoxRef::new(Box::new(foo)).map(|foo| { - match foo.tag { - 0 => &foo.x, - 1 => &foo.y, - 2 => &foo.z, - _ => panic!(), - } - }); - - assert_eq!(*or, 200); -} -``` - -## Caching a reference to an entry in a vector - -``` -use rustc_data_structures::owning_ref::VecRef; - -fn main() { - let v = VecRef::new(vec![1, 2, 3, 4, 5]).map(|v| &v[3]); - assert_eq!(*v, 4); -} -``` - -## Caching a subslice of a String - -``` -use rustc_data_structures::owning_ref::StringRef; - -fn main() { - let s = StringRef::new("hello world".to_owned()) - .map(|s| s.split(' ').nth(1).unwrap()); - - assert_eq!(&*s, "world"); -} -``` - -## Reference counted slices that share ownership of the backing storage - -``` -use rustc_data_structures::owning_ref::RcRef; -use std::rc::Rc; - -fn main() { - let rc: RcRef<[i32]> = RcRef::new(Rc::new([1, 2, 3, 4]) as Rc<[i32]>); - assert_eq!(&*rc, &[1, 2, 3, 4]); - - let rc_a: RcRef<[i32]> = rc.clone().map(|s| &s[0..2]); - let rc_b = rc.clone().map(|s| &s[1..3]); - let rc_c = rc.clone().map(|s| &s[2..4]); - assert_eq!(&*rc_a, &[1, 2]); - assert_eq!(&*rc_b, &[2, 3]); - assert_eq!(&*rc_c, &[3, 4]); - - let rc_c_a = rc_c.clone().map(|s| &s[1]); - assert_eq!(&*rc_c_a, &4); -} -``` - -## Atomic reference counted slices that share ownership of the backing storage - -``` -use rustc_data_structures::owning_ref::ArcRef; -use std::sync::Arc; - -fn main() { - use std::thread; - - fn par_sum(rc: ArcRef<[i32]>) -> i32 { - if rc.len() == 0 { - return 0; - } else if rc.len() == 1 { - return rc[0]; - } - let mid = rc.len() / 2; - let left = rc.clone().map(|s| &s[..mid]); - let right = rc.map(|s| &s[mid..]); - - let left = thread::spawn(move || par_sum(left)); - let right = thread::spawn(move || par_sum(right)); - - left.join().unwrap() + right.join().unwrap() - } - - let rc: Arc<[i32]> = Arc::new([1, 2, 3, 4]); - let rc: ArcRef<[i32]> = rc.into(); - - assert_eq!(par_sum(rc), 10); -} -``` - -## References into RAII locks - -``` -use rustc_data_structures::owning_ref::RefRef; -use std::cell::{RefCell, Ref}; - -fn main() { - let refcell = RefCell::new((1, 2, 3, 4)); - // Also works with Mutex and RwLock - - let refref = { - let refref = RefRef::new(refcell.borrow()).map(|x| &x.3); - assert_eq!(*refref, 4); - - // We move the RAII lock and the reference to one of - // the subfields in the data it guards here: - refref - }; - - assert_eq!(*refref, 4); - - drop(refref); - - assert_eq!(*refcell.borrow(), (1, 2, 3, 4)); -} -``` - -## Mutable reference - -When the owned container implements `DerefMut`, it is also possible to make -a _mutable owning reference_. (e.g., with `Box`, `RefMut`, `MutexGuard`) - -``` -use rustc_data_structures::owning_ref::RefMutRefMut; -use std::cell::{RefCell, RefMut}; - -fn main() { - let refcell = RefCell::new((1, 2, 3, 4)); - - let mut refmut_refmut = { - let mut refmut_refmut = RefMutRefMut::new(refcell.borrow_mut()).map_mut(|x| &mut x.3); - assert_eq!(*refmut_refmut, 4); - *refmut_refmut *= 2; - - refmut_refmut - }; - - assert_eq!(*refmut_refmut, 8); - *refmut_refmut *= 2; - - drop(refmut_refmut); - - assert_eq!(*refcell.borrow(), (1, 2, 3, 16)); -} -``` -*/ - -pub use stable_deref_trait::{ - CloneStableDeref as CloneStableAddress, StableDeref as StableAddress, -}; -use std::mem; - -/// An owning reference. -/// -/// This wraps an owner `O` and a reference `&T` pointing -/// at something reachable from `O::Target` while keeping -/// the ability to move `self` around. -/// -/// The owner is usually a pointer that points at some base type. -/// -/// For more details and examples, see the module and method docs. -pub struct OwningRef { - owner: O, - reference: *const T, -} - -/// An mutable owning reference. -/// -/// This wraps an owner `O` and a reference `&mut T` pointing -/// at something reachable from `O::Target` while keeping -/// the ability to move `self` around. -/// -/// The owner is usually a pointer that points at some base type. -/// -/// For more details and examples, see the module and method docs. -pub struct OwningRefMut { - owner: O, - reference: *mut T, -} - -/// Helper trait for an erased concrete type an owner dereferences to. -/// This is used in form of a trait object for keeping -/// something around to (virtually) call the destructor. -pub trait Erased {} -impl Erased for T {} - -/// Helper trait for erasing the concrete type of what an owner dereferences to, -/// for example `Box -> Box`. This would be unneeded with -/// higher kinded types support in the language. -#[allow(unused_lifetimes)] -pub unsafe trait IntoErased<'a> { - /// Owner with the dereference type substituted to `Erased`. - type Erased; - /// Performs the type erasure. - fn into_erased(self) -> Self::Erased; -} - -/// Helper trait for erasing the concrete type of what an owner dereferences to, -/// for example `Box -> Box`. This would be unneeded with -/// higher kinded types support in the language. -#[allow(unused_lifetimes)] -pub unsafe trait IntoErasedSend<'a> { - /// Owner with the dereference type substituted to `Erased + Send`. - type Erased: Send; - /// Performs the type erasure. - fn into_erased_send(self) -> Self::Erased; -} - -/// Helper trait for erasing the concrete type of what an owner dereferences to, -/// for example `Box -> Box`. This would be unneeded with -/// higher kinded types support in the language. -#[allow(unused_lifetimes)] -pub unsafe trait IntoErasedSendSync<'a> { - /// Owner with the dereference type substituted to `Erased + Send + Sync`. - type Erased: Send + Sync; - /// Performs the type erasure. - fn into_erased_send_sync(self) -> Self::Erased; -} - -///////////////////////////////////////////////////////////////////////////// -// OwningRef -///////////////////////////////////////////////////////////////////////////// - -impl OwningRef { - /// Creates a new owning reference from an owner - /// initialized to the direct dereference of it. - /// - /// # Example - /// ``` - /// use rustc_data_structures::owning_ref::OwningRef; - /// - /// fn main() { - /// let owning_ref = OwningRef::new(Box::new(42)); - /// assert_eq!(*owning_ref, 42); - /// } - /// ``` - pub fn new(o: O) -> Self - where - O: StableAddress, - O: Deref, - { - OwningRef { reference: &*o, owner: o } - } - - /// Like `new`, but doesn’t require `O` to implement the `StableAddress` trait. - /// Instead, the caller is responsible to make the same promises as implementing the trait. - /// - /// This is useful for cases where coherence rules prevents implementing the trait - /// without adding a dependency to this crate in a third-party library. - pub unsafe fn new_assert_stable_address(o: O) -> Self - where - O: Deref, - { - OwningRef { reference: &*o, owner: o } - } - - /// Converts `self` into a new owning reference that points at something reachable - /// from the previous one. - /// - /// This can be a reference to a field of `U`, something reachable from a field of - /// `U`, or even something unrelated with a `'static` lifetime. - /// - /// # Example - /// ``` - /// use rustc_data_structures::owning_ref::OwningRef; - /// - /// fn main() { - /// let owning_ref = OwningRef::new(Box::new([1, 2, 3, 4])); - /// - /// // create an owning reference that points at the - /// // third element of the array. - /// let owning_ref = owning_ref.map(|array| &array[2]); - /// assert_eq!(*owning_ref, 3); - /// } - /// ``` - pub fn map(self, f: F) -> OwningRef - where - O: StableAddress, - F: FnOnce(&T) -> &U, - { - OwningRef { reference: f(&self), owner: self.owner } - } - - /// Tries to convert `self` into a new owning reference that points - /// at something reachable from the previous one. - /// - /// This can be a reference to a field of `U`, something reachable from a field of - /// `U`, or even something unrelated with a `'static` lifetime. - /// - /// # Example - /// ``` - /// use rustc_data_structures::owning_ref::OwningRef; - /// - /// fn main() { - /// let owning_ref = OwningRef::new(Box::new([1, 2, 3, 4])); - /// - /// // create an owning reference that points at the - /// // third element of the array. - /// let owning_ref = owning_ref.try_map(|array| { - /// if array[2] == 3 { Ok(&array[2]) } else { Err(()) } - /// }); - /// assert_eq!(*owning_ref.unwrap(), 3); - /// } - /// ``` - pub fn try_map(self, f: F) -> Result, E> - where - O: StableAddress, - F: FnOnce(&T) -> Result<&U, E>, - { - Ok(OwningRef { reference: f(&self)?, owner: self.owner }) - } - - /// Converts `self` into a new owning reference with a different owner type. - /// - /// The new owner type needs to still contain the original owner in some way - /// so that the reference into it remains valid. This function is marked unsafe - /// because the user needs to manually uphold this guarantee. - pub unsafe fn map_owner(self, f: F) -> OwningRef - where - O: StableAddress, - P: StableAddress, - F: FnOnce(O) -> P, - { - OwningRef { reference: self.reference, owner: f(self.owner) } - } - - /// Converts `self` into a new owning reference where the owner is wrapped - /// in an additional `Box`. - /// - /// This can be used to safely erase the owner of any `OwningRef` - /// to an `OwningRef, T>`. - pub fn map_owner_box(self) -> OwningRef, T> { - OwningRef { reference: self.reference, owner: Box::new(self.owner) } - } - - /// Erases the concrete base type of the owner with a trait object. - /// - /// This allows mixing of owned references with different owner base types. - /// - /// # Example - /// ``` - /// use rustc_data_structures::owning_ref::{OwningRef, Erased}; - /// - /// fn main() { - /// // N.B., using the concrete types here for explicitness. - /// // For less verbose code type aliases like `BoxRef` are provided. - /// - /// let owning_ref_a: OwningRef, [i32; 4]> - /// = OwningRef::new(Box::new([1, 2, 3, 4])); - /// - /// let owning_ref_b: OwningRef>, Vec<(i32, bool)>> - /// = OwningRef::new(Box::new(vec![(0, false), (1, true)])); - /// - /// let owning_ref_a: OwningRef, i32> - /// = owning_ref_a.map(|a| &a[0]); - /// - /// let owning_ref_b: OwningRef>, i32> - /// = owning_ref_b.map(|a| &a[1].0); - /// - /// let owning_refs: [OwningRef, i32>; 2] - /// = [owning_ref_a.erase_owner(), owning_ref_b.erase_owner()]; - /// - /// assert_eq!(*owning_refs[0], 1); - /// assert_eq!(*owning_refs[1], 1); - /// } - /// ``` - pub fn erase_owner<'a>(self) -> OwningRef - where - O: IntoErased<'a>, - { - OwningRef { reference: self.reference, owner: self.owner.into_erased() } - } - - /// Erases the concrete base type of the owner with a trait object which implements `Send`. - /// - /// This allows mixing of owned references with different owner base types. - pub fn erase_send_owner<'a>(self) -> OwningRef - where - O: IntoErasedSend<'a>, - { - OwningRef { reference: self.reference, owner: self.owner.into_erased_send() } - } - - /// Erases the concrete base type of the owner with a trait object - /// which implements `Send` and `Sync`. - /// - /// This allows mixing of owned references with different owner base types. - pub fn erase_send_sync_owner<'a>(self) -> OwningRef - where - O: IntoErasedSendSync<'a>, - { - OwningRef { reference: self.reference, owner: self.owner.into_erased_send_sync() } - } - - // UNIMPLEMENTED: wrap_owner - - // FIXME: Naming convention? - /// A getter for the underlying owner. - pub fn owner(&self) -> &O { - &self.owner - } - - // FIXME: Naming convention? - /// Discards the reference and retrieves the owner. - pub fn into_inner(self) -> O { - self.owner - } -} - -impl OwningRefMut { - /// Creates a new owning reference from an owner - /// initialized to the direct dereference of it. - /// - /// # Example - /// ``` - /// use rustc_data_structures::owning_ref::OwningRefMut; - /// - /// fn main() { - /// let owning_ref_mut = OwningRefMut::new(Box::new(42)); - /// assert_eq!(*owning_ref_mut, 42); - /// } - /// ``` - pub fn new(mut o: O) -> Self - where - O: StableAddress, - O: DerefMut, - { - OwningRefMut { reference: &mut *o, owner: o } - } - - /// Like `new`, but doesn’t require `O` to implement the `StableAddress` trait. - /// Instead, the caller is responsible to make the same promises as implementing the trait. - /// - /// This is useful for cases where coherence rules prevents implementing the trait - /// without adding a dependency to this crate in a third-party library. - pub unsafe fn new_assert_stable_address(mut o: O) -> Self - where - O: DerefMut, - { - OwningRefMut { reference: &mut *o, owner: o } - } - - /// Converts `self` into a new _shared_ owning reference that points at - /// something reachable from the previous one. - /// - /// This can be a reference to a field of `U`, something reachable from a field of - /// `U`, or even something unrelated with a `'static` lifetime. - /// - /// # Example - /// ``` - /// use rustc_data_structures::owning_ref::OwningRefMut; - /// - /// fn main() { - /// let owning_ref_mut = OwningRefMut::new(Box::new([1, 2, 3, 4])); - /// - /// // create an owning reference that points at the - /// // third element of the array. - /// let owning_ref = owning_ref_mut.map(|array| &array[2]); - /// assert_eq!(*owning_ref, 3); - /// } - /// ``` - pub fn map(mut self, f: F) -> OwningRef - where - O: StableAddress, - F: FnOnce(&mut T) -> &U, - { - OwningRef { reference: f(&mut self), owner: self.owner } - } - - /// Converts `self` into a new _mutable_ owning reference that points at - /// something reachable from the previous one. - /// - /// This can be a reference to a field of `U`, something reachable from a field of - /// `U`, or even something unrelated with a `'static` lifetime. - /// - /// # Example - /// ``` - /// use rustc_data_structures::owning_ref::OwningRefMut; - /// - /// fn main() { - /// let owning_ref_mut = OwningRefMut::new(Box::new([1, 2, 3, 4])); - /// - /// // create an owning reference that points at the - /// // third element of the array. - /// let owning_ref_mut = owning_ref_mut.map_mut(|array| &mut array[2]); - /// assert_eq!(*owning_ref_mut, 3); - /// } - /// ``` - pub fn map_mut(mut self, f: F) -> OwningRefMut - where - O: StableAddress, - F: FnOnce(&mut T) -> &mut U, - { - OwningRefMut { reference: f(&mut self), owner: self.owner } - } - - /// Tries to convert `self` into a new _shared_ owning reference that points - /// at something reachable from the previous one. - /// - /// This can be a reference to a field of `U`, something reachable from a field of - /// `U`, or even something unrelated with a `'static` lifetime. - /// - /// # Example - /// ``` - /// use rustc_data_structures::owning_ref::OwningRefMut; - /// - /// fn main() { - /// let owning_ref_mut = OwningRefMut::new(Box::new([1, 2, 3, 4])); - /// - /// // create an owning reference that points at the - /// // third element of the array. - /// let owning_ref = owning_ref_mut.try_map(|array| { - /// if array[2] == 3 { Ok(&array[2]) } else { Err(()) } - /// }); - /// assert_eq!(*owning_ref.unwrap(), 3); - /// } - /// ``` - pub fn try_map(mut self, f: F) -> Result, E> - where - O: StableAddress, - F: FnOnce(&mut T) -> Result<&U, E>, - { - Ok(OwningRef { reference: f(&mut self)?, owner: self.owner }) - } - - /// Tries to convert `self` into a new _mutable_ owning reference that points - /// at something reachable from the previous one. - /// - /// This can be a reference to a field of `U`, something reachable from a field of - /// `U`, or even something unrelated with a `'static` lifetime. - /// - /// # Example - /// ``` - /// use rustc_data_structures::owning_ref::OwningRefMut; - /// - /// fn main() { - /// let owning_ref_mut = OwningRefMut::new(Box::new([1, 2, 3, 4])); - /// - /// // create an owning reference that points at the - /// // third element of the array. - /// let owning_ref_mut = owning_ref_mut.try_map_mut(|array| { - /// if array[2] == 3 { Ok(&mut array[2]) } else { Err(()) } - /// }); - /// assert_eq!(*owning_ref_mut.unwrap(), 3); - /// } - /// ``` - pub fn try_map_mut(mut self, f: F) -> Result, E> - where - O: StableAddress, - F: FnOnce(&mut T) -> Result<&mut U, E>, - { - Ok(OwningRefMut { reference: f(&mut self)?, owner: self.owner }) - } - - /// Converts `self` into a new owning reference with a different owner type. - /// - /// The new owner type needs to still contain the original owner in some way - /// so that the reference into it remains valid. This function is marked unsafe - /// because the user needs to manually uphold this guarantee. - pub unsafe fn map_owner(self, f: F) -> OwningRefMut - where - O: StableAddress, - P: StableAddress, - F: FnOnce(O) -> P, - { - OwningRefMut { reference: self.reference, owner: f(self.owner) } - } - - /// Converts `self` into a new owning reference where the owner is wrapped - /// in an additional `Box`. - /// - /// This can be used to safely erase the owner of any `OwningRefMut` - /// to an `OwningRefMut, T>`. - pub fn map_owner_box(self) -> OwningRefMut, T> { - OwningRefMut { reference: self.reference, owner: Box::new(self.owner) } - } - - /// Erases the concrete base type of the owner with a trait object. - /// - /// This allows mixing of owned references with different owner base types. - /// - /// # Example - /// ``` - /// use rustc_data_structures::owning_ref::{OwningRefMut, Erased}; - /// - /// fn main() { - /// // N.B., using the concrete types here for explicitness. - /// // For less verbose code type aliases like `BoxRef` are provided. - /// - /// let owning_ref_mut_a: OwningRefMut, [i32; 4]> - /// = OwningRefMut::new(Box::new([1, 2, 3, 4])); - /// - /// let owning_ref_mut_b: OwningRefMut>, Vec<(i32, bool)>> - /// = OwningRefMut::new(Box::new(vec![(0, false), (1, true)])); - /// - /// let owning_ref_mut_a: OwningRefMut, i32> - /// = owning_ref_mut_a.map_mut(|a| &mut a[0]); - /// - /// let owning_ref_mut_b: OwningRefMut>, i32> - /// = owning_ref_mut_b.map_mut(|a| &mut a[1].0); - /// - /// let owning_refs_mut: [OwningRefMut, i32>; 2] - /// = [owning_ref_mut_a.erase_owner(), owning_ref_mut_b.erase_owner()]; - /// - /// assert_eq!(*owning_refs_mut[0], 1); - /// assert_eq!(*owning_refs_mut[1], 1); - /// } - /// ``` - pub fn erase_owner<'a>(self) -> OwningRefMut - where - O: IntoErased<'a>, - { - OwningRefMut { reference: self.reference, owner: self.owner.into_erased() } - } - - // UNIMPLEMENTED: wrap_owner - - // FIXME: Naming convention? - /// A getter for the underlying owner. - pub fn owner(&self) -> &O { - &self.owner - } - - // FIXME: Naming convention? - /// Discards the reference and retrieves the owner. - pub fn into_inner(self) -> O { - self.owner - } -} - -///////////////////////////////////////////////////////////////////////////// -// OwningHandle -///////////////////////////////////////////////////////////////////////////// - -use std::ops::{Deref, DerefMut}; - -/// `OwningHandle` is a complement to `OwningRef`. Where `OwningRef` allows -/// consumers to pass around an owned object and a dependent reference, -/// `OwningHandle` contains an owned object and a dependent _object_. -/// -/// `OwningHandle` can encapsulate a `RefMut` along with its associated -/// `RefCell`, or an `RwLockReadGuard` along with its associated `RwLock`. -/// However, the API is completely generic and there are no restrictions on -/// what types of owning and dependent objects may be used. -/// -/// `OwningHandle` is created by passing an owner object (which dereferences -/// to a stable address) along with a callback which receives a pointer to -/// that stable location. The callback may then dereference the pointer and -/// mint a dependent object, with the guarantee that the returned object will -/// not outlive the referent of the pointer. -/// -/// Since the callback needs to dereference a raw pointer, it requires `unsafe` -/// code. To avoid forcing this unsafety on most callers, the `ToHandle` trait is -/// implemented for common data structures. Types that implement `ToHandle` can -/// be wrapped into an `OwningHandle` without passing a callback. -pub struct OwningHandle -where - O: StableAddress, - H: Deref, -{ - handle: H, - _owner: O, -} - -impl Deref for OwningHandle -where - O: StableAddress, - H: Deref, -{ - type Target = H::Target; - fn deref(&self) -> &H::Target { - self.handle.deref() - } -} - -unsafe impl StableAddress for OwningHandle -where - O: StableAddress, - H: StableAddress, -{ -} - -impl DerefMut for OwningHandle -where - O: StableAddress, - H: DerefMut, -{ - fn deref_mut(&mut self) -> &mut H::Target { - self.handle.deref_mut() - } -} - -/// Trait to implement the conversion of owner to handle for common types. -pub trait ToHandle { - /// The type of handle to be encapsulated by the OwningHandle. - type Handle: Deref; - - /// Given an appropriately-long-lived pointer to ourselves, create a - /// handle to be encapsulated by the `OwningHandle`. - unsafe fn to_handle(x: *const Self) -> Self::Handle; -} - -/// Trait to implement the conversion of owner to mutable handle for common types. -pub trait ToHandleMut { - /// The type of handle to be encapsulated by the OwningHandle. - type HandleMut: DerefMut; - - /// Given an appropriately-long-lived pointer to ourselves, create a - /// mutable handle to be encapsulated by the `OwningHandle`. - unsafe fn to_handle_mut(x: *const Self) -> Self::HandleMut; -} - -impl OwningHandle -where - O: StableAddress>, - H: Deref, -{ - /// Creates a new `OwningHandle` for a type that implements `ToHandle`. For types - /// that don't implement `ToHandle`, callers may invoke `new_with_fn`, which accepts - /// a callback to perform the conversion. - pub fn new(o: O) -> Self { - OwningHandle::new_with_fn(o, |x| unsafe { O::Target::to_handle(x) }) - } -} - -impl OwningHandle -where - O: StableAddress>, - H: DerefMut, -{ - /// Creates a new mutable `OwningHandle` for a type that implements `ToHandleMut`. - pub fn new_mut(o: O) -> Self { - OwningHandle::new_with_fn(o, |x| unsafe { O::Target::to_handle_mut(x) }) - } -} - -impl OwningHandle -where - O: StableAddress, - H: Deref, -{ - /// Creates a new OwningHandle. The provided callback will be invoked with - /// a pointer to the object owned by `o`, and the returned value is stored - /// as the object to which this `OwningHandle` will forward `Deref` and - /// `DerefMut`. - pub fn new_with_fn(o: O, f: F) -> Self - where - F: FnOnce(*const O::Target) -> H, - { - let h: H; - { - h = f(o.deref() as *const O::Target); - } - - OwningHandle { handle: h, _owner: o } - } - - /// Creates a new OwningHandle. The provided callback will be invoked with - /// a pointer to the object owned by `o`, and the returned value is stored - /// as the object to which this `OwningHandle` will forward `Deref` and - /// `DerefMut`. - pub fn try_new(o: O, f: F) -> Result - where - F: FnOnce(*const O::Target) -> Result, - { - let h: H; - { - h = f(o.deref() as *const O::Target)?; - } - - Ok(OwningHandle { handle: h, _owner: o }) - } -} - -///////////////////////////////////////////////////////////////////////////// -// std traits -///////////////////////////////////////////////////////////////////////////// - -use std::borrow::Borrow; -use std::cmp::Ordering; -use std::fmt::{self, Debug}; -use std::hash::{Hash, Hasher}; - -impl Deref for OwningRef { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.reference } - } -} - -impl Deref for OwningRefMut { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.reference } - } -} - -impl DerefMut for OwningRefMut { - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.reference } - } -} - -unsafe impl StableAddress for OwningRef {} - -impl AsRef for OwningRef { - fn as_ref(&self) -> &T { - self - } -} - -impl AsRef for OwningRefMut { - fn as_ref(&self) -> &T { - self - } -} - -impl AsMut for OwningRefMut { - fn as_mut(&mut self) -> &mut T { - self - } -} - -impl Borrow for OwningRef { - fn borrow(&self) -> &T { - self - } -} - -impl From for OwningRef -where - O: StableAddress, - O: Deref, -{ - fn from(owner: O) -> Self { - OwningRef::new(owner) - } -} - -impl From for OwningRefMut -where - O: StableAddress, - O: DerefMut, -{ - fn from(owner: O) -> Self { - OwningRefMut::new(owner) - } -} - -impl From> for OwningRef -where - O: StableAddress, - O: DerefMut, -{ - fn from(other: OwningRefMut) -> Self { - OwningRef { owner: other.owner, reference: other.reference } - } -} - -// ^ FIXME: Is an Into impl for calling into_inner() possible as well? - -impl Debug for OwningRef -where - O: Debug, - T: Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "OwningRef {{ owner: {:?}, reference: {:?} }}", self.owner(), &**self) - } -} - -impl Debug for OwningRefMut -where - O: Debug, - T: Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "OwningRefMut {{ owner: {:?}, reference: {:?} }}", self.owner(), &**self) - } -} - -impl Clone for OwningRef -where - O: CloneStableAddress, -{ - fn clone(&self) -> Self { - OwningRef { owner: self.owner.clone(), reference: self.reference } - } -} - -unsafe impl CloneStableAddress for OwningRef where O: CloneStableAddress {} - -unsafe impl Send for OwningRef -where - O: Send, - for<'a> &'a T: Send, -{ -} -unsafe impl Sync for OwningRef -where - O: Sync, - for<'a> &'a T: Sync, -{ -} - -unsafe impl Send for OwningRefMut -where - O: Send, - for<'a> &'a mut T: Send, -{ -} -unsafe impl Sync for OwningRefMut -where - O: Sync, - for<'a> &'a mut T: Sync, -{ -} - -impl Debug for dyn Erased { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "",) - } -} - -impl PartialEq for OwningRef -where - T: PartialEq, -{ - fn eq(&self, other: &Self) -> bool { - self.deref().eq(other.deref()) - } -} - -impl Eq for OwningRef where T: Eq {} - -impl PartialOrd for OwningRef -where - T: PartialOrd, -{ - fn partial_cmp(&self, other: &Self) -> Option { - self.deref().partial_cmp(other.deref()) - } -} - -impl Ord for OwningRef -where - T: Ord, -{ - fn cmp(&self, other: &Self) -> Ordering { - self.deref().cmp(other.deref()) - } -} - -impl Hash for OwningRef -where - T: Hash, -{ - fn hash(&self, state: &mut H) { - self.deref().hash(state); - } -} - -impl PartialEq for OwningRefMut -where - T: PartialEq, -{ - fn eq(&self, other: &Self) -> bool { - self.deref().eq(other.deref()) - } -} - -impl Eq for OwningRefMut where T: Eq {} - -impl PartialOrd for OwningRefMut -where - T: PartialOrd, -{ - fn partial_cmp(&self, other: &Self) -> Option { - self.deref().partial_cmp(other.deref()) - } -} - -impl Ord for OwningRefMut -where - T: Ord, -{ - fn cmp(&self, other: &Self) -> Ordering { - self.deref().cmp(other.deref()) - } -} - -impl Hash for OwningRefMut -where - T: Hash, -{ - fn hash(&self, state: &mut H) { - self.deref().hash(state); - } -} - -///////////////////////////////////////////////////////////////////////////// -// std types integration and convenience type defs -///////////////////////////////////////////////////////////////////////////// - -use std::cell::{Ref, RefCell, RefMut}; -use std::rc::Rc; -use std::sync::Arc; -use std::sync::{MutexGuard, RwLockReadGuard, RwLockWriteGuard}; - -impl ToHandle for RefCell { - type Handle = Ref<'static, T>; - unsafe fn to_handle(x: *const Self) -> Self::Handle { - (*x).borrow() - } -} - -impl ToHandleMut for RefCell { - type HandleMut = RefMut<'static, T>; - unsafe fn to_handle_mut(x: *const Self) -> Self::HandleMut { - (*x).borrow_mut() - } -} - -// N.B., implementing ToHandle{,Mut} for Mutex and RwLock requires a decision -// about which handle creation to use (i.e., read() vs try_read()) as well as -// what to do with error results. - -/// Typedef of an owning reference that uses a `Box` as the owner. -pub type BoxRef = OwningRef, U>; -/// Typedef of an owning reference that uses a `Vec` as the owner. -pub type VecRef = OwningRef, U>; -/// Typedef of an owning reference that uses a `String` as the owner. -pub type StringRef = OwningRef; - -/// Typedef of an owning reference that uses an `Rc` as the owner. -pub type RcRef = OwningRef, U>; -/// Typedef of an owning reference that uses an `Arc` as the owner. -pub type ArcRef = OwningRef, U>; - -/// Typedef of an owning reference that uses a `Ref` as the owner. -pub type RefRef<'a, T, U = T> = OwningRef, U>; -/// Typedef of an owning reference that uses a `RefMut` as the owner. -pub type RefMutRef<'a, T, U = T> = OwningRef, U>; -/// Typedef of an owning reference that uses a `MutexGuard` as the owner. -pub type MutexGuardRef<'a, T, U = T> = OwningRef, U>; -/// Typedef of an owning reference that uses an `RwLockReadGuard` as the owner. -pub type RwLockReadGuardRef<'a, T, U = T> = OwningRef, U>; -/// Typedef of an owning reference that uses an `RwLockWriteGuard` as the owner. -pub type RwLockWriteGuardRef<'a, T, U = T> = OwningRef, U>; - -/// Typedef of a mutable owning reference that uses a `Box` as the owner. -pub type BoxRefMut = OwningRefMut, U>; -/// Typedef of a mutable owning reference that uses a `Vec` as the owner. -pub type VecRefMut = OwningRefMut, U>; -/// Typedef of a mutable owning reference that uses a `String` as the owner. -pub type StringRefMut = OwningRefMut; - -/// Typedef of a mutable owning reference that uses a `RefMut` as the owner. -pub type RefMutRefMut<'a, T, U = T> = OwningRefMut, U>; -/// Typedef of a mutable owning reference that uses a `MutexGuard` as the owner. -pub type MutexGuardRefMut<'a, T, U = T> = OwningRefMut, U>; -/// Typedef of a mutable owning reference that uses an `RwLockWriteGuard` as the owner. -pub type RwLockWriteGuardRefMut<'a, T, U = T> = OwningRef, U>; - -unsafe impl<'a, T: 'a> IntoErased<'a> for Box { - type Erased = Box; - fn into_erased(self) -> Self::Erased { - self - } -} -unsafe impl<'a, T: 'a> IntoErased<'a> for Rc { - type Erased = Rc; - fn into_erased(self) -> Self::Erased { - self - } -} -unsafe impl<'a, T: 'a> IntoErased<'a> for Arc { - type Erased = Arc; - fn into_erased(self) -> Self::Erased { - self - } -} - -unsafe impl<'a, T: Send + 'a> IntoErasedSend<'a> for Box { - type Erased = Box; - fn into_erased_send(self) -> Self::Erased { - self - } -} - -unsafe impl<'a, T: Send + 'a> IntoErasedSendSync<'a> for Box { - type Erased = Box; - fn into_erased_send_sync(self) -> Self::Erased { - let result: Box = self; - // This is safe since Erased can always implement Sync - // Only the destructor is available and it takes &mut self - unsafe { mem::transmute(result) } - } -} - -unsafe impl<'a, T: Send + Sync + 'a> IntoErasedSendSync<'a> for Arc { - type Erased = Arc; - fn into_erased_send_sync(self) -> Self::Erased { - self - } -} - -/// Typedef of an owning reference that uses an erased `Box` as the owner. -pub type ErasedBoxRef = OwningRef, U>; -/// Typedef of an owning reference that uses an erased `Rc` as the owner. -pub type ErasedRcRef = OwningRef, U>; -/// Typedef of an owning reference that uses an erased `Arc` as the owner. -pub type ErasedArcRef = OwningRef, U>; - -/// Typedef of a mutable owning reference that uses an erased `Box` as the owner. -pub type ErasedBoxRefMut = OwningRefMut, U>; - -#[cfg(test)] -mod tests; diff --git a/compiler/rustc_data_structures/src/owning_ref/tests.rs b/compiler/rustc_data_structures/src/owning_ref/tests.rs deleted file mode 100644 index a9b187c4ce0..00000000000 --- a/compiler/rustc_data_structures/src/owning_ref/tests.rs +++ /dev/null @@ -1,711 +0,0 @@ -// FIXME: owning_ref is not sound under stacked borrows. Preferably, get rid of it. -#[cfg(not(miri))] -mod owning_ref { - use super::super::OwningRef; - use super::super::{BoxRef, Erased, ErasedBoxRef, RcRef}; - use std::cmp::Ordering; - use std::collections::hash_map::DefaultHasher; - use std::collections::HashMap; - use std::hash::{Hash, Hasher}; - use std::rc::Rc; - - #[derive(Debug, PartialEq)] - struct Example(u32, String, [u8; 3]); - fn example() -> Example { - Example(42, "hello world".to_string(), [1, 2, 3]) - } - - #[test] - fn new_deref() { - let or: OwningRef, ()> = OwningRef::new(Box::new(())); - assert_eq!(&*or, &()); - } - - #[test] - fn into() { - let or: OwningRef, ()> = Box::new(()).into(); - assert_eq!(&*or, &()); - } - - #[test] - fn map_offset_ref() { - let or: BoxRef = Box::new(example()).into(); - let or: BoxRef<_, u32> = or.map(|x| &x.0); - assert_eq!(&*or, &42); - - let or: BoxRef = Box::new(example()).into(); - let or: BoxRef<_, u8> = or.map(|x| &x.2[1]); - assert_eq!(&*or, &2); - } - - #[test] - fn map_heap_ref() { - let or: BoxRef = Box::new(example()).into(); - let or: BoxRef<_, str> = or.map(|x| &x.1[..5]); - assert_eq!(&*or, "hello"); - } - - #[test] - fn map_static_ref() { - let or: BoxRef<()> = Box::new(()).into(); - let or: BoxRef<_, str> = or.map(|_| "hello"); - assert_eq!(&*or, "hello"); - } - - #[test] - fn map_chained() { - let or: BoxRef = Box::new(example().1).into(); - let or: BoxRef<_, str> = or.map(|x| &x[1..5]); - let or: BoxRef<_, str> = or.map(|x| &x[..2]); - assert_eq!(&*or, "el"); - } - - #[test] - fn map_chained_inference() { - let or = BoxRef::new(Box::new(example().1)).map(|x| &x[..5]).map(|x| &x[1..3]); - assert_eq!(&*or, "el"); - } - - #[test] - fn owner() { - let or: BoxRef = Box::new(example().1).into(); - let or = or.map(|x| &x[..5]); - assert_eq!(&*or, "hello"); - assert_eq!(&**or.owner(), "hello world"); - } - - #[test] - fn into_inner() { - let or: BoxRef = Box::new(example().1).into(); - let or = or.map(|x| &x[..5]); - assert_eq!(&*or, "hello"); - let s = *or.into_inner(); - assert_eq!(&s, "hello world"); - } - - #[test] - fn fmt_debug() { - let or: BoxRef = Box::new(example().1).into(); - let or = or.map(|x| &x[..5]); - let s = format!("{:?}", or); - assert_eq!(&s, "OwningRef { owner: \"hello world\", reference: \"hello\" }"); - } - - #[test] - fn erased_owner() { - let o1: BoxRef = BoxRef::new(Box::new(example())).map(|x| &x.1[..]); - - let o2: BoxRef = BoxRef::new(Box::new(example().1)).map(|x| &x[..]); - - let os: Vec> = vec![o1.erase_owner(), o2.erase_owner()]; - assert!(os.iter().all(|e| &e[..] == "hello world")); - } - - #[test] - fn raii_locks() { - use super::super::{MutexGuardRef, RwLockReadGuardRef, RwLockWriteGuardRef}; - use super::super::{RefMutRef, RefRef}; - use std::cell::RefCell; - use std::sync::{Mutex, RwLock}; - - { - let a = RefCell::new(1); - let a = { - let a = RefRef::new(a.borrow()); - assert_eq!(*a, 1); - a - }; - assert_eq!(*a, 1); - drop(a); - } - { - let a = RefCell::new(1); - let a = { - let a = RefMutRef::new(a.borrow_mut()); - assert_eq!(*a, 1); - a - }; - assert_eq!(*a, 1); - drop(a); - } - { - let a = Mutex::new(1); - let a = { - let a = MutexGuardRef::new(a.lock().unwrap()); - assert_eq!(*a, 1); - a - }; - assert_eq!(*a, 1); - drop(a); - } - { - let a = RwLock::new(1); - let a = { - let a = RwLockReadGuardRef::new(a.read().unwrap()); - assert_eq!(*a, 1); - a - }; - assert_eq!(*a, 1); - drop(a); - } - { - let a = RwLock::new(1); - let a = { - let a = RwLockWriteGuardRef::new(a.write().unwrap()); - assert_eq!(*a, 1); - a - }; - assert_eq!(*a, 1); - drop(a); - } - } - - #[test] - fn eq() { - let or1: BoxRef<[u8]> = BoxRef::new(vec![1, 2, 3].into_boxed_slice()); - let or2: BoxRef<[u8]> = BoxRef::new(vec![1, 2, 3].into_boxed_slice()); - assert_eq!(or1.eq(&or2), true); - } - - #[test] - fn cmp() { - let or1: BoxRef<[u8]> = BoxRef::new(vec![1, 2, 3].into_boxed_slice()); - let or2: BoxRef<[u8]> = BoxRef::new(vec![4, 5, 6].into_boxed_slice()); - assert_eq!(or1.cmp(&or2), Ordering::Less); - } - - #[test] - fn partial_cmp() { - let or1: BoxRef<[u8]> = BoxRef::new(vec![4, 5, 6].into_boxed_slice()); - let or2: BoxRef<[u8]> = BoxRef::new(vec![1, 2, 3].into_boxed_slice()); - assert_eq!(or1.partial_cmp(&or2), Some(Ordering::Greater)); - } - - #[test] - fn hash() { - let mut h1 = DefaultHasher::new(); - let mut h2 = DefaultHasher::new(); - - let or1: BoxRef<[u8]> = BoxRef::new(vec![1, 2, 3].into_boxed_slice()); - let or2: BoxRef<[u8]> = BoxRef::new(vec![1, 2, 3].into_boxed_slice()); - - or1.hash(&mut h1); - or2.hash(&mut h2); - - assert_eq!(h1.finish(), h2.finish()); - } - - #[test] - fn borrow() { - let mut hash = HashMap::new(); - let key = RcRef::::new(Rc::new("foo-bar".to_string())).map(|s| &s[..]); - - hash.insert(key.clone().map(|s| &s[..3]), 42); - hash.insert(key.clone().map(|s| &s[4..]), 23); - - assert_eq!(hash.get("foo"), Some(&42)); - assert_eq!(hash.get("bar"), Some(&23)); - } - - #[test] - fn total_erase() { - let a: OwningRef, [u8]> = OwningRef::new(vec![]).map(|x| &x[..]); - let b: OwningRef, [u8]> = - OwningRef::new(vec![].into_boxed_slice()).map(|x| &x[..]); - - let c: OwningRef>, [u8]> = unsafe { a.map_owner(Rc::new) }; - let d: OwningRef>, [u8]> = unsafe { b.map_owner(Rc::new) }; - - let e: OwningRef, [u8]> = c.erase_owner(); - let f: OwningRef, [u8]> = d.erase_owner(); - - let _g = e.clone(); - let _h = f.clone(); - } - - #[test] - fn total_erase_box() { - let a: OwningRef, [u8]> = OwningRef::new(vec![]).map(|x| &x[..]); - let b: OwningRef, [u8]> = - OwningRef::new(vec![].into_boxed_slice()).map(|x| &x[..]); - - let c: OwningRef>, [u8]> = a.map_owner_box(); - let d: OwningRef>, [u8]> = b.map_owner_box(); - - let _e: OwningRef, [u8]> = c.erase_owner(); - let _f: OwningRef, [u8]> = d.erase_owner(); - } - - #[test] - fn try_map1() { - use std::any::Any; - - let x = Box::new(123_i32); - let y: Box = x; - - assert!(OwningRef::new(y).try_map(|x| x.downcast_ref::().ok_or(())).is_ok()); - } - - #[test] - fn try_map2() { - use std::any::Any; - - let x = Box::new(123_i32); - let y: Box = x; - - assert!(!OwningRef::new(y).try_map(|x| x.downcast_ref::().ok_or(())).is_err()); - } -} - -mod owning_handle { - use super::super::OwningHandle; - use super::super::RcRef; - use std::cell::RefCell; - use std::rc::Rc; - use std::sync::Arc; - use std::sync::RwLock; - - #[test] - fn owning_handle() { - use std::cell::RefCell; - let cell = Rc::new(RefCell::new(2)); - let cell_ref = RcRef::new(cell); - let mut handle = - OwningHandle::new_with_fn(cell_ref, |x| unsafe { x.as_ref() }.unwrap().borrow_mut()); - assert_eq!(*handle, 2); - *handle = 3; - assert_eq!(*handle, 3); - } - - #[test] - fn try_owning_handle_ok() { - use std::cell::RefCell; - let cell = Rc::new(RefCell::new(2)); - let cell_ref = RcRef::new(cell); - let mut handle = OwningHandle::try_new::<_, ()>(cell_ref, |x| { - Ok(unsafe { x.as_ref() }.unwrap().borrow_mut()) - }) - .unwrap(); - assert_eq!(*handle, 2); - *handle = 3; - assert_eq!(*handle, 3); - } - - #[test] - fn try_owning_handle_err() { - use std::cell::RefCell; - let cell = Rc::new(RefCell::new(2)); - let cell_ref = RcRef::new(cell); - let handle = OwningHandle::try_new::<_, ()>(cell_ref, |x| { - if false { - return Ok(unsafe { x.as_ref() }.unwrap().borrow_mut()); - } - Err(()) - }); - assert!(handle.is_err()); - } - - #[test] - fn nested() { - use std::cell::RefCell; - use std::sync::{Arc, RwLock}; - - let result = { - let complex = Rc::new(RefCell::new(Arc::new(RwLock::new("someString")))); - let curr = RcRef::new(complex); - let curr = - OwningHandle::new_with_fn(curr, |x| unsafe { x.as_ref() }.unwrap().borrow_mut()); - let mut curr = OwningHandle::new_with_fn(curr, |x| { - unsafe { x.as_ref() }.unwrap().try_write().unwrap() - }); - assert_eq!(*curr, "someString"); - *curr = "someOtherString"; - curr - }; - assert_eq!(*result, "someOtherString"); - } - - #[test] - fn owning_handle_safe() { - use std::cell::RefCell; - let cell = Rc::new(RefCell::new(2)); - let cell_ref = RcRef::new(cell); - let handle = OwningHandle::new(cell_ref); - assert_eq!(*handle, 2); - } - - #[test] - fn owning_handle_mut_safe() { - use std::cell::RefCell; - let cell = Rc::new(RefCell::new(2)); - let cell_ref = RcRef::new(cell); - let mut handle = OwningHandle::new_mut(cell_ref); - assert_eq!(*handle, 2); - *handle = 3; - assert_eq!(*handle, 3); - } - - #[test] - fn owning_handle_safe_2() { - let result = { - let complex = Rc::new(RefCell::new(Arc::new(RwLock::new("someString")))); - let curr = RcRef::new(complex); - let curr = - OwningHandle::new_with_fn(curr, |x| unsafe { x.as_ref() }.unwrap().borrow_mut()); - let mut curr = OwningHandle::new_with_fn(curr, |x| { - unsafe { x.as_ref() }.unwrap().try_write().unwrap() - }); - assert_eq!(*curr, "someString"); - *curr = "someOtherString"; - curr - }; - assert_eq!(*result, "someOtherString"); - } -} - -// FIXME: owning_ref is not sound under stacked borrows. Preferably, get rid of it. -#[cfg(not(miri))] -mod owning_ref_mut { - use super::super::BoxRef; - use super::super::{BoxRefMut, Erased, ErasedBoxRefMut, OwningRefMut}; - use std::cmp::Ordering; - use std::collections::hash_map::DefaultHasher; - use std::collections::HashMap; - use std::hash::{Hash, Hasher}; - - #[derive(Debug, PartialEq)] - struct Example(u32, String, [u8; 3]); - fn example() -> Example { - Example(42, "hello world".to_string(), [1, 2, 3]) - } - - #[test] - fn new_deref() { - let or: OwningRefMut, ()> = OwningRefMut::new(Box::new(())); - assert_eq!(&*or, &()); - } - - #[test] - fn new_deref_mut() { - let mut or: OwningRefMut, ()> = OwningRefMut::new(Box::new(())); - assert_eq!(&mut *or, &mut ()); - } - - #[test] - fn mutate() { - let mut or: OwningRefMut, usize> = OwningRefMut::new(Box::new(0)); - assert_eq!(&*or, &0); - *or = 1; - assert_eq!(&*or, &1); - } - - #[test] - fn into() { - let or: OwningRefMut, ()> = Box::new(()).into(); - assert_eq!(&*or, &()); - } - - #[test] - fn map_offset_ref() { - let or: BoxRefMut = Box::new(example()).into(); - let or: BoxRef<_, u32> = or.map(|x| &mut x.0); - assert_eq!(&*or, &42); - - let or: BoxRefMut = Box::new(example()).into(); - let or: BoxRef<_, u8> = or.map(|x| &mut x.2[1]); - assert_eq!(&*or, &2); - } - - #[test] - fn map_heap_ref() { - let or: BoxRefMut = Box::new(example()).into(); - let or: BoxRef<_, str> = or.map(|x| &mut x.1[..5]); - assert_eq!(&*or, "hello"); - } - - #[test] - fn map_static_ref() { - let or: BoxRefMut<()> = Box::new(()).into(); - let or: BoxRef<_, str> = or.map(|_| "hello"); - assert_eq!(&*or, "hello"); - } - - #[test] - fn map_mut_offset_ref() { - let or: BoxRefMut = Box::new(example()).into(); - let or: BoxRefMut<_, u32> = or.map_mut(|x| &mut x.0); - assert_eq!(&*or, &42); - - let or: BoxRefMut = Box::new(example()).into(); - let or: BoxRefMut<_, u8> = or.map_mut(|x| &mut x.2[1]); - assert_eq!(&*or, &2); - } - - #[test] - fn map_mut_heap_ref() { - let or: BoxRefMut = Box::new(example()).into(); - let or: BoxRefMut<_, str> = or.map_mut(|x| &mut x.1[..5]); - assert_eq!(&*or, "hello"); - } - - #[test] - fn map_mut_static_ref() { - static mut MUT_S: [u8; 5] = *b"hello"; - - let mut_s: &'static mut [u8] = unsafe { &mut MUT_S }; - - let or: BoxRefMut<()> = Box::new(()).into(); - let or: BoxRefMut<_, [u8]> = or.map_mut(move |_| mut_s); - assert_eq!(&*or, b"hello"); - } - - #[test] - fn map_mut_chained() { - let or: BoxRefMut = Box::new(example().1).into(); - let or: BoxRefMut<_, str> = or.map_mut(|x| &mut x[1..5]); - let or: BoxRefMut<_, str> = or.map_mut(|x| &mut x[..2]); - assert_eq!(&*or, "el"); - } - - #[test] - fn map_chained_inference() { - let or = BoxRefMut::new(Box::new(example().1)) - .map_mut(|x| &mut x[..5]) - .map_mut(|x| &mut x[1..3]); - assert_eq!(&*or, "el"); - } - - #[test] - fn try_map_mut() { - let or: BoxRefMut = Box::new(example().1).into(); - let or: Result, ()> = or.try_map_mut(|x| Ok(&mut x[1..5])); - assert_eq!(&*or.unwrap(), "ello"); - - let or: BoxRefMut = Box::new(example().1).into(); - let or: Result, ()> = or.try_map_mut(|_| Err(())); - assert!(or.is_err()); - } - - #[test] - fn owner() { - let or: BoxRefMut = Box::new(example().1).into(); - let or = or.map_mut(|x| &mut x[..5]); - assert_eq!(&*or, "hello"); - assert_eq!(&**or.owner(), "hello world"); - } - - #[test] - fn into_inner() { - let or: BoxRefMut = Box::new(example().1).into(); - let or = or.map_mut(|x| &mut x[..5]); - assert_eq!(&*or, "hello"); - let s = *or.into_inner(); - assert_eq!(&s, "hello world"); - } - - #[test] - fn fmt_debug() { - let or: BoxRefMut = Box::new(example().1).into(); - let or = or.map_mut(|x| &mut x[..5]); - let s = format!("{:?}", or); - assert_eq!(&s, "OwningRefMut { owner: \"hello world\", reference: \"hello\" }"); - } - - #[test] - fn erased_owner() { - let o1: BoxRefMut = - BoxRefMut::new(Box::new(example())).map_mut(|x| &mut x.1[..]); - - let o2: BoxRefMut = - BoxRefMut::new(Box::new(example().1)).map_mut(|x| &mut x[..]); - - let os: Vec> = vec![o1.erase_owner(), o2.erase_owner()]; - assert!(os.iter().all(|e| &e[..] == "hello world")); - } - - #[test] - fn raii_locks() { - use super::super::RefMutRefMut; - use super::super::{MutexGuardRefMut, RwLockWriteGuardRefMut}; - use std::cell::RefCell; - use std::sync::{Mutex, RwLock}; - - { - let a = RefCell::new(1); - let a = { - let a = RefMutRefMut::new(a.borrow_mut()); - assert_eq!(*a, 1); - a - }; - assert_eq!(*a, 1); - drop(a); - } - { - let a = Mutex::new(1); - let a = { - let a = MutexGuardRefMut::new(a.lock().unwrap()); - assert_eq!(*a, 1); - a - }; - assert_eq!(*a, 1); - drop(a); - } - { - let a = RwLock::new(1); - let a = { - let a = RwLockWriteGuardRefMut::new(a.write().unwrap()); - assert_eq!(*a, 1); - a - }; - assert_eq!(*a, 1); - drop(a); - } - } - - #[test] - fn eq() { - let or1: BoxRefMut<[u8]> = BoxRefMut::new(vec![1, 2, 3].into_boxed_slice()); - let or2: BoxRefMut<[u8]> = BoxRefMut::new(vec![1, 2, 3].into_boxed_slice()); - assert_eq!(or1.eq(&or2), true); - } - - #[test] - fn cmp() { - let or1: BoxRefMut<[u8]> = BoxRefMut::new(vec![1, 2, 3].into_boxed_slice()); - let or2: BoxRefMut<[u8]> = BoxRefMut::new(vec![4, 5, 6].into_boxed_slice()); - assert_eq!(or1.cmp(&or2), Ordering::Less); - } - - #[test] - fn partial_cmp() { - let or1: BoxRefMut<[u8]> = BoxRefMut::new(vec![4, 5, 6].into_boxed_slice()); - let or2: BoxRefMut<[u8]> = BoxRefMut::new(vec![1, 2, 3].into_boxed_slice()); - assert_eq!(or1.partial_cmp(&or2), Some(Ordering::Greater)); - } - - #[test] - fn hash() { - let mut h1 = DefaultHasher::new(); - let mut h2 = DefaultHasher::new(); - - let or1: BoxRefMut<[u8]> = BoxRefMut::new(vec![1, 2, 3].into_boxed_slice()); - let or2: BoxRefMut<[u8]> = BoxRefMut::new(vec![1, 2, 3].into_boxed_slice()); - - or1.hash(&mut h1); - or2.hash(&mut h2); - - assert_eq!(h1.finish(), h2.finish()); - } - - #[test] - fn borrow() { - let mut hash = HashMap::new(); - let key1 = BoxRefMut::::new(Box::new("foo".to_string())).map(|s| &s[..]); - let key2 = BoxRefMut::::new(Box::new("bar".to_string())).map(|s| &s[..]); - - hash.insert(key1, 42); - hash.insert(key2, 23); - - assert_eq!(hash.get("foo"), Some(&42)); - assert_eq!(hash.get("bar"), Some(&23)); - } - - #[test] - fn total_erase() { - let a: OwningRefMut, [u8]> = OwningRefMut::new(vec![]).map_mut(|x| &mut x[..]); - let b: OwningRefMut, [u8]> = - OwningRefMut::new(vec![].into_boxed_slice()).map_mut(|x| &mut x[..]); - - let c: OwningRefMut>, [u8]> = unsafe { a.map_owner(Box::new) }; - let d: OwningRefMut>, [u8]> = unsafe { b.map_owner(Box::new) }; - - let _e: OwningRefMut, [u8]> = c.erase_owner(); - let _f: OwningRefMut, [u8]> = d.erase_owner(); - } - - #[test] - fn total_erase_box() { - let a: OwningRefMut, [u8]> = OwningRefMut::new(vec![]).map_mut(|x| &mut x[..]); - let b: OwningRefMut, [u8]> = - OwningRefMut::new(vec![].into_boxed_slice()).map_mut(|x| &mut x[..]); - - let c: OwningRefMut>, [u8]> = a.map_owner_box(); - let d: OwningRefMut>, [u8]> = b.map_owner_box(); - - let _e: OwningRefMut, [u8]> = c.erase_owner(); - let _f: OwningRefMut, [u8]> = d.erase_owner(); - } - - #[test] - fn try_map1() { - use std::any::Any; - - let x = Box::new(123_i32); - let y: Box = x; - - assert!(OwningRefMut::new(y).try_map_mut(|x| x.downcast_mut::().ok_or(())).is_ok()); - } - - #[test] - fn try_map2() { - use std::any::Any; - - let x = Box::new(123_i32); - let y: Box = x; - - assert!(!OwningRefMut::new(y).try_map_mut(|x| x.downcast_mut::().ok_or(())).is_err()); - } - - #[test] - fn try_map3() { - use std::any::Any; - - let x = Box::new(123_i32); - let y: Box = x; - - assert!(OwningRefMut::new(y).try_map(|x| x.downcast_ref::().ok_or(())).is_ok()); - } - - #[test] - fn try_map4() { - use std::any::Any; - - let x = Box::new(123_i32); - let y: Box = x; - - assert!(!OwningRefMut::new(y).try_map(|x| x.downcast_ref::().ok_or(())).is_err()); - } - - #[test] - fn into_owning_ref() { - use super::super::BoxRef; - - let or: BoxRefMut<()> = Box::new(()).into(); - let or: BoxRef<()> = or.into(); - assert_eq!(&*or, &()); - } - - struct Foo { - u: u32, - } - struct Bar { - f: Foo, - } - - #[test] - fn ref_mut() { - use std::cell::RefCell; - - let a = RefCell::new(Bar { f: Foo { u: 42 } }); - let mut b = OwningRefMut::new(a.borrow_mut()); - assert_eq!(b.f.u, 42); - b.f.u = 43; - let mut c = b.map_mut(|x| &mut x.f); - assert_eq!(c.u, 43); - c.u = 44; - let mut d = c.map_mut(|x| &mut x.u); - assert_eq!(*d, 44); - *d = 45; - assert_eq!(*d, 45); - } -} diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 855a7eb331f..215b8088741 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -7,9 +7,6 @@ //! while the serial versions degenerate straightforwardly to serial execution. //! The operations include `join`, `parallel`, `par_iter`, and `par_for_each`. //! -//! `rustc_erase_owner!` erases an `OwningRef` owner into `Erased` for the -//! serial version and `Erased + Send + Sync` for the parallel version. -//! //! Types //! ----- //! The parallel versions of types provide various kinds of synchronization, @@ -60,12 +57,6 @@ cfg_if! { pub unsafe auto trait Send {} pub unsafe auto trait Sync {} - #[macro_export] - macro_rules! rustc_erase_owner { - ($v:expr) => { - $v.erase_owner() - } - } unsafe impl Send for T {} unsafe impl Sync for T {} @@ -376,15 +367,6 @@ cfg_if! { /// This makes locks panic if they are already held. /// It is only useful when you are running in a single thread const ERROR_CHECKING: bool = false; - - #[macro_export] - macro_rules! rustc_erase_owner { - ($v:expr) => {{ - let v = $v; - ::rustc_data_structures::sync::assert_send_val(&v); - v.erase_send_sync_owner() - }} - } } } From 9405f586f5ce5db00b01468788bdd71c09ffd1bc Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 5 Apr 2023 13:48:19 +0000 Subject: [PATCH 4/9] Fix typo --- compiler/rustc_data_structures/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 47d8c84ba20..e373bd18402 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -1,5 +1,5 @@ //! Various data structures used by the Rust compiler. The intention -//! is that code in here should be not be *specific* to rustc, so that +//! is that code in here should not be *specific* to rustc, so that //! it can be easily unit tested and so forth. //! //! # Note From 504c4c40e9716fb47346d6e8a27389467763552a Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Wed, 5 Apr 2023 18:54:15 +0200 Subject: [PATCH 5/9] Add context to phantom comment --- compiler/rustc_data_structures/src/owned_slice.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_data_structures/src/owned_slice.rs b/compiler/rustc_data_structures/src/owned_slice.rs index dce209adfaa..146f06a1c69 100644 --- a/compiler/rustc_data_structures/src/owned_slice.rs +++ b/compiler/rustc_data_structures/src/owned_slice.rs @@ -26,10 +26,10 @@ pub struct OwnedSlice { // | touch the owner — otherwise the owner | // | could invalidate out `bytes` pointer | // | | - // | so be quite | + // | so be quiet | // +----+ +-------------------------------+ // \/ - // ⊂(´・◡・⊂ )∘˚˳° + // ⊂(´・◡・⊂ )∘˚˳° (I am the phantom remnant of #97770) #[expect(dead_code)] owner: Box, } From b6970d0e260aa33bb1e22ae7df739470ecad19e0 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 6 Apr 2023 17:03:42 +0000 Subject: [PATCH 6/9] Use `FnOnce` for `slice_owned` instead of `Fn` --- compiler/rustc_codegen_ssa/src/back/metadata.rs | 2 +- compiler/rustc_data_structures/src/owned_slice.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index c0f263d1478..3e3fcc08bd6 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -37,7 +37,7 @@ pub struct DefaultMetadataLoader; fn load_metadata_with( path: &Path, - f: impl for<'a> Fn(&'a [u8]) -> Result<&'a [u8], String>, + f: impl for<'a> FnOnce(&'a [u8]) -> Result<&'a [u8], String>, ) -> Result { let file = File::open(path).map_err(|e| format!("failed to open file '{}': {}", path.display(), e))?; diff --git a/compiler/rustc_data_structures/src/owned_slice.rs b/compiler/rustc_data_structures/src/owned_slice.rs index 146f06a1c69..e35a93f2a1d 100644 --- a/compiler/rustc_data_structures/src/owned_slice.rs +++ b/compiler/rustc_data_structures/src/owned_slice.rs @@ -59,7 +59,7 @@ pub struct OwnedSlice { pub fn slice_owned(owner: O, slicer: F) -> OwnedSlice where O: Send + Sync + 'static, - F: Fn(&O) -> &[u8], + F: FnOnce(&O) -> &[u8], { try_slice_owned(owner, |x| Ok::<_, !>(slicer(x))).into_ok() } @@ -70,7 +70,7 @@ where pub fn try_slice_owned(owner: O, slicer: F) -> Result where O: Send + Sync + 'static, - F: Fn(&O) -> Result<&[u8], E>, + F: FnOnce(&O) -> Result<&[u8], E>, { // We box the owner of the bytes, so it doesn't move. // From e0e39caf842579230bc73d2d32c1e293ca55efcb Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 6 Apr 2023 17:33:55 +0000 Subject: [PATCH 7/9] Add basic tests for `OwnedSlice` --- .../rustc_data_structures/src/owned_slice.rs | 3 + .../src/owned_slice/tests.rs | 68 +++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 compiler/rustc_data_structures/src/owned_slice/tests.rs diff --git a/compiler/rustc_data_structures/src/owned_slice.rs b/compiler/rustc_data_structures/src/owned_slice.rs index e35a93f2a1d..c0a3cb07194 100644 --- a/compiler/rustc_data_structures/src/owned_slice.rs +++ b/compiler/rustc_data_structures/src/owned_slice.rs @@ -111,3 +111,6 @@ unsafe impl Send for OwnedSlice {} // Safety: `OwnedSlice` is conceptually `(&'self.1 [u8], Box)`, which is `Sync` unsafe impl Sync for OwnedSlice {} + +#[cfg(test)] +mod tests; diff --git a/compiler/rustc_data_structures/src/owned_slice/tests.rs b/compiler/rustc_data_structures/src/owned_slice/tests.rs new file mode 100644 index 00000000000..fe4173f422a --- /dev/null +++ b/compiler/rustc_data_structures/src/owned_slice/tests.rs @@ -0,0 +1,68 @@ +use std::{cell::Cell, ops::Deref, rc::Rc}; + +use crate::{ + owned_slice::{slice_owned, try_slice_owned, OwnedSlice}, + OnDrop, +}; + +#[test] +fn smoke() { + let slice = slice_owned(vec![1, 2, 3, 4, 5, 6], Vec::as_slice); + + assert_eq!(&*slice, [1, 2, 3, 4, 5, 6]); +} + +#[test] +fn static_storage() { + let slice = slice_owned(Box::new(String::from("what")), |_| b"bytes boo"); + + assert_eq!(&*slice, b"bytes boo"); +} + +#[test] +fn slice_the_slice() { + let slice = slice_owned(vec![1, 2, 3, 4, 5, 6], Vec::as_slice); + let slice = slice_owned(slice, |s| &s[1..][..4]); + let slice = slice_owned(slice, |s| s); + let slice = slice_owned(slice, |s| &s[1..]); + + assert_eq!(&*slice, &[1, 2, 3, 4, 5, 6][1..][..4][1..]); +} + +#[test] +fn try_and_fail() { + let res = try_slice_owned(vec![0], |v| v.get(12..).ok_or(())); + + assert!(res.is_err()); +} + +#[test] +fn boxed() { + // It's important that we don't cause UB because of `Box`'es uniqueness + + let boxed: Box<[u8]> = vec![1, 1, 2, 3, 5, 8, 13, 21].into_boxed_slice(); + let slice = slice_owned(boxed, Deref::deref); + + assert_eq!(&*slice, [1, 1, 2, 3, 5, 8, 13, 21]); +} + +#[test] +fn drop() { + let flag = Rc::new(Cell::new(false)); + let flag_prime = Rc::clone(&flag); + let d = OnDrop(move || flag_prime.set(true)); + + let slice = slice_owned(d, |_| &[]); + + assert_eq!(flag.get(), false); + + std::mem::drop(slice); + + assert_eq!(flag.get(), true); +} + +#[test] +fn send_sync() { + crate::sync::assert_send::(); + crate::sync::assert_sync::(); +} From 2733c29bb1a385779c1d74dbce96180799e15e56 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 6 Apr 2023 18:05:59 +0000 Subject: [PATCH 8/9] Support multithreaded mode in `OwnedSlice` tests --- .../src/owned_slice/tests.rs | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_data_structures/src/owned_slice/tests.rs b/compiler/rustc_data_structures/src/owned_slice/tests.rs index fe4173f422a..e715fb55362 100644 --- a/compiler/rustc_data_structures/src/owned_slice/tests.rs +++ b/compiler/rustc_data_structures/src/owned_slice/tests.rs @@ -1,4 +1,10 @@ -use std::{cell::Cell, ops::Deref, rc::Rc}; +use std::{ + ops::Deref, + sync::{ + atomic::{self, AtomicBool}, + Arc, + }, +}; use crate::{ owned_slice::{slice_owned, try_slice_owned, OwnedSlice}, @@ -47,18 +53,18 @@ fn boxed() { } #[test] -fn drop() { - let flag = Rc::new(Cell::new(false)); - let flag_prime = Rc::clone(&flag); - let d = OnDrop(move || flag_prime.set(true)); +fn drop_drops() { + let flag = Arc::new(AtomicBool::new(false)); + let flag_prime = Arc::clone(&flag); + let d = OnDrop(move || flag_prime.store(true, atomic::Ordering::Relaxed)); let slice = slice_owned(d, |_| &[]); - assert_eq!(flag.get(), false); + assert_eq!(flag.load(atomic::Ordering::Relaxed), false); - std::mem::drop(slice); + drop(slice); - assert_eq!(flag.get(), true); + assert_eq!(flag.load(atomic::Ordering::Relaxed), true); } #[test] From fbe0591c6897216042a201795185bc0c0dac42c1 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 7 Apr 2023 10:14:50 +0000 Subject: [PATCH 9/9] Mark `OwnedSlice::{deref, borrow}` as `#[inline]` --- compiler/rustc_data_structures/src/owned_slice.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rustc_data_structures/src/owned_slice.rs b/compiler/rustc_data_structures/src/owned_slice.rs index c0a3cb07194..048401f66c2 100644 --- a/compiler/rustc_data_structures/src/owned_slice.rs +++ b/compiler/rustc_data_structures/src/owned_slice.rs @@ -92,6 +92,7 @@ where impl Deref for OwnedSlice { type Target = [u8]; + #[inline] fn deref(&self) -> &[u8] { // Safety: // `self.bytes` is valid per the construction in `slice_owned` @@ -101,6 +102,7 @@ impl Deref for OwnedSlice { } impl Borrow<[u8]> for OwnedSlice { + #[inline] fn borrow(&self) -> &[u8] { self }