PtrOwn

Struct PtrOwn 

Source
pub struct PtrOwn<T: ?Sized>(/* private fields */);
Expand description

Token that represents the ownership of a memory cell

A PtrOwn value only exists in the ghost world, but can be used in combination with a corresponding *const to access and modify memory.

A warning regarding memory leaks: dropping a Ghost<PtrOwn<T>> (we only ever handle ghost PtrOwn values) cannot deallocate the memory corresponding to the pointer because it is a ghost value. One must thus remember to explicitly call drop in order to free the memory tracked by a PtrOwn token.

§Safety

When using Creusot to verify the code, all methods should be safe to call. Indeed, Creusot ensures that every operation on the inner value uses the right PtrOwn object created by PtrOwn::new, ensuring safety in a manner similar to ghost_cell.

§#[check(terminates)]

PtrOwn methods, particularly constructors (new, from_box, from_ref, from_mut), are marked check(terminates) rather than check(ghost) to prevent two things from happening in ghost code:

  1. running out of pointer addresses;
  2. allocating too large objects.

Note that we already can’t guard against these issues in program code. But preventing them in ghost code is even more imperative to ensure soundness.

Specifically, creating too many pointers contradicts the PtrOwn::disjoint_lemma, and allocating too large objects contradicts the PtrOwn::invariant that allocations have size at most isize::MAX.

Implementations§

Source§

impl<T: ?Sized> PtrOwn<T>

Source

pub fn ptr(self) -> *const T

The raw pointer whose ownership is tracked by this PtrOwn

(opaque)

Source

pub fn val<'a>(self) -> &'a T

The value currently stored at address self.ptr()

(opaque)

Source§

impl<T> PtrOwn<T>

Source

pub fn new(v: T) -> (*mut T, Ghost<PtrOwn<T>>)

Creates a new PtrOwn and associated *const by allocating a new memory cell initialized with v.

terminates

ensures

result.1.ptr() == result.0 && *result.1.val() == v

Source

pub fn disjoint_lemma(own1: &mut PtrOwn<T>, own2: &PtrOwn<T>)

If one owns two PtrOwns for non-zero sized types, then they are for different pointers.

terminates

ghost

requires

size_of_logic::<T>() != 0

ensures

own1.ptr().addr_logic() != own2.ptr().addr_logic()

ensures

*own1 == ^own1

Source§

impl<T: ?Sized> PtrOwn<T>

Source

pub fn from_box(val: Box<T>) -> (*mut T, Ghost<PtrOwn<T>>)

Creates a ghost PtrOwn and associated *const from an existing Box.

terminates

ensures

result.1.ptr() == result.0 && *result.1.val() == *val

erasure

Box::into_raw

Source

pub fn from_ref(r: &T) -> (*const T, Ghost<&PtrOwn<T>>)

Decompose a shared reference into a raw pointer and a ghost PtrOwn.

§Erasure

This function erases to a raw reborrow of a reference.

PtrOwn::from_ref(r)
// erases to
r as *const T  // or *mut T (both are allowed)

terminates

ensures

result.1.ptr() == result.0

ensures

*result.1.val() == *r

Source

pub fn from_mut(r: &mut T) -> (*mut T, Ghost<&mut PtrOwn<T>>)

Decompose a mutable reference into a raw pointer and a ghost PtrOwn.

§Erasure

This function erases to a raw reborrow of a reference.

PtrOwn::from_mut(r)
// erases to
r as *const T  // or *mut T (both are allowed)

terminates

ensures

result.1.ptr() == result.0

ensures

*result.1.val() == *r

ensures

*(^result.1.inner_logic()).val() == ^r

Source

pub unsafe fn as_ref(ptr: *const T, own: Ghost<&PtrOwn<T>>) -> &T

Immutably borrows the underlying T.

§Safety

Safety requirements are the same as a direct dereference: &*ptr.

Creusot will check that all calls to this function are indeed safe: see the type documentation.

§Erasure

This function erases to a cast from raw pointer to shared reference.

PtrOwn::as_ref(ptr, own)
// erases to
& *ptr

terminates

requires

ptr == own.ptr()

ensures

*result == *own.val()

Source

pub unsafe fn as_mut(ptr: *mut T, own: Ghost<&mut PtrOwn<T>>) -> &mut T

Mutably borrows the underlying T.

§Safety

Safety requirements are the same as a direct dereference: &mut *ptr.

Creusot will check that all calls to this function are indeed safe: see the type documentation.

§Erasure

This function erases to a cast from raw pointer to mutable reference.

PtrOwn::as_mut(ptr, own)
// erases to
&mut *ptr

terminates

requires

ptr as *const T == own.ptr()

ensures

*result == *own.val()

ensures

(^own).ptr() == own.ptr()

ensures

*(^own).val() == ^result

Source

pub unsafe fn to_box(ptr: *mut T, own: Ghost<PtrOwn<T>>) -> Box<T>

Transfers ownership of own back into a Box.

§Safety

Safety requirements are the same as Box::from_raw.

Creusot will check that all calls to this function are indeed safe: see the type documentation.

terminates

requires

ptr as *const T == own.ptr()

ensures

*result == *own.val()

erasure

Box::from_raw

Source

pub unsafe fn drop(ptr: *mut T, own: Ghost<PtrOwn<T>>)

Deallocates the memory pointed by ptr.

§Safety

Safety requirements are the same as Box::from_raw.

Creusot will check that all calls to this function are indeed safe: see the type documentation.

terminates

requires

ptr as *const T == own.ptr()

Source

pub fn ptr_is_aligned_lemma(&self)

The pointer of a PtrOwn is always aligned.

terminates

ghost

ensures

self.ptr().is_aligned_logic()

Source

pub fn ptr_is_aligned_opaque(self) -> bool

Opaque wrapper around [std::ptr::is_aligned_logic]. We use this to hide alignment logic by default in invariant because it confuses SMT solvers sometimes. The underlying property is exposed by PtrOwn::ptr_is_aligned_lemma.

(open(pub(self)))

Trait Implementations§

Source§

impl<T: ?Sized> Invariant for PtrOwn<T>

Source§

fn invariant(self) -> bool

(open, prophetic)

pearlite! {
    !self.ptr().is_null_logic()
        && self.ptr_is_aligned_opaque()
        && metadata_matches(*self.val(), metadata_logic(self.ptr()))
        // Allocations can never be larger than `isize` (source: https://doc.rust-lang.org/std/ptr/index.html#allocation)
        && size_of_val_logic(*self.val()) <= isize::MAX@
        // The allocation fits in the address space
        // (this is needed to verify (a `PtrOwn` variant of) `<*const T>::add`, which checks this condition)
        && self.ptr().addr_logic()@ + size_of_val_logic(*self.val()) <= usize::MAX@
        && inv(self.val())
}

Auto Trait Implementations§

§

impl<T> Freeze for PtrOwn<T>
where T: ?Sized,

§

impl<T> RefUnwindSafe for PtrOwn<T>
where T: RefUnwindSafe + ?Sized,

§

impl<T> Send for PtrOwn<T>
where T: Send + ?Sized,

§

impl<T> Sync for PtrOwn<T>
where T: Sync + ?Sized,

§

impl<T> Unpin for PtrOwn<T>
where T: Unpin + ?Sized,

§

impl<T> UnwindSafe for PtrOwn<T>
where T: UnwindSafe + ?Sized,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.