google/zerocopy

Encode size and alignment in the type system

joshlf opened this issue · 1 comments

Any design for this issue will interact with other issues tracked by #885; make sure to read that issue when tackling this one.

Without generic_const_exprs, we can't use size_of::<T>() or align_of::<T>() in a generic context. We have many use cases for these, so it would be good to work around the issue with a sort of polyfill.

In general, we'd like to be able to express in the type system:

  • size_of::<T>() == size_of::<U>()
  • align_of::<T>() >= align_of::<U>() (useful for reference transmutations from T to U)
  • size_of::<T>() == N
  • align_of::<T>() >= N or <= N (to support #280)

API

The most obvious approach is to add machinery to KnownLayout, although we need this machinery to not rely on const generics (like the associated LAYOUT type currently does), so it would need to be a true type-level representation. It might be the case that:

  • There are types we want to support which KnownLayout can't
  • There are types which KnownLayout currently supports which can't support this approach

Thus, we may need to introduce a separate trait or traits.

Given that the knowledge is now encoded in the type system, we need some way of expressing bounds. In other words, we need some sort of:

  • T: SizeEq<U>
  • T: AlignGtEq<U>
  • T: SizeEqConst<N>
  • T: AlignGtEqConst<N>

These could either be blanket impls in terms of KnownLayout, or be separate from KnownLayout entirely (and might need to be derived).


Merging #125 into this:

This is a half-baked idea, and it requires the unstable associated_const_equality feature. Also, the AlignedTo impls don't currently work thanks to rust-lang/rust#103292.

trait Size {
    const SIZE: usize;
}

impl<T> Size for T {
    const SIZE: usize = core::mem::size_of::<T>();
}

trait Align {
    const ALIGN: usize;
}

impl<T> Align for T {
    const ALIGN: usize = core::mem::align_of::<T>();
}

trait Zst {}

impl<T: Size<SIZE = 0>> Zst for T {}

trait SameSizeAs<T> {}

impl<const SIZE: usize, T: Size<SIZE = { SIZE }>, U: Size<SIZE = { SIZE }>> SameSizeAs<U> for T {}

trait AlignedTo<T> {}

impl<T: Align<ALIGN = 1>, U: Align<ALIGN = 1>> AlignedTo<U> for T {}
impl<T: Align<ALIGN = 2>, U: Align<ALIGN = 2>> AlignedTo<U> for T {}
impl<T: Align<ALIGN = 2>, U: Align<ALIGN = 1>> AlignedTo<U> for T {}
impl<T: Align<ALIGN = 4>, U: Align<ALIGN = 4>> AlignedTo<U> for T {}
impl<T: Align<ALIGN = 4>, U: Align<ALIGN = 2>> AlignedTo<U> for T {}
impl<T: Align<ALIGN = 4>, U: Align<ALIGN = 1>> AlignedTo<U> for T {}

I have some typenum-like scratch code here and here as well