From 970c613e4a785325469d4f694a16505d5deac17a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Sun, 3 Dec 2017 13:49:01 +0100 Subject: [PATCH] Add sync module to rustc_data_structures --- src/Cargo.lock | 38 +- src/bootstrap/bin/rustc.rs | 4 + src/bootstrap/compile.rs | 3 + src/bootstrap/config.rs | 3 + src/librustc/Cargo.toml | 1 - src/librustc/lib.rs | 1 - src/librustc/middle/cstore.rs | 2 +- src/librustc_data_structures/Cargo.toml | 7 + src/librustc_data_structures/lib.rs | 9 + .../owning_ref/LICENSE | 21 + .../owning_ref/mod.rs | 1902 +++++++++++++++++ src/librustc_data_structures/sync.rs | 403 ++++ src/librustc_driver/Cargo.toml | 1 - src/librustc_metadata/Cargo.toml | 1 - src/librustc_metadata/cstore.rs | 2 +- src/librustc_metadata/lib.rs | 1 - src/librustc_metadata/locator.rs | 2 +- src/librustc_trans/Cargo.toml | 1 - src/librustc_trans/lib.rs | 1 - src/librustc_trans/metadata.rs | 2 +- src/librustc_trans_utils/Cargo.toml | 1 - src/librustc_trans_utils/lib.rs | 1 - src/librustc_trans_utils/trans_crate.rs | 2 +- src/tools/tidy/src/lib.rs | 1 + 24 files changed, 2391 insertions(+), 19 deletions(-) create mode 100644 src/librustc_data_structures/owning_ref/LICENSE create mode 100644 src/librustc_data_structures/owning_ref/mod.rs create mode 100644 src/librustc_data_structures/sync.rs diff --git a/src/Cargo.lock b/src/Cargo.lock index 8cf9c3f2381..54a58854f0a 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -1313,6 +1313,27 @@ dependencies = [ "unwind 0.0.0", ] +[[package]] +name = "parking_lot" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot_core" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "percent-encoding" version = "1.0.1" @@ -1649,7 +1670,6 @@ dependencies = [ "graphviz 0.0.0", "jobserver 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_apfloat 0.0.0", "rustc_back 0.0.0", "rustc_const_math 0.0.0", @@ -1773,8 +1793,12 @@ dependencies = [ name = "rustc_data_structures" version = "0.0.0" dependencies = [ + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "serialize 0.0.0", + "stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1786,7 +1810,6 @@ dependencies = [ "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "graphviz 0.0.0", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc_allocator 0.0.0", "rustc_back 0.0.0", @@ -1874,7 +1897,6 @@ version = "0.0.0" dependencies = [ "flate2 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "proc_macro 0.0.0", "rustc 0.0.0", "rustc_back 0.0.0", @@ -1993,7 +2015,6 @@ dependencies = [ "jobserver 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_allocator 0.0.0", @@ -2020,7 +2041,6 @@ dependencies = [ "ar 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "flate2 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc_back 0.0.0", "rustc_data_structures 0.0.0", @@ -2228,6 +2248,11 @@ name = "smallvec" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "smallvec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "socket2" version = "0.2.4" @@ -2820,6 +2845,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum openssl-sys 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)" = "5483bdc56756041ba6aa37c9cb59cc2219f012a2a1377d97ad35556ac6676ee7" "checksum os_pipe 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "998bfbb3042e715190fe2a41abfa047d7e8cb81374d2977d7f100eacd8619cb1" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" +"checksum parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3e7f7c9857874e54afeb950eebeae662b1e51a2493666d2ea4c0a5d91dcf0412" +"checksum parking_lot_core 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "12d20aac4f67aa75f681aded784bac91f910ba3f2af1812573cdcf687414e122" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum pest 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0a6dda33d67c26f0aac90d324ab2eb7239c819fc7b2552fe9faa4fe88441edc8" "checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc" @@ -2871,6 +2898,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum shell-escape 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "dd5cc96481d54583947bfe88bf30c23d53f883c6cd0145368b69989d97b84ef8" "checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537" "checksum smallvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4f8266519bc1d17d0b5b16f6c21295625d562841c708f6376f49028a43e9c11e" +"checksum smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44db0ecb22921ef790d17ae13a3f6d15784183ff5f2a01aa32098c7498d2b4b9" "checksum socket2 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "36b4896961171cd3317c7e9603d88f379f8c6e45342212235d356496680c68fd" "checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b" "checksum string_cache 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "413fc7852aeeb5472f1986ef755f561ddf0c789d3d796e65f0b6fe293ecd4ef8" diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs index 30afd52f448..37336a56d76 100644 --- a/src/bootstrap/bin/rustc.rs +++ b/src/bootstrap/bin/rustc.rs @@ -265,6 +265,10 @@ fn main() { } } + if env::var_os("RUSTC_PARALLEL_QUERIES").is_some() { + cmd.arg("--cfg").arg("parallel_queries"); + } + let color = match env::var("RUSTC_COLOR") { Ok(s) => usize::from_str(&s).expect("RUSTC_COLOR should be an integer"), Err(_) => 0, diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index db013691bb1..4c76230ced8 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -561,6 +561,9 @@ pub fn rustc_cargo(build: &Build, if let Some(ref s) = build.config.rustc_default_linker { cargo.env("CFG_DEFAULT_LINKER", s); } + if build.config.rustc_parallel_queries { + cargo.env("RUSTC_PARALLEL_QUERIES", "1"); + } } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 9dd37d8e456..87b1db33a7a 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -87,6 +87,7 @@ pub struct Config { pub rust_debuginfo_lines: bool, pub rust_debuginfo_only_std: bool, pub rust_rpath: bool, + pub rustc_parallel_queries: bool, pub rustc_default_linker: Option, pub rust_optimize_tests: bool, pub rust_debuginfo_tests: bool, @@ -266,6 +267,7 @@ struct Rust { debuginfo: Option, debuginfo_lines: Option, debuginfo_only_std: Option, + experimental_parallel_queries: Option, debug_jemalloc: Option, use_jemalloc: Option, backtrace: Option, @@ -474,6 +476,7 @@ impl Config { set(&mut config.rust_dist_src, rust.dist_src); set(&mut config.quiet_tests, rust.quiet_tests); set(&mut config.test_miri, rust.test_miri); + config.rustc_parallel_queries = rust.experimental_parallel_queries.unwrap_or(false); config.rustc_default_linker = rust.default_linker.clone(); config.musl_root = rust.musl_root.clone().map(PathBuf::from); config.save_toolstates = rust.save_toolstates.clone().map(PathBuf::from); diff --git a/src/librustc/Cargo.toml b/src/librustc/Cargo.toml index a8892cb2210..3a4b30294db 100644 --- a/src/librustc/Cargo.toml +++ b/src/librustc/Cargo.toml @@ -15,7 +15,6 @@ fmt_macros = { path = "../libfmt_macros" } graphviz = { path = "../libgraphviz" } jobserver = "0.1" log = "0.3" -owning_ref = "0.3.3" rustc_apfloat = { path = "../librustc_apfloat" } rustc_back = { path = "../librustc_back" } rustc_const_math = { path = "../librustc_const_math" } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index bf7484156a6..8dc927c451b 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -77,7 +77,6 @@ extern crate getopts; extern crate graphviz; #[cfg(windows)] extern crate libc; -extern crate owning_ref; extern crate rustc_back; #[macro_use] extern crate rustc_data_structures; extern crate serialize; diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index 914fb0670b6..9708afd2045 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -38,7 +38,7 @@ use std::any::Any; use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use std::rc::Rc; -use owning_ref::ErasedBoxRef; +use rustc_data_structures::owning_ref::ErasedBoxRef; use syntax::ast; use syntax::ext::base::SyntaxExtension; use syntax::symbol::Symbol; diff --git a/src/librustc_data_structures/Cargo.toml b/src/librustc_data_structures/Cargo.toml index 343b1ed68b8..82075ce1f1f 100644 --- a/src/librustc_data_structures/Cargo.toml +++ b/src/librustc_data_structures/Cargo.toml @@ -11,3 +11,10 @@ crate-type = ["dylib"] [dependencies] log = "0.3" serialize = { path = "../libserialize" } +cfg-if = "0.1.2" +stable_deref_trait = "1.0.0" +parking_lot_core = "0.2.8" + +[dependencies.parking_lot] +version = "0.5" +features = ["nightly"] \ No newline at end of file diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index 8862ba3545e..24048e606df 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -31,7 +31,10 @@ #![feature(i128)] #![feature(conservative_impl_trait)] #![feature(specialization)] +#![feature(optin_builtin_traits)] #![feature(underscore_lifetimes)] +#![feature(macro_vis_matcher)] +#![feature(allow_internal_unstable)] #![cfg_attr(unix, feature(libc))] #![cfg_attr(test, feature(test))] @@ -42,6 +45,10 @@ extern crate log; extern crate serialize as rustc_serialize; // used by deriving #[cfg(unix)] extern crate libc; +extern crate parking_lot; +#[macro_use] +extern crate cfg_if; +extern crate stable_deref_trait; pub use rustc_serialize::hex::ToHex; @@ -67,6 +74,8 @@ pub mod tuple_slice; pub mod veccell; pub mod control_flow_graph; pub mod flock; +pub mod sync; +pub mod owning_ref; // See comments in src/librustc/lib.rs #[doc(hidden)] diff --git a/src/librustc_data_structures/owning_ref/LICENSE b/src/librustc_data_structures/owning_ref/LICENSE new file mode 100644 index 00000000000..dff72d1e432 --- /dev/null +++ b/src/librustc_data_structures/owning_ref/LICENSE @@ -0,0 +1,21 @@ +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/src/librustc_data_structures/owning_ref/mod.rs b/src/librustc_data_structures/owning_ref/mod.rs new file mode 100644 index 00000000000..23e0733748b --- /dev/null +++ b/src/librustc_data_structures/owning_ref/mod.rs @@ -0,0 +1,1902 @@ +#![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 a `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: + +```rust,ignore +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: + +```rust +# extern crate owning_ref; +# use 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 + +``` +extern crate owning_ref; +use 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 + +``` +extern crate owning_ref; +use 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 + +``` +extern crate owning_ref; +use 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 + +``` +extern crate owning_ref; +use 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 + +``` +extern crate owning_ref; +use 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 + +``` +extern crate owning_ref; +use 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 + +``` +extern crate owning_ref; +use 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`) + +``` +extern crate owning_ref; +use 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::{StableDeref as StableAddress, CloneStableDeref as CloneStableAddress}; + +/// 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 derferences to, +/// for example `Box -> Box`. This would be unneeded with +/// higher kinded types support in the language. +pub unsafe trait IntoErased<'a> { + /// Owner with the dereference type substituted to `Erased`. + type Erased; + /// Perform the type erasure. + fn into_erased(self) -> Self::Erased; +} + +/// Helper trait for erasing the concrete type of what an owner derferences to, +/// for example `Box -> Box`. This would be unneeded with +/// higher kinded types support in the language. +pub unsafe trait IntoErasedSendSync<'a>: Send + Sync { + /// Owner with the dereference type substituted to `Erased + Send + Sync`. + type Erased: Send + Sync; + /// Perform the type erasure. + fn into_erased_send_sync(self) -> Self::Erased; +} + +///////////////////////////////////////////////////////////////////////////// +// OwningRef +///////////////////////////////////////////////////////////////////////////// + +impl OwningRef { + /// Creates a new owning reference from a owner + /// initialized to the direct dereference of it. + /// + /// # Example + /// ``` + /// extern crate owning_ref; + /// use 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 + /// ``` + /// extern crate owning_ref; + /// use owning_ref::OwningRef; + /// + /// fn main() { + /// let owning_ref = OwningRef::new(Box::new([1, 2, 3, 4])); + /// + /// // create a 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 + /// ``` + /// extern crate owning_ref; + /// use owning_ref::OwningRef; + /// + /// fn main() { + /// let owning_ref = OwningRef::new(Box::new([1, 2, 3, 4])); + /// + /// // create a 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 a `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 + /// ``` + /// extern crate owning_ref; + /// use owning_ref::{OwningRef, Erased}; + /// + /// fn main() { + /// // NB: Using the concrete types here for explicitnes. + /// // 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` 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(), + } + } + + // TODO: 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 a owner + /// initialized to the direct dereference of it. + /// + /// # Example + /// ``` + /// extern crate owning_ref; + /// use 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 + /// ``` + /// extern crate owning_ref; + /// use owning_ref::OwningRefMut; + /// + /// fn main() { + /// let owning_ref_mut = OwningRefMut::new(Box::new([1, 2, 3, 4])); + /// + /// // create a 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 + /// ``` + /// extern crate owning_ref; + /// use owning_ref::OwningRefMut; + /// + /// fn main() { + /// let owning_ref_mut = OwningRefMut::new(Box::new([1, 2, 3, 4])); + /// + /// // create a 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 + /// ``` + /// extern crate owning_ref; + /// use owning_ref::OwningRefMut; + /// + /// fn main() { + /// let owning_ref_mut = OwningRefMut::new(Box::new([1, 2, 3, 4])); + /// + /// // create a 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 + /// ``` + /// extern crate owning_ref; + /// use owning_ref::OwningRefMut; + /// + /// fn main() { + /// let owning_ref_mut = OwningRefMut::new(Box::new([1, 2, 3, 4])); + /// + /// // create a 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 a `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 + /// ``` + /// extern crate owning_ref; + /// use owning_ref::{OwningRefMut, Erased}; + /// + /// fn main() { + /// // NB: Using the concrete types here for explicitnes. + /// // 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(), + } + } + + // TODO: 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, O::Target: ToHandle, H: Deref, +{ + /// Create 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, O::Target: ToHandleMut, H: DerefMut, +{ + /// Create 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, +{ + /// Create 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, + } + } + + /// Create 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::convert::From; +use std::fmt::{self, Debug}; +use std::marker::{Send, Sync}; +use std::cmp::{Eq, PartialEq, Ord, PartialOrd, Ordering}; +use std::hash::{Hash, Hasher}; +use std::borrow::Borrow; + +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 { + &mut *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 a 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) -> Result<(), fmt::Error> { + write!(f, + "OwningRef {{ owner: {:?}, reference: {:?} }}", + self.owner(), + &**self) + } +} + +impl Debug for OwningRefMut + where O: Debug, + T: Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + 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 Erased { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "",) + } +} + +impl PartialEq for OwningRef where T: PartialEq { + fn eq(&self, other: &Self) -> bool { + (&*self as &T).eq(&*other as &T) + } +} + +impl Eq for OwningRef where T: Eq {} + +impl PartialOrd for OwningRef where T: PartialOrd { + fn partial_cmp(&self, other: &Self) -> Option { + (&*self as &T).partial_cmp(&*other as &T) + } +} + +impl Ord for OwningRef where T: Ord { + fn cmp(&self, other: &Self) -> Ordering { + (&*self as &T).cmp(&*other as &T) + } +} + +impl Hash for OwningRef where T: Hash { + fn hash(&self, state: &mut H) { + (&*self as &T).hash(state); + } +} + +impl PartialEq for OwningRefMut where T: PartialEq { + fn eq(&self, other: &Self) -> bool { + (&*self as &T).eq(&*other as &T) + } +} + +impl Eq for OwningRefMut where T: Eq {} + +impl PartialOrd for OwningRefMut where T: PartialOrd { + fn partial_cmp(&self, other: &Self) -> Option { + (&*self as &T).partial_cmp(&*other as &T) + } +} + +impl Ord for OwningRefMut where T: Ord { + fn cmp(&self, other: &Self) -> Ordering { + (&*self as &T).cmp(&*other as &T) + } +} + +impl Hash for OwningRefMut where T: Hash { + fn hash(&self, state: &mut H) { + (&*self as &T).hash(state); + } +} + +///////////////////////////////////////////////////////////////////////////// +// std types integration and convenience type defs +///////////////////////////////////////////////////////////////////////////// + +use std::boxed::Box; +use std::rc::Rc; +use std::sync::Arc; +use std::sync::{MutexGuard, RwLockReadGuard, RwLockWriteGuard}; +use std::cell::{Ref, RefCell, RefMut}; + +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() } +} + +// NB: 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 a owning reference that uses a `Box` as the owner. +pub type BoxRef = OwningRef, U>; +/// Typedef of a owning reference that uses a `Vec` as the owner. +pub type VecRef = OwningRef, U>; +/// Typedef of a owning reference that uses a `String` as the owner. +pub type StringRef = OwningRef; + +/// Typedef of a owning reference that uses a `Rc` as the owner. +pub type RcRef = OwningRef, U>; +/// Typedef of a owning reference that uses a `Arc` as the owner. +pub type ArcRef = OwningRef, U>; + +/// Typedef of a owning reference that uses a `Ref` as the owner. +pub type RefRef<'a, T, U = T> = OwningRef, U>; +/// Typedef of a owning reference that uses a `RefMut` as the owner. +pub type RefMutRef<'a, T, U = T> = OwningRef, U>; +/// Typedef of a owning reference that uses a `MutexGuard` as the owner. +pub type MutexGuardRef<'a, T, U = T> = OwningRef, U>; +/// Typedef of a owning reference that uses a `RwLockReadGuard` as the owner. +pub type RwLockReadGuardRef<'a, T, U = T> = OwningRef, U>; +/// Typedef of a owning reference that uses a `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 a `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 + Sync + 'a> IntoErasedSendSync<'a> for Box { + type Erased = Box; + fn into_erased_send_sync(self) -> Self::Erased { + self + } +} + +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 a owning reference that uses an erased `Box` as the owner. +pub type ErasedBoxRef = OwningRef, U>; +/// Typedef of a owning reference that uses an erased `Rc` as the owner. +pub type ErasedRcRef = OwningRef, U>; +/// Typedef of a 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 { + mod owning_ref { + use super::super::OwningRef; + use super::super::{RcRef, BoxRef, Erased, ErasedBoxRef}; + use std::cmp::{PartialEq, Ord, PartialOrd, Ordering}; + use std::hash::{Hash, Hasher}; + use std::collections::hash_map::DefaultHasher; + use std::collections::HashMap; + 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::{RefRef, RefMutRef}; + use std::cell::RefCell; + use super::super::{MutexGuardRef, RwLockReadGuardRef, RwLockWriteGuardRef}; + 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; + + 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; + + 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::rc::Rc; + use std::cell::RefCell; + 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"); + } + } + + mod owning_ref_mut { + use super::super::{OwningRefMut, BoxRefMut, Erased, ErasedBoxRefMut}; + use super::super::BoxRef; + use std::cmp::{PartialEq, Ord, PartialOrd, Ordering}; + use std::hash::{Hash, Hasher}; + use std::collections::hash_map::DefaultHasher; + use std::collections::HashMap; + + #[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 std::cell::RefCell; + use super::super::{MutexGuardRefMut, RwLockWriteGuardRefMut}; + 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; + + 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; + + 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; + + 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; + + 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/src/librustc_data_structures/sync.rs b/src/librustc_data_structures/sync.rs new file mode 100644 index 00000000000..b1ab4eaa069 --- /dev/null +++ b/src/librustc_data_structures/sync.rs @@ -0,0 +1,403 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! This mdoule defines types which are thread safe if cfg!(parallel_queries) is true. +//! +//! `Lrc` is an alias of either Rc or Arc. +//! +//! `Lock` is a mutex. +//! It internally uses `parking_lot::Mutex` if cfg!(parallel_queries) is true, +//! `RefCell` otherwise. +//! +//! `RwLock` is a read-write lock. +//! It internally uses `parking_lot::RwLock` if cfg!(parallel_queries) is true, +//! `RefCell` otherwise. +//! +//! `LockCell` is a thread safe version of `Cell`, with `set` and `get` operations. +//! It can never deadlock. It uses `Cell` when +//! cfg!(parallel_queries) is false, otherwise it is a `Lock`. +//! +//! `MTLock` is a mutex which disappears if cfg!(parallel_queries) is false. +//! +//! `rustc_global!` gives us a way to declare variables which are intended to be +//! global for the current rustc session. This currently maps to thread-locals, +//! since rustdoc uses the rustc libraries in multiple threads. +//! These globals should eventually be moved into the `Session` structure. +//! +//! `rustc_erase_owner!` erases a OwningRef owner into Erased or Erased + Send + Sync +//! depending on the value of cfg!(parallel_queries). + +use std::cmp::Ordering; +use std::fmt::Debug; +use std::fmt::Formatter; +use std::fmt; +use owning_ref::{Erased, OwningRef}; + +cfg_if! { + if #[cfg(not(parallel_queries))] { + pub auto trait Send {} + pub auto trait Sync {} + + impl Send for T {} + impl Sync for T {} + + #[macro_export] + macro_rules! rustc_erase_owner { + ($v:expr) => { + $v.erase_owner() + } + } + + pub type MetadataRef = OwningRef, [u8]>; + + pub use std::rc::Rc as Lrc; + pub use std::cell::Ref as ReadGuard; + pub use std::cell::RefMut as WriteGuard; + pub use std::cell::RefMut as LockGuard; + + pub use std::cell::RefCell as RwLock; + use std::cell::RefCell as InnerLock; + + use std::cell::Cell; + + #[derive(Debug)] + pub struct MTLock(T); + + impl MTLock { + #[inline(always)] + pub fn new(inner: T) -> Self { + MTLock(inner) + } + + #[inline(always)] + pub fn into_inner(self) -> T { + self.0 + } + + #[inline(always)] + pub fn get_mut(&mut self) -> &mut T { + &mut self.0 + } + + #[inline(always)] + pub fn lock(&self) -> &T { + &self.0 + } + + #[inline(always)] + pub fn borrow(&self) -> &T { + &self.0 + } + + #[inline(always)] + pub fn borrow_mut(&self) -> &T { + &self.0 + } + } + + // FIXME: Probably a bad idea (in the threaded case) + impl Clone for MTLock { + #[inline] + fn clone(&self) -> Self { + MTLock(self.0.clone()) + } + } + + pub struct LockCell(Cell); + + impl LockCell { + #[inline(always)] + pub fn new(inner: T) -> Self { + LockCell(Cell::new(inner)) + } + + #[inline(always)] + pub fn into_inner(self) -> T { + self.0.into_inner() + } + + #[inline(always)] + pub fn set(&self, new_inner: T) { + self.0.set(new_inner); + } + + #[inline(always)] + pub fn get(&self) -> T where T: Copy { + self.0.get() + } + + #[inline(always)] + pub fn set_mut(&mut self, new_inner: T) { + self.0.set(new_inner); + } + + #[inline(always)] + pub fn get_mut(&mut self) -> T where T: Copy { + self.0.get() + } + } + + impl LockCell> { + #[inline(always)] + pub fn take(&self) -> Option { + unsafe { (*self.0.as_ptr()).take() } + } + } + } else { + pub use std::marker::Send as Send; + pub use std::marker::Sync as Sync; + + pub use parking_lot::RwLockReadGuard as ReadGuard; + pub use parking_lot::RwLockWriteGuard as WriteGuard; + + pub use parking_lot::MutexGuard as LockGuard; + + use parking_lot; + + pub use std::sync::Arc as Lrc; + + pub use self::Lock as MTLock; + + use parking_lot::Mutex as InnerLock; + + pub type MetadataRef = OwningRef, [u8]>; + + /// 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_sync_val(&v); + v.erase_send_sync_owner() + }} + } + + pub struct LockCell(Lock); + + impl LockCell { + #[inline(always)] + pub fn new(inner: T) -> Self { + LockCell(Lock::new(inner)) + } + + #[inline(always)] + pub fn into_inner(self) -> T { + self.0.into_inner() + } + + #[inline(always)] + pub fn set(&self, new_inner: T) { + *self.0.lock() = new_inner; + } + + #[inline(always)] + pub fn get(&self) -> T where T: Copy { + *self.0.lock() + } + + #[inline(always)] + pub fn set_mut(&mut self, new_inner: T) { + *self.0.get_mut() = new_inner; + } + + #[inline(always)] + pub fn get_mut(&mut self) -> T where T: Copy { + *self.0.get_mut() + } + } + + impl LockCell> { + #[inline(always)] + pub fn take(&self) -> Option { + self.0.lock().take() + } + } + + #[derive(Debug)] + pub struct RwLock(parking_lot::RwLock); + + impl RwLock { + #[inline(always)] + pub fn new(inner: T) -> Self { + RwLock(parking_lot::RwLock::new(inner)) + } + + #[inline(always)] + pub fn borrow(&self) -> ReadGuard { + if ERROR_CHECKING { + self.0.try_read().expect("lock was already held") + } else { + self.0.read() + } + } + + #[inline(always)] + pub fn borrow_mut(&self) -> WriteGuard { + if ERROR_CHECKING { + self.0.try_write().expect("lock was already held") + } else { + self.0.write() + } + } + } + + // FIXME: Probably a bad idea + impl Clone for RwLock { + #[inline] + fn clone(&self) -> Self { + RwLock::new(self.borrow().clone()) + } + } + } +} + +pub fn assert_sync() {} +pub fn assert_send_sync_val(_t: &T) {} + +#[macro_export] +#[allow_internal_unstable] +macro_rules! rustc_global { + // empty (base case for the recursion) + () => {}; + + // process multiple declarations + ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => ( + thread_local!($(#[$attr])* $vis static $name: $t = $init); + rustc_global!($($rest)*); + ); + + // handle a single declaration + ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => ( + thread_local!($(#[$attr])* $vis static $name: $t = $init); + ); +} + +#[macro_export] +macro_rules! rustc_access_global { + ($name:path, $callback:expr) => { + $name.with($callback) + } +} + +impl Debug for LockCell { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.debug_struct("LockCell") + .field("value", &self.get()) + .finish() + } +} + +impl Default for LockCell { + /// Creates a `LockCell`, with the `Default` value for T. + #[inline] + fn default() -> LockCell { + LockCell::new(Default::default()) + } +} + +impl PartialEq for LockCell { + #[inline] + fn eq(&self, other: &LockCell) -> bool { + self.get() == other.get() + } +} + +impl Eq for LockCell {} + +impl PartialOrd for LockCell { + #[inline] + fn partial_cmp(&self, other: &LockCell) -> Option { + self.get().partial_cmp(&other.get()) + } + + #[inline] + fn lt(&self, other: &LockCell) -> bool { + self.get() < other.get() + } + + #[inline] + fn le(&self, other: &LockCell) -> bool { + self.get() <= other.get() + } + + #[inline] + fn gt(&self, other: &LockCell) -> bool { + self.get() > other.get() + } + + #[inline] + fn ge(&self, other: &LockCell) -> bool { + self.get() >= other.get() + } +} + +impl Ord for LockCell { + #[inline] + fn cmp(&self, other: &LockCell) -> Ordering { + self.get().cmp(&other.get()) + } +} + +#[derive(Debug)] +pub struct Lock(InnerLock); + +impl Lock { + #[inline(always)] + pub fn new(inner: T) -> Self { + Lock(InnerLock::new(inner)) + } + + #[inline(always)] + pub fn into_inner(self) -> T { + self.0.into_inner() + } + + #[inline(always)] + pub fn get_mut(&mut self) -> &mut T { + self.0.get_mut() + } + + #[cfg(parallel_queries)] + #[inline(always)] + pub fn lock(&self) -> LockGuard { + if ERROR_CHECKING { + self.0.try_lock().expect("lock was already held") + } else { + self.0.lock() + } + } + + #[cfg(not(parallel_queries))] + #[inline(always)] + pub fn lock(&self) -> LockGuard { + self.0.borrow_mut() + } + + #[inline(always)] + pub fn borrow(&self) -> LockGuard { + self.lock() + } + + #[inline(always)] + pub fn borrow_mut(&self) -> LockGuard { + self.lock() + } +} + +// FIXME: Probably a bad idea +impl Clone for Lock { + #[inline] + fn clone(&self) -> Self { + Lock::new(self.borrow().clone()) + } +} diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml index d6155f53485..04c0f9b3518 100644 --- a/src/librustc_driver/Cargo.toml +++ b/src/librustc_driver/Cargo.toml @@ -12,7 +12,6 @@ crate-type = ["dylib"] arena = { path = "../libarena" } graphviz = { path = "../libgraphviz" } log = { version = "0.3", features = ["release_max_level_info"] } -owning_ref = "0.3.3" env_logger = { version = "0.4", default-features = false } rustc = { path = "../librustc" } rustc_allocator = { path = "../librustc_allocator" } diff --git a/src/librustc_metadata/Cargo.toml b/src/librustc_metadata/Cargo.toml index 40b75be36fe..3d61bc102e2 100644 --- a/src/librustc_metadata/Cargo.toml +++ b/src/librustc_metadata/Cargo.toml @@ -11,7 +11,6 @@ crate-type = ["dylib"] [dependencies] flate2 = "0.2" log = "0.3" -owning_ref = "0.3.3" proc_macro = { path = "../libproc_macro" } rustc = { path = "../librustc" } rustc_back = { path = "../librustc_back" } diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs index 3a4ba6768a7..87e41cae60d 100644 --- a/src/librustc_metadata/cstore.rs +++ b/src/librustc_metadata/cstore.rs @@ -24,7 +24,7 @@ use rustc::util::nodemap::{FxHashMap, FxHashSet, NodeMap}; use std::cell::{RefCell, Cell}; use std::rc::Rc; -use owning_ref::ErasedBoxRef; +use rustc_data_structures::owning_ref::ErasedBoxRef; use syntax::{ast, attr}; use syntax::ext::base::SyntaxExtension; use syntax::symbol::Symbol; diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs index 6c1ca362323..a65ddcecf88 100644 --- a/src/librustc_metadata/lib.rs +++ b/src/librustc_metadata/lib.rs @@ -31,7 +31,6 @@ extern crate syntax; extern crate syntax_pos; extern crate flate2; extern crate serialize as rustc_serialize; // used by deriving -extern crate owning_ref; extern crate rustc_errors as errors; extern crate syntax_ext; extern crate proc_macro; diff --git a/src/librustc_metadata/locator.rs b/src/librustc_metadata/locator.rs index 8abccb503d6..90c469eea84 100644 --- a/src/librustc_metadata/locator.rs +++ b/src/librustc_metadata/locator.rs @@ -243,7 +243,7 @@ use std::path::{Path, PathBuf}; use std::time::Instant; use flate2::read::DeflateDecoder; -use owning_ref::{ErasedBoxRef, OwningRef}; +use rustc_data_structures::owning_ref::{ErasedBoxRef, OwningRef}; pub struct CrateMismatch { path: PathBuf, diff --git a/src/librustc_trans/Cargo.toml b/src/librustc_trans/Cargo.toml index d8318ea8082..131cf59a571 100644 --- a/src/librustc_trans/Cargo.toml +++ b/src/librustc_trans/Cargo.toml @@ -15,7 +15,6 @@ flate2 = "0.2" jobserver = "0.1.5" log = "0.3" num_cpus = "1.0" -owning_ref = "0.3.3" rustc = { path = "../librustc" } rustc-demangle = "0.1.4" rustc_allocator = { path = "../librustc_allocator" } diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 3c2e56bf2a1..f6cc1215699 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -40,7 +40,6 @@ use syntax_pos::symbol::Symbol; extern crate bitflags; extern crate flate2; extern crate libc; -extern crate owning_ref; #[macro_use] extern crate rustc; extern crate jobserver; extern crate num_cpus; diff --git a/src/librustc_trans/metadata.rs b/src/librustc_trans/metadata.rs index 883808c5909..d57624da0c6 100644 --- a/src/librustc_trans/metadata.rs +++ b/src/librustc_trans/metadata.rs @@ -15,7 +15,7 @@ use llvm; use llvm::{False, ObjectFile, mk_section_iter}; use llvm::archive_ro::ArchiveRO; -use owning_ref::{ErasedBoxRef, OwningRef}; +use rustc_data_structures::owning_ref::{ErasedBoxRef, OwningRef}; use std::path::Path; use std::ptr; use std::slice; diff --git a/src/librustc_trans_utils/Cargo.toml b/src/librustc_trans_utils/Cargo.toml index 7d9d7cea933..cab44778baa 100644 --- a/src/librustc_trans_utils/Cargo.toml +++ b/src/librustc_trans_utils/Cargo.toml @@ -12,7 +12,6 @@ test = false [dependencies] ar = "0.3.0" flate2 = "0.2" -owning_ref = "0.3.3" log = "0.3" syntax = { path = "../libsyntax" } diff --git a/src/librustc_trans_utils/lib.rs b/src/librustc_trans_utils/lib.rs index d6f8707b874..3a42462e41e 100644 --- a/src/librustc_trans_utils/lib.rs +++ b/src/librustc_trans_utils/lib.rs @@ -29,7 +29,6 @@ extern crate ar; extern crate flate2; -extern crate owning_ref; #[macro_use] extern crate log; diff --git a/src/librustc_trans_utils/trans_crate.rs b/src/librustc_trans_utils/trans_crate.rs index 64589860161..800c3063c02 100644 --- a/src/librustc_trans_utils/trans_crate.rs +++ b/src/librustc_trans_utils/trans_crate.rs @@ -28,7 +28,7 @@ use std::fs::File; use std::path::Path; use std::sync::mpsc; -use owning_ref::{ErasedBoxRef, OwningRef}; +use rustc_data_structures::owning_ref::{ErasedBoxRef, OwningRef}; use ar::{Archive, Builder, Header}; use flate2::Compression; use flate2::write::DeflateEncoder; diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 3fd844f3261..61d71986b03 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -56,6 +56,7 @@ fn filter_dirs(path: &Path) -> bool { "src/llvm", "src/libbacktrace", "src/libcompiler_builtins", + "src/librustc_data_structures/owning_ref", "src/compiler-rt", "src/liblibc", "src/vendor",