Skip to content

Instantly share code, notes, and snippets.

@vedantk
Created September 7, 2015 22:58
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vedantk/41e7e24503968a437453 to your computer and use it in GitHub Desktop.
Save vedantk/41e7e24503968a437453 to your computer and use it in GitHub Desktop.
A safe version of tagged pointers in rust
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();
}
@nwsharp
Copy link

nwsharp commented Dec 25, 2020

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment