Created
September 7, 2015 22:58
-
-
Save vedantk/41e7e24503968a437453 to your computer and use it in GitHub Desktop.
A safe version of tagged pointers in rust
This file contains hidden or 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
use std::mem; | |
use std::ops::{Deref, DerefMut}; | |
#[derive(Copy, Clone, Debug)] | |
struct Tagged<'a, T: 'a> { | |
pointer: &'a T | |
} | |
impl<'a, T> Tagged<'a, T> { | |
fn tag(&'a mut self) -> &'a mut Tagged<'a, T> { | |
unsafe { | |
let pod: usize = mem::transmute(self.pointer); | |
let pod = pod | 1; | |
self.pointer = mem::transmute(pod); | |
} | |
self | |
} | |
fn untag(&'a mut self) -> &'a mut Tagged<'a, T> { | |
unsafe { | |
let pod: usize = mem::transmute(self.pointer); | |
let pod = pod & (!1 as usize); | |
self.pointer = mem::transmute(pod); | |
} | |
self | |
} | |
fn is_tagged(&'a mut self) -> bool { | |
unsafe { | |
let pod: usize = mem::transmute(self.pointer); | |
(pod & 1) == 1 | |
} | |
} | |
} | |
impl<'a, T> Deref for Tagged<'a, T> { | |
type Target = T; | |
fn deref<'b>(&'b self) -> &'b T { | |
unsafe { | |
let pod: usize = mem::transmute(self.pointer); | |
let pod = pod & (!1 as usize); | |
mem::transmute(pod) | |
} | |
} | |
} | |
impl<'a, T> DerefMut for Tagged<'a, T> { | |
fn deref_mut<'b>(&'b mut self) -> &'b mut T { | |
unsafe { | |
let pod: usize = mem::transmute(self.pointer); | |
let pod = pod & (!1 as usize); | |
mem::transmute(pod) | |
} | |
} | |
} | |
fn check_tagged0() { | |
let mut p = Tagged { pointer: &42 }; | |
assert_eq!(p.is_tagged(), false); | |
} | |
fn check_tagged1() { | |
let mut p = Tagged { pointer: &42 }; | |
let mut p = p.tag(); | |
assert_eq!(p.is_tagged(), true); | |
} | |
fn check_deref0() { | |
let p = Tagged { pointer: &42 }; | |
assert_eq!(*p, 42); | |
} | |
fn check_deref1() { | |
let mut p = Tagged { pointer: &42 }; | |
let p = *p.tag(); | |
assert_eq!(*p, 42); | |
} | |
fn check_deref2() { | |
let n = 42; | |
let mut p = Tagged { pointer: &n }; | |
*p = 0; | |
assert_eq!(*p, 0); | |
} | |
fn check_deref3() { | |
let n = 42; | |
let mut p = Tagged { pointer: &n }; | |
let mut p = *p.tag(); | |
*p = 0; | |
assert_eq!(*p, 0); | |
} | |
fn check_untag() { | |
let n = 42; | |
let mut p: Tagged<i32> = Tagged { pointer: &n }; | |
let mut p: &mut Tagged<i32> = p.tag(); | |
assert_eq!(p.clone().is_tagged(), true); | |
let p: &mut Tagged<i32> = p.untag(); | |
assert_eq!(p.clone().is_tagged(), false); | |
} | |
fn main() { | |
check_tagged0(); | |
check_tagged1(); | |
check_deref0(); | |
check_deref1(); | |
check_deref2(); | |
check_deref3(); | |
check_untag(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Woe to the poor souls that landed here from Google. This code is not sound.
In particular, this code assumes that
mem::align_of::<T>() > 1
. Additionally, it constructs references which are not always valid to dereference, which is immediate UB. Furthermore, this type makes it possible to retrieve a mutable reference from a shared reference, which is unsound in all cases.