Skip to content

Commit c01416d

Browse files
bors[bot]taiki-e
andauthored
Merge taiki-e#91
91: Make PinnedDrop safe trait r=taiki-e a=taiki-e taiki-e#86 (comment) Co-authored-by: Taiki Endo <te316e89@gmail.com>
2 parents 5b84c34 + 4cd2fce commit c01416d

File tree

8 files changed

+60
-50
lines changed

8 files changed

+60
-50
lines changed

examples/pinned_drop-expanded.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,18 +72,27 @@ impl<'a, T> ::core::ops::Drop for Foo<'a, T> {
7272
// Safety - we're in 'drop', so we know that 'self' will
7373
// never move again.
7474
let pinned_self = unsafe { ::core::pin::Pin::new_unchecked(self) };
75-
// We call `pinned_drop` only once. Since `UnsafePinnedDrop::drop`
75+
// We call `pinned_drop` only once. Since `PinnedDrop::drop`
7676
// is an unsafe function and a private API, it is never called again in safe
7777
// code *unless the user uses a maliciously crafted macro*.
7878
unsafe {
79-
::pin_project::__private::UnsafePinnedDrop::drop(pinned_self);
79+
::pin_project::__private::PinnedDrop::drop(pinned_self);
8080
}
8181
}
8282
}
8383

