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
4 changes: 4 additions & 0 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
36 changes: 36 additions & 0 deletions compiler/rustc_hir_analysis/src/coherence/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})?;
Expand Down Expand Up @@ -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;
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
14 changes: 14 additions & 0 deletions tests/ui/pin-ergonomics/impl-unpin.adt.stderr
Original file line number Diff line number Diff line change
@@ -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

15 changes: 15 additions & 0 deletions tests/ui/pin-ergonomics/impl-unpin.assoc.stderr
Original file line number Diff line number Diff line change
@@ -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 changes: 74 additions & 0 deletions tests/ui/pin-ergonomics/impl-unpin.rs
Original file line number Diff line number Diff line change
@@ -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() {}
27 changes: 27 additions & 0 deletions tests/ui/pin-ergonomics/impl-unpin.tait.stderr
Original file line number Diff line number Diff line change
@@ -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`.
14 changes: 14 additions & 0 deletions tests/ui/pin-ergonomics/impl-unpin.ty_alias.stderr
Original file line number Diff line number Diff line change
@@ -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

Loading