Skip to main content

Module place

Module place 

Source
Expand description

§Operations on places

Birds-eye view of this module

Here are all the items defined by this module without any attributes or other distractions:

pub trait PlaceProxy {
    type Target: ?Sized;
}

pub unsafe trait CreateHandle<ProxyTiming: Timing>: PlaceProxy {
    type Handle: PlaceHandle<Target = Self::Target>;
    const ACCESS: AccessKind;
    unsafe fn handle_from_raw(this: *const Self) -> Self::Handle;
}

pub trait PlaceHandle: Sized {
    type Target: ?Sized;
}

pub unsafe trait ReadPlace: PlaceHandle {
    const ACCESS: AccessKind;
    const SAFE: bool;
    unsafe fn read_place(self) -> Self::Target;
}

pub unsafe trait MovePlace: ReadPlace {}

pub unsafe trait WritePlace: PlaceHandle {
    const ACCESS: AccessKind;
    const SAFE: bool;
    unsafe fn write_place(self, value: Self::Target);
}

pub unsafe trait BorrowPlace<Output>: PlaceHandle {
    const ACCESS: AccessKind;
    type Timing: Timing;
    const SAFE: bool;
    unsafe fn borrow(self) -> Output;
}

pub trait Indexable<Idx> {
    type Element: ?Sized;
}

pub unsafe trait IndexPlace<Idx, H, PointeeTiming, PointerTiming>:
    Indexable<Idx>
where
    H: PlaceHandle<Target = Self>,
    PointeeTiming: Timing,
    PointerTiming: Timing,
{
    type ElementHandle: PlaceHandle<Target = Self::Element>;
    const POINTEE_ACCESS: AccessKind;
    const POINTER_ACCESS: AccessKind;
    const SAFE: bool;
    fn index(handle: H, idx: Idx) -> Self::ElementHandle;
}

pub unsafe trait DerefPlace<PointeeTiming, PointerTiming>:
    PlaceHandle
where
    Self::Target: CreateHandle<PointeeTiming>,
    PointeeTiming: Timing,
    PointerTiming: Timing,
{
    const POINTEE_ACCESS: AccessKind;
    const POINTER_ACCESS: AccessKind;
    const SAFE: bool;
    unsafe fn deref_place(
        self,
    ) -> <Self::Target as CreateHandle<PointeeTiming>>::Handle;
}

pub unsafe trait ProjectPlace<S>: PlaceHandle
where
    S: Subplace<Source = Self::Target>,
{
    type Projected: PlaceHandle<Target = S::Target>;
    unsafe fn project_place(self, subplace: S) -> Self::Projected;
}

pub trait PlaceWrapper {
    type Inner: ?Sized;
}

pub unsafe trait WrapPlace<S>: PlaceWrapper
where
    S: Subplace<Source = Self::Inner>,
{
    type Wrapped: Subplace<Source = Self>;
    fn wrap(subplace: S) -> Self::Wrapped;
}

pub unsafe trait DropPlace: PlaceHandle {
    unsafe fn drop_place(self);
}

pub unsafe trait DropHusk: PlaceProxy {
    unsafe fn drop_husk(this: *mut Self);
}

pub unsafe trait ReadMetadata: PlaceHandle {
    fn metadata(self) -> Metadata<Self::Target>;
}

pub unsafe trait ReadVariant: PlaceHandle
where
    Self::Target: Matchable,
{
    unsafe fn read_variant(self) -> &'static str;
}

pub unsafe trait VariantPlace<const VARIANT: &'static str>:
    ReadVariant
where
    Self::Target: Matchable,
    Self::Target: HasVariant<VARIANT>,
{
    type ToVariant: PlaceHandle<Target = VariantType<Self::Target, VARIANT>>;
    unsafe fn cast(self) -> Self::ToVariant;
}

This module provides traits to customize the place operations of Rust.

§Places and Place Expressions

A place in Rust is a particular location in memory. Places are represented by place expressions, which have the following syntax:

  • $path: paths that refer to local variables (also parameters) and statics,
  • *$place: dereferencing another place expression,
  • $place[$expr]: indexing into another place expression,
  • $place.$ident: accessing a field of another place expression,
  • ($place): parenthesized place expressions,
  • $value: an arbitrary expression; it’s value is stored in a temporary place whose lifetime is determined from its context.

Further reading:

Place expressions have a direct representation in the form of a PlaceHandle. A handle points at a place and is responsible for performing all available place operations on the represented place. Any valid place expression is converted into a handle by the compiler. We represent this conversion with the handle! pseudo-macro, explained below.

The information a handle encodes can be broken into three primary categories:

  • The type of the place the handle refers to.
  • Runtime information needed to access the place; usually, a pointer.
  • The capabilities of the pointer the handle was derived from.

For example:

let a: &mut Struct;
let b: MutHandle<'b, Field> = handle!((*a).field);

Semantically, we can tell from the type of the handle that this place expression is derived from a mutable reference, that the place is of type Field, and that the handle has permission to access the place for the lifetime 'b.

The definition of MutHandle<'a, T> itself is simple: It stores a pointer to the place (the field, in the example above). It also embeds a PhantomData<&'a mut T> to mark the type T as invariant.

To see how a place handle represents capabilities, observe how we turn this expression back into a mutable reference of the field:

let c: &mut Field = <MutHandle<'_, Field> as BorrowPlace<&mut Field>>::borrow(b);

