-
Notifications
You must be signed in to change notification settings - Fork 105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
UnalignUnsized #1828
base: v0.8.x
Are you sure you want to change the base?
UnalignUnsized #1828
Conversation
a081f40
to
41b007b
Compare
e7dc008
to
7d7b050
Compare
src/lib.rs
Outdated
@@ -893,6 +930,57 @@ unsafe impl<T> KnownLayout for [T] { | |||
// struct `Foo(i32, [u8])` or `(u64, Foo)`. | |||
slc.len() | |||
} | |||
|
|||
#[cfg(feature = "alloc")] | |||
unsafe fn drop(slf: &mut UnalignUnsized<Self>) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this play nicely with Pin
'd types?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, Pin bars you from moving the referent before running its destructor. It doesn't place any restrictions on what you do in the destructor.
6860aa4
to
a9458e8
Compare
271b768
to
96b54d1
Compare
7d7b050
to
c1205be
Compare
96b54d1
to
9844c1f
Compare
c1205be
to
e8cc8eb
Compare
d94bda0
to
944a13e
Compare
167b002
to
60f0a43
Compare
6420680
to
08cdb04
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## v0.8.x #1828 +/- ##
==========================================
- Coverage 87.42% 84.74% -2.69%
==========================================
Files 16 16
Lines 6115 6403 +288
==========================================
+ Hits 5346 5426 +80
- Misses 769 977 +208 ☔ View full report in Codecov by Sentry. |
08cdb04
to
6a618c8
Compare
6a618c8
to
0b72808
Compare
This is achieved by adding a `MaybeUninit` associated type to `KnownLayout`, whose layout is identical to `Self` except that it admits uninitialized bytes in all positions. For sized types, this is bound to `mem::MaybeUninit<Self>`. For potentially unsized structs, we synthesize a doppelganger with the same `repr`, whose leading fields are wrapped in `mem::MaybeUninit` and whose trailing field is the `MaybeUninit` associated type of struct's original trailing field type. This type-level recursion bottoms out at `[T]`, whose `MaybeUninit` associated type is bound to `[mem::MaybeUninit<T>]`. Makes progress towards #1797
0b72808
to
b2f8e08
Compare
b2f8e08
to
d7f8249
Compare
This PR introduces
UnalignUnsized<T: ?Sized + KnownLayout>
, a wrapper around a valueT
that makes it trivially aligned. This nearly obsoletes zerocopy's existingUnalign<T>
wrapper, which cannot support unsized types but also does not requireT: KnownLayout
.Background
Zerocopy presently restricts
Unalign
'sT
toSized
because of language-level shortcomings of Rust. For example, this naive attempt to unsizeUnalign
:...produces a compilation error:
This may be circumvented either by bounding
T
withCopy
(which precludes it from having a non-trivial destructor), or by wrappingT
inManuallyDrop
; e.g.:Rust accepts this, but at the cost of
Drop
: The wrapped value's destructor will not be executed. To recoverT
's destructor, we must find a way to first align it so it may be dropped.Rust achieves this for
Unalign
withT: Sized
by first reading its wrapped value onto the stack, then dropping it. We cannot immediately apply the same technique forT: ?Sized
; doing this:...produces the error:
On nightly, we could perhaps circumvent this with
#[feature(unsized_locals)]
(rust-lang/rust#48055) if Rust bounded core::ptr::read_unaligned withT: ?Sized
. For now, it does not.Although we trivially can "fix" this problem by not dropping
T
, this strikes us as a design smell:Unalign
andManuallyDrop
are orthogonal, and the former should not imply the latter.Design
An acceptable design of
Unalign<T: ?Sized>
will:T
s with non-trivial dropsUnalign<T: ?Sized + Copy>
forT
with trivial dropsUnalign<T: Sized>
forT
with non-trivial dropsT
Approach Sketch
This approach of this PR is guided by two insights:
Box
then dropping theBox
We anticipate that occurrences of the post-monomorphization compilation error will be relatively rare, since it will require that:
no_std
Drop
Dropping DSTs
To drop a DST, we simply:
Box<MaybeUninit<T>>
(made possible forT: ?Sized
in Add initial support for unsizedMaybeUninit
wrapper type #2055)Box
Box
toBox<T>
Box
Eliminating Silent Forgets
The above approach for dropping DSTs depends on having a global allocator. In contexts where this is unavailable and the wrapped type has both a non-trivial alignment and non-trivial destructor, we emit a post-monomorhpization errors. Consider this toy example:
Commenting out the
mem::forget
produces a compilation error:This error, though admittedly poor, is better than a panic in production. We can additionally take steps to minimize needless errors. We can amend
drop
to only error ifT
has a non-trivial drop; e.g.:And, leveraging
KnownLayout
, to only error ifT
is also non-trivially aligned (otherwise it can be dropped in-place); e.g.: