From 826bc678c4ea03351d5ace86f719685d797877a0 Mon Sep 17 00:00:00 2001 From: Noam Ta Shma Date: Thu, 24 Jun 2021 00:01:31 +0300 Subject: [PATCH 01/10] multiple borrows extension --- Cargo.toml | 2 + src/ghost_cell.rs | 94 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 827b8ec..a2a0d4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,5 @@ categories = ["memory-management", "no-std"] [features] # Enables the use of unproven GhostCursor. experimental-ghost-cursor = [] +# Enables the use of unproven multiple mutable borrows. +experimental-multiple-mutable-borrows = [] diff --git a/src/ghost_cell.rs b/src/ghost_cell.rs index ba86b98..3d93bf5 100644 --- a/src/ghost_cell.rs +++ b/src/ghost_cell.rs @@ -232,6 +232,100 @@ impl<'brand, T: ?Sized> GhostCell<'brand, T> { // - `GhostCell<'_, T>` has the same in-memory representation as `T`. unsafe { mem::transmute(t) } } + + #[cfg(feature = "experimental-multiple-mutable-borrows")] + /// Borrows two `GhostCell`s at the same time. + /// If they are the same `GhostCell`, returns `None`. + /// Only enabled under experimental feature "experimental-multiple-mutable-borrows". + /// + /// # Example + /// + /// ```rust + /// use ghost_cell::{GhostToken, GhostCell}; + /// + /// let n = 42; + /// + /// let value = GhostToken::new(|mut token| { + /// let cell1 = GhostCell::new(42); + /// let cell2 = GhostCell::new(47); + /// + /// let (reference1, reference2): (&mut i32, &mut i32) + /// = GhostCell::borrow_mut_twice(&cell1, &cell2, &mut token).unwrap(); + /// *reference1 = 33; + /// *reference2 = 34; + /// // here we stop mutating, so the token isn't mutably borrowed anymore, and we can read again + /// + /// (*cell1.borrow(&token), *cell2.borrow(&token)) + /// }); + /// + /// assert_eq!((33, 34), value); + /// ``` + pub fn borrow_mut_twice<'a, Q>( + cell_a: &'a GhostCell<'brand, T>, + cell_b: &'a GhostCell<'brand, Q>, + _: &'a mut GhostToken<'brand> + ) -> Option<(&'a mut T, &'a mut Q)> where T: Sized { + // we require that `T`, `Q` are `Sized`, so no fat pointer problems. + if core::ptr::eq(cell_a, cell_b as *const _ as *const _) { + None + } else { + unsafe { + Some((&mut *cell_a.value.get(), &mut *cell_b.value.get())) + } + } + } + + #[cfg(feature = "experimental-multiple-mutable-borrows")] + /// Borrows three `GhostCell`s at the same time. + /// If any of them are the same `GhostCell`, returns `None`. + /// Only enabled under experimental feature "experimental-multiple-mutable-borrows". + /// + /// # Example + /// + /// ```rust + /// use ghost_cell::{GhostToken, GhostCell}; + /// + /// let n = 42; + /// + /// let value = GhostToken::new(|mut token| { + /// let cell1 = GhostCell::new(42); + /// let cell2 = GhostCell::new(47); + /// let cell3 = GhostCell::new(7); + /// + /// let (reference1, reference2, reference3): (&mut i32, &mut i32, &mut i32) + /// = GhostCell::borrow_mut_thrice(&cell1, &cell2, &cell3, &mut token).unwrap(); + /// *reference1 = 33; + /// *reference2 = 34; + /// *reference3 = 35; + /// // here we stop mutating, so the token isn't mutably borrowed anymore, and we can read again + /// + /// (*cell1.borrow(&token), *cell2.borrow(&token), *cell3.borrow(&token)) + /// }); + /// + /// assert_eq!((33, 34, 35), value); + /// ``` + pub fn borrow_mut_thrice<'a, Q, R>( + cell_a: &'a GhostCell<'brand, T>, + cell_b: &'a GhostCell<'brand, Q>, + cell_c: &'a GhostCell<'brand, R>, + _: &'a mut GhostToken<'brand> + ) -> Option<(&'a mut T, &'a mut Q, &'a mut R)> where T: Sized { + // we require that `T`, `Q`, `R` are `Sized`, so no fat pointer problems. + if core::ptr::eq(cell_a, cell_b as *const _ as *const _) + || core::ptr::eq(cell_b, cell_c as *const _ as *const _) + || core::ptr::eq(cell_c, cell_a as *const _ as *const _) + { + None + } else { + unsafe { + Some(( + &mut *cell_a.value.get(), + &mut *cell_b.value.get(), + &mut *cell_c.value.get() + )) + } + } + } } // Safe, convenience methods From 21d8016d96dfe88433edcf5542194f1d02a923b4 Mon Sep 17 00:00:00 2001 From: Noam Ta Shma Date: Sat, 26 Jun 2021 13:01:19 +0300 Subject: [PATCH 02/10] basic implementation of macro-based generalization of multiple borrowing --- src/ghost_cell.rs | 111 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 95 insertions(+), 16 deletions(-) diff --git a/src/ghost_cell.rs b/src/ghost_cell.rs index 3d93bf5..2b6103a 100644 --- a/src/ghost_cell.rs +++ b/src/ghost_cell.rs @@ -266,12 +266,12 @@ impl<'brand, T: ?Sized> GhostCell<'brand, T> { _: &'a mut GhostToken<'brand> ) -> Option<(&'a mut T, &'a mut Q)> where T: Sized { // we require that `T`, `Q` are `Sized`, so no fat pointer problems. - if core::ptr::eq(cell_a, cell_b as *const _ as *const _) { - None - } else { - unsafe { - Some((&mut *cell_a.value.get(), &mut *cell_b.value.get())) - } + check_distinct([cell_a as *const _ as *const (), cell_b as *const _ as *const ()])?; + unsafe { + Some(( + &mut *cell_a.value.get(), + &mut *cell_b.value.get() + )) } } @@ -311,23 +311,78 @@ impl<'brand, T: ?Sized> GhostCell<'brand, T> { _: &'a mut GhostToken<'brand> ) -> Option<(&'a mut T, &'a mut Q, &'a mut R)> where T: Sized { // we require that `T`, `Q`, `R` are `Sized`, so no fat pointer problems. - if core::ptr::eq(cell_a, cell_b as *const _ as *const _) - || core::ptr::eq(cell_b, cell_c as *const _ as *const _) - || core::ptr::eq(cell_c, cell_a as *const _ as *const _) - { - None - } else { + check_distinct([cell_a as *const _ as *const (), cell_b as *const _ as *const (), cell_c as *const _ as *const ()])?; + unsafe { + Some(( + &mut *cell_a.value.get(), + &mut *cell_b.value.get(), + &mut *cell_c.value.get() + )) + } + } +} + +#[cfg(feature = "experimental-multiple-mutable-borrows")] +/// Returns `Some(())` if the inputs are distinct, and `None` otherwise. +fn check_distinct(arr: [*const (); N]) -> Option<()> { + for i in 0..N { + for j in i+1..N { + if core::ptr::eq(arr[i], arr[j]) { + return None; + } + } + } + Some(()) + // TODO: if the array is large enough, sort the values instead. +} + + +#[cfg(feature = "experimental-multiple-mutable-borrows")] +macro_rules! generate_multiple_borrow_mut { + ( $func_name:ident, $( $input_name:ident, $input_type:ident ),* ) => { + pub fn $func_name <'a, 'brand, $( $input_type ,)* >( + $( $input_name : &'a GhostCell<'brand, $input_type>, )* + _: &'a mut GhostToken<'brand> + ) -> Option<( $(&'a mut $input_type,)* )> where T: Sized { + // we require that the types are `Sized`, so no fat pointer problems. + check_distinct([ $( $input_name as *const (), )* ])?; unsafe { Some(( - &mut *cell_a.value.get(), - &mut *cell_b.value.get(), - &mut *cell_c.value.get() + $( &mut * $input_name.value.get() ,)* )) } } - } + }; +} + +#[cfg(feature = "experimental-multiple-mutable-borrows")] +macro_rules! generate_multiple_borrow_mut_impl { + ( $func_name:ident, $first_name:ident, $first_type:ident, $( $input_name:ident, $input_type:ident ),* ) => { + impl<'brand, $first_type> GhostCell<'brand, $first_type> { + /// doc stub + pub fn $func_name <'a, $( $input_type ,)* >( + $first_name : &'a GhostCell<'brand, $first_type>, + $( $input_name : &'a GhostCell<'brand, $input_type>, )* + _: &'a mut GhostToken<'brand> + ) -> Option<( &'a mut $first_type, $(&'a mut $input_type,)* )> where $first_type: Sized { + // we require that the types are `Sized`, so no fat pointer problems. + check_distinct([ $first_name as *const _ as *const(), $( $input_name as *const _ as *const (), )* ])?; + unsafe { + Some(( + &mut * $first_name.value.get(), $( &mut * $input_name.value.get() ,)* + )) + } + } + } + }; } +#[cfg(feature = "experimental-multiple-mutable-borrows")] +generate_multiple_borrow_mut_impl!(borrow_mut_four, call_a, T, call_b, Q, cell_c, R, call_d, S); +#[cfg(feature = "experimental-multiple-mutable-borrows")] +generate_multiple_borrow_mut_impl!(borrow_mut_five, call_1, T1, call_2, T2, cell_3, T3, call_4, T4, cell_5, T5); + + // Safe, convenience methods #[forbid(unsafe_code)] impl<'brand, T> GhostCell<'brand, T> { @@ -558,4 +613,28 @@ pub fn cell_get_mut_borrows_cell_mutably() {} /// ``` pub fn cell_from_mut_borrows_value_mutably() {} +#[cfg(feature = "experimental-multiple-mutable-borrows")] +///```rust +///fn multiple_borrows_test() { +/// use ghost_cell::{GhostToken, GhostCell}; +/// let n = 42; +/// let value = GhostToken::new(|mut token| { +/// let cell1 = GhostCell::new(42); +/// let cell2 = GhostCell::new(47); +/// let cell3 = GhostCell::new(7); +/// let cell4 = GhostCell::new(9); +/// let (reference1, reference2, reference3, reference4): (&mut i32, &mut i32, &mut i32, &mut i32) +/// = GhostCell::borrow_mut_four(&cell1, &cell2, &cell3, &cell4, &mut token).unwrap(); +/// *reference1 = 33; +/// *reference2 = 34; +/// *reference3 = 35; +/// *reference4 = 36; +/// // here we stop mutating, so the token isn't mutably borrowed anymore, and we can read again +/// (*cell1.borrow(&token), *cell2.borrow(&token), *cell3.borrow(&token)) +/// }); +/// assert_eq!((33, 34, 35), value); +///} +///``` +pub fn multiple_borrows_test() {} + } // mod compile_tests From 24c93033978cef694d9d5c3718cc66110309a145 Mon Sep 17 00:00:00 2001 From: Noam Ta Shma Date: Sun, 27 Jun 2021 00:49:09 +0300 Subject: [PATCH 03/10] reworked generated generic multiple borrows --- src/ghost_cell.rs | 227 +++++++++++++++++++--------------------------- 1 file changed, 92 insertions(+), 135 deletions(-) diff --git a/src/ghost_cell.rs b/src/ghost_cell.rs index 2b6103a..128ad75 100644 --- a/src/ghost_cell.rs +++ b/src/ghost_cell.rs @@ -232,144 +232,75 @@ impl<'brand, T: ?Sized> GhostCell<'brand, T> { // - `GhostCell<'_, T>` has the same in-memory representation as `T`. unsafe { mem::transmute(t) } } +} - #[cfg(feature = "experimental-multiple-mutable-borrows")] - /// Borrows two `GhostCell`s at the same time. - /// If they are the same `GhostCell`, returns `None`. - /// Only enabled under experimental feature "experimental-multiple-mutable-borrows". - /// - /// # Example - /// - /// ```rust - /// use ghost_cell::{GhostToken, GhostCell}; - /// - /// let n = 42; - /// - /// let value = GhostToken::new(|mut token| { - /// let cell1 = GhostCell::new(42); - /// let cell2 = GhostCell::new(47); - /// - /// let (reference1, reference2): (&mut i32, &mut i32) - /// = GhostCell::borrow_mut_twice(&cell1, &cell2, &mut token).unwrap(); - /// *reference1 = 33; - /// *reference2 = 34; - /// // here we stop mutating, so the token isn't mutably borrowed anymore, and we can read again - /// - /// (*cell1.borrow(&token), *cell2.borrow(&token)) - /// }); - /// - /// assert_eq!((33, 34), value); - /// ``` - pub fn borrow_mut_twice<'a, Q>( - cell_a: &'a GhostCell<'brand, T>, - cell_b: &'a GhostCell<'brand, Q>, - _: &'a mut GhostToken<'brand> - ) -> Option<(&'a mut T, &'a mut Q)> where T: Sized { - // we require that `T`, `Q` are `Sized`, so no fat pointer problems. - check_distinct([cell_a as *const _ as *const (), cell_b as *const _ as *const ()])?; - unsafe { - Some(( - &mut *cell_a.value.get(), - &mut *cell_b.value.get() - )) +#[cfg(feature = "experimental-multiple-mutable-borrows")] +/// Returns `Some(())` if the inputs are distinct, and `None` otherwise. +fn check_distinct(arr: [*const (); N]) -> Option<()> { + for i in 0..N { + for j in i+1..N { + if core::ptr::eq(arr[i], arr[j]) { + return None; + } } } + Some(()) + // TODO: if the array is large enough, sort the values instead. +} - #[cfg(feature = "experimental-multiple-mutable-borrows")] - /// Borrows three `GhostCell`s at the same time. +#[cfg(feature = "experimental-multiple-mutable-borrows")] +/// A Sealed trait for implementing multiple borrows for any number of arguments. +pub trait SealedTupleTrait: private_tuple_trait::PrivateTupleTrait { + /// The tuple of references you get as a result. For example, if Self is + /// `(&'a GhostCell<'brand, T>, &'a GhostCell<'brand, Q>)` then `Result` is + /// `(&'a mut T, &'a mut Q)`. + type Result; + /// The ghost token: A `&'a mut GhostToken<'brand>`. + type Token; + /// Borrows any number of `GhostCell`s at the same time. /// If any of them are the same `GhostCell`, returns `None`. /// Only enabled under experimental feature "experimental-multiple-mutable-borrows". /// /// # Example /// /// ```rust - /// use ghost_cell::{GhostToken, GhostCell}; + /// use ghost_cell::{GhostToken, GhostCell, ghost_cell::SealedTupleTrait}; /// /// let n = 42; /// /// let value = GhostToken::new(|mut token| { /// let cell1 = GhostCell::new(42); /// let cell2 = GhostCell::new(47); - /// let cell3 = GhostCell::new(7); /// - /// let (reference1, reference2, reference3): (&mut i32, &mut i32, &mut i32) - /// = GhostCell::borrow_mut_thrice(&cell1, &cell2, &cell3, &mut token).unwrap(); + /// let (reference1, reference2): (&mut i32, &mut i32) + /// = (&cell1, &cell2).multiple_borrow_mut(&mut token).unwrap(); /// *reference1 = 33; /// *reference2 = 34; - /// *reference3 = 35; /// // here we stop mutating, so the token isn't mutably borrowed anymore, and we can read again /// - /// (*cell1.borrow(&token), *cell2.borrow(&token), *cell3.borrow(&token)) + /// (*cell1.borrow(&token), *cell2.borrow(&token)) /// }); /// - /// assert_eq!((33, 34, 35), value); + /// assert_eq!((33, 34), value); /// ``` - pub fn borrow_mut_thrice<'a, Q, R>( - cell_a: &'a GhostCell<'brand, T>, - cell_b: &'a GhostCell<'brand, Q>, - cell_c: &'a GhostCell<'brand, R>, - _: &'a mut GhostToken<'brand> - ) -> Option<(&'a mut T, &'a mut Q, &'a mut R)> where T: Sized { - // we require that `T`, `Q`, `R` are `Sized`, so no fat pointer problems. - check_distinct([cell_a as *const _ as *const (), cell_b as *const _ as *const (), cell_c as *const _ as *const ()])?; - unsafe { - Some(( - &mut *cell_a.value.get(), - &mut *cell_b.value.get(), - &mut *cell_c.value.get() - )) - } - } -} - -#[cfg(feature = "experimental-multiple-mutable-borrows")] -/// Returns `Some(())` if the inputs are distinct, and `None` otherwise. -fn check_distinct(arr: [*const (); N]) -> Option<()> { - for i in 0..N { - for j in i+1..N { - if core::ptr::eq(arr[i], arr[j]) { - return None; - } - } - } - Some(()) - // TODO: if the array is large enough, sort the values instead. -} - - -#[cfg(feature = "experimental-multiple-mutable-borrows")] -macro_rules! generate_multiple_borrow_mut { - ( $func_name:ident, $( $input_name:ident, $input_type:ident ),* ) => { - pub fn $func_name <'a, 'brand, $( $input_type ,)* >( - $( $input_name : &'a GhostCell<'brand, $input_type>, )* - _: &'a mut GhostToken<'brand> - ) -> Option<( $(&'a mut $input_type,)* )> where T: Sized { - // we require that the types are `Sized`, so no fat pointer problems. - check_distinct([ $( $input_name as *const (), )* ])?; - unsafe { - Some(( - $( &mut * $input_name.value.get() ,)* - )) - } - } - }; + fn multiple_borrow_mut(self, token: Self::Token) -> Option; } -#[cfg(feature = "experimental-multiple-mutable-borrows")] -macro_rules! generate_multiple_borrow_mut_impl { - ( $func_name:ident, $first_name:ident, $first_type:ident, $( $input_name:ident, $input_type:ident ),* ) => { - impl<'brand, $first_type> GhostCell<'brand, $first_type> { - /// doc stub - pub fn $func_name <'a, $( $input_type ,)* >( - $first_name : &'a GhostCell<'brand, $first_type>, - $( $input_name : &'a GhostCell<'brand, $input_type>, )* - _: &'a mut GhostToken<'brand> - ) -> Option<( &'a mut $first_type, $(&'a mut $input_type,)* )> where $first_type: Sized { +macro_rules! generate_public_instance { + ( $($name:ident),* ; $($type_letter:ident),* ) => { + #[cfg(feature = "experimental-multiple-mutable-borrows")] + impl<'a, 'brand, $($type_letter,)*> SealedTupleTrait for + ( $(&'a GhostCell<'brand, $type_letter>, )* ) + { + type Result = ( $(&'a mut $type_letter, )* ); + type Token = &'a mut GhostToken<'brand>; + fn multiple_borrow_mut(self, _: Self::Token) -> Option { + let ($($name,)*) = self; // we require that the types are `Sized`, so no fat pointer problems. - check_distinct([ $first_name as *const _ as *const(), $( $input_name as *const _ as *const (), )* ])?; + check_distinct([ $( $name as *const _ as *const (), )* ])?; unsafe { Some(( - &mut * $first_name.value.get(), $( &mut * $input_name.value.get() ,)* + $( &mut * $name.value.get() ,)* )) } } @@ -377,10 +308,60 @@ macro_rules! generate_multiple_borrow_mut_impl { }; } +generate_public_instance!(a ; T); +generate_public_instance!(a, b ; T, Q); +generate_public_instance!(a, b, c ; T, Q, R); +generate_public_instance!(a, b, c, d ; T, Q, R, S); +generate_public_instance!(a, b, c, d, e ; T1, T2, T3, T4, T5); +generate_public_instance!(a, b, c, d, e, f ; T1, T2, T3, T4, T5, T6); +generate_public_instance!(a, b, c, d, e, f, g ; T1, T2, T3, T4, T5, T6, T7); +generate_public_instance!(a, b, c, d, e, f, g, h ; T1, T2, T3, T4, T5, T6, T7, T8); +generate_public_instance!(a, b, c, d, e, f, g, h, i ; T1, T2, T3, T4, T5, T6, T7, T8, T9); +generate_public_instance!(a, b, c, d, e, f, g, h, i, j ; T1, T2, T3, T4, T5, T6, T7, T8, T9, T10); + #[cfg(feature = "experimental-multiple-mutable-borrows")] -generate_multiple_borrow_mut_impl!(borrow_mut_four, call_a, T, call_b, Q, cell_c, R, call_d, S); +mod private_tuple_trait { + pub trait PrivateTupleTrait {} + + macro_rules! generate_private_instance { + ( $($type_letter:ident),* ) => { + impl<'a, 'brand, $($type_letter,)*> PrivateTupleTrait for + ( $(&'a crate::ghost_cell::GhostCell<'brand, $type_letter>, )* ) + {} + }; + } + + generate_private_instance!(T); + generate_private_instance!(T, Q); + generate_private_instance!(T, Q, R); + generate_private_instance!(T, Q, R, S); + generate_private_instance!(T1, T2, T3, T4, T5); + generate_private_instance!(T1, T2, T3, T4, T5, T6); + generate_private_instance!(T1, T2, T3, T4, T5, T6, T7); + generate_private_instance!(T1, T2, T3, T4, T5, T6, T7, T8); + generate_private_instance!(T1, T2, T3, T4, T5, T6, T7, T8, T9); + generate_private_instance!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10); +} + #[cfg(feature = "experimental-multiple-mutable-borrows")] -generate_multiple_borrow_mut_impl!(borrow_mut_five, call_1, T1, call_2, T2, cell_3, T3, call_4, T4, cell_5, T5); +#[test] +fn multiple_borrows_test() { + let value = GhostToken::new(|mut token| { + let cell1 = GhostCell::new(42); + let cell2 = GhostCell::new(47); + let cell3 = GhostCell::new(7); + let cell4 = GhostCell::new(9); + let (reference1, reference2, reference3, reference4): (&mut i32, &mut i32, &mut i32, &mut i32) + = (&cell1, &cell2, &cell3, &cell4).multiple_borrow_mut(&mut token).unwrap(); + *reference1 = 33; + *reference2 = 34; + *reference3 = 35; + *reference4 = 36; + // here we stop mutating, so the token isn't mutably borrowed anymore, and we can read again + (*cell1.borrow(&token), *cell2.borrow(&token), *cell3.borrow(&token)) + }); + assert_eq!((33, 34, 35), value); +} // Safe, convenience methods @@ -613,28 +594,4 @@ pub fn cell_get_mut_borrows_cell_mutably() {} /// ``` pub fn cell_from_mut_borrows_value_mutably() {} -#[cfg(feature = "experimental-multiple-mutable-borrows")] -///```rust -///fn multiple_borrows_test() { -/// use ghost_cell::{GhostToken, GhostCell}; -/// let n = 42; -/// let value = GhostToken::new(|mut token| { -/// let cell1 = GhostCell::new(42); -/// let cell2 = GhostCell::new(47); -/// let cell3 = GhostCell::new(7); -/// let cell4 = GhostCell::new(9); -/// let (reference1, reference2, reference3, reference4): (&mut i32, &mut i32, &mut i32, &mut i32) -/// = GhostCell::borrow_mut_four(&cell1, &cell2, &cell3, &cell4, &mut token).unwrap(); -/// *reference1 = 33; -/// *reference2 = 34; -/// *reference3 = 35; -/// *reference4 = 36; -/// // here we stop mutating, so the token isn't mutably borrowed anymore, and we can read again -/// (*cell1.borrow(&token), *cell2.borrow(&token), *cell3.borrow(&token)) -/// }); -/// assert_eq!((33, 34, 35), value); -///} -///``` -pub fn multiple_borrows_test() {} - } // mod compile_tests From a83ef9ab69e8506f5c04c2f9181988f8ab993abc Mon Sep 17 00:00:00 2001 From: Noam Ta Shma Date: Sun, 27 Jun 2021 00:56:34 +0300 Subject: [PATCH 04/10] minor renamings --- src/ghost_cell.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/ghost_cell.rs b/src/ghost_cell.rs index 128ad75..6f0078e 100644 --- a/src/ghost_cell.rs +++ b/src/ghost_cell.rs @@ -238,7 +238,7 @@ impl<'brand, T: ?Sized> GhostCell<'brand, T> { /// Returns `Some(())` if the inputs are distinct, and `None` otherwise. fn check_distinct(arr: [*const (); N]) -> Option<()> { for i in 0..N { - for j in i+1..N { + for j in 0..i { if core::ptr::eq(arr[i], arr[j]) { return None; } @@ -249,8 +249,9 @@ fn check_distinct(arr: [*const (); N]) -> Option<()> { } #[cfg(feature = "experimental-multiple-mutable-borrows")] -/// A Sealed trait for implementing multiple borrows for any number of arguments. -pub trait SealedTupleTrait: private_tuple_trait::PrivateTupleTrait { +/// A Sealed trait for implementing multiple borrows for any number of arguments, +/// Using a `GhostToken`. +pub trait MultipleMutableBorrows: multiple_mutable_borrows_private_module::PrivateTupleTrait { /// The tuple of references you get as a result. For example, if Self is /// `(&'a GhostCell<'brand, T>, &'a GhostCell<'brand, Q>)` then `Result` is /// `(&'a mut T, &'a mut Q)`. @@ -264,7 +265,7 @@ pub trait SealedTupleTrait: private_tuple_trait::PrivateTupleTrait { /// # Example /// /// ```rust - /// use ghost_cell::{GhostToken, GhostCell, ghost_cell::SealedTupleTrait}; + /// use ghost_cell::{GhostToken, GhostCell, ghost_cell::MultipleMutableBorrows}; /// /// let n = 42; /// @@ -273,7 +274,7 @@ pub trait SealedTupleTrait: private_tuple_trait::PrivateTupleTrait { /// let cell2 = GhostCell::new(47); /// /// let (reference1, reference2): (&mut i32, &mut i32) - /// = (&cell1, &cell2).multiple_borrow_mut(&mut token).unwrap(); + /// = (&cell1, &cell2).borrow_mut(&mut token).unwrap(); /// *reference1 = 33; /// *reference2 = 34; /// // here we stop mutating, so the token isn't mutably borrowed anymore, and we can read again @@ -283,18 +284,18 @@ pub trait SealedTupleTrait: private_tuple_trait::PrivateTupleTrait { /// /// assert_eq!((33, 34), value); /// ``` - fn multiple_borrow_mut(self, token: Self::Token) -> Option; + fn borrow_mut(self, token: Self::Token) -> Option; } macro_rules! generate_public_instance { ( $($name:ident),* ; $($type_letter:ident),* ) => { #[cfg(feature = "experimental-multiple-mutable-borrows")] - impl<'a, 'brand, $($type_letter,)*> SealedTupleTrait for + impl<'a, 'brand, $($type_letter,)*> MultipleMutableBorrows for ( $(&'a GhostCell<'brand, $type_letter>, )* ) { type Result = ( $(&'a mut $type_letter, )* ); type Token = &'a mut GhostToken<'brand>; - fn multiple_borrow_mut(self, _: Self::Token) -> Option { + fn borrow_mut(self, _: Self::Token) -> Option { let ($($name,)*) = self; // we require that the types are `Sized`, so no fat pointer problems. check_distinct([ $( $name as *const _ as *const (), )* ])?; @@ -320,7 +321,7 @@ generate_public_instance!(a, b, c, d, e, f, g, h, i ; T1, T2, T3, T4, T5, T6, T7 generate_public_instance!(a, b, c, d, e, f, g, h, i, j ; T1, T2, T3, T4, T5, T6, T7, T8, T9, T10); #[cfg(feature = "experimental-multiple-mutable-borrows")] -mod private_tuple_trait { +mod multiple_mutable_borrows_private_module { pub trait PrivateTupleTrait {} macro_rules! generate_private_instance { @@ -352,7 +353,7 @@ fn multiple_borrows_test() { let cell3 = GhostCell::new(7); let cell4 = GhostCell::new(9); let (reference1, reference2, reference3, reference4): (&mut i32, &mut i32, &mut i32, &mut i32) - = (&cell1, &cell2, &cell3, &cell4).multiple_borrow_mut(&mut token).unwrap(); + = (&cell1, &cell2, &cell3, &cell4).borrow_mut(&mut token).unwrap(); *reference1 = 33; *reference2 = 34; *reference3 = 35; From 9347dd6b44ed9afbb784e254b121424597826ada Mon Sep 17 00:00:00 2001 From: Noam Ta Shma Date: Tue, 29 Jun 2021 15:58:51 +0300 Subject: [PATCH 05/10] encapsulated additions into a module and raised 'a, 'brand to the trait level --- src/ghost_cell.rs | 231 +++++++++++++++++++++++----------------------- 1 file changed, 115 insertions(+), 116 deletions(-) diff --git a/src/ghost_cell.rs b/src/ghost_cell.rs index 6f0078e..b8c4d81 100644 --- a/src/ghost_cell.rs +++ b/src/ghost_cell.rs @@ -235,133 +235,132 @@ impl<'brand, T: ?Sized> GhostCell<'brand, T> { } #[cfg(feature = "experimental-multiple-mutable-borrows")] -/// Returns `Some(())` if the inputs are distinct, and `None` otherwise. -fn check_distinct(arr: [*const (); N]) -> Option<()> { - for i in 0..N { - for j in 0..i { - if core::ptr::eq(arr[i], arr[j]) { - return None; +pub use multiple_borrows::*; +#[cfg(feature = "experimental-multiple-mutable-borrows")] +mod multiple_borrows { + use crate::ghost_cell::*; + /// Returns `Some(())` if the inputs are distinct, and `None` otherwise. + fn check_distinct(arr: [*const (); N]) -> Option<()> { + for i in 0..N { + for j in 0..i { + if core::ptr::eq(arr[i], arr[j]) { + return None; + } } } + Some(()) + // TODO: if the array is large enough, sort the values instead. } - Some(()) - // TODO: if the array is large enough, sort the values instead. -} -#[cfg(feature = "experimental-multiple-mutable-borrows")] -/// A Sealed trait for implementing multiple borrows for any number of arguments, -/// Using a `GhostToken`. -pub trait MultipleMutableBorrows: multiple_mutable_borrows_private_module::PrivateTupleTrait { - /// The tuple of references you get as a result. For example, if Self is - /// `(&'a GhostCell<'brand, T>, &'a GhostCell<'brand, Q>)` then `Result` is - /// `(&'a mut T, &'a mut Q)`. - type Result; - /// The ghost token: A `&'a mut GhostToken<'brand>`. - type Token; - /// Borrows any number of `GhostCell`s at the same time. - /// If any of them are the same `GhostCell`, returns `None`. - /// Only enabled under experimental feature "experimental-multiple-mutable-borrows". - /// - /// # Example - /// - /// ```rust - /// use ghost_cell::{GhostToken, GhostCell, ghost_cell::MultipleMutableBorrows}; - /// - /// let n = 42; - /// - /// let value = GhostToken::new(|mut token| { - /// let cell1 = GhostCell::new(42); - /// let cell2 = GhostCell::new(47); - /// - /// let (reference1, reference2): (&mut i32, &mut i32) - /// = (&cell1, &cell2).borrow_mut(&mut token).unwrap(); - /// *reference1 = 33; - /// *reference2 = 34; - /// // here we stop mutating, so the token isn't mutably borrowed anymore, and we can read again - /// - /// (*cell1.borrow(&token), *cell2.borrow(&token)) - /// }); - /// - /// assert_eq!((33, 34), value); - /// ``` - fn borrow_mut(self, token: Self::Token) -> Option; -} + /// A Sealed trait for implementing multiple borrows for any number of arguments, + /// Using a `GhostToken`. + pub trait MultipleMutableBorrows<'a, 'brand>: + multiple_mutable_borrows_private_module::PrivateTupleTrait { + /// The tuple of references you get as a result. For example, if Self is + /// `(&'a GhostCell<'brand, T>, &'a GhostCell<'brand, Q>)` then `Result` is + /// `(&'a mut T, &'a mut Q)`. + type Result; + /// Borrows any number of `GhostCell`s at the same time. + /// If any of them are the same `GhostCell`, returns `None`. + /// Only enabled under experimental feature "experimental-multiple-mutable-borrows". + /// + /// # Example + /// + /// ```rust + /// use ghost_cell::{GhostToken, GhostCell, ghost_cell::MultipleMutableBorrows}; + /// + /// let n = 42; + /// + /// let value = GhostToken::new(|mut token| { + /// let cell1 = GhostCell::new(42); + /// let cell2 = GhostCell::new(47); + /// + /// let (reference1, reference2): (&mut i32, &mut i32) + /// = (&cell1, &cell2).borrow_mut(&mut token).unwrap(); + /// *reference1 = 33; + /// *reference2 = 34; + /// // here we stop mutating, so the token isn't mutably borrowed anymore, and we can read again + /// + /// (*cell1.borrow(&token), *cell2.borrow(&token)) + /// }); + /// + /// assert_eq!((33, 34), value); + /// ``` + fn borrow_mut(self, token: &'a mut GhostToken<'brand>) -> Self::Result; + } -macro_rules! generate_public_instance { - ( $($name:ident),* ; $($type_letter:ident),* ) => { - #[cfg(feature = "experimental-multiple-mutable-borrows")] - impl<'a, 'brand, $($type_letter,)*> MultipleMutableBorrows for - ( $(&'a GhostCell<'brand, $type_letter>, )* ) - { - type Result = ( $(&'a mut $type_letter, )* ); - type Token = &'a mut GhostToken<'brand>; - fn borrow_mut(self, _: Self::Token) -> Option { - let ($($name,)*) = self; - // we require that the types are `Sized`, so no fat pointer problems. - check_distinct([ $( $name as *const _ as *const (), )* ])?; - unsafe { - Some(( - $( &mut * $name.value.get() ,)* - )) + macro_rules! generate_public_instance { + ( $($name:ident),* ; $($type_letter:ident),* ) => { + impl<'a, 'brand, $($type_letter,)*> MultipleMutableBorrows<'a, 'brand> for + ( $(&'a GhostCell<'brand, $type_letter>, )* ) + { + type Result = Option<( $(&'a mut $type_letter, )* )>; + fn borrow_mut(self, _: &'a mut GhostToken<'brand>) -> Self::Result { + let ($($name,)*) = self; + // we require that the types are `Sized`, so no fat pointer problems. + check_distinct([ $( $name as *const _ as *const (), )* ])?; + unsafe { + Some(( + $( &mut * $name.value.get() ,)* + )) + } } } - } - }; -} - -generate_public_instance!(a ; T); -generate_public_instance!(a, b ; T, Q); -generate_public_instance!(a, b, c ; T, Q, R); -generate_public_instance!(a, b, c, d ; T, Q, R, S); -generate_public_instance!(a, b, c, d, e ; T1, T2, T3, T4, T5); -generate_public_instance!(a, b, c, d, e, f ; T1, T2, T3, T4, T5, T6); -generate_public_instance!(a, b, c, d, e, f, g ; T1, T2, T3, T4, T5, T6, T7); -generate_public_instance!(a, b, c, d, e, f, g, h ; T1, T2, T3, T4, T5, T6, T7, T8); -generate_public_instance!(a, b, c, d, e, f, g, h, i ; T1, T2, T3, T4, T5, T6, T7, T8, T9); -generate_public_instance!(a, b, c, d, e, f, g, h, i, j ; T1, T2, T3, T4, T5, T6, T7, T8, T9, T10); - -#[cfg(feature = "experimental-multiple-mutable-borrows")] -mod multiple_mutable_borrows_private_module { - pub trait PrivateTupleTrait {} - - macro_rules! generate_private_instance { - ( $($type_letter:ident),* ) => { - impl<'a, 'brand, $($type_letter,)*> PrivateTupleTrait for - ( $(&'a crate::ghost_cell::GhostCell<'brand, $type_letter>, )* ) - {} }; } - generate_private_instance!(T); - generate_private_instance!(T, Q); - generate_private_instance!(T, Q, R); - generate_private_instance!(T, Q, R, S); - generate_private_instance!(T1, T2, T3, T4, T5); - generate_private_instance!(T1, T2, T3, T4, T5, T6); - generate_private_instance!(T1, T2, T3, T4, T5, T6, T7); - generate_private_instance!(T1, T2, T3, T4, T5, T6, T7, T8); - generate_private_instance!(T1, T2, T3, T4, T5, T6, T7, T8, T9); - generate_private_instance!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10); -} + generate_public_instance!(a ; T); + generate_public_instance!(a, b ; T, Q); + generate_public_instance!(a, b, c ; T, Q, R); + generate_public_instance!(a, b, c, d ; T, Q, R, S); + generate_public_instance!(a, b, c, d, e ; T1, T2, T3, T4, T5); + generate_public_instance!(a, b, c, d, e, f ; T1, T2, T3, T4, T5, T6); + generate_public_instance!(a, b, c, d, e, f, g ; T1, T2, T3, T4, T5, T6, T7); + generate_public_instance!(a, b, c, d, e, f, g, h ; T1, T2, T3, T4, T5, T6, T7, T8); + generate_public_instance!(a, b, c, d, e, f, g, h, i ; T1, T2, T3, T4, T5, T6, T7, T8, T9); + generate_public_instance!(a, b, c, d, e, f, g, h, i, j ; T1, T2, T3, T4, T5, T6, T7, T8, T9, T10); + + mod multiple_mutable_borrows_private_module { + pub trait PrivateTupleTrait {} + + macro_rules! generate_private_instance { + ( $($type_letter:ident),* ) => { + impl<'a, 'brand, $($type_letter,)*> PrivateTupleTrait for + ( $(&'a crate::ghost_cell::GhostCell<'brand, $type_letter>, )* ) + {} + }; + } -#[cfg(feature = "experimental-multiple-mutable-borrows")] -#[test] -fn multiple_borrows_test() { - let value = GhostToken::new(|mut token| { - let cell1 = GhostCell::new(42); - let cell2 = GhostCell::new(47); - let cell3 = GhostCell::new(7); - let cell4 = GhostCell::new(9); - let (reference1, reference2, reference3, reference4): (&mut i32, &mut i32, &mut i32, &mut i32) - = (&cell1, &cell2, &cell3, &cell4).borrow_mut(&mut token).unwrap(); - *reference1 = 33; - *reference2 = 34; - *reference3 = 35; - *reference4 = 36; - // here we stop mutating, so the token isn't mutably borrowed anymore, and we can read again - (*cell1.borrow(&token), *cell2.borrow(&token), *cell3.borrow(&token)) - }); - assert_eq!((33, 34, 35), value); + generate_private_instance!(T); + generate_private_instance!(T, Q); + generate_private_instance!(T, Q, R); + generate_private_instance!(T, Q, R, S); + generate_private_instance!(T1, T2, T3, T4, T5); + generate_private_instance!(T1, T2, T3, T4, T5, T6); + generate_private_instance!(T1, T2, T3, T4, T5, T6, T7); + generate_private_instance!(T1, T2, T3, T4, T5, T6, T7, T8); + generate_private_instance!(T1, T2, T3, T4, T5, T6, T7, T8, T9); + generate_private_instance!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10); + } + + #[test] + fn multiple_borrows_test() { + let value = GhostToken::new(|mut token| { + let cell1 = GhostCell::new(42); + let cell2 = GhostCell::new(47); + let cell3 = GhostCell::new(7); + let cell4 = GhostCell::new(9); + let (reference1, reference2, reference3, reference4): (&mut i32, &mut i32, &mut i32, &mut i32) + = (&cell1, &cell2, &cell3, &cell4).borrow_mut(&mut token).unwrap(); + *reference1 = 33; + *reference2 = 34; + *reference3 = 35; + *reference4 = 36; + // here we stop mutating, so the token isn't mutably borrowed anymore, and we can read again + (*cell1.borrow(&token), *cell2.borrow(&token), *cell3.borrow(&token)) + }); + assert_eq!((33, 34, 35), value); + } } From a51e4e2d292894e3993e9103031a906873056af8 Mon Sep 17 00:00:00 2001 From: Noam Ta Shma Date: Tue, 29 Jun 2021 16:35:57 +0300 Subject: [PATCH 06/10] implementing more borrowing cases --- src/ghost_cell.rs | 48 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/src/ghost_cell.rs b/src/ghost_cell.rs index b8c4d81..1255084 100644 --- a/src/ghost_cell.rs +++ b/src/ghost_cell.rs @@ -64,6 +64,7 @@ unsafe impl<'brand> Sync for GhostToken<'brand> {} /// - Unique access to the cell allows unimpeded access to the contained value. /// - Shared access to the cell requires mediating access through the associated `GhostToken<'x, T>` which will /// enforce at compile-time the Aliasing XOR Mutability safety property. +#[repr(transparent)] pub struct GhostCell<'brand, T: ?Sized> { _marker: InvariantLifetime<'brand>, value: UnsafeCell, @@ -255,7 +256,7 @@ mod multiple_borrows { /// A Sealed trait for implementing multiple borrows for any number of arguments, /// Using a `GhostToken`. pub trait MultipleMutableBorrows<'a, 'brand>: - multiple_mutable_borrows_private_module::PrivateTupleTrait { + multiple_mutable_borrows_private_module::PrivateTrait { /// The tuple of references you get as a result. For example, if Self is /// `(&'a GhostCell<'brand, T>, &'a GhostCell<'brand, Q>)` then `Result` is /// `(&'a mut T, &'a mut Q)`. @@ -289,6 +290,24 @@ mod multiple_borrows { fn borrow_mut(self, token: &'a mut GhostToken<'brand>) -> Self::Result; } + impl<'a, 'brand, T> MultipleMutableBorrows<'a, 'brand> for &'a [GhostCell<'brand, T>] { + type Result = &'a mut [T]; + + fn borrow_mut(self, _: &'a mut GhostToken<'brand>) -> Self::Result { + #[allow(mutable_transmutes)] + unsafe { core::mem::transmute::(self) } + } + } + + impl<'a, 'brand, T, const N: usize> MultipleMutableBorrows<'a, 'brand> for &'a [GhostCell<'brand, T>; N] { + type Result = &'a mut [T; N]; + + fn borrow_mut(self, _: &'a mut GhostToken<'brand>) -> Self::Result { + #[allow(mutable_transmutes)] + unsafe { core::mem::transmute::(self) } + } + } + macro_rules! generate_public_instance { ( $($name:ident),* ; $($type_letter:ident),* ) => { impl<'a, 'brand, $($type_letter,)*> MultipleMutableBorrows<'a, 'brand> for @@ -306,6 +325,16 @@ mod multiple_borrows { } } } + + impl<'a, 'brand, $($type_letter,)*> MultipleMutableBorrows<'a, 'brand> for + &'a ( $(GhostCell<'brand, $type_letter>, )* ) + { + type Result = &'a mut ( $($type_letter, )* ); + fn borrow_mut(self, _: &'a mut GhostToken<'brand>) -> Self::Result { + #[allow(mutable_transmutes)] + unsafe { core::mem::transmute::(self) } + } + } }; } @@ -321,13 +350,22 @@ mod multiple_borrows { generate_public_instance!(a, b, c, d, e, f, g, h, i, j ; T1, T2, T3, T4, T5, T6, T7, T8, T9, T10); mod multiple_mutable_borrows_private_module { - pub trait PrivateTupleTrait {} + use crate::ghost_cell::*; + pub trait PrivateTrait {} + + impl<'a, 'brand, T> PrivateTrait for &'a [GhostCell<'brand, T>] {} + impl<'a, 'brand, T, const N: usize> PrivateTrait for &'a [GhostCell<'brand, T>; N] {} + impl<'a, 'brand, T, const N: usize> PrivateTrait for [&'a GhostCell<'brand, T>; N] {} macro_rules! generate_private_instance { ( $($type_letter:ident),* ) => { - impl<'a, 'brand, $($type_letter,)*> PrivateTupleTrait for - ( $(&'a crate::ghost_cell::GhostCell<'brand, $type_letter>, )* ) - {} + impl<'a, 'brand, $($type_letter,)*> PrivateTrait for + ( $(&'a crate::ghost_cell::GhostCell<'brand, $type_letter>, )* ) + {} + + impl<'a, 'brand, $($type_letter,)*> PrivateTrait for + &'a ( $(crate::ghost_cell::GhostCell<'brand, $type_letter>, )* ) + {} }; } From 7f28882a0646036c003147cefd553e3c11f23507 Mon Sep 17 00:00:00 2001 From: Noam Ta Shma Date: Tue, 29 Jun 2021 17:35:18 +0300 Subject: [PATCH 07/10] not-quite-working implementation of case 4 --- src/ghost_cell.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/ghost_cell.rs b/src/ghost_cell.rs index 1255084..c68b826 100644 --- a/src/ghost_cell.rs +++ b/src/ghost_cell.rs @@ -308,6 +308,40 @@ mod multiple_borrows { } } + // almost working implementation + /* + impl<'a, 'brand, T, const N: usize> MultipleMutableBorrows<'a, 'brand> for [&'a GhostCell<'brand, T>; N] { + type Result = Option<[&'a mut T; N]>; + + fn borrow_mut(self, _: &'a mut GhostToken<'brand>) -> Self::Result { + use core::mem::*; + check_distinct(unsafe { core::mem::transmute::<&Self, &[*const (); N]>(&self) })?; + // Safety: `MaybeUninit<&'a mut T>` does not require initialization. + let res: [MaybeUninit<&'a mut T>; N] = unsafe { + MaybeUninit::uninit().assume_init() + }; + + // duplicating the code of `check_distinct` because it can't get called here + for i in 0..N { + for j in 0..i { + if core::ptr::eq(self[i], self[j]) { + return None; + } + } + } + + for i in 0..N { + res[i] = core::mem::MaybeUninit::new( + unsafe { transmute::<&'a GhostCell<'brand, T>, &'a mut T>(self[i]) } + ); + } + + Some(unsafe { MaybeUninit::array_assume_init(res) } ) + } + } + */ + + macro_rules! generate_public_instance { ( $($name:ident),* ; $($type_letter:ident),* ) => { impl<'a, 'brand, $($type_letter,)*> MultipleMutableBorrows<'a, 'brand> for From 7b1e8a862f58ff31dced80b5665eb12acd32cff6 Mon Sep 17 00:00:00 2001 From: Noam Ta Shma Date: Tue, 29 Jun 2021 17:45:52 +0300 Subject: [PATCH 08/10] better documentation --- src/ghost_cell.rs | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/ghost_cell.rs b/src/ghost_cell.rs index c68b826..cd2c8ca 100644 --- a/src/ghost_cell.rs +++ b/src/ghost_cell.rs @@ -254,17 +254,25 @@ mod multiple_borrows { } /// A Sealed trait for implementing multiple borrows for any number of arguments, - /// Using a `GhostToken`. - pub trait MultipleMutableBorrows<'a, 'brand>: + /// Using a `GhostToken<'a, 'brand>`. + /// Implemented for a mixture of tuple and array types. + /// Only enabled under experimental feature "experimental-multiple-mutable-borrows". + pub trait GhostBorrowMut<'a, 'brand>: multiple_mutable_borrows_private_module::PrivateTrait { /// The tuple of references you get as a result. For example, if Self is /// `(&'a GhostCell<'brand, T>, &'a GhostCell<'brand, Q>)` then `Result` is - /// `(&'a mut T, &'a mut Q)`. + /// `Option<(&'a mut T, &'a mut Q)>`. + /// + /// The Result is an `Option`, if it can't be assumed at compile time + /// that the cells are distinct, and for one element tuples. type Result; /// Borrows any number of `GhostCell`s at the same time. /// If any of them are the same `GhostCell`, returns `None`. /// Only enabled under experimental feature "experimental-multiple-mutable-borrows". /// + /// Receives a `&'a mut GhostToken<'brand>` to ensure the caller has unique ownership + /// of the values in the cells. + /// /// # Example /// /// ```rust @@ -290,19 +298,25 @@ mod multiple_borrows { fn borrow_mut(self, token: &'a mut GhostToken<'brand>) -> Self::Result; } - impl<'a, 'brand, T> MultipleMutableBorrows<'a, 'brand> for &'a [GhostCell<'brand, T>] { + impl<'a, 'brand, T> GhostBorrowMut<'a, 'brand> for &'a [GhostCell<'brand, T>] { type Result = &'a mut [T]; fn borrow_mut(self, _: &'a mut GhostToken<'brand>) -> Self::Result { + // Safety: the types have the same representation (`GhostCell` is marked `repr(transparent)`). + // In addition, thanks to the token, we have unique ownership of the values inside the `GhostCell`. + // All of the GhostCells are distinct, since they must be adjacent in memory. #[allow(mutable_transmutes)] unsafe { core::mem::transmute::(self) } } } - impl<'a, 'brand, T, const N: usize> MultipleMutableBorrows<'a, 'brand> for &'a [GhostCell<'brand, T>; N] { + impl<'a, 'brand, T, const N: usize> GhostBorrowMut<'a, 'brand> for &'a [GhostCell<'brand, T>; N] { type Result = &'a mut [T; N]; fn borrow_mut(self, _: &'a mut GhostToken<'brand>) -> Self::Result { + // Safety: the types have the same representation (`GhostCell` is marked `repr(transparent)`). + // In addition, thanks to the token, we have unique ownership of the values inside the `GhostCell`. + // All of the GhostCells are distinct, since they must be adjacent in memory. #[allow(mutable_transmutes)] unsafe { core::mem::transmute::(self) } } @@ -344,7 +358,7 @@ mod multiple_borrows { macro_rules! generate_public_instance { ( $($name:ident),* ; $($type_letter:ident),* ) => { - impl<'a, 'brand, $($type_letter,)*> MultipleMutableBorrows<'a, 'brand> for + impl<'a, 'brand, $($type_letter,)*> GhostBorrowMut<'a, 'brand> for ( $(&'a GhostCell<'brand, $type_letter>, )* ) { type Result = Option<( $(&'a mut $type_letter, )* )>; @@ -352,6 +366,8 @@ mod multiple_borrows { let ($($name,)*) = self; // we require that the types are `Sized`, so no fat pointer problems. check_distinct([ $( $name as *const _ as *const (), )* ])?; + // Safety: Thanks to the token, we have unique ownership of the values inside the `GhostCell`. + // The GhostCells have been checked to be distinct. unsafe { Some(( $( &mut * $name.value.get() ,)* @@ -360,11 +376,14 @@ mod multiple_borrows { } } - impl<'a, 'brand, $($type_letter,)*> MultipleMutableBorrows<'a, 'brand> for + impl<'a, 'brand, $($type_letter,)*> GhostBorrowMut<'a, 'brand> for &'a ( $(GhostCell<'brand, $type_letter>, )* ) { type Result = &'a mut ( $($type_letter, )* ); fn borrow_mut(self, _: &'a mut GhostToken<'brand>) -> Self::Result { + // Safety: the types have the same representation (`GhostCell` is marked `repr(transparent)`). + // In addition, thanks to the token, we have unique ownership of the values inside the `GhostCell`. + // All of the GhostCells are distinct, since they must be adjacent in memory. #[allow(mutable_transmutes)] unsafe { core::mem::transmute::(self) } } From 101365f3e3ca99f4e8c783f0f19ec5558d73d518 Mon Sep 17 00:00:00 2001 From: Noam Ta Shma Date: Fri, 2 Jul 2021 16:18:48 +0300 Subject: [PATCH 09/10] changed interface to Error --- src/ghost_cell.rs | 105 ++++++++++++++++++++++------------------------ 1 file changed, 50 insertions(+), 55 deletions(-) diff --git a/src/ghost_cell.rs b/src/ghost_cell.rs index cd2c8ca..521a493 100644 --- a/src/ghost_cell.rs +++ b/src/ghost_cell.rs @@ -240,16 +240,17 @@ pub use multiple_borrows::*; #[cfg(feature = "experimental-multiple-mutable-borrows")] mod multiple_borrows { use crate::ghost_cell::*; - /// Returns `Some(())` if the inputs are distinct, and `None` otherwise. - fn check_distinct(arr: [*const (); N]) -> Option<()> { + + /// Returns `Err(GhostAliasingError())` if the inputs are distinct, and `Ok(())` otherwise. + fn check_distinct(arr: [*const (); N]) -> Result<(), GhostAliasingError> { for i in 0..N { for j in 0..i { if core::ptr::eq(arr[i], arr[j]) { - return None; + return Err(GhostAliasingError()); } } } - Some(()) + Ok(()) // TODO: if the array is large enough, sort the values instead. } @@ -261,11 +262,10 @@ mod multiple_borrows { multiple_mutable_borrows_private_module::PrivateTrait { /// The tuple of references you get as a result. For example, if Self is /// `(&'a GhostCell<'brand, T>, &'a GhostCell<'brand, Q>)` then `Result` is - /// `Option<(&'a mut T, &'a mut Q)>`. - /// - /// The Result is an `Option`, if it can't be assumed at compile time - /// that the cells are distinct, and for one element tuples. + /// `(&'a mut T, &'a mut Q)`. type Result; + /// The error case. Is `VoidError` if an error is impossible, and `GhostAliasingError` otherwise. + type Error : Into; /// Borrows any number of `GhostCell`s at the same time. /// If any of them are the same `GhostCell`, returns `None`. /// Only enabled under experimental feature "experimental-multiple-mutable-borrows". @@ -295,83 +295,78 @@ mod multiple_borrows { /// /// assert_eq!((33, 34), value); /// ``` - fn borrow_mut(self, token: &'a mut GhostToken<'brand>) -> Self::Result; + fn borrow_mut(self, token: &'a mut GhostToken<'brand>) -> Result; + } + + /// A void struct. Used as the error case when The error case is impossible. + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] + pub enum VoidError{} + impl VoidError { + /// Returns any type. Can't happen since `VoidError` can't be constructed. + pub fn absurd(self) -> T { + match self {} + // could also be implemented as: + // unsafe { core::hint::unreachable_unchecked() } + } } + // For uniformity, if anyone wants it. Can't do + // impl From for T + // because of conflicting implementations. + impl From for GhostAliasingError { + fn from(e: VoidError) -> Self { + e.absurd() + } + } + + /// An error signifying that two `GhostCell`s that need to be distinct were actually + /// the same cell. + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] + pub struct GhostAliasingError(); impl<'a, 'brand, T> GhostBorrowMut<'a, 'brand> for &'a [GhostCell<'brand, T>] { type Result = &'a mut [T]; + type Error = VoidError; - fn borrow_mut(self, _: &'a mut GhostToken<'brand>) -> Self::Result { + fn borrow_mut(self, _: &'a mut GhostToken<'brand>) -> Result { // Safety: the types have the same representation (`GhostCell` is marked `repr(transparent)`). // In addition, thanks to the token, we have unique ownership of the values inside the `GhostCell`. // All of the GhostCells are distinct, since they must be adjacent in memory. #[allow(mutable_transmutes)] - unsafe { core::mem::transmute::(self) } + Ok(unsafe { core::mem::transmute::(self) }) } } impl<'a, 'brand, T, const N: usize> GhostBorrowMut<'a, 'brand> for &'a [GhostCell<'brand, T>; N] { type Result = &'a mut [T; N]; + type Error = VoidError; - fn borrow_mut(self, _: &'a mut GhostToken<'brand>) -> Self::Result { + fn borrow_mut(self, _: &'a mut GhostToken<'brand>) -> Result { // Safety: the types have the same representation (`GhostCell` is marked `repr(transparent)`). // In addition, thanks to the token, we have unique ownership of the values inside the `GhostCell`. // All of the GhostCells are distinct, since they must be adjacent in memory. #[allow(mutable_transmutes)] - unsafe { core::mem::transmute::(self) } - } - } - - // almost working implementation - /* - impl<'a, 'brand, T, const N: usize> MultipleMutableBorrows<'a, 'brand> for [&'a GhostCell<'brand, T>; N] { - type Result = Option<[&'a mut T; N]>; - - fn borrow_mut(self, _: &'a mut GhostToken<'brand>) -> Self::Result { - use core::mem::*; - check_distinct(unsafe { core::mem::transmute::<&Self, &[*const (); N]>(&self) })?; - // Safety: `MaybeUninit<&'a mut T>` does not require initialization. - let res: [MaybeUninit<&'a mut T>; N] = unsafe { - MaybeUninit::uninit().assume_init() - }; - - // duplicating the code of `check_distinct` because it can't get called here - for i in 0..N { - for j in 0..i { - if core::ptr::eq(self[i], self[j]) { - return None; - } - } - } - - for i in 0..N { - res[i] = core::mem::MaybeUninit::new( - unsafe { transmute::<&'a GhostCell<'brand, T>, &'a mut T>(self[i]) } - ); - } - - Some(unsafe { MaybeUninit::array_assume_init(res) } ) + Ok(unsafe { core::mem::transmute::(self) }) } } - */ - macro_rules! generate_public_instance { ( $($name:ident),* ; $($type_letter:ident),* ) => { impl<'a, 'brand, $($type_letter,)*> GhostBorrowMut<'a, 'brand> for ( $(&'a GhostCell<'brand, $type_letter>, )* ) { - type Result = Option<( $(&'a mut $type_letter, )* )>; - fn borrow_mut(self, _: &'a mut GhostToken<'brand>) -> Self::Result { + type Result = ( $(&'a mut $type_letter, )* ); + type Error = GhostAliasingError; + + fn borrow_mut(self, _: &'a mut GhostToken<'brand>) -> Result { let ($($name,)*) = self; // we require that the types are `Sized`, so no fat pointer problems. check_distinct([ $( $name as *const _ as *const (), )* ])?; // Safety: Thanks to the token, we have unique ownership of the values inside the `GhostCell`. // The GhostCells have been checked to be distinct. unsafe { - Some(( - $( &mut * $name.value.get() ,)* - )) + Ok( + ( $( &mut * $name.value.get() ,)* ) + ) } } } @@ -380,12 +375,13 @@ mod multiple_borrows { &'a ( $(GhostCell<'brand, $type_letter>, )* ) { type Result = &'a mut ( $($type_letter, )* ); - fn borrow_mut(self, _: &'a mut GhostToken<'brand>) -> Self::Result { + type Error = VoidError; + fn borrow_mut(self, _: &'a mut GhostToken<'brand>) -> Result { // Safety: the types have the same representation (`GhostCell` is marked `repr(transparent)`). // In addition, thanks to the token, we have unique ownership of the values inside the `GhostCell`. // All of the GhostCells are distinct, since they must be adjacent in memory. #[allow(mutable_transmutes)] - unsafe { core::mem::transmute::(self) } + Ok(unsafe { core::mem::transmute::(self) }) } } }; @@ -408,7 +404,6 @@ mod multiple_borrows { impl<'a, 'brand, T> PrivateTrait for &'a [GhostCell<'brand, T>] {} impl<'a, 'brand, T, const N: usize> PrivateTrait for &'a [GhostCell<'brand, T>; N] {} - impl<'a, 'brand, T, const N: usize> PrivateTrait for [&'a GhostCell<'brand, T>; N] {} macro_rules! generate_private_instance { ( $($type_letter:ident),* ) => { From ceef7326793b5cc9d4fe6fa6c265489c3e0308e5 Mon Sep 17 00:00:00 2001 From: Noam Ta Shma Date: Fri, 2 Jul 2021 17:42:37 +0300 Subject: [PATCH 10/10] some tests --- src/ghost_cell.rs | 92 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 74 insertions(+), 18 deletions(-) diff --git a/src/ghost_cell.rs b/src/ghost_cell.rs index 521a493..00885e6 100644 --- a/src/ghost_cell.rs +++ b/src/ghost_cell.rs @@ -276,7 +276,7 @@ mod multiple_borrows { /// # Example /// /// ```rust - /// use ghost_cell::{GhostToken, GhostCell, ghost_cell::MultipleMutableBorrows}; + /// use ghost_cell::{GhostToken, GhostCell, ghost_cell::GhostBorrowMut}; /// /// let n = 42; /// @@ -429,23 +429,79 @@ mod multiple_borrows { generate_private_instance!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10); } - #[test] - fn multiple_borrows_test() { - let value = GhostToken::new(|mut token| { - let cell1 = GhostCell::new(42); - let cell2 = GhostCell::new(47); - let cell3 = GhostCell::new(7); - let cell4 = GhostCell::new(9); - let (reference1, reference2, reference3, reference4): (&mut i32, &mut i32, &mut i32, &mut i32) - = (&cell1, &cell2, &cell3, &cell4).borrow_mut(&mut token).unwrap(); - *reference1 = 33; - *reference2 = 34; - *reference3 = 35; - *reference4 = 36; - // here we stop mutating, so the token isn't mutably borrowed anymore, and we can read again - (*cell1.borrow(&token), *cell2.borrow(&token), *cell3.borrow(&token)) - }); - assert_eq!((33, 34, 35), value); + #[doc(hidden)] + mod multiple_borrows_tests { + use crate::{*, ghost_cell::GhostBorrowMut}; + #[test] + fn multiple_borrows_tuple() { + let value = GhostToken::new(|mut token| { + let cell1 = GhostCell::new(42); + let cell2 = GhostCell::new(47); + let cell3 = GhostCell::new(7); + let cell4 = GhostCell::new(9); + let (reference1, reference2, reference3, reference4): (&mut i32, &mut i32, &mut i32, &mut i32) + = (&cell1, &cell2, &cell3, &cell4).borrow_mut(&mut token).unwrap(); + *reference1 = 33; + *reference2 = 34; + *reference3 = 35; + *reference4 = 36; + // here we stop mutating, so the token isn't mutably borrowed anymore, and we can read again + (*cell1.borrow(&token), *cell2.borrow(&token), *cell3.borrow(&token)) + }); + assert_eq!((33, 34, 35), value); + } + + #[test] + #[should_panic] + fn multiple_borrows_tuple_alisased() { + GhostToken::new(|mut token| { + let cell1 = GhostCell::new(42); + let cell2 = GhostCell::new(47); + let cell3 = GhostCell::new(7); + let _: (&mut i32, &mut i32, &mut i32, &mut i32) + = (&cell1, &cell2, &cell3, &cell2).borrow_mut(&mut token).unwrap(); + }); + } + + #[test] + fn multiple_borrows_tuple_ref() { + let value = GhostToken::new(|mut token| { + let cell1 = GhostCell::new(42); + let cell2 = GhostCell::new(47); + let cell3 = GhostCell::new(7); + let cell4 = GhostCell::new(9); + let tuple = (cell1, cell2, cell3, cell4); + let reference: &mut (i32, i32, i32, i32) + = tuple.borrow_mut(&mut token).unwrap(); + reference.0 = 33; + reference.1 = 34; + reference.2 = 35; + reference.3 = 36; + // here we stop mutating, so the token isn't mutably borrowed anymore, and we can read again + (*tuple.0.borrow(&token), *tuple.1.borrow(&token), *tuple.2.borrow(&token)) + }); + assert_eq!((33, 34, 35), value); + } + + #[test] + fn multiple_borrows_array_ref() { + let value = GhostToken::new(|mut token| { + let cell1 = GhostCell::new(42); + let cell2 = GhostCell::new(47); + let cell3 = GhostCell::new(7); + let cell4 = GhostCell::new(9); + let array = [cell1, cell2, cell3, cell4]; + let reference: &mut [i32; 4] + = array.borrow_mut(&mut token).unwrap(); + reference[0] = 33; + reference[1] = 34; + reference[2] = 35; + reference[3] = 36; + // here we stop mutating, so the token isn't mutably borrowed anymore, and we can read again + (*array[0].borrow(&token), *array[1].borrow(&token), *array[2].borrow(&token)) + }); + assert_eq!((33, 34, 35), value); + } } }