-
-
Notifications
You must be signed in to change notification settings - Fork 15.5k
Introduce a PinSafePointer trait that generalizes PinCoerceUnsized
#156935
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -260,7 +260,7 @@ use core::ops::{Residual, Try}; | |
| use core::panic::{RefUnwindSafe, UnwindSafe}; | ||
| #[cfg(not(no_global_oom_handling))] | ||
| use core::pin::Pin; | ||
| use core::pin::PinCoerceUnsized; | ||
| use core::pin::PinSafePointer; | ||
| use core::ptr::{self, NonNull, drop_in_place}; | ||
| #[cfg(not(no_global_oom_handling))] | ||
| use core::slice::from_raw_parts_mut; | ||
|
|
@@ -2446,12 +2446,19 @@ impl<T: ?Sized, A: Allocator> Deref for Rc<T, A> { | |
| } | ||
| } | ||
|
|
||
| // The API of this pointer type enforces that if the `T` is pinned, then *all* | ||
| // clones of this `Rc<T>` are wrapped as `Pin<Rc<T>>`. Since an `&Rc<T>` could | ||
| // be used to obtain an `Rc<T>` that is not wrapped in `Pin` (and later used | ||
| // with `Rc::get_mut`), this means that this type treats `&Rc<T>` as evidence | ||
| // that the `T` is not pinned. The implementations of various traits are written | ||
| // accordingly. Since this type is not fundamental, downstream crates cannot | ||
| // provide malicious implementations of any of the traits relevant for `Pin`. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this relies on something about the orphan rule, right? That makes me uneasy.^^
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, by the orphan rule downstream crates cannot implement
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Those are the current orphan rules. How do we know they will never be relaxed? The only promise the orphan rules are making is "there will never be more than one impl for the same trait + type". I don't think we should rely on any property that goes beyond this, and I don't see how the argument here follows from that property.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can add a negative impl to simplify the reasoning for
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, even if I add the I do not think we can get out of this without orphan-rules based reasoning.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, we need new language features to support this properly I think. Maybe if we could have a "dual" negative impl? impl<T: ?Sized + !fmt::Debug, A> !fmt::Debug for Rc<T, A>Since every concrete type either impls |
||
| #[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] | ||
| unsafe impl<T: ?Sized, A: Allocator> PinCoerceUnsized for Rc<T, A> {} | ||
| unsafe impl<T: ?Sized, A: Allocator + 'static> PinSafePointer for Rc<T, A> {} | ||
|
|
||
| //#[unstable(feature = "unique_rc_arc", issue = "112566")] | ||
| #[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] | ||
| unsafe impl<T: ?Sized, A: Allocator> PinCoerceUnsized for UniqueRc<T, A> {} | ||
| unsafe impl<T: ?Sized, A: Allocator + 'static> PinSafePointer for UniqueRc<T, A> {} | ||
|
|
||
| #[unstable(feature = "deref_pure_trait", issue = "87121")] | ||
| unsafe impl<T: ?Sized, A: Allocator> DerefPure for Rc<T, A> {} | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1080,9 +1080,8 @@ pub use self::unsafe_pinned::UnsafePinned; | |
| /// [subtle-details]: self#subtle-details-and-the-drop-guarantee "pin subtle details" | ||
| /// [`unsafe`]: ../../std/keyword.unsafe.html "keyword unsafe" | ||
| // | ||
| // Note: the `Clone` derive below causes unsoundness as it's possible to implement | ||
| // `Clone` for mutable references. | ||
| // See <https://internals.rust-lang.org/t/unsoundness-in-pin/11311> for more details. | ||
| // Note: the `Clone` derive below is sound because either `Ptr: PinSafePointer` | ||
| // or the pointee is `Unpin`. | ||
| #[stable(feature = "pin", since = "1.33.0")] | ||
| #[lang = "pin"] | ||
| #[fundamental] | ||
|
|
@@ -1097,7 +1096,8 @@ pub struct Pin<Ptr> { | |
| // issues. `&self.pointer` should not be accessible to untrusted trait | ||
| // implementations. | ||
| // | ||
| // See <https://internals.rust-lang.org/t/unsoundness-in-pin/11311/73> for more details. | ||
| // See <https://internals.rust-lang.org/t/unsoundness-in-pin/11311/73> and the | ||
| // `PinSafePointer` trait for more details. | ||
|
|
||
| #[stable(feature = "pin_trait_impls", since = "1.41.0")] | ||
| impl<Ptr: Deref, Q: Deref> PartialEq<Pin<Q>> for Pin<Ptr> | ||
|
|
@@ -1230,11 +1230,15 @@ impl<Ptr: Deref> Pin<Ptr> { | |
| /// points to is pinned, that is a violation of the API contract and may lead to undefined | ||
| /// behavior in later (even safe) operations. | ||
| /// | ||
| /// By using this method, you are also making a promise about the [`Deref`], | ||
| /// [`DerefMut`], and [`Drop`] implementations of `Ptr`, if they exist. Most importantly, they | ||
| /// must not move out of their `self` arguments: `Pin::as_mut` and `Pin::as_ref` | ||
| /// will call `DerefMut::deref_mut` and `Deref::deref` *on the pointer type `Ptr`* | ||
| /// and expect these methods to uphold the pinning invariants. | ||
| /// By using this method, you are also making a promise about several trait | ||
| /// implementations of `Ptr` itself, if they exist. Most importantly, they | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm wondering if there's some formulation of "if they exist" like "if they exist now or could exist in the future"...
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I could add an:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That seems like a good idea to me. |
||
| /// must not move out of their `self` arguments: `Pin::as_mut` and | ||
| /// `Pin::as_ref` will call `DerefMut::deref_mut` and `Deref::deref` *on the | ||
| /// pointer type `Ptr`* and expect these methods to uphold the pinning | ||
| /// invariants. These requirements are specified in more detail on the | ||
| /// [`PinSafePointer`] trait, and `Ptr` must abide by the safety | ||
| /// requirements of that trait. | ||
| /// | ||
| /// Moreover, by calling this method you promise that the reference `Ptr` | ||
| /// dereferences to will not be moved out of again; in particular, it | ||
| /// must not be possible to obtain a `&mut Ptr::Target` and then | ||
|
|
@@ -1690,7 +1694,7 @@ const impl<Ptr: [const] Deref> Deref for Pin<Ptr> { | |
| mod helper { | ||
| /// Helper that prevents downstream crates from implementing `DerefMut` for `Pin`. | ||
| /// | ||
| /// The `Pin` type implements the unsafe trait `PinCoerceUnsized`, which essentially requires | ||
| /// The `Pin` type implements the unsafe trait `PinSafePointer`, which essentially requires | ||
| /// that the type does not have a malicious `Deref` or `DerefMut` impl. However, without this | ||
| /// helper module, downstream crates are able to write `impl DerefMut for Pin<LocalType>` as | ||
| /// long as it does not overlap with the impl provided by stdlib. This is because `Pin` is | ||
|
|
@@ -1781,6 +1785,10 @@ unsafe impl<Ptr: DerefPure> DerefPure for Pin<Ptr> {} | |
| #[unstable(feature = "legacy_receiver_trait", issue = "none")] | ||
| impl<Ptr: LegacyReceiver> LegacyReceiver for Pin<Ptr> {} | ||
|
|
||
| // The following implementations allow untrusted trait implementations to access | ||
| // `&self.pointer`, which is only sound because these traits are mentioned in | ||
| // the safety requirements of `PinSafePointer`. | ||
|
|
||
| #[stable(feature = "pin", since = "1.33.0")] | ||
| impl<Ptr: fmt::Debug> fmt::Debug for Pin<Ptr> { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
|
|
@@ -1810,49 +1818,157 @@ impl<Ptr: fmt::Pointer> fmt::Pointer for Pin<Ptr> { | |
| #[stable(feature = "pin", since = "1.33.0")] | ||
| impl<Ptr, U> CoerceUnsized<Pin<U>> for Pin<Ptr> | ||
| where | ||
| Ptr: CoerceUnsized<U> + PinCoerceUnsized, | ||
| U: PinCoerceUnsized, | ||
| Ptr: CoerceUnsized<U> + PinSafePointer, | ||
| U: PinSafePointer, | ||
| { | ||
| } | ||
|
|
||
| #[stable(feature = "pin", since = "1.33.0")] | ||
| impl<Ptr, U> DispatchFromDyn<Pin<U>> for Pin<Ptr> | ||
| where | ||
| Ptr: DispatchFromDyn<U> + PinCoerceUnsized, | ||
| U: PinCoerceUnsized, | ||
| Ptr: DispatchFromDyn<U> + PinSafePointer, | ||
| U: PinSafePointer, | ||
| { | ||
| } | ||
|
|
||
| #[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] | ||
| /// Trait that indicates that this is a pointer or a wrapper for one, where | ||
| /// unsizing can be performed on the pointee when it is pinned. | ||
| /// Trait that indicates that this is a pointer that does not misbehave when | ||
| /// combined with [`Pin`]. | ||
| /// | ||
| /// Note that for backwards compatibility reasons, it is possible to create a | ||
| /// [`Pin<P>`] for pointer types `P` that do not implement this trait. However, | ||
| /// this can only be done safely if `<P as Deref>::Target` implements `Unpin`, | ||
| /// which means that pinning has no effect. | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// Given a pointer of this type, the concrete type returned by its | ||
| /// `deref` method and (if it implements `DerefMut`) its `deref_mut` method | ||
| /// must be the same type and must not change without a modification. | ||
| /// The following operations are not considered modifications: | ||
| /// | ||
| /// * Moving the pointer. | ||
| /// * Performing unsizing coercions on the pointer. | ||
| /// * Performing dynamic dispatch with the pointer. | ||
| /// * Calling `deref` or `deref_mut` on the pointer. | ||
| /// | ||
| /// The concrete type of a trait object is the type that the vtable corresponds | ||
| /// to. The concrete type of a slice is an array of the same element type and | ||
| /// the length specified in the metadata. The concrete type of a sized type | ||
| /// is the type itself. | ||
| pub unsafe trait PinCoerceUnsized: Deref {} | ||
| /// Types that implement this trait must not provide "malicious" implementations | ||
| /// of any safe traits used by [`Pin`]. | ||
| /// | ||
| /// ## The pointer must always reference the same object | ||
| /// | ||
| /// Calls to [`deref`]/[`deref_mut`] on the same `Pin<P>` instance must always | ||
| /// refer to the same object. That is, the address returned by these methods | ||
| /// must not change. This applies even if the pointer type is moved. | ||
|
Comment on lines
+1850
to
+1852
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this is still doing the thing where it talks about a
The same concern applies to the requirements on
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that it's best to avoid this kind of safety requirement, but I think we have no choice. Luckily we do not need to worry about This PR is adding comments to every impl of
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah in std we can do that because we have negative impls. But our users can't really do that... and the moment one wants to
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Negative impls are only required for fundamental types, so I'm not too concerned about that.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With regards to types where
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have seen that PR and ran away screaming.^^ I really hope the reasoning is sound, but I do not have a coherent proof in my head -- it relies too much on orphan rule details.
Comment on lines
+1851
to
+1852
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This seems to imply that it's OK for
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. True, the part about concrete types should not be behind an "if the smart pointer can participate in unsizing coercions" and should just be unconditional.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this still pending an edit? I admit I don't quite understand why we have such a strong condition, but if we think it's reasonable then I don't have direct objections. My main concern with all these rules is that I am not sure how to audit for them effectively (especially as we seem to keep changing them as we find more potential problems).
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated now. |
||
| /// | ||
| /// These coercions must also not change the underlying concrete type. Here, the | ||
| /// concrete type of a trait object is the type that the vtable corresponds to. | ||
| /// The concrete type of a slice is an array of the same element type and the | ||
| /// length specified in the metadata. The concrete type of a sized type is the | ||
| /// type itself. | ||
| /// | ||
| /// As an example, after unsizing coercing a pinned pointer, `deref_mut` must | ||
| /// not return a `#[repr(transparent)]` wrapper around the value it referenced | ||
| /// before being unsized, even if the address is unchanged. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this accurate? Or is changing to repr(transparent) wrapper fine as long as the impls are all the same on both, i.e., not actually malicious?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can choose to disallow obscure cases even if they are sound. I mean, if the |
||
| /// | ||
| /// ## The pointer must not move its pointee | ||
| /// | ||
| /// The [`deref_mut`] method and the pointer type's destructor are called with a | ||
| /// `&mut self` receiver, but they must behave as-if it was a `self: Pin<&mut | ||
| /// Self>` receiver. That is, they must not move out of the underlying value. | ||
| /// | ||
| /// As an example, `deref_mut` must not invoke `swap` on the inner value. | ||
| /// | ||
| /// ## Shared access to the pointer | ||
| /// | ||
| /// If this pointer type uses `&P` references as evidence that this value is not | ||
| /// pinned, then it must not treat the `&self` argument passed to [`Clone`] or | ||
| /// the formatting traits ([`fmt::Debug`], [`fmt::Display`], [`fmt::Pointer`]) | ||
| /// as such evidence. | ||
| /// | ||
| /// As an example, given a `Pin<Arc<T>>` there is no way to obtain an `&Arc<T>` | ||
| /// (note that `Deref` just gives a `&T`). Because of this, the [`Arc`] type can | ||
| /// assume that an `&Arc<T>` value can only exist if the `T` is not pinned, | ||
| /// which justifies the soundness of the [`Arc::get_mut`] method. | ||
|
Comment on lines
+1874
to
+1882
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't realize |
||
| /// | ||
| /// ## Cloning pinned pointers | ||
| /// | ||
| /// When a [`Pin<P>`] is cloned, the `P` pointer value returned by `clone` is | ||
| /// passed to [`Pin::new_unchecked`]. The implementation of [`Clone`] must | ||
| /// return a value such that this is sound. | ||
| /// | ||
| /// For example, when a `Pin<&T>` is cloned, the resulting `&T` points at the | ||
| /// same value. The value is known to be pinned since a `Pin<&T>` to it exists, | ||
| /// so it is safe to wrap the `&T` returned by `clone` in `Pin`. | ||
| /// | ||
| /// [`deref`]: Deref::deref | ||
| /// [`deref_mut`]: DerefMut::deref_mut | ||
| /// [`clone`]: Clone::clone | ||
| /// [`Arc`]: ../../std/sync/struct.Arc.html "Arc" | ||
| /// [`Arc::get_mut`]: ../../std/sync/struct.Arc.html#method.get_mut "Arc::get_mut" | ||
| pub unsafe trait PinSafePointer: Deref + Sized {} | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added a |
||
|
|
||
| // A pointer can only be pin safe if it does not implement certain safe traits | ||
| // maliciously. Since `&T` is fundamental, downstream crates may be able to | ||
| // implement those traits for `&LocalType`, so we must carefully check that | ||
| // this is not a problem for each trait. | ||
| // | ||
| // The `&T` type always implements [`Deref`] and [`Clone`], so despite being | ||
| // fundamental, downstream crates cannot implement these traits for | ||
| // `&LocalType`. | ||
| // | ||
| // The `&T` type has a negative blanket implementations for [`DerefMut`], so | ||
| // downstream crates cannot implement `DerefMut` for `&LocalType`. | ||
| // | ||
| // Conversely, downstream crates are able to implement `Debug` and `Display` for | ||
| // `&LocalType` as long as `LocalType` does not implement said trait. However, | ||
| // the existence of an `&&T` cannot be treated as evidence that the `T` is not | ||
| // pinned, so this is not problematic. | ||
| #[stable(feature = "pin", since = "1.33.0")] | ||
| unsafe impl<'a, T: ?Sized> PinCoerceUnsized for &'a T {} | ||
| unsafe impl<'a, T: ?Sized> PinSafePointer for &'a T {} | ||
|
|
||
| // A pointer can only be pin safe if it does not implement certain safe traits | ||
| // maliciously. Since `&mut T` is fundamental, downstream crates may be able to | ||
| // implement those traits for `&mut LocalType`, so we must carefully check that | ||
| // this is not a problem for each trait. | ||
| // | ||
| // The `&mut T` type always implements [`Deref`] and [`DerefMut`], so despite | ||
| // being fundamental, downstream crates cannot implement these traits for `&mut | ||
| // LocalType`. | ||
| // | ||
| // The `&mut T` type has a negative blanket implementations for [`Clone`], so | ||
| // downstream crates cannot implement `Clone` for `&mut LocalType`. | ||
| // | ||
| // Conversely, downstream crates are able to implement `Debug` and `Display` | ||
| // for `&mut LocalType` as long as `LocalType` does not implement said trait. | ||
| // However, the existence of an `&&mut T` cannot be treated as evidence that the | ||
| // `T` is not pinned, so this is not problematic. | ||
| #[stable(feature = "pin", since = "1.33.0")] | ||
| unsafe impl<'a, T: ?Sized> PinCoerceUnsized for &'a mut T {} | ||
| unsafe impl<'a, T: ?Sized> PinSafePointer for &'a mut T {} | ||
|
|
||
| // A pointer can only be pin safe if it does not implement certain safe traits | ||
| // maliciously. `Pin` implements these traits by forwarding to `P`, which also | ||
| // asserts that these implementations are not malicious, so the implementations | ||
| // provided by `core` are ok. However, since `Pin<T>` is fundamental, | ||
| // downstream crates may be able to implement those traits for `Pin<LocalType>` | ||
| // directly, so we must carefully check that if a downstream crate can | ||
| // implement these traits for `Pin<LocalType>`, then this does not lead to any | ||
| // problems for the `Pin<Pin<LocalType>>` type. | ||
| // | ||
| // The `Pin<P>` type only implements `Deref` when `P: Deref`, so downstream | ||
| // crates can implement `Deref` for `Pin<LocalType>` in cases where `LocalType: | ||
| // !Deref`. However, as `Deref` is a super-trait for `PinSafePointer`, we do | ||
| // not assert that `Pin<P>` is pin safe in that scenario. | ||
| // | ||
| // The `Pin<P>` type only implements `DerefMut` when `P: DerefMut` and | ||
| // `P::Target: Unpin`, so normally downstream crates would be able to provide | ||
| // an implementation of `DerefMut` for `Pin<LocalType>` when `LocalType` does | ||
| // not satisfy those conditions. However, a special hack is used to prevent | ||
| // such downstream implementations, so this is not a problem. See | ||
| // [#145608](https://github.com/rust-lang/rust/pull/145608) for details. | ||
| // | ||
| // Conversely, downstream crates are able to implement `Clone`, `Debug` and | ||
| // `Display` for `Pin<LocalType>` as long as `LocalType` does not implement | ||
| // said trait. However, the existence of an `&Pin<P>` cannot be treated as | ||
| // evidence that the value is not pinned, so this is not problematic. | ||
| // | ||
| // Furthermore, in the case of `Clone`, cloning a `Pin<Pin<P>>` will utilize | ||
| // `Pin::new_unchecked` to convert from `Pin<P>` to `Pin<Pin<P>>`. However, | ||
| // given that the implementation of `Clone` returned a `Pin<P>`, we know that | ||
| // the target value is pinned, so this conversion is okay even if `Clone` was | ||
| // implemented for `Pin<P>` by a downstream crate. | ||
| #[stable(feature = "pin", since = "1.33.0")] | ||
| unsafe impl<T: PinCoerceUnsized> PinCoerceUnsized for Pin<T> {} | ||
| unsafe impl<P: PinSafePointer> PinSafePointer for Pin<P> {} | ||
|
|
||
| /// Constructs a <code>[Pin]<[&mut] T></code>, by pinning a `value: T` locally. | ||
| /// | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
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.
I added
A: 'staticbounds to these impls since you generally cannot pin values in memory from a non-static allocator.View changes since the review
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.
You can create a
Pin<Box<T, A>>for a non-'static allocator by first pinning usingMyAllocator<'static>, and then use subtyping to turn that intoMyAllocator<'a>.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.
But that's kind of awkward, then. In that scenario, we have a non-static
Pin<Box<T, A<'a>>>, we clone the inner box to create a newBox<T, A<'a>>, and then we wrap the clone inPinto get a secondPin<Box<T, A<'a>>>. But you can normally only wrap a box inPinif the allocator is'static, so the argument I'm using forBoxdoesn't quite apply anymore.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.
Oh dear.......
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.
This feels unsound, but I haven't figured out yet how.
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.
It's unsound (even without using the
PinCoerceUnsizedimpl). I filed this as #157089.