Skip to content

Instantly share code, notes, and snippets.

@vedantk
Created September 7, 2015 22:58
Show Gist options
  • Select an option

  • Save vedantk/41e7e24503968a437453 to your computer and use it in GitHub Desktop.

Select an option

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

nwsharp commented Dec 25, 2020

Copy link
Copy Markdown

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