Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ symbols! {
PathBuf,
Pending,
PinCoerceUnsized,
PinDerefMutHelper,
Pointer,
Poll,
ProcMacro,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3476,6 +3476,24 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
// can do about it. As far as they are concerned, `?` is compiler magic.
return;
}
if tcx.is_diagnostic_item(sym::PinDerefMutHelper, parent_def_id) {
let parent_predicate =
self.resolve_vars_if_possible(data.derived.parent_trait_pred);

// Skip PinDerefMutHelper in suggestions, but still show downstream suggestions.
ensure_sufficient_stack(|| {
self.note_obligation_cause_code(
body_id,
err,
parent_predicate,
param_env,
&data.derived.parent_code,
obligated_types,
seen_requirements,
)
});
return;
}
let self_ty_str =
tcx.short_string(parent_trait_pred.skip_binder().self_ty(), err.long_ty_path());
let trait_name = tcx.short_string(
Expand Down
82 changes: 81 additions & 1 deletion library/core/src/pin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1689,9 +1689,89 @@ impl<Ptr: [const] Deref> const 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
/// 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
/// `#[fundamental]`, so stdlib promises to never implement traits for `Pin` that it does not
/// implement today.
///
/// However, this is problematic. Downstream crates could implement `DerefMut` for
/// `Pin<&LocalType>`, and they could do so maliciously. To prevent this, the implementation for
/// `Pin` delegates to this helper module. Since `helper::Pin` is not `#[fundamental]`, the
/// orphan rules assume that stdlib might implement `helper::DerefMut` for `helper::Pin<&_>` in
/// the future. Because of this, downstream crates can no longer provide an implementation of
/// `DerefMut` for `Pin<&_>`, as it might overlap with a trait impl that, according to the
/// orphan rules, the stdlib could introduce without a breaking change in a future release.
///
/// See <https://github.com/rust-lang/rust/issues/85099> for the issue this fixes.
#[repr(transparent)]
#[unstable(feature = "pin_derefmut_internals", issue = "none")]
#[allow(missing_debug_implementations)]
pub struct PinHelper<Ptr> {
pointer: Ptr,
}

#[unstable(feature = "pin_derefmut_internals", issue = "none")]
#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
#[rustc_diagnostic_item = "PinDerefMutHelper"]
pub const trait PinDerefMutHelper {
type Target: ?Sized;
fn deref_mut(&mut self) -> &mut Self::Target;
}

#[unstable(feature = "pin_derefmut_internals", issue = "none")]
#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
impl<Ptr: [const] super::DerefMut> const PinDerefMutHelper for PinHelper<Ptr>
where
Ptr::Target: crate::marker::Unpin,
{
type Target = Ptr::Target;

#[inline(always)]
fn deref_mut(&mut self) -> &mut Ptr::Target {
&mut self.pointer
}
}
}

#[stable(feature = "pin", since = "1.33.0")]
#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
impl<Ptr: [const] DerefMut<Target: Unpin>> const DerefMut for Pin<Ptr> {
#[cfg(not(doc))]
impl<Ptr> const DerefMut for Pin<Ptr>
where
Ptr: [const] Deref,
helper::PinHelper<Ptr>: [const] helper::PinDerefMutHelper<Target = Self::Target>,
{
#[inline]
fn deref_mut(&mut self) -> &mut Ptr::Target {
// SAFETY: Pin and PinHelper have the same layout, so this is equivalent to
// `&mut self.pointer` which is safe because `Target: Unpin`.
helper::PinDerefMutHelper::deref_mut(unsafe {
&mut *(self as *mut Pin<Ptr> as *mut helper::PinHelper<Ptr>)
})
}
}

/// The `Target` type is restricted to `Unpin` types as it's not safe to obtain a mutable reference
/// to a pinned value.
///
/// For soundness reasons, implementations of `DerefMut` for `Pin<T>` are rejected even when `T` is
/// a local type not covered by this impl block. (Since `Pin` is [fundamental], such implementations
/// would normally be possible.)
///
/// [fundamental]: ../../reference/items/implementations.html#r-items.impl.trait.fundamental
#[stable(feature = "pin", since = "1.33.0")]
#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
#[cfg(doc)]
impl<Ptr> const DerefMut for Pin<Ptr>
where
Ptr: [const] DerefMut,
<Ptr as Deref>::Target: Unpin,
{
fn deref_mut(&mut self) -> &mut Ptr::Target {
Pin::get_mut(Pin::as_mut(self))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,27 +63,25 @@
+ let mut _44: &mut std::future::Ready<()>;
+ let mut _45: &mut std::pin::Pin<&mut std::future::Ready<()>>;
+ scope 14 (inlined <Pin<&mut std::future::Ready<()>> as DerefMut>::deref_mut) {
+ scope 15 (inlined Pin::<&mut std::future::Ready<()>>::as_mut) {
+ let mut _46: &mut &mut std::future::Ready<()>;
+ scope 16 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) {
+ let mut _46: *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>>;
+ let mut _47: *mut std::pin::Pin<&mut std::future::Ready<()>>;
+ scope 15 (inlined <pin::helper::PinHelper<&mut std::future::Ready<()>> as pin::helper::PinDerefMutHelper>::deref_mut) {
+ let mut _48: &mut &mut std::future::Ready<()>;
+ scope 16 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) {
+ }
+ scope 18 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) {
+ }
+ }
+ scope 17 (inlined Pin::<&mut std::future::Ready<()>>::get_mut) {
+ }
+ }
+ scope 19 (inlined Option::<()>::take) {
+ let mut _47: std::option::Option<()>;
+ scope 20 (inlined std::mem::replace::<Option<()>>) {
+ scope 21 {
+ scope 17 (inlined Option::<()>::take) {
+ let mut _49: std::option::Option<()>;
+ scope 18 (inlined std::mem::replace::<Option<()>>) {
+ scope 19 {
+ }
+ }
+ }
+ scope 22 (inlined #[track_caller] Option::<()>::expect) {
+ let mut _48: isize;
+ let mut _49: !;
+ scope 23 {
+ scope 20 (inlined #[track_caller] Option::<()>::expect) {
+ let mut _50: isize;
+ let mut _51: !;
+ scope 21 {
+ }
+ }
+ }
Expand Down Expand Up @@ -217,18 +215,23 @@
+ _22 = &mut (*_23);
+ StorageDead(_24);
+ StorageLive(_44);
+ StorageLive(_49);
+ StorageLive(_46);
+ StorageLive(_51);
+ StorageLive(_41);
+ StorageLive(_42);
+ _44 = copy (_19.0: &mut std::future::Ready<()>);
+ StorageLive(_47);
+ _47 = Option::<()>::None;
+ _42 = copy ((*_44).0: std::option::Option<()>);
+ ((*_44).0: std::option::Option<()>) = copy _47;
+ _47 = &raw mut _19;
+ _46 = copy _47 as *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>> (PtrToPtr);
+ StorageDead(_47);
+ StorageLive(_48);
+ _48 = discriminant(_42);
+ switchInt(move _48) -> [0: bb11, 1: bb12, otherwise: bb5];
+ _44 = copy ((*_46).0: &mut std::future::Ready<()>);
+ StorageLive(_49);
+ _49 = Option::<()>::None;
+ _42 = copy ((*_44).0: std::option::Option<()>);
+ ((*_44).0: std::option::Option<()>) = copy _49;
+ StorageDead(_49);
+ StorageLive(_50);
+ _50 = discriminant(_42);
+ switchInt(move _50) -> [0: bb11, 1: bb12, otherwise: bb5];
}
+
+ bb5: {
Expand Down Expand Up @@ -291,16 +294,17 @@
+ }
+
+ bb11: {
+ _49 = option::expect_failed(const "`Ready` polled after completion") -> unwind unreachable;
+ _51 = option::expect_failed(const "`Ready` polled after completion") -> unwind unreachable;
+ }
+
+ bb12: {
+ _41 = move ((_42 as Some).0: ());
+ StorageDead(_48);
+ StorageDead(_50);
+ StorageDead(_42);
+ _18 = Poll::<()>::Ready(move _41);
+ StorageDead(_41);
+ StorageDead(_49);
+ StorageDead(_51);
+ StorageDead(_46);
+ StorageDead(_44);
+ StorageDead(_22);
+ StorageDead(_19);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,27 +65,25 @@
+ let mut _46: &mut std::future::Ready<()>;
+ let mut _47: &mut std::pin::Pin<&mut std::future::Ready<()>>;
+ scope 14 (inlined <Pin<&mut std::future::Ready<()>> as DerefMut>::deref_mut) {
+ scope 15 (inlined Pin::<&mut std::future::Ready<()>>::as_mut) {
+ let mut _48: &mut &mut std::future::Ready<()>;
+ scope 16 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) {
+ let mut _48: *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>>;
+ let mut _49: *mut std::pin::Pin<&mut std::future::Ready<()>>;
+ scope 15 (inlined <pin::helper::PinHelper<&mut std::future::Ready<()>> as pin::helper::PinDerefMutHelper>::deref_mut) {
+ let mut _50: &mut &mut std::future::Ready<()>;
+ scope 16 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) {
+ }
+ scope 18 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) {
+ }
+ }
+ scope 17 (inlined Pin::<&mut std::future::Ready<()>>::get_mut) {
+ }
+ }
+ scope 19 (inlined Option::<()>::take) {
+ let mut _49: std::option::Option<()>;
+ scope 20 (inlined std::mem::replace::<Option<()>>) {
+ scope 21 {
+ scope 17 (inlined Option::<()>::take) {
+ let mut _51: std::option::Option<()>;
+ scope 18 (inlined std::mem::replace::<Option<()>>) {
+ scope 19 {
+ }
+ }
+ }
+ scope 22 (inlined #[track_caller] Option::<()>::expect) {
+ let mut _50: isize;
+ let mut _51: !;
+ scope 23 {
+ scope 20 (inlined #[track_caller] Option::<()>::expect) {
+ let mut _52: isize;
+ let mut _53: !;
+ scope 21 {
+ }
+ }
+ }
Expand Down Expand Up @@ -234,18 +232,23 @@
+ _22 = &mut (*_23);
+ StorageDead(_24);
+ StorageLive(_46);
+ StorageLive(_51);
+ StorageLive(_48);
+ StorageLive(_53);
+ StorageLive(_43);
+ StorageLive(_44);
+ _46 = copy (_19.0: &mut std::future::Ready<()>);
+ StorageLive(_49);
+ _49 = Option::<()>::None;
+ _44 = copy ((*_46).0: std::option::Option<()>);
+ ((*_46).0: std::option::Option<()>) = copy _49;
+ _49 = &raw mut _19;
+ _48 = copy _49 as *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>> (PtrToPtr);
+ StorageDead(_49);
+ StorageLive(_50);
+ _50 = discriminant(_44);
+ switchInt(move _50) -> [0: bb16, 1: bb17, otherwise: bb7];
+ _46 = copy ((*_48).0: &mut std::future::Ready<()>);
+ StorageLive(_51);
+ _51 = Option::<()>::None;
+ _44 = copy ((*_46).0: std::option::Option<()>);
+ ((*_46).0: std::option::Option<()>) = copy _51;
+ StorageDead(_51);
+ StorageLive(_52);
+ _52 = discriminant(_44);
+ switchInt(move _52) -> [0: bb16, 1: bb17, otherwise: bb7];
}

- bb6 (cleanup): {
Expand Down Expand Up @@ -332,16 +335,17 @@
+ }
+
+ bb16: {
+ _51 = option::expect_failed(const "`Ready` polled after completion") -> bb11;
+ _53 = option::expect_failed(const "`Ready` polled after completion") -> bb11;
+ }
+
+ bb17: {
+ _43 = move ((_44 as Some).0: ());
+ StorageDead(_50);
+ StorageDead(_52);
+ StorageDead(_44);
+ _18 = Poll::<()>::Ready(move _43);
+ StorageDead(_43);
+ StorageDead(_51);
+ StorageDead(_53);
+ StorageDead(_48);
+ StorageDead(_46);
+ StorageDead(_22);
+ StorageDead(_19);
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/deref/pin-impl-deref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ impl MyPinType {
fn impl_deref_mut(_: impl DerefMut) {}
fn unpin_impl_ref(r_unpin: Pin<&MyUnpinType>) {
impl_deref_mut(r_unpin)
//~^ ERROR: the trait bound `Pin<&MyUnpinType>: DerefMut` is not satisfied
//~^ ERROR: the trait bound `&MyUnpinType: DerefMut` is not satisfied
}
fn unpin_impl_mut(r_unpin: Pin<&mut MyUnpinType>) {
impl_deref_mut(r_unpin)
}
fn pin_impl_ref(r_pin: Pin<&MyPinType>) {
impl_deref_mut(r_pin)
//~^ ERROR: `PhantomPinned` cannot be unpinned
//~| ERROR: the trait bound `Pin<&MyPinType>: DerefMut` is not satisfied
//~| ERROR: the trait bound `&MyPinType: DerefMut` is not satisfied
}
fn pin_impl_mut(r_pin: Pin<&mut MyPinType>) {
impl_deref_mut(r_pin)
Expand Down
18 changes: 6 additions & 12 deletions tests/ui/deref/pin-impl-deref.stderr
Original file line number Diff line number Diff line change
@@ -1,40 +1,34 @@
error[E0277]: the trait bound `Pin<&MyUnpinType>: DerefMut` is not satisfied
error[E0277]: the trait bound `&MyUnpinType: DerefMut` is not satisfied
--> $DIR/pin-impl-deref.rs:24:20
|
LL | impl_deref_mut(r_unpin)
| -------------- ^^^^^^^ the trait `DerefMut` is not implemented for `Pin<&MyUnpinType>`
| -------------- ^^^^^^^ the trait `DerefMut` is not implemented for `&MyUnpinType`
| |
| required by a bound introduced by this call
|
= note: `DerefMut` is implemented for `&mut MyUnpinType`, but not for `&MyUnpinType`
= note: required for `Pin<&MyUnpinType>` to implement `DerefMut`
note: required by a bound in `impl_deref_mut`
--> $DIR/pin-impl-deref.rs:22:27
|
LL | fn impl_deref_mut(_: impl DerefMut) {}
| ^^^^^^^^ required by this bound in `impl_deref_mut`
help: consider mutably borrowing here
|
LL | impl_deref_mut(&mut r_unpin)
| ++++

error[E0277]: the trait bound `Pin<&MyPinType>: DerefMut` is not satisfied
error[E0277]: the trait bound `&MyPinType: DerefMut` is not satisfied
--> $DIR/pin-impl-deref.rs:31:20
|
LL | impl_deref_mut(r_pin)
| -------------- ^^^^^ the trait `DerefMut` is not implemented for `Pin<&MyPinType>`
| -------------- ^^^^^ the trait `DerefMut` is not implemented for `&MyPinType`
| |
| required by a bound introduced by this call
|
= note: `DerefMut` is implemented for `&mut MyPinType`, but not for `&MyPinType`
= note: required for `Pin<&MyPinType>` to implement `DerefMut`
note: required by a bound in `impl_deref_mut`
--> $DIR/pin-impl-deref.rs:22:27
|
LL | fn impl_deref_mut(_: impl DerefMut) {}
| ^^^^^^^^ required by this bound in `impl_deref_mut`
help: consider mutably borrowing here
|
LL | impl_deref_mut(&mut r_pin)
| ++++

error[E0277]: `PhantomPinned` cannot be unpinned
--> $DIR/pin-impl-deref.rs:31:20
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/typeck/pin-unsound-issue-85099-derefmut.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//@ check-pass
//@ known-bug: #85099
//@ check-fail

// Should fail. Can coerce `Pin<T>` into `Pin<U>` where
// `T: Deref<Target: Unpin>` and `U: Deref<Target: !Unpin>`, using the
Expand Down Expand Up @@ -43,6 +42,7 @@ impl<'a, Fut: Future<Output = ()>> SomeTrait<'a, Fut> for Fut {
}

impl<'b, 'a, Fut> DerefMut for Pin<&'b dyn SomeTrait<'a, Fut>> {
//~^ ERROR: conflicting implementations of trait `DerefMut`
fn deref_mut<'c>(
self: &'c mut Pin<&'b dyn SomeTrait<'a, Fut>>,
) -> &'c mut (dyn SomeTrait<'a, Fut> + 'b) {
Expand Down
Loading
Loading