@@ -173,6 +173,19 @@ struct MySmartPointer<#[pointee] T: ?Sized, U> {
173
173
Specifying ` #[pointee] ` when the struct has only one type parameter is allowed,
174
174
but not required.
175
175
176
+ ## Pinned pointers
177
+
178
+ The ` #[derive(SmartPointer)] ` macro is not sufficient to coerce the smart
179
+ pointer when it is wrapped in ` Pin ` . That is, even if ` MySmartPointer<MyStruct> `
180
+ coerces to ` MySmartPointer<dyn MyTrait> ` , you will not be able to coerce
181
+ ` Pin<MySmartPointer<MyStruct>> ` to ` Pin<MySmartPointer<dyn MyTrait>> ` .
182
+ Similarly, traits with self types of ` Pin<MySmartPointer<Self>> ` are not object
183
+ safe.
184
+
185
+ If you implement the unstable unsafe trait called ` PinCoerceUnsized ` for
186
+ ` MySmartPointer ` , then the smart pointer will gain the ability to be coerced
187
+ when wrapped in ` Pin ` . The trait is not being stabilized by this RFC.
188
+
176
189
## Example of a custom Rc
177
190
[ custom-rc ] : #example-of-a-custom-rc
178
191
@@ -230,7 +243,6 @@ impl<T: ?Sized> Drop for Rc<T> {
230
243
In this example, ` #[derive(SmartPointer)] ` makes it possible to use `Rc<dyn
231
244
MyTrait>`.
232
245
233
-
234
246
# Reference-level explanation
235
247
[ reference-level-explanation ] : #reference-level-explanation
236
248
@@ -337,6 +349,47 @@ you can use them for dynamic dispatch.
337
349
As seen in the ` Rc ` example, the macro needs to be usable even if the pointer
338
350
is ` NonNull<RcInner<T>> ` (as opposed to ` NonNull<T> ` ).
339
351
352
+ ## ` PinCoerceUnsized `
353
+
354
+ The standard library defines the following unstable trait:
355
+ ``` rust
356
+ /// Trait that indicates that this is a pointer or a wrapper for one, where
357
+ /// unsizing can be performed on the pointee when it is pinned.
358
+ ///
359
+ /// # Safety
360
+ ///
361
+ /// If this type implements `Deref`, then the concrete type returned by `deref`
362
+ /// and `deref_mut` must not change without a modification. The following
363
+ /// operations are not considered modifications:
364
+ ///
365
+ /// * Moving the pointer.
366
+ /// * Performing unsizing coercions on the pointer.
367
+ /// * Performing dynamic dispatch with the pointer.
368
+ /// * Calling `deref` or `deref_mut` on the pointer.
369
+ ///
370
+ /// The concrete type of a trait object is the type that the vtable corresponds
371
+ /// to. The concrete type of a slice is an array of the same element type and
372
+ /// the length specified in the metadata. The concrete type of a sized type
373
+ /// is the type itself.
374
+ pub unsafe trait PinCoerceUnsized <U >: CoerceUnsized <U > {}
375
+
376
+ impl <T , U > CoerceUnsized <Pin <U >> for Pin <T >
377
+ where
378
+ T : PinCoerceUnsized <U >,
379
+ {}
380
+
381
+ impl <T , U > DispatchFromDyn <Pin <U >> for Pin <T >
382
+ where
383
+ T : PinCoerceUnsized <U > + DispatchFromDyn <U >,
384
+ {}
385
+ ```
386
+ The trait is implemented for all standard library types that implement
387
+ ` CoerceUnsized ` .
388
+
389
+ Although this RFC proposes to add the ` PinCoerceUnsized ` trait to ensure that
390
+ unsizing coercions of pinned pointers cannot be used to cause unsoundness, the
391
+ RFC does not propose to stabilize the trait.
392
+
340
393
# Drawbacks
341
394
[ drawbacks ] : #drawbacks
342
395
@@ -537,6 +590,131 @@ implementations of these traits for `Pin` with stricter trait bounds than what
537
590
is specified on the struct. That will get much more complicated if we use a
538
591
mechanism other than traits to specify this logic.
539
592
593
+ ## ` PinCoerceUnsized `
594
+
595
+ Beyond the addition of the ` #[derive(SmartPointer)] ` macro, this RFC also
596
+ proposes to add a new unstable trait called ` PinCoerceUnsized ` . This trait is
597
+ necessary because the API proposed by this RFC would otherwise by unsound:
598
+
599
+ > You could use ` Pin::new ` to create a ` Pin<SmartPtr<MyUnpinFuture>> ` and coerce
600
+ > that to ` Pin<SmartPtr<dyn Future>> ` . Then, if ` SmartPtr ` has a malicious
601
+ > implementation of the ` Deref ` trait, then ` deref ` could return a `&mut dyn
602
+ > Future` whose concrete type is not ` MyUnpinFuture`, but instead some other
603
+ > future type that * does* need to be pinned. Since no unsafe code is involved in
604
+ > any of these steps, this means that we are able to safely create a pinned
605
+ > pointer to a value that has not been pinned.
606
+
607
+ Adding the unsafe ` PinCoerceUnsized ` trait ensures that the user cannot coerce
608
+ ` Pin<SmartPtr<MyUnpinFuture>> ` to ` Pin<SmartPtr<dyn Future>> ` without using
609
+ unsafe to promise that the concrete type returned when calling ` deref ` on the
610
+ resulting ` Pin<SmartPtr<dyn Future>> ` is ` MyUnpinFuture ` .
611
+
612
+ This RFC does not propose to stabilize ` PinCoerceUnsized ` because of naming
613
+ issues. If we do not know whether ` CoerceUnsized ` will still use that name when
614
+ we stabilize it, then we can't stabilize a trait called ` PinCoerceUnsized ` .
615
+ Furthermore, the Linux kernel (which forms the motivation for this RFC) does not
616
+ currently need it to be stabilized.
617
+
618
+ There are some alternatives to ` PinCoerceUnsized ` . The primary contender for an
619
+ alternative solution is ` DerefPure ` . However, that solution involves a minor
620
+ breaking change, and we can always decide to switch to ` DerefPure ` later even if
621
+ we adopt ` PinCoerceUnsized ` now.
622
+
623
+ ### ` StableDeref `
624
+
625
+ A previous version of this RFC proposed to instead add a trait called
626
+ ` StableDeref ` that pretty much had the same requirements as ` PinCoerceUnsized ` ,
627
+ except that it also required the address returned by ` deref ` to be stable.
628
+
629
+ The motivation behind adding a ` StableDeref ` trait instead of ` PinCoerceUnsized `
630
+ is that ` StableDeref ` would also be useful for other things, and that both
631
+ traits essentially just say that the ` Deref ` implementation doesn't do anything
632
+ unreasonable. The requirement that the address is stable is not strictly
633
+ required to keep the API sound, but semantically it is incoherent to have a
634
+ pinned pointer whose address can change, so it is not overly burdensome to
635
+ require it.
636
+
637
+ However, this suggestion was abandoned due to an inconsistency with the
638
+ ` StableDeref ` trait defined by the ecosystem. That trait requires that raw
639
+ pointers to the contents of the pointer stay valid even if the smart pointer is
640
+ moved, but this is not satisfied by ` Box ` or ` &mut T ` because moving these
641
+ pointers asserts that they are unique. This is a problem because whichever trait
642
+ we use for pinned unsizing coercions, it * must* be implemented by ` Box ` and
643
+ ` &mut T ` .
644
+
645
+ ### ` DerefPure `
646
+
647
+ In a similar manner to the ` StableDeref ` option, we can use the existing
648
+ ` DerefPure ` trait. This option is a reasonable way forward, but this RFC does
649
+ not propose it because it would be a breaking change. (Note that ` StableDeref `
650
+ is also a breaking change for the same reason.)
651
+
652
+ Basically, the problem is that ` Deref ` is a supertrait of ` DerefPure ` , but there
653
+ are a few types that can be coerced when pinned that do not implement ` Deref ` .
654
+ For example, this code compiles today:
655
+ ``` rust
656
+ trait MyTrait {}
657
+ impl MyTrait for String {}
658
+
659
+ fn pin_cell_map (p : Pin <Cell <Box <String >>>) -> Pin <Cell <Box <dyn MyTrait >>> {
660
+ p
661
+ }
662
+ ```
663
+ The ` Cell ` type does not implement ` Deref ` , but the above code still compiles.
664
+ Note that since all methods on ` Pin ` _ do_ require ` Deref ` , such pinned pointers
665
+ are useless and impossible to construct. But it is a breaking change
666
+ nonetheless.
667
+
668
+ If this breakage is considered acceptable, then using ` DerefPure ` instead of a
669
+ new ` PinCoerceUnsized ` would be a reasonable way forward.
670
+
671
+ ### Make the derive macro unsafe
672
+
673
+ We could just make the macro unsafe in a similar vein to [ the unsafe attributes
674
+ RFC] [ unsafe-attribute ] .
675
+ ``` rust
676
+ // SAFETY: The Deref impl is not malicious.
677
+ #[unsafe (derive(SmartPointer ))]
678
+ pub struct Rc <T : ? Sized > {
679
+ inner : NonNull <RcInner <T >>,
680
+ }
681
+ ```
682
+ This would solve the unsoundness, but this RFC does not propose it because it
683
+ raises forwards compatibility hazards. We might start out with an unsafe derive
684
+ macro, and then in the future we might decide to instead use the
685
+ ` PinCoerceUnsized ` solution. Then, ` #[unsafe(derive(SmartPointer))] ` would have
686
+ to generate an implementation of ` PinCoerceUnsized ` trait too, because otherwise
687
+ ` #[unsafe(derive(SmartPointer))] Pin<Rc<MyStruct>> ` would lose the ability to be
688
+ unsize coerced, which would be a breaking change. This means that
689
+ ` #[unsafe(derive(SmartPointer))] ` and ` #[derive(SmartPointer)] ` could end up
690
+ expanding to _ different_ things.
691
+
692
+ ### Negative trait bounds
693
+
694
+ There are also various solutions that involve negative trait bounds. For
695
+ example, you might instead modify ` CoerceUnsized ` like this:
696
+ ``` rust
697
+ // Permit going from `Pin<impl Unpin>` to` Pin<impl Unpin>`
698
+ impl <P , U > CoerceUnsized <Pin <U >> for Pin <P >
699
+ where
700
+ P : CoerceUnsized <U >,
701
+ P : Deref <Target : Unpin >,
702
+ U : Deref <Target : Unpin >,
703
+ { }
704
+
705
+ // Permit going from `Pin<impl !Unpin>` to `Pin<impl !Unpin>`
706
+ impl <P , U > CoerceUnsized <Pin <U >> for Pin <P >
707
+ where
708
+ P : CoerceUnsized <U >,
709
+ P : core :: ops :: Deref <Target : ! Unpin >,
710
+ U : core :: ops :: Deref <Target : ! Unpin >,
711
+ { }
712
+ ```
713
+ This RFC does not propose it because it is a breaking change and the
714
+ ` PinCoerceUnsized ` or ` DerefPure ` solutions are simpler. This solution is
715
+ discussed in more details in [ the pre-RFC for stabilizing the underlying
716
+ traits] [ pre-rfc ] .
717
+
540
718
# Prior art
541
719
[ prior-art ] : #prior-art
542
720
@@ -574,156 +752,7 @@ feature, though it does so for a different reason than
574
752
# Unresolved questions
575
753
[ unresolved-questions ] : #unresolved-questions
576
754
577
- Unfortunately, the API proposed by this RFC is unsound. :(
578
-
579
- Basically, the issue is that if ` MyStruct ` is ` Unpin ` , then you can create a
580
- ` Pin<SmartPointer<MyStruct>> ` safely, even though you can coerce that to
581
- ` Pin<SmartPointer<dyn MyTrait>> ` (and ` dyn MyTrait ` may be ` !Unpin ` ). If
582
- ` SmartPointer ` has a malicious implementation of ` Deref ` , then this can lead to
583
- unsoundness. Since ` Deref ` is a safe trait, we cannot outlaw malicious
584
- implementations of ` Deref ` .
585
-
586
- Intuitively, the way that ` Deref ` can be malicious is by not always derefing to
587
- the same value.
588
-
589
- Some solution idea is outlined below, but the authors need your input on what
590
- to do about this problem.
591
-
592
- ## Unsafe macro
593
-
594
- The easiest solution is probably to just make the macro unsafe. We could do this
595
- in a similar vein to [ the unsafe attributes RFC] [ unsafe-attribute ] .
596
- ``` rust
597
- // SAFETY: The Deref impl always returns the same value.
598
- #[unsafe (derive(SmartPointer ))]
599
- pub struct Rc <T : ? Sized > {
600
- inner : NonNull <RcInner <T >>,
601
- }
602
- ```
603
- This way, if you coerce an ` Pin<Rc<MyStruct>> ` to ` Pin<Rc<dyn MyTrait>> ` and
604
- this is unsound due to a weird ` Deref ` impl, then it's your fault because you
605
- unsafely asserted that you have a reasonable ` Deref ` implementation.
606
-
607
- That said, there are some possible forwards compatibility hazards with this
608
- solution. We might start out with an unsafe derive macro, and then in the future
609
- we might decide to instead use the ` StableDeref ` solution mentioned below. Then,
610
- ` #[unsafe(derive(SmartPointer))] ` would have to generate an implementation of
611
- the ` StableDeref ` trait too, because otherwise ` Pin<Rc<MyStruct>> ` would lose
612
- the ability to be unsize coerced, which would be a breaking change. This means
613
- that ` #[unsafe(derive(SmartPointer))] ` and ` #[derive(SmartPointer)] ` could end
614
- up expanding to _ different_ things.
615
-
616
- [ unsafe-attribute ] : https://github.com/rust-lang/rfcs/pull/3325
617
-
618
- ## StableDeref
619
-
620
- We are quite limited in how we can work around this issue due to backwards
621
- compatibility concerns with ` Pin ` . We cannot prevent you from using ` Pin::new `
622
- with structs that have malicious ` Deref ` implementations. However, one possible
623
- place we can intervene is the coercion from ` Pin<SmartPointer<MyStruct>> ` to
624
- ` Pin<SmartPointer<dyn MyTrait>> ` . If you need to use unsafe before those
625
- coercions are possible, then the problem is solved. For example, we might
626
- introduce a ` StableDeref ` trait:
627
- ``` rs
628
- /// # Safety
629
- ///
630
- /// Any two calls to `deref` must return the same value at the same address unless
631
- /// `self` has been modified in the meantime. Moves and unsizing coercions of `self`
632
- /// are not considered modifications.
633
- ///
634
- /// Here, "same value" means that if `deref` returns a trait object, then the actual
635
- /// type behind that trait object must not change. Additionally, when you unsize
636
- /// coerce from `Self` to `Unsized`, then if you call `deref` on `Unsized` and get a
637
- /// trait object, then the underlying type of that trait object must be `<Self as
638
- /// Deref>::Target`.
639
- ///
640
- /// Analogous requirements apply to other unsized types. E.g., if `deref` returns
641
- /// `[T]`, then the length must not change. (The underlying type must not change
642
- /// from `[T; N]` to `[T; M]`.)
643
- ///
644
- /// If this type implements `DerefMut`, then the same restrictions apply to calls
645
- /// to `deref_mut`.
646
- unsafe trait StableDeref : Deref { }
647
- ```
648
- Then we make it so that you can only coerce pinned pointers when they implement
649
- ` StableDeref ` . We can do that by modifying its trait implementations to this:
650
- ``` rs
651
- impl <T , U > CoerceUnsized <Pin <U >> for Pin <T >
652
- where
653
- T : CoerceUnsized <U >,
654
- T : StableDeref ,
655
- U : StableDeref ,
656
- {}
657
-
658
- impl <T , U > DispatchFromDyn <Pin <U >> for Pin <T >
659
- where
660
- T : CoerceUnsized <U >,
661
- T : StableDeref ,
662
- U : StableDeref ,
663
- {}
664
- ```
665
- This way, the user must implement the unsafe trait before they can coerce
666
- pinned versions of the pointer. Since the trait is unsafe, it is not our fault
667
- if that leads to unsoundness.
668
-
669
- This should not be a breaking change as long as we implement ` StableDeref ` for
670
- all standard library types that can be coerced when wrapped in ` Pin ` .
671
-
672
- The proposed trait is called ` StableDeref ` because the way that ` Deref `
673
- implementations can be malicious is essentially by having
674
- ` SmartPointer<MyStruct> ` and ` SmartPointer<dyn MyTrait> ` deref to two different
675
- values. There may be some opportunity to reuse the ` DerefPure ` trait from [ the
676
- deref patterns feature] [ deref-patterns ] , and there is also some prior art with
677
- the [ ` stable_deref_trait ` ] ( https://docs.rs/stable_deref_trait ) crate,
678
-
679
- [ deref-patterns ] : https://github.com/rust-lang/rust/issues/87121/
680
-
681
- ## Don't allow Pin coercions with custom smart pointers
682
-
683
- This solution is essentially the ` StableDeref ` solution except that we don't
684
- stabilize ` StableDeref ` . This way, there's no stable way to use
685
- ` #[derive(SmartPointer)] ` types with ` Pin ` coercions.
686
-
687
- This solutions isn't a problem for the Linux Kernel right now because our custom
688
- ` Arc ` happens to be implicitly pinned for convenience reasons, but if it wasn't,
689
- then we would need coercions of ` Pin ` -wrapped values.
690
-
691
- ## Negative trait bounds?
692
-
693
- There are also various solutions that involve negative trait bounds. For
694
- example, you might instead modify ` CoerceUnsized ` like this:
695
- ``` rust
696
- // Permit going from `Pin<impl Unpin>` to` Pin<impl Unpin>`
697
- impl <P , U > CoerceUnsized <Pin <U >> for Pin <P >
698
- where
699
- P : CoerceUnsized <U >,
700
- P : Deref <Target : Unpin >,
701
- U : Deref <Target : Unpin >,
702
- { }
703
-
704
- // Permit going from `Pin<impl !Unpin>` to `Pin<impl !Unpin>`
705
- impl <P , U > CoerceUnsized <Pin <U >> for Pin <P >
706
- where
707
- P : CoerceUnsized <U >,
708
- P : core :: ops :: Deref <Target : ! Unpin >,
709
- U : core :: ops :: Deref <Target : ! Unpin >,
710
- { }
711
- ```
712
- This way, you can only coerce pinned pointers when this doesn't change whether
713
- the target type is ` Unpin ` .
714
-
715
- It would solve the unsoundness, but it does have the disadvantage of having no
716
- path to making these pinned coercions possible for smart pointers that don't
717
- have malicious ` Deref ` implementations. Another downside of this solution is
718
- that it's a breaking change, because it disallows these pinned coercions even
719
- with the standard library pointers, which allows them today.
720
-
721
- There are also other variations on the negative trait bounds, which become
722
- different implementations of the "Don't allow Pin coercions with custom smart
723
- pointers" solution.
724
-
725
- This solution is discussed in more details in [ the pre-RFC for stabilizing the
726
- underlying traits] [ pre-rfc ] .
755
+ No unresolved questions.
727
756
728
757
# Future possibilities
729
758
[ future-possibilities ] : #future-possibilities
0 commit comments