From e12acb9251eb9d5a967aeafbe7ff3b3e97ae401f Mon Sep 17 00:00:00 2001 From: Frank King Date: Mon, 24 Nov 2025 15:18:59 +0800 Subject: [PATCH] Forbid manual `Unpin` impls for structurally pinned types --- compiler/rustc_hir_analysis/messages.ftl | 4 + .../src/coherence/builtin.rs | 36 +++++++++ compiler/rustc_hir_analysis/src/errors.rs | 11 +++ tests/ui/pin-ergonomics/impl-unpin.adt.stderr | 14 ++++ .../ui/pin-ergonomics/impl-unpin.assoc.stderr | 15 ++++ tests/ui/pin-ergonomics/impl-unpin.rs | 74 +++++++++++++++++++ .../ui/pin-ergonomics/impl-unpin.tait.stderr | 27 +++++++ .../pin-ergonomics/impl-unpin.ty_alias.stderr | 14 ++++ 8 files changed, 195 insertions(+) create mode 100644 tests/ui/pin-ergonomics/impl-unpin.adt.stderr create mode 100644 tests/ui/pin-ergonomics/impl-unpin.assoc.stderr create mode 100644 tests/ui/pin-ergonomics/impl-unpin.rs create mode 100644 tests/ui/pin-ergonomics/impl-unpin.tait.stderr create mode 100644 tests/ui/pin-ergonomics/impl-unpin.ty_alias.stderr diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index d9f8eba65c4a2..33b8d3c829655 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -224,6 +224,10 @@ hir_analysis_impl_not_marked_default = `{$ident}` specializes an item from a par hir_analysis_impl_not_marked_default_err = `{$ident}` specializes an item from a parent `impl`, but that item is not marked `default` .note = parent implementation is in crate `{$cname}` +hir_analysis_impl_unpin_for_pin_projected_type = explicit impls for the `Unpin` trait are not permitted for structurally pinned types + .label = impl of `Unpin` not allowed + .help = `{$adt_name}` is structurally pinned because it is marked as `#[pin_v2]` + hir_analysis_inherent_dyn = cannot define inherent `impl` for a dyn auto trait .label = impl requires at least one non-auto trait .note = define and implement a new trait or type instead diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 61562cc1e4f30..f81913a9c86f3 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -38,6 +38,7 @@ pub(super) fn check_trait<'tcx>( checker.check(lang_items.drop_trait(), visit_implementation_of_drop)?; checker.check(lang_items.async_drop_trait(), visit_implementation_of_drop)?; checker.check(lang_items.copy_trait(), visit_implementation_of_copy)?; + checker.check(lang_items.unpin_trait(), visit_implementation_of_unpin)?; checker.check(lang_items.const_param_ty_trait(), |checker| { visit_implementation_of_const_param_ty(checker) })?; @@ -134,6 +135,41 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran } } +fn visit_implementation_of_unpin(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { + let tcx = checker.tcx; + let impl_header = checker.impl_header; + let impl_did = checker.impl_def_id; + debug!("visit_implementation_of_unpin: impl_did={:?}", impl_did); + + let self_type = impl_header.trait_ref.instantiate_identity().self_ty(); + debug!("visit_implementation_of_unpin: self_type={:?}", self_type); + + let span = tcx.def_span(impl_did); + + if tcx.features().pin_ergonomics() { + match self_type.kind() { + // Soundness concerns: a type `T` annotated with `#[pin_v2]` is allowed to project + // `Pin<&mut T>` to its field `Pin<&mut U>` safely (even if `U: !Unpin`). + // If `T` is allowed to impl `Unpin` manually (note that `Unpin` is a safe trait, + // which cannot carry safety properties), then `&mut U` could be obtained from + // `&mut T` that dereferenced by `Pin<&mut T>`, which breaks the safety contract of + // `Pin<&mut U>` for `U: !Unpin`. + ty::Adt(adt, _) if adt.is_pin_project() => { + return Err(tcx.dcx().emit_err(crate::errors::ImplUnpinForPinProjectedType { + span, + adt_span: tcx.def_span(adt.did()), + adt_name: tcx.item_name(adt.did()), + })); + } + ty::Adt(_, _) => {} + _ => { + return Err(tcx.dcx().span_delayed_bug(span, "impl of `Unpin` for a non-adt type")); + } + }; + } + Ok(()) +} + fn visit_implementation_of_const_param_ty(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { let tcx = checker.tcx; let header = checker.impl_header; diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 2a77d0b997e2c..f0c9023c522cf 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1690,3 +1690,14 @@ pub(crate) struct EiiWithGenerics { pub eii_name: Symbol, pub impl_name: Symbol, } + +#[derive(Diagnostic)] +#[diag(hir_analysis_impl_unpin_for_pin_projected_type)] +pub(crate) struct ImplUnpinForPinProjectedType { + #[primary_span] + #[label] + pub span: Span, + #[help] + pub adt_span: Span, + pub adt_name: Symbol, +} diff --git a/tests/ui/pin-ergonomics/impl-unpin.adt.stderr b/tests/ui/pin-ergonomics/impl-unpin.adt.stderr new file mode 100644 index 0000000000000..56fac42d4e32e --- /dev/null +++ b/tests/ui/pin-ergonomics/impl-unpin.adt.stderr @@ -0,0 +1,14 @@ +error: explicit impls for the `Unpin` trait are not permitted for structurally pinned types + --> $DIR/impl-unpin.rs:14:5 + | +LL | impl Unpin for Foo {} + | ^^^^^^^^^^^^^^^^^^ impl of `Unpin` not allowed + | +help: `Foo` is structurally pinned because it is marked as `#[pin_v2]` + --> $DIR/impl-unpin.rs:7:1 + | +LL | struct Foo; + | ^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/pin-ergonomics/impl-unpin.assoc.stderr b/tests/ui/pin-ergonomics/impl-unpin.assoc.stderr new file mode 100644 index 0000000000000..7f0ee1ddd898d --- /dev/null +++ b/tests/ui/pin-ergonomics/impl-unpin.assoc.stderr @@ -0,0 +1,15 @@ +error[E0321]: cross-crate traits with a default impl, like `Unpin`, can only be implemented for a struct/enum type, not `::Assoc` + --> $DIR/impl-unpin.rs:68:5 + | +LL | impl Unpin for ::Assoc {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type + +error[E0321]: cross-crate traits with a default impl, like `Unpin`, can only be implemented for a struct/enum type, not `::Assoc` + --> $DIR/impl-unpin.rs:70:5 + | +LL | impl Unpin for ::Assoc {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0321`. diff --git a/tests/ui/pin-ergonomics/impl-unpin.rs b/tests/ui/pin-ergonomics/impl-unpin.rs new file mode 100644 index 0000000000000..ded8b4774dd99 --- /dev/null +++ b/tests/ui/pin-ergonomics/impl-unpin.rs @@ -0,0 +1,74 @@ +//@ revisions: adt tait ty_alias assoc +#![feature(pin_ergonomics)] +#![cfg_attr(tait, feature(type_alias_impl_trait))] +#![allow(incomplete_features)] + +#[pin_v2] +struct Foo; +struct Bar; + +#[cfg(adt)] +mod adt { + use super::*; + + impl Unpin for Foo {} + //[adt]~^ ERROR explicit impls for the `Unpin` trait are not permitted for structurally pinned types + impl Unpin for Bar {} // ok +} + +#[cfg(ty_alias)] +mod ty_alias { + use super::*; + + type Identity = T; + + impl Unpin for Identity {} + //[ty_alias]~^ ERROR explicit impls for the `Unpin` trait are not permitted for structurally pinned types + impl Unpin for Identity {} // ok +} + +#[cfg(tait)] +mod tait { + use super::*; + + trait Identity {} + + impl Identity for T {} + + type FooAlias = impl Identity; + type BarAlias = impl Identity; + + #[define_opaque(FooAlias)] + fn foo_alias() -> FooAlias { + Foo + } + #[define_opaque(BarAlias)] + fn bar_alias() -> BarAlias { + Bar + } + + impl Unpin for FooAlias {} + //[tait]~^ ERROR only traits defined in the current crate can be implemented for arbitrary types + impl Unpin for BarAlias {} + //[tait]~^ ERROR only traits defined in the current crate can be implemented for arbitrary types +} + +#[cfg(assoc)] +mod assoc { + use super::*; + + trait Identity { + type Assoc; + } + + impl Identity for T { + type Assoc = T; + } + + impl Unpin for ::Assoc {} + //[assoc]~^ ERROR cross-crate traits with a default impl, like `Unpin`, can only be implemented for a struct/enum type, not `::Assoc` + impl Unpin for ::Assoc {} + //[assoc]~^ ERROR cross-crate traits with a default impl, like `Unpin`, can only be implemented for a struct/enum type, not `::Assoc` +} + +fn main() {} diff --git a/tests/ui/pin-ergonomics/impl-unpin.tait.stderr b/tests/ui/pin-ergonomics/impl-unpin.tait.stderr new file mode 100644 index 0000000000000..5d9392745df3d --- /dev/null +++ b/tests/ui/pin-ergonomics/impl-unpin.tait.stderr @@ -0,0 +1,27 @@ +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/impl-unpin.rs:50:5 + | +LL | impl Unpin for FooAlias {} + | ^^^^^^^^^^^^^^^-------- + | | + | type alias impl trait is treated as if it were foreign, because its hidden type could be from a foreign crate + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/impl-unpin.rs:52:5 + | +LL | impl Unpin for BarAlias {} + | ^^^^^^^^^^^^^^^-------- + | | + | type alias impl trait is treated as if it were foreign, because its hidden type could be from a foreign crate + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0117`. diff --git a/tests/ui/pin-ergonomics/impl-unpin.ty_alias.stderr b/tests/ui/pin-ergonomics/impl-unpin.ty_alias.stderr new file mode 100644 index 0000000000000..82c1a7d29ddf4 --- /dev/null +++ b/tests/ui/pin-ergonomics/impl-unpin.ty_alias.stderr @@ -0,0 +1,14 @@ +error: explicit impls for the `Unpin` trait are not permitted for structurally pinned types + --> $DIR/impl-unpin.rs:25:5 + | +LL | impl Unpin for Identity {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `Unpin` not allowed + | +help: `Foo` is structurally pinned because it is marked as `#[pin_v2]` + --> $DIR/impl-unpin.rs:7:1 + | +LL | struct Foo; + | ^^^^^^^^^^ + +error: aborting due to 1 previous error +