Skip to content

Support DST pointer casts in const fn #1967

@joshlf

Description

@joshlf

The following is not valid:

fn cast<T: ?Sized, U: ?Sized>(t: *mut T) -> *mut U {
    t as *mut U
}

Rust isn't able to guarantee that T and U have the same "vtable kinds", and thus isn't able to prove that *mut T and *mut U have compatible pointer metadata. We currently work around this using this method on KnownLayout:

fn raw_from_ptr_len(bytes: NonNull<u8>, meta: Self::PointerMetadata) -> NonNull<Self>;

This has a limitation: It can't be called in a const context.

Instead, we could make casting unsafe, requiring the caller to promise to ensure that the pointers are either both thin or both fat, and use a union-transmute under the hood to avoid the vtable problem:

/// # Safety
///
/// The caller must ensure that `Src` and `Dst` must either both be `Sized` or
/// both be unsized.
const unsafe fn cast_unchecked<Src: ?Sized, Dst: ?Sized>(src: *mut Src) -> *mut Dst {
    #[repr(C)]
    union Transmute<Src: Copy, Dst: Copy> {
        src: Src,
        dst: Dst,
    }

    unsafe { Transmute { src }.dst }
}

The behavior of this union-transmute is almost well-defined, but not quite. The Reference guarantees the behavior of raw pointer casts, but makes no guarantee that the equivalent transmute has the same behavior as a cast. I've put up a PR to guarantee that this is well-defined: rust-lang/reference#1661

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions