google / google/zerocopy

Size Specialization

Open
#2,149 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
2.6k
Forks
179
Avg merge
1d 19h
Merged PRs (30d)
29

Description

#1828 implements initial support for an `UnalignUnsized` wrapper, that drop its value by moving it to an aligned place, then running its destructor. For derivations of `KnownLayout` on default repr types (which are always sized), this achieved by stack allocating the well-aligned place. For derivations of `KnownLayout` on `repr(C)` types (which may or may not be sized), this is achieved by heap allocation.

These conditions are less precise than we'd wish. Rather than depending on the syntax `T`, we'd like the drop behavior to depend on the *actual* sizedness of `T`. I.e., if `T` is sized, a place for it it can be stack allocated; otherwise, heap allocated.

## Single Trait Approach

Unfortunately, it is impossible to straight-forwardly extend `KnownLayout` with this capability; e.g., this attempt induces solver cycles on implementations of `KnownLayout` for wrapper types ([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=6868471f2f1a0af1207043b9924269db)):

```rust
use std::cell::UnsafeCell;
use std::mem::MaybeUninit;

// Helps ensure that the correct metadata kind is used and
// can hold methods whose impl differs on `T`'s (un)sizedness.
trait Metadata {}
impl Metadata for () {}
impl Metadata for usize {}

// A trait encoding layout facts about `Self`.
trait KnownLayout {
// What metadata do pointers to `Self` have?
type Metadata: Metadata;

// A type with a layout like `Self`, but allowing uninit
// bytes in all positions.
type MaybeUninit: ?Sized + KnownLayout;
}

// A working-as-intended impl of `KnownLayout`:
impl KnownLayout for [T] {
type Metadata = usize;
type MaybeUninit = [MaybeUninit];
}

// Is there anything we can do to get this impl to compile?
impl KnownLayout for UnsafeCell {
type Metadata = ::Metadata;

type MaybeUninit = UnsafeCell<::MaybeUninit>;
}
```

## Layered Approach

To break the cycles, we must introduce an additional trait layer. In the below approach, `derive(KnownLayout)` actually emits an impl for `KnownLayoutInternal`, while `KnownLayout` (now backed by a blanket impl bounded by `MetadataCoherent`):

```rust
use std::cell::UnsafeCell;
use std::mem::MaybeUninit;

pub trait Metadata {}
impl Metadata for () {}
impl Metadata for usize {}

// This trait is the one implemented by `derive(KnownLayout)`
#[doc(hidden)]
pub trait KnownLayoutInternal {
// What metadata do pointers to `Self` have?
type Metadata: Metadata;

// A type with a layout like `Self`, but allowing uninit
// bytes in all positions.
type MaybeUninit: ?Sized + KnownLayoutInternal;
}

#[doc(hidden)]
pub trait MetadataCoherent: Metadata {}

impl MetadataCoherent for usize
where
T: ?Sized + KnownLayoutInternal
{}

impl MetadataCoherent for ()
where
T: KnownLayoutInternal
{}

// This trait is the one that appears as a bound in public APIs.
pub trait KnownLayout: KnownLayoutInternal
where
Self::Metadata: MetadataCoherent
{}

impl KnownLayout for T
where
Self::Metadata: MetadataCoherent
{}

// A working-as-intended impl of `KnownLayoutInternal`:
impl KnownLayoutInternal for [T] {
type Metadata = usize;
type MaybeUninit = [MaybeUninit];
}

// And this now compiles, too!
impl KnownLayoutInternal for UnsafeCell
{
type Metadata = ::Metadata;

type MaybeUninit = UnsafeCell<::MaybeUninit>;
}
```

Unfortunately, the
```rust
where
Self::Metadata: MetadataCoherent
```
on `KnownLayout` is not implied by `KnownLayout`!

If you define a function with an argument bounded by `KnownLayout`; e.g.:
```rust
fn foo() {}
```
...rustc produces this error:
```
error[E0277]: the trait bound `::Metadata: MetadataCoherent` is not satisfied
--> src/lib.rs:1:11
|
1 | fn foo() {}
| ^^^^^^^^^^^ the trait `MetadataCoherent` is not implemented for `::Metadata`
|
note: required by a bound in `KnownLayout`
--> src/lib.rs:37:21
|
35 | pub trait KnownLayout: KnownLayoutInternal
| ----------- required by a bound in this trait
36 | where
37 | Self::Metadata: MetadataCoherent
| ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `KnownLayout`
help: consider further restricting the associated type
|
1 | fn foo() where ::Metadata: MetadataCoherent {}
| +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.