84+
// It is safe to implement PinnedDrop::drop, but it is not safe to call it.
85+
// This is because destructors can be called multiple times (double dropping
86+
// is unsound: rust-lang/rust#62360).
87+
//
88+
// Ideally, it would be desirable to be able to prohibit manual calls in the
89+
// same way as Drop::drop, but the library cannot. So, by using macros and
90+
// replacing them with private traits, we prevent users from calling
91+
// PinnedDrop::drop.
92+
//
8493
// Users can implement `Drop` safely using `#[pinned_drop]`.
8594
// **Do not call or implement this trait directly.**
86-
unsafe impl<T> ::pin_project::__private::UnsafePinnedDrop for Foo<'_, T> {
95+
impl<T> ::pin_project::__private::PinnedDrop for Foo<'_, T> {
8796
// Since calling it twice on the same object would be UB,
8897
// this method is unsafe.
8998
unsafe fn drop(mut self: ::core::pin::Pin<&mut Self>) {

pin-project-internal/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ repository = "https://github.com/taiki-e/pin-project"
88
documentation = "https://docs.rs/pin-project-internal/"
99
keywords = ["pin", "macros", "attribute"]
1010
categories = ["rust-patterns"]
11-
description = "An interal crate to support pin_project - do not use directly"
11+
description = "An internal crate to support pin_project - do not use directly"
1212

1313
[package.metadata.docs.rs]
1414
all-features = true

pin-project-internal/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! An interal crate to support pin_project - **do not use directly**
1+
//! An internal crate to support pin_project - **do not use directly**
22
33
#![recursion_limit = "256"]
44
#![doc(html_root_url = "https://docs.rs/pin-project-internal/0.4.0-alpha.11")]

pin-project-internal/src/pin_project/attribute.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ impl Context {
227227
let crate_path = &self.crate_path;
228228

229229
let call = quote_spanned! { pinned_drop =>
230-
::#crate_path::__private::UnsafePinnedDrop::drop(pinned_self)
230+
::#crate_path::__private::PinnedDrop::drop(pinned_self)
231231
};
232232

233233
quote! {
@@ -237,7 +237,7 @@ impl Context {
237237
// Safety - we're in 'drop', so we know that 'self' will
238238
// never move again.
239239
let pinned_self = unsafe { ::core::pin::Pin::new_unchecked(self) };
240-
// We call `pinned_drop` only once. Since `UnsafePinnedDrop::drop`
240+
// We call `pinned_drop` only once. Since `PinnedDrop::drop`
241241
// is an unsafe function and a private API, it is never called again in safe
242242
// code *unless the user uses a maliciously crafted macro*.
243243
unsafe {

pin-project-internal/src/pinned_drop.rs

Lines changed: 28 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,16 @@ pub(crate) fn attribute(mut input: ItemImpl) -> TokenStream {
1111
let (impl_generics, _, where_clause) = input.generics.split_for_impl();
1212

1313
let mut tokens = e.to_compile_error();
14-
// Generate a dummy `UnsafePinnedDrop` implementation.
14+
// Generate a dummy `PinnedDrop` implementation.
1515
// In many cases, `#[pinned_drop] impl` is declared after `#[pin_project]`.
1616
// Therefore, if `pinned_drop` compile fails, you will also get an error
17-
// about `UnsafePinnedDrop` not being implemented.
17+
// about `PinnedDrop` not being implemented.
1818
// This can be prevented to some extent by generating a dummy
19-
// `UnsafePinnedDrop` implementation.
19+
// `PinnedDrop` implementation.
2020
// We already know that we will get a compile error, so this won't
2121
// accidentally compile successfully.
2222
tokens.extend(quote! {
23-
unsafe impl #impl_generics ::#crate_path::__private::UnsafePinnedDrop
24-
for #self_ty #where_clause
25-
{
23+
impl #impl_generics ::#crate_path::__private::PinnedDrop for #self_ty #where_clause {
2624
unsafe fn drop(self: ::core::pin::Pin<&mut Self>) {}
2725
}
2826
});
@@ -101,7 +99,7 @@ fn parse(item: &mut ItemImpl) -> Result<()> {
10199
let crate_path = crate_path();
102100

103101
*path = syn::parse2(quote_spanned! { path.span() =>
104-
::#crate_path::__private::UnsafePinnedDrop
102+
::#crate_path::__private::PinnedDrop
105103
})
106104
.unwrap();
107105
} else {
@@ -120,35 +118,33 @@ fn parse(item: &mut ItemImpl) -> Result<()> {
120118
if item.unsafety.is_some() {
121119
return Err(error!(item.unsafety, "implementing the trait `PinnedDrop` is not unsafe"));
122120
}
123-
item.unsafety = Some(token::Unsafe::default());
124-
125121
if item.items.is_empty() {
126122
return Err(error!(item, "not all trait items implemented, missing: `drop`"));
127-
} else {
128-
for (i, item) in item.items.iter().enumerate() {
129-
match item {
130-
ImplItem::Const(item) => {
131-
return Err(error!(
132-
item,
133-
"const `{}` is not a member of trait `PinnedDrop`", item.ident
134-
));
135-
}
136-
ImplItem::Type(item) => {
137-
return Err(error!(
138-
item,
139-
"type `{}` is not a member of trait `PinnedDrop`", item.ident
140-
));
141-
}
142-
ImplItem::Method(method) => {
143-
parse_method(method)?;
144-
if i != 0 {
145-
return Err(error!(method, "duplicate definitions with name `drop`"));
146-
}
147-
}
148-
_ => {
149-
let _: Nothing = syn::parse2(item.to_token_stream())?;
123+
}
124+
125+
for (i, item) in item.items.iter().enumerate() {
126+
match item {
127+
ImplItem::Const(item) => {
128+
return Err(error!(
129+
item,
130+
"const `{}` is not a member of trait `PinnedDrop`", item.ident
131+
));
132+
}
133+
ImplItem::Type(item) => {
134+
return Err(error!(
135+
item,
136+
"type `{}` is not a member of trait `PinnedDrop`", item.ident
137+
));
138+
}
139+
ImplItem::Method(method) => {
140+
parse_method(method)?;
141+
if i != 0 {
142+
return Err(error!(method, "duplicate definitions with name `drop`"));
150143
}
151144
}
145+
_ => {
146+
let _: Nothing = syn::parse2(item.to_token_stream())?;
147+
}
152148
}
153149
}
154150

src/lib.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,17 +127,22 @@ pub mod __private {
127127
#[doc(hidden)]
128128
pub use pin_project_internal::__PinProjectAutoImplUnpin;
129129

130-
// This is an internal helper trait used by `pin-project-internal`.
131-
// This allows us to force an error if the user tries to provide
132-
// a regular `Drop` impl when they specify the `PinnedDrop` argument.
130+
// It is safe to implement PinnedDrop::drop, but it is not safe to call it.
131+
// This is because destructors can be called multiple times (double dropping
132+
// is unsound: rust-lang/rust#62360).
133+
//
134+
// Ideally, it would be desirable to be able to prohibit manual calls in the
135+
// same way as Drop::drop, but the library cannot. So, by using macros and
136+
// replacing them with private traits, we prevent users from calling
137+
// PinnedDrop::drop.
133138
//
134139
// Users can implement `Drop` safely using `#[pinned_drop]`.
135140
// **Do not call or implement this trait directly.**
136-
#[allow(unsafe_code)]
137141
#[doc(hidden)]
138-
pub unsafe trait UnsafePinnedDrop {
142+
pub trait PinnedDrop {
139143
// Since calling it twice on the same object would be UB,
140144
// this method is unsafe.
145+
#[allow(unsafe_code)]
141146
#[doc(hidden)]
142147
unsafe fn drop(self: Pin<&mut Self>);
143148
}

tests/ui/pinned_drop/forget-pinned-drop.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
error[E0277]: the trait bound `Foo: pin_project::__private::UnsafePinnedDrop` is not satisfied
1+
error[E0277]: the trait bound `Foo: pin_project::__private::PinnedDrop` is not satisfied
22
--> $DIR/forget-pinned-drop.rs:6:15
33
|
44
6 | #[pin_project(PinnedDrop)] //~ ERROR E0277
5-
| ^^^^^^^^^^ the trait `pin_project::__private::UnsafePinnedDrop` is not implemented for `Foo`
5+
| ^^^^^^^^^^ the trait `pin_project::__private::PinnedDrop` is not implemented for `Foo`
66
|
7-
= note: required by `pin_project::__private::UnsafePinnedDrop::drop`
7+
= note: required by `pin_project::__private::PinnedDrop::drop`
88

99
error: aborting due to previous error
1010

tests/ui/pinned_drop/invalid.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,13 @@ error: duplicate definitions with name `drop`
7070
143 | fn drop(self: Pin<&mut Self>) {} //~ ERROR duplicate definitions with name `drop`
7171
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
7272

73-
error[E0277]: the trait bound `A: pin_project::__private::UnsafePinnedDrop` is not satisfied
73+
error[E0277]: the trait bound `A: pin_project::__private::PinnedDrop` is not satisfied
7474
--> $DIR/invalid.rs:6:15
7575
|
7676
6 | #[pin_project(PinnedDrop)]
77-
| ^^^^^^^^^^ the trait `pin_project::__private::UnsafePinnedDrop` is not implemented for `A`
77+
| ^^^^^^^^^^ the trait `pin_project::__private::PinnedDrop` is not implemented for `A`
7878
|
79-
= note: required by `pin_project::__private::UnsafePinnedDrop::drop`
79+
= note: required by `pin_project::__private::PinnedDrop::drop`
8080

8181
error: aborting due to 13 previous errors
8282

0 commit comments

Comments
 (0)