If the handle had been derived from a shared reference (a was &Struct above), the handle type would instead be RefHandle<'_, Field> which does not implement BorrowPlace<&mut Field>. It does implement BorrowPlace<&Field>, so the following code compiles.

let a: &mut Struct;
let b: RefHandle<'b, Field> = handle!((*a).field);
let c: &Field = <RefHandle<'_, Field> as BorrowPlace<&Field>>::borrow(b);

§Converting place expressions into handles

Any valid place expression is converted into a handle by the compiler. We give this desugaring as a pseudo1-macro definition:

macro_rules! handle {
    ($path:path) => { LocalHandle::new(&raw {const,mut} $path) };

    ($place:place[$expr:expr]) => {
        <
            typeof($place) as IndexPlace<typeof($expr), _>
        >::index(handle!($place), $expr)
    };

    (*$place:place) => { DerefPlace::deref_place(handle!($place)) };

    ($place:place.$field:member) => {
        let subplace
            = subplace!(typeof($place), $field, handle!($place));
        ProjectPlace::<typeof(subplace)>::project_place(
            handle!($place),
            subplace,
        )
    };

    (($place:place)) => { handle!($place) };

    // value-to-place coercions are turned into temporaries:
    ($value:expr) => {{
        super let value = $value;
        handle!(value)
    }};
}

Links to the types and traits used: LocalHandle, IndexPlace, DerefPlace, and ProjectPlace.

The need for a subplace! pseudo-macro might seem surprising, but it’s required to support place wrappers, which we will cover in the next section. Without place wrappers, we could simply replace the subplace! invocation with <field_of!(typeof($place), $field)>::default().

§Place Wrappers

Place wrappers are a special kind of place proxy. They “physically contain” the place they are proxying for. A good example is MaybeUninit<T>. To support subplaces of these place wrappers, the WrapPlace trait exists. It allows forwarding subplaces to the proxy and changing the subplace access information. With MaybeUninit<T>, this allows accessing any subplace under the transformation that its type is wrapped in MaybeUninit. So given &MaybeUninit<Struct>, the field subplace can be borrowed using & and it has type &MaybeUninit<Field>.

§Place Operation Traits

Aside from the three basic place operations of ReadPlace, WritePlace, and BorrowPlace, there are also the following other operations:

  • MovePlace – moving out of a place (looks the same as a read operation).
  • IndexPlace – using the index operator on places (place[idx]).
  • DerefPlace – dereferencing a pointer that’s in a place (*place).
  • ProjectPlace – accessing a subplace (place.field).
  • DropPlace – dropping the contents of a place (no surface syntax, emitted by the compiler).
  • ReadMetadata – TODO (no surface syntax, emitted by the compiler).
  • ReadVariant – TODO (no surface syntax, emitted by the compiler).
  • VariantPlace – TODO (no surface syntax, emitted by the compiler).

The place operations are desugared in a similar manner to place expressions. In particular, a place operation always operates upon a place expression, which relies on the desugaring detailed in the previous section. Refer to the operation traits for their desugaring.

§Safety

All operation functions are unsafe, since they have raw pointer arguments that have safety preconditions. The arguments are raw pointers, because the values they point to need not be in a valid state (they may be partially moved out or borrowed).

The safety requirements for the operation functions have not been figured out at this point in time. Since we expect several changes to the design, we do not want to commit to writing down good safety documentation before having finished the design.

What is clear at the moment is that the safety requirements will heavily interact with the borrow checker. It will ensure that simultaneous place operations on the same value are allowed, since they either affect disjoint subplaces, or because they both only require shared access. For example:

  • reading ptr.field.subfield and borrowing ptr.field with &T are allowed to happen at the same time,
  • writing ptr.field and borrowing ptr.field.subfield at the same time is not allowed.

The safety of using the place operations via the operators will depend on the value of the SAFE constant in the operation traits. At the moment we will only permit a literal value of true or false in implementations. It will dictate if people have to write for example unsafe { &*ptr } or if *ptr is allowed. It should be set to true when the borrow checker’s guarantees of either disjoint subplaces or “all concurrent operations are shared” are enough to calling the operations’ function correctly. If there are additional requirements, such as “ptr is valid”, then SAFE should be set to false. For example, &mut T will have SAFE = true in ReadPlace, but NonNull<T> will set it to false.

§Handle Validity

TODO


  1. we use the non-existent macro fragment specifiers of place for place expressions and member for struct field names and tuple indices. Additionally, we require eager expansion of macros, as we use the output of the handle! macro in the input of the subplace! macro. 

Modules§

borrowck

Traits§

PlaceProxy
A type proxying for a place.
CreateHandle
TODO: Defines creation of handles.
PlaceHandle
A handle to a place.
ReadPlace
let _ = place; – Read from a place.
MovePlace
let _ = place; – Move out of a place.
WritePlace
place = value; – Write to a place.
BorrowPlace
&place/@place – Borrow a place as Output.
Indexable
place[idx] – Enable indexing into Self.
IndexPlace
place[idx] – Index into Self via the handle H.
DerefPlace
*place – Dereference the contents of a place.
ProjectPlace
place.field – Project a handle to a subplace.
PlaceWrapper
Wrap a place, exposing some of its subplaces.
WrapPlace
place.field – Expose a modified subplace of the wrapped place.
DropPlace
Drop the contents of a place.
DropHusk
Destroy a PlaceProxy where its contents have been moved out/dropped.
ReadMetadata
Obtain the metadata of the contents of a place.
ReadVariant
Obtain the discriminant of the contents of a place.
VariantPlace
Cast a handle to a place to a subtype.