Created
May 15, 2019 22:02
-
-
Save rust-play/8d190468b1d048261cedce9ac9b2f87c to your computer and use it in GitHub Desktop.
Code shared from the Rust Playground
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![allow(dead_code)] | |
use std::cell::UnsafeCell; | |
use std::collections::HashMap; | |
use std::iter; | |
use std::marker::PhantomData; | |
use std::mem; | |
use std::ops::Deref; | |
use std::ops::Range; | |
use std::sync::Mutex; | |
use std::thread; | |
use lazy_static::lazy_static; | |
// Jump to the examples at the end! | |
// The metadata we store is just a colour for each memory address. | |
struct MetaData(Mutex<HashMap<Address, Colour>>); | |
lazy_static! { static ref METADATA: MetaData = MetaData(Mutex::new(HashMap::new())); } | |
// Memory addresses are just a word | |
type Address = usize; | |
// The different states of memory | |
#[derive(Clone, Copy, Debug, Eq, PartialEq)] | |
enum Colour { | |
Unreachable, // Memory that is allocated but can't be reached from safe code | |
Unique, // Memory that is reachable from safe code as a &mut T reference | |
Shared(usize), // Memory that is reachable from safe code as a &T reference | |
} | |
// Types that know how to iterate over their addresses | |
trait Addressable { | |
type Addresses: Iterator<Item = Address>; | |
fn addresses(&self) -> Self::Addresses; | |
} | |
impl Addressable for u8 { | |
type Addresses = iter::Once<Address>; | |
fn addresses(&self) -> Self::Addresses { | |
iter::once(self as *const u8 as Address) | |
} | |
} | |
impl Addressable for u16 { | |
type Addresses = Range<Address>; | |
fn addresses(&self) -> Self::Addresses { | |
let address = self as *const u16 as Address; | |
(address..address+2) | |
} | |
} | |
impl Addressable for u32 { | |
type Addresses = Range<Address>; | |
fn addresses(&self) -> Self::Addresses { | |
let address = self as *const u32 as Address; | |
(address..address+4) | |
} | |
} | |
impl Addressable for u64 { | |
type Addresses = Range<Address>; | |
fn addresses(&self) -> Self::Addresses { | |
let address = self as *const u64 as Address; | |
(address..address+8) | |
} | |
} | |
impl<T, U> Addressable for (T, U) where T: Addressable, U: Addressable { | |
type Addresses = iter::Chain<T::Addresses, U::Addresses>; | |
fn addresses(&self) -> Self::Addresses { | |
self.0.addresses().chain(self.1.addresses()) | |
} | |
} | |
impl<T> Addressable for UnsafeCell<T> where T: Addressable { | |
type Addresses = iter::Empty<Address>; | |
fn addresses(&self) -> Self::Addresses { | |
iter::empty() | |
} | |
} | |
// How we model &T | |
struct Ref<'a, T: 'a + Addressable>(&'a T); | |
impl<'a, T: 'a + Addressable> Drop for Ref<'a, T> { | |
fn drop(&mut self) { | |
if thread::panicking() { return; } | |
let mut metadata = METADATA.0.lock().unwrap(); | |
for address in self.0.addresses() { | |
let n = match metadata.get(&address) { | |
Some(Colour::Shared(n)) => *n, | |
colour => panic!("Expected Shared, found {:?} at address 0x{:x}", colour, address), | |
}; | |
assert!(n > 0); | |
let colour = if n == 1 { | |
Colour::Unreachable | |
} else { | |
Colour::Shared(n - 1) | |
}; | |
metadata.insert(address, colour); | |
println!("Colouring 0x{:x} as {:?}", address, colour); | |
} | |
} | |
} | |
impl<'a, T: 'a + Addressable> Clone for Ref<'a, T> { | |
fn clone(&self) -> Self { | |
let mut metadata = METADATA.0.lock().unwrap(); | |
for address in self.0.addresses() { | |
let n = match metadata.get(&address) { | |
Some(Colour::Shared(n)) => *n, | |
colour => panic!("Expected Shared, found {:?} at address 0x{:x}", colour, address), | |
}; | |
metadata.insert(address, Colour::Shared(n + 1)); | |
println!("Colouring 0x{:x} as Shared({})", address, n + 1); | |
} | |
Ref(self.0) | |
} | |
} | |
impl<'a, T: 'a + Addressable> Ref<'a, T> { | |
fn get(&self) -> T where T: Clone { | |
self.0.clone() | |
} | |
fn as_ptr(self) -> *const T { | |
self.0 as *const T | |
} | |
unsafe fn from_ptr(ptr: *const T) -> Ref<'a, T> { | |
let mut metadata = METADATA.0.lock().unwrap(); | |
let reference = &*ptr; | |
for address in reference.addresses() { | |
let n = match metadata.get(&address).cloned().unwrap_or(Colour::Unreachable) { | |
Colour::Shared(n) => n, | |
Colour::Unreachable => 0, | |
colour => panic!("UB: Expected Shared or Unreachable, found {:?} at address 0x{:x}", colour, address), | |
}; | |
metadata.insert(address, Colour::Shared(n + 1)); | |
println!("Colouring 0x{:x} as Shared({})", address, n + 1); | |
} | |
Ref(reference) | |
} | |
} | |
impl<'a, T: 'a + Addressable> Ref<'a, UnsafeCell<T>> { | |
fn get_from_cell(&self) -> *mut T { | |
self.0.get() | |
} | |
} | |
// How we model &mut T | |
struct RefMut<'a, T: 'a + Addressable>(&'a mut T); | |
impl<'a, T: 'a + Addressable> RefMut<'a, T> { | |
fn new(x: &'a mut T) -> Self { | |
let mut metadata = METADATA.0.lock().unwrap(); | |
for address in x.addresses() { | |
let colour = metadata.get(&address).cloned().unwrap_or(Colour::Unreachable); | |
assert!(colour == Colour::Unreachable, | |
"Expected Unreachable, found {:?} at address 0x{:x}", colour, address); | |
metadata.insert(address, Colour::Unique); | |
println!("Colouring 0x{:x} as Unique", address); | |
} | |
RefMut(x) | |
} | |
fn as_ref<'b>(&'b mut self) -> Ref<'b, T> { | |
let mut metadata = METADATA.0.lock().unwrap(); | |
for address in self.0.addresses() { | |
let colour = metadata.get(&address).cloned().unwrap_or(Colour::Unreachable); | |
assert!((colour == Colour::Unreachable) || (colour == Colour::Unique), | |
"Expected Unreachable or Unique, found {:?} at address 0x{:x}", colour, address); | |
metadata.insert(address, Colour::Shared(1)); | |
println!("Colouring 0x{:x} as Shared(1)", address); | |
} | |
Ref(self.0) | |
} | |
unsafe fn from_ptr(ptr: *mut T) -> RefMut<'a, T> { | |
let mut metadata = METADATA.0.lock().unwrap(); | |
let reference = &mut *ptr; | |
for address in reference.addresses() { | |
let colour = metadata.get(&address).cloned().unwrap_or(Colour::Unreachable); | |
assert!(colour == Colour::Unreachable, | |
"UB: Expected Unreachable, found {:?} at address 0x{:x}", colour, address); | |
metadata.insert(address, Colour::Unique); | |
println!("Colouring 0x{:x} as Unique", address); | |
} | |
RefMut(reference) | |
} | |
fn set(&mut self, x: T) { | |
let mut metadata = METADATA.0.lock().unwrap(); | |
for address in self.0.addresses() { | |
let colour = metadata.get(&address).cloned().unwrap_or(Colour::Unreachable); | |
if colour != Colour::Unique { | |
assert!(colour == Colour::Unreachable, | |
"Expected Unreachable or Unique, found {:?} at address 0x{:x}", colour, address); | |
metadata.insert(address, Colour::Unique); | |
println!("Colouring 0x{:x} as Unique", address); | |
} | |
} | |
*self.0 = x; | |
} | |
} | |
impl<'a, T: 'a + Addressable> Drop for RefMut<'a, T> { | |
fn drop(&mut self) { | |
if thread::panicking() { return; } | |
let mut metadata = METADATA.0.lock().unwrap(); | |
for address in self.0.addresses() { | |
let colour = metadata.get(&address).cloned().unwrap_or(Colour::Unreachable); | |
assert!(colour == Colour::Unique, | |
"Expected Unreachable, found {:?} at address 0x{:x}", colour, address); | |
metadata.insert(address, Colour::Unreachable); | |
println!("Colouring 0x{:x} as Unreachable", address); | |
} | |
} | |
} | |
macro_rules! let_ref_mut { | |
($x:ident = $e:expr) => { | |
let ref mut tmp = $e; | |
let mut $x = RefMut::new(tmp); | |
} | |
} | |
// An example from Ralf Jung | |
fn two_phase() { unsafe { | |
let_ref_mut!(cell = UnsafeCell::new(0 as u8)); | |
let x = cell.as_ref(); | |
let x2 = x.clone(); | |
// We can have a unique reference | |
let mut uniq_ref = RefMut::from_ptr(x.get_from_cell()); | |
uniq_ref.set(5); | |
drop(uniq_ref); | |
// and then use our shared references again | |
let shared_ref = Ref::from_ptr(x2.get_from_cell()); | |
println!("contents = {}", shared_ref.get()); | |
} } | |
/* | |
// Another example, from Ralf Jung | |
fn aliasing_mut_and_shr() { | |
fn inner(rc: &RefCell<u32>, aliasing: &mut u32) { | |
*aliasing += 4; | |
let _escape_to_raw : *const RefCell<u32> = rc; | |
*aliasing += 4; | |
let _shr = &*rc; | |
*aliasing += 4; | |
// also turning this into a frozen ref now must work | |
let aliasing : &u32 = aliasing; | |
let _val = *aliasing; | |
let _escape_to_raw : *const RefCell<u32> = rc; | |
let _val = *aliasing; | |
let _shr = &*rc; // this must NOT unfreeze | |
let _val = *aliasing; | |
} | |
let refcell = RefCell::new(23); | |
let rc: &mut RefCell<u32> = &mut refcell; | |
let rc: &RefCell<u32> = rc; | |
let mut bmut : RefMut<u32> = rc.borrow_mut(); | |
inner(&rc, &mut *bmut); | |
drop(bmut); | |
assert_eq!(*rc.borrow(), 23+12); | |
} | |
fn main() { | |
aliasing_mut_and_shr() | |
} | |
*/ | |
fn bad() { | |
fn inner(p: Ref<u8>) { | |
let q = p.as_ptr() as *mut u8; | |
let mut r = unsafe { RefMut::from_ptr(q) }; | |
r.set(5); | |
} | |
let_ref_mut!(x = 37); | |
let y = x.as_ref(); | |
println!("*y = {}", y.get()); | |
inner(y); | |
let z = x.as_ref(); | |
println!("*z = {}", z.get()); | |
inner(z.clone()); | |
println!("*z = {}", z.get()); | |
} | |
fn main() { | |
two_phase() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment