Forbid manual Unpin impls for structurally pinned types

This commit is contained in:
Frank King
2025-11-24 15:18:59 +08:00
parent 35a31ba763
commit e12acb9251
8 changed files with 195 additions and 0 deletions
+4
View File
@@ -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
@@ -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;
+11
View File
@@ -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,
}
@@ -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
@@ -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 `<Foo as Identity>::Assoc`
--> $DIR/impl-unpin.rs:68:5
|
LL | impl Unpin for <Foo as Identity>::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 `<Bar as Identity>::Assoc`
--> $DIR/impl-unpin.rs:70:5
|
LL | impl Unpin for <Bar as Identity>::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`.
+74
View File
@@ -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> = T;
impl Unpin for Identity<Foo> {}
//[ty_alias]~^ ERROR explicit impls for the `Unpin` trait are not permitted for structurally pinned types
impl Unpin for Identity<Bar> {} // ok
}
#[cfg(tait)]
mod tait {
use super::*;
trait Identity<T> {}
impl<T> Identity<T> for T {}
type FooAlias = impl Identity<Foo>;
type BarAlias = impl Identity<Bar>;
#[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<T> Identity for T {
type Assoc = T;
}
impl Unpin for <Foo as Identity>::Assoc {}
//[assoc]~^ ERROR cross-crate traits with a default impl, like `Unpin`, can only be implemented for a struct/enum type, not `<Foo as Identity>::Assoc`
impl Unpin for <Bar as Identity>::Assoc {}
//[assoc]~^ ERROR cross-crate traits with a default impl, like `Unpin`, can only be implemented for a struct/enum type, not `<Bar as Identity>::Assoc`
}
fn main() {}
@@ -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`.
@@ -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<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