Skip to content

Commit

Permalink
Merge #135
Browse files Browse the repository at this point in the history
135: Forbid custom Unpin/Drop impl if trait has Pin<&mut self> receiver r=taiki-e a=taiki-e

If a user creates their own `Unpin` or `Drop` implementation, trait implementations with `Pin<&mut self>` receiver can cause unsoundness.

This was not a problem in `#[auto_enum]` attribute where enums are anonymized, but it becomes a problem when users have access to enums (i.e., when using `#[enum_derive]`).

This is essentially a problem of `derive_utils`, the backend of the derive implementation of this crate, and I was investigating ways to fix it on the `derive_utils` side, but finally decided it would be difficult and removed support for the `Pin<&mut self>` receiver from `derive_utils`. (taiki-e/derive_utils#41)

So, we ensure safety here by an `Unpin` implementation that implements `Unpin` only if all fields are `Unpin` (this also forbids custom `Unpin` implementation), and a hack that forbids custom `Drop` implementation. (Both are what `pin-project` does by default.) The `repr(packed)` check is unnecessary since `repr(packed)` is unavailable on enum.

This would make it impossible to use together with `#[pin_project]` or `pin_project!`, or to create custom `Drop` or `Unpin` implementations -- If someone needs it, it is possible to add an option like use_pin_project to support them, so please leave a comment.

Co-authored-by: Taiki Endo <te316e89@gmail.com>
  • Loading branch information
bors[bot] and taiki-e committed Dec 10, 2022
2 parents 36acf01 + 4781dfe commit 3a46654
Show file tree
Hide file tree
Showing 32 changed files with 1,115 additions and 344 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ trusted_len = []

# Note: futures, tokio, serde, and rayon are public dependencies.
[dependencies]
derive_utils = { version = "0.11" }
derive_utils = { version = "0.12" }
proc-macro2 = "1"
quote = "1"
syn = { version = "1.0.56", features = ["full", "visit-mut"] }
Expand Down
4 changes: 2 additions & 2 deletions src/derive/core/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pub(crate) mod as_ref {

pub(crate) const NAME: &[&str] = &["AsRef"];

pub(crate) fn derive(data: &Data) -> Result<TokenStream> {
pub(crate) fn derive(_cx: &Context, data: &Data) -> Result<TokenStream> {
Ok(derive_trait(data, parse_quote!(::core::convert::AsRef), None, parse_quote! {
trait AsRef<__T: ?Sized> {
#[inline]
Expand All @@ -18,7 +18,7 @@ pub(crate) mod as_mut {

pub(crate) const NAME: &[&str] = &["AsMut"];

pub(crate) fn derive(data: &Data) -> Result<TokenStream> {
pub(crate) fn derive(_cx: &Context, data: &Data) -> Result<TokenStream> {
Ok(derive_trait(data, parse_quote!(::core::convert::AsMut), None, parse_quote! {
trait AsMut<__T: ?Sized> {
#[inline]
Expand Down
4 changes: 2 additions & 2 deletions src/derive/core/fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ macro_rules! derive_fmt {

pub(crate) const NAME: &[&str] = &[$($name),*];

pub(crate) fn derive(data: &Data) -> Result<TokenStream> {
pub(crate) fn derive(_cx: &Context, data: &Data) -> Result<TokenStream> {
Ok(derive_trait(data, parse_quote!(::core::fmt::$Trait), None, parse_quote! {
trait $Trait {
#[inline]
Expand Down Expand Up @@ -40,7 +40,7 @@ pub(crate) mod write {

pub(crate) const NAME: &[&str] = &["fmt::Write"];

pub(crate) fn derive(data: &Data) -> Result<TokenStream> {
pub(crate) fn derive(_cx: &Context, data: &Data) -> Result<TokenStream> {
Ok(derive_trait(data, parse_quote!(::core::fmt::Write), None, parse_quote! {
trait Write {
#[inline]
Expand Down
36 changes: 28 additions & 8 deletions src/derive/core/future.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,36 @@
use quote::ToTokens;

use crate::derive::*;

pub(crate) const NAME: &[&str] = &["Future"];

pub(crate) fn derive(data: &Data) -> Result<TokenStream> {
Ok(derive_trait(data, parse_quote!(::core::future::Future), None, parse_quote! {
pub(crate) fn derive(cx: &Context, data: &Data) -> Result<TokenStream> {
cx.needs_pin_projection();

let ident = &data.ident;
let pin = quote!(::core::pin::Pin);
let trait_: syn::Path = parse_quote!(::core::future::Future);
let mut impl_ = EnumImpl::from_trait(data, trait_.clone(), None, parse_quote! {
trait Future {
type Output;
#[inline]
fn poll(
self: ::core::pin::Pin<&mut Self>,
cx: &mut ::core::task::Context<'_>,
) -> ::core::task::Poll<Self::Output>;
}
}))
})
.build_impl();

let poll = data
.variant_idents()
.map(|v| quote!(#ident::#v(x) => #trait_::poll(#pin::new_unchecked(x), cx)));
impl_.items.push(parse_quote! {
#[inline]
fn poll(
self: #pin<&mut Self>,
cx: &mut ::core::task::Context<'_>,
) -> ::core::task::Poll<Self::Output> {
unsafe {
match self.get_unchecked_mut() { #(#poll,)* }
}
}
});

Ok(impl_.into_token_stream())
}
12 changes: 6 additions & 6 deletions src/derive/core/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pub(crate) mod iterator {

pub(crate) const NAME: &[&str] = &["Iterator"];

pub(crate) fn derive(data: &Data) -> Result<TokenStream> {
pub(crate) fn derive(_cx: &Context, data: &Data) -> Result<TokenStream> {
// TODO: When `try_trait` stabilized, add `try_fold` and remove `fold`, `find` etc. conditionally.

// It is equally efficient if `try_fold` can be used.
Expand Down Expand Up @@ -47,7 +47,7 @@ pub(crate) mod double_ended_iterator {

pub(crate) const NAME: &[&str] = &["DoubleEndedIterator"];

pub(crate) fn derive(data: &Data) -> Result<TokenStream> {
pub(crate) fn derive(_cx: &Context, data: &Data) -> Result<TokenStream> {
// TODO: When `try_trait` stabilized, add `try_rfold` and remove `rfold` and `rfind` conditionally.

// It is equally efficient if `try_rfold` can be used.
Expand Down Expand Up @@ -82,7 +82,7 @@ pub(crate) mod exact_size_iterator {

pub(crate) const NAME: &[&str] = &["ExactSizeIterator"];

pub(crate) fn derive(data: &Data) -> Result<TokenStream> {
pub(crate) fn derive(_cx: &Context, data: &Data) -> Result<TokenStream> {
// TODO: When `exact_size_is_empty` stabilized, add `is_empty` conditionally.

Ok(derive_trait(
Expand All @@ -104,7 +104,7 @@ pub(crate) mod fused_iterator {

pub(crate) const NAME: &[&str] = &["FusedIterator"];

pub(crate) fn derive(data: &Data) -> Result<TokenStream> {
pub(crate) fn derive(_cx: &Context, data: &Data) -> Result<TokenStream> {
Ok(derive_trait(
data,
parse_quote!(::core::iter::FusedIterator),
Expand All @@ -122,7 +122,7 @@ pub(crate) mod trusted_len {

pub(crate) const NAME: &[&str] = &["TrustedLen"];

pub(crate) fn derive(data: &Data) -> Result<TokenStream> {
pub(crate) fn derive(_cx: &Context, data: &Data) -> Result<TokenStream> {
Ok(derive_trait(
data,
parse_quote!(::core::iter::TrustedLen),
Expand All @@ -139,7 +139,7 @@ pub(crate) mod extend {

pub(crate) const NAME: &[&str] = &["Extend"];

pub(crate) fn derive(data: &Data) -> Result<TokenStream> {
pub(crate) fn derive(_cx: &Context, data: &Data) -> Result<TokenStream> {
Ok(derive_trait(data, parse_quote!(::core::iter::Extend), None, parse_quote! {
trait Extend<__A> {
#[inline]
Expand Down
52 changes: 36 additions & 16 deletions src/derive/core/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub(crate) mod deref {

pub(crate) const NAME: &[&str] = &["Deref"];

pub(crate) fn derive(data: &Data) -> Result<TokenStream> {
pub(crate) fn derive(_cx: &Context, data: &Data) -> Result<TokenStream> {
Ok(derive_trait(data, parse_quote!(::core::ops::Deref), None, parse_quote! {
trait Deref {
type Target;
Expand All @@ -21,7 +21,7 @@ pub(crate) mod deref_mut {

pub(crate) const NAME: &[&str] = &["DerefMut"];

pub(crate) fn derive(data: &Data) -> Result<TokenStream> {
pub(crate) fn derive(_cx: &Context, data: &Data) -> Result<TokenStream> {
Ok(derive_trait(
data,
parse_quote!(::core::ops::DerefMut),
Expand All @@ -42,7 +42,7 @@ pub(crate) mod index {

pub(crate) const NAME: &[&str] = &["Index"];

pub(crate) fn derive(data: &Data) -> Result<TokenStream> {
pub(crate) fn derive(_cx: &Context, data: &Data) -> Result<TokenStream> {
Ok(derive_trait(data, parse_quote!(::core::ops::Index), None, parse_quote! {
trait Index<__Idx> {
type Output;
Expand All @@ -59,7 +59,7 @@ pub(crate) mod index_mut {

pub(crate) const NAME: &[&str] = &["IndexMut"];

pub(crate) fn derive(data: &Data) -> Result<TokenStream> {
pub(crate) fn derive(_cx: &Context, data: &Data) -> Result<TokenStream> {
Ok(derive_trait(
data,
parse_quote!(::core::ops::IndexMut),
Expand All @@ -80,7 +80,7 @@ pub(crate) mod range_bounds {

pub(crate) const NAME: &[&str] = &["RangeBounds"];

pub(crate) fn derive(data: &Data) -> Result<TokenStream> {
pub(crate) fn derive(_cx: &Context, data: &Data) -> Result<TokenStream> {
Ok(derive_trait(data, parse_quote!(::core::ops::RangeBounds), None, parse_quote! {
trait RangeBounds<__T: ?Sized> {
#[inline]
Expand All @@ -94,22 +94,42 @@ pub(crate) mod range_bounds {

#[cfg(feature = "generator_trait")]
pub(crate) mod generator {
use quote::ToTokens;

use crate::derive::*;

pub(crate) const NAME: &[&str] = &["Generator"];

pub(crate) fn derive(data: &Data) -> Result<TokenStream> {
Ok(derive_trait(data, parse_quote!(::core::ops::Generator), None, parse_quote! {
pub(crate) fn derive(cx: &Context, data: &Data) -> Result<TokenStream> {
cx.needs_pin_projection();

let ident = &data.ident;
let pin = quote!(::core::pin::Pin);
let trait_: syn::Path = parse_quote!(::core::ops::Generator);
let mut impl_ = EnumImpl::from_trait(data, trait_.clone(), None, parse_quote! {
trait Generator<R> {
type Yield;
type Return;
#[inline]
fn resume(
self: ::core::pin::Pin<&mut Self>,
arg: R,
) -> ::core::ops::GeneratorState<Self::Yield, Self::Return>;
}
}))
})
.build_impl();

let resume = data
.variant_idents()
.map(|v| quote!(#ident::#v(x) => #trait_::resume(#pin::new_unchecked(x), arg)));
impl_.items.push(parse_quote! {
#[inline]
fn resume(
self: #pin<&mut Self>,
arg: R,
) -> ::core::ops::GeneratorState<Self::Yield, Self::Return> {
unsafe {
match self.get_unchecked_mut() { #(#resume,)* }
}
}
});

Ok(impl_.into_token_stream())
}
}

Expand All @@ -122,7 +142,7 @@ pub(crate) mod fn_ {

pub(crate) const NAME: &[&str] = &["Fn"];

pub(crate) fn derive(data: &Data) -> Result<TokenStream> {
pub(crate) fn derive(_cx: &Context, data: &Data) -> Result<TokenStream> {
let trait_path = quote!(::core::ops::Fn);
let trait_ = quote!(#trait_path(__T) -> __U);
let fst = data.field_types().next();
Expand Down Expand Up @@ -155,7 +175,7 @@ pub(crate) mod fn_mut {

pub(crate) const NAME: &[&str] = &["FnMut"];

pub(crate) fn derive(data: &Data) -> Result<TokenStream> {
pub(crate) fn derive(_cx: &Context, data: &Data) -> Result<TokenStream> {
let trait_path = quote!(::core::ops::FnMut);
let trait_ = quote!(#trait_path(__T) -> __U);
let fst = data.field_types().next();
Expand Down Expand Up @@ -188,7 +208,7 @@ pub(crate) mod fn_once {

pub(crate) const NAME: &[&str] = &["FnOnce"];

pub(crate) fn derive(data: &Data) -> Result<TokenStream> {
pub(crate) fn derive(_cx: &Context, data: &Data) -> Result<TokenStream> {
let trait_path = quote!(::core::ops::FnOnce);
let trait_ = quote!(#trait_path(__T) -> __U);
let fst = data.field_types().next();
Expand Down
6 changes: 3 additions & 3 deletions src/derive/external/futures01.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pub(crate) mod future {

pub(crate) const NAME: &[&str] = &["futures01::Future"];

pub(crate) fn derive(data: &Data) -> Result<TokenStream> {
pub(crate) fn derive(_cx: &Context, data: &Data) -> Result<TokenStream> {
Ok(derive_trait(data, parse_quote!(::futures::future::Future), None, parse_quote! {
trait Future {
type Item;
Expand All @@ -20,7 +20,7 @@ pub(crate) mod stream {

pub(crate) const NAME: &[&str] = &["futures01::Stream"];

pub(crate) fn derive(data: &Data) -> Result<TokenStream> {
pub(crate) fn derive(_cx: &Context, data: &Data) -> Result<TokenStream> {
Ok(derive_trait(data, parse_quote!(::futures::stream::Stream), None, parse_quote! {
trait Stream {
type Item;
Expand All @@ -39,7 +39,7 @@ pub(crate) mod sink {

pub(crate) const NAME: &[&str] = &["futures01::Sink"];

pub(crate) fn derive(data: &Data) -> Result<TokenStream> {
pub(crate) fn derive(_cx: &Context, data: &Data) -> Result<TokenStream> {
Ok(derive_trait(data, parse_quote!(::futures::sink::Sink), None, parse_quote! {
trait Sink {
type SinkItem;
Expand Down
Loading

0 comments on commit 3a46654

Please sign in to comment.