diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /third_party/rust/pin-project-internal/src | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/pin-project-internal/src')
7 files changed, 2675 insertions, 0 deletions
diff --git a/third_party/rust/pin-project-internal/src/lib.rs b/third_party/rust/pin-project-internal/src/lib.rs new file mode 100644 index 0000000000..07683c417e --- /dev/null +++ b/third_party/rust/pin-project-internal/src/lib.rs @@ -0,0 +1,584 @@ +//! Implementation detail of the `pin-project` crate. - **do not use directly** + +#![doc(test( + no_crate_inject, + attr( + deny(warnings, rust_2018_idioms, single_use_lifetimes), + allow(dead_code, unused_variables) + ) +))] +#![forbid(unsafe_code)] +#![warn(rust_2018_idioms, single_use_lifetimes, unreachable_pub)] +#![warn(clippy::pedantic)] +#![allow( + clippy::needless_doctest_main, + clippy::similar_names, + clippy::single_match_else, + clippy::too_many_lines, + clippy::unnested_or_patterns +)] + +// older compilers require explicit `extern crate`. +#[allow(unused_extern_crates)] +extern crate proc_macro; + +#[macro_use] +mod utils; + +mod pin_project; +mod pinned_drop; + +use proc_macro::TokenStream; + +/// An attribute that creates projection types covering all the fields of +/// struct or enum. +/// +/// This attribute creates projection types according to the following rules: +/// +/// - For the fields that use `#[pin]` attribute, create the pinned reference to +/// the field. +/// - For the other fields, create a normal reference to the field. +/// +/// And the following methods are implemented on the original type: +/// +/// ```rust +/// # use std::pin::Pin; +/// # type Projection<'a> = &'a (); +/// # type ProjectionRef<'a> = &'a (); +/// # trait Dox { +/// fn project(self: Pin<&mut Self>) -> Projection<'_>; +/// fn project_ref(self: Pin<&Self>) -> ProjectionRef<'_>; +/// # } +/// ``` +/// +/// By passing an argument with the same name as the method to the attribute, +/// you can name the projection type returned from the method. This allows you +/// to use pattern matching on the projected types. +/// +/// ```rust +/// # use pin_project::pin_project; +/// # use std::pin::Pin; +/// #[pin_project(project = EnumProj)] +/// enum Enum<T> { +/// Variant(#[pin] T), +/// } +/// +/// impl<T> Enum<T> { +/// fn method(self: Pin<&mut Self>) { +/// let this: EnumProj<'_, T> = self.project(); +/// match this { +/// EnumProj::Variant(x) => { +/// let _: Pin<&mut T> = x; +/// } +/// } +/// } +/// } +/// ``` +/// +/// Note that the projection types returned by `project` and `project_ref` have +/// an additional lifetime at the beginning of generics. +/// +/// ```text +/// let this: EnumProj<'_, T> = self.project(); +/// ^^ +/// ``` +/// +/// The visibility of the projected types and projection methods is based on the +/// original type. However, if the visibility of the original type is `pub`, the +/// visibility of the projected types and the projection methods is downgraded +/// to `pub(crate)`. +/// +/// # Safety +/// +/// This attribute is completely safe. In the absence of other `unsafe` code +/// *that you write*, it is impossible to cause [undefined +/// behavior][undefined-behavior] with this attribute. +/// +/// This is accomplished by enforcing the four requirements for pin projection +/// stated in [the Rust documentation][pin-projection]: +/// +/// 1. The struct must only be [`Unpin`] if all the structural fields are +/// [`Unpin`]. +/// +/// To enforce this, this attribute will automatically generate an [`Unpin`] +/// implementation for you, which will require that all structurally pinned +/// fields be [`Unpin`]. +/// +/// If you attempt to provide an [`Unpin`] impl, the blanket impl will then +/// apply to your type, causing a compile-time error due to the conflict with +/// the second impl. +/// +/// If you wish to provide a manual [`Unpin`] impl, you can do so via the +/// [`UnsafeUnpin`][unsafe-unpin] argument. +/// +/// 2. The destructor of the struct must not move structural fields out of its +/// argument. +/// +/// To enforce this, this attribute will generate code like this: +/// +/// ```rust +/// struct MyStruct {} +/// trait MyStructMustNotImplDrop {} +/// # #[allow(unknown_lints, drop_bounds)] +/// impl<T: Drop> MyStructMustNotImplDrop for T {} +/// impl MyStructMustNotImplDrop for MyStruct {} +/// ``` +/// +/// If you attempt to provide an [`Drop`] impl, the blanket impl will then +/// apply to your type, causing a compile-time error due to the conflict with +/// the second impl. +/// +/// If you wish to provide a custom [`Drop`] impl, you can annotate an impl +/// with [`#[pinned_drop]`][pinned-drop]. This impl takes a pinned version of +/// your struct - that is, [`Pin`]`<&mut MyStruct>` where `MyStruct` is the +/// type of your struct. +/// +/// You can call `.project()` on this type as usual, along with any other +/// methods you have defined. Because your code is never provided with +/// a `&mut MyStruct`, it is impossible to move out of pin-projectable +/// fields in safe code in your destructor. +/// +/// 3. You must make sure that you uphold the [`Drop` +/// guarantee][drop-guarantee]: once your struct is pinned, the memory that +/// contains the content is not overwritten or deallocated without calling +/// the content's destructors. +/// +/// Safe code doesn't need to worry about this - the only way to violate +/// this requirement is to manually deallocate memory (which is `unsafe`), +/// or to overwrite a field with something else. +/// Because your custom destructor takes [`Pin`]`<&mut MyStruct>`, it's +/// impossible to obtain a mutable reference to a pin-projected field in safe +/// code. +/// +/// 4. You must not offer any other operations that could lead to data being +/// moved out of the structural fields when your type is pinned. +/// +/// As with requirement 3, it is impossible for safe code to violate this. +/// This crate ensures that safe code can never obtain a mutable reference to +/// `#[pin]` fields, which prevents you from ever moving out of them in safe +/// code. +/// +/// Pin projections are also incompatible with [`#[repr(packed)]`][repr-packed] +/// types. Attempting to use this attribute on a `#[repr(packed)]` type results +/// in a compile-time error. +/// +/// # Examples +/// +/// `#[pin_project]` can be used on structs and enums. +/// +/// ```rust +/// use std::pin::Pin; +/// +/// use pin_project::pin_project; +/// +/// #[pin_project] +/// struct Struct<T, U> { +/// #[pin] +/// pinned: T, +/// unpinned: U, +/// } +/// +/// impl<T, U> Struct<T, U> { +/// fn method(self: Pin<&mut Self>) { +/// let this = self.project(); +/// let _: Pin<&mut T> = this.pinned; +/// let _: &mut U = this.unpinned; +/// } +/// } +/// ``` +/// +/// ```rust +/// use std::pin::Pin; +/// +/// use pin_project::pin_project; +/// +/// #[pin_project] +/// struct TupleStruct<T, U>(#[pin] T, U); +/// +/// impl<T, U> TupleStruct<T, U> { +/// fn method(self: Pin<&mut Self>) { +/// let this = self.project(); +/// let _: Pin<&mut T> = this.0; +/// let _: &mut U = this.1; +/// } +/// } +/// ``` +/// +/// To use `#[pin_project]` on enums, you need to name the projection type +/// returned from the method. +/// +/// ```rust +/// use std::pin::Pin; +/// +/// use pin_project::pin_project; +/// +/// #[pin_project(project = EnumProj)] +/// enum Enum<T, U> { +/// Tuple(#[pin] T), +/// Struct { field: U }, +/// Unit, +/// } +/// +/// impl<T, U> Enum<T, U> { +/// fn method(self: Pin<&mut Self>) { +/// match self.project() { +/// EnumProj::Tuple(x) => { +/// let _: Pin<&mut T> = x; +/// } +/// EnumProj::Struct { field } => { +/// let _: &mut U = field; +/// } +/// EnumProj::Unit => {} +/// } +/// } +/// } +/// ``` +/// +/// When `#[pin_project]` is used on enums, only named projection types and +/// methods are generated because there is no way to access variants of +/// projected types without naming it. +/// For example, in the above example, only the `project` method is generated, +/// and the `project_ref` method is not generated. +/// (When `#[pin_project]` is used on structs, both methods are always generated.) +/// +/// ```rust,compile_fail,E0599 +/// # use pin_project::pin_project; +/// # use std::pin::Pin; +/// # +/// # #[pin_project(project = EnumProj)] +/// # enum Enum<T, U> { +/// # Tuple(#[pin] T), +/// # Struct { field: U }, +/// # Unit, +/// # } +/// # +/// impl<T, U> Enum<T, U> { +/// fn call_project_ref(self: Pin<&Self>) { +/// let _this = self.project_ref(); +/// //~^ ERROR no method named `project_ref` found for struct `Pin<&Enum<T, U>>` in the current scope +/// } +/// } +/// ``` +/// +/// If you want to call `.project()` multiple times or later use the +/// original [`Pin`] type, it needs to use [`.as_mut()`][`Pin::as_mut`] to avoid +/// consuming the [`Pin`]. +/// +/// ```rust +/// use std::pin::Pin; +/// +/// use pin_project::pin_project; +/// +/// #[pin_project] +/// struct Struct<T> { +/// #[pin] +/// field: T, +/// } +/// +/// impl<T> Struct<T> { +/// fn call_project_twice(mut self: Pin<&mut Self>) { +/// // `project` consumes `self`, so reborrow the `Pin<&mut Self>` via `as_mut`. +/// self.as_mut().project(); +/// self.as_mut().project(); +/// } +/// } +/// ``` +/// +/// # `!Unpin` +/// +/// If you want to ensure that [`Unpin`] is not implemented, use the `!Unpin` +/// argument to `#[pin_project]`. +/// +/// ```rust +/// use pin_project::pin_project; +/// +/// #[pin_project(!Unpin)] +/// struct Struct<T> { +/// field: T, +/// } +/// ``` +/// +/// This is equivalent to using `#[pin]` attribute for the [`PhantomPinned`] +/// field. +/// +/// ```rust +/// use std::marker::PhantomPinned; +/// +/// use pin_project::pin_project; +/// +/// #[pin_project] +/// struct Struct<T> { +/// field: T, +/// #[pin] // <------ This `#[pin]` is required to make `Struct` to `!Unpin`. +/// _pin: PhantomPinned, +/// } +/// ``` +/// +/// Note that using [`PhantomPinned`] without `#[pin]` attribute has no effect. +/// +/// # `UnsafeUnpin` +/// +/// If you want to implement [`Unpin`] manually, you must use the `UnsafeUnpin` +/// argument to `#[pin_project]`. +/// +/// ```rust +/// use pin_project::{pin_project, UnsafeUnpin}; +/// +/// #[pin_project(UnsafeUnpin)] +/// struct Struct<T, U> { +/// #[pin] +/// pinned: T, +/// unpinned: U, +/// } +/// +/// unsafe impl<T: Unpin, U> UnsafeUnpin for Struct<T, U> {} +/// ``` +/// +/// Note the usage of the unsafe [`UnsafeUnpin`] trait, instead of the usual +/// [`Unpin`] trait. [`UnsafeUnpin`] behaves exactly like [`Unpin`], except that +/// is unsafe to implement. This unsafety comes from the fact that pin +/// projections are being used. If you implement [`UnsafeUnpin`], you must +/// ensure that it is only implemented when all pin-projected fields implement +/// [`Unpin`]. +/// +/// See [`UnsafeUnpin`] trait for more details. +/// +/// # `#[pinned_drop]` +/// +/// In order to correctly implement pin projections, a type's [`Drop`] impl must +/// not move out of any structurally pinned fields. Unfortunately, +/// [`Drop::drop`] takes `&mut Self`, not [`Pin`]`<&mut Self>`. +/// +/// To ensure that this requirement is upheld, the `#[pin_project]` attribute +/// will provide a [`Drop`] impl for you. This [`Drop`] impl will delegate to +/// an impl block annotated with `#[pinned_drop]` if you use the `PinnedDrop` +/// argument to `#[pin_project]`. +/// +/// This impl block acts just like a normal [`Drop`] impl, +/// except for the following two: +/// +/// - `drop` method takes [`Pin`]`<&mut Self>` +/// - Name of the trait is `PinnedDrop`. +/// +/// ```rust +/// # use std::pin::Pin; +/// pub trait PinnedDrop { +/// fn drop(self: Pin<&mut Self>); +/// } +/// ``` +/// +/// `#[pin_project]` implements the actual [`Drop`] trait via `PinnedDrop` you +/// implemented. To drop a type that implements `PinnedDrop`, use the [`drop`] +/// function just like dropping a type that directly implements [`Drop`]. +/// +/// In particular, it will never be called more than once, just like +/// [`Drop::drop`]. +/// +/// For example: +/// +/// ```rust +/// use std::{fmt::Debug, pin::Pin}; +/// +/// use pin_project::{pin_project, pinned_drop}; +/// +/// #[pin_project(PinnedDrop)] +/// struct PrintOnDrop<T: Debug, U: Debug> { +/// #[pin] +/// pinned_field: T, +/// unpin_field: U, +/// } +/// +/// #[pinned_drop] +/// impl<T: Debug, U: Debug> PinnedDrop for PrintOnDrop<T, U> { +/// fn drop(self: Pin<&mut Self>) { +/// println!("Dropping pinned field: {:?}", self.pinned_field); +/// println!("Dropping unpin field: {:?}", self.unpin_field); +/// } +/// } +/// +/// fn main() { +/// let _x = PrintOnDrop { pinned_field: true, unpin_field: 40 }; +/// } +/// ``` +/// +/// See also [`#[pinned_drop]`][macro@pinned_drop] attribute. +/// +/// # `project_replace` method +/// +/// In addition to the `project` and `project_ref` methods which are always +/// provided when you use the `#[pin_project]` attribute, there is a third +/// method, `project_replace` which can be useful in some situations. It is +/// equivalent to [`Pin::set`], except that the unpinned fields are moved and +/// returned, instead of being dropped in-place. +/// +/// ```rust +/// # use std::pin::Pin; +/// # type ProjectionOwned = (); +/// # trait Dox { +/// fn project_replace(self: Pin<&mut Self>, other: Self) -> ProjectionOwned; +/// # } +/// ``` +/// +/// The `ProjectionOwned` type is identical to the `Self` type, except that +/// all pinned fields have been replaced by equivalent [`PhantomData`] types. +/// +/// This method is opt-in, because it is only supported for [`Sized`] types, and +/// because it is incompatible with the [`#[pinned_drop]`][pinned-drop] +/// attribute described above. It can be enabled by using +/// `#[pin_project(project_replace)]`. +/// +/// For example: +/// +/// ```rust +/// use std::{marker::PhantomData, pin::Pin}; +/// +/// use pin_project::pin_project; +/// +/// #[pin_project(project_replace)] +/// struct Struct<T, U> { +/// #[pin] +/// pinned_field: T, +/// unpinned_field: U, +/// } +/// +/// impl<T, U> Struct<T, U> { +/// fn method(self: Pin<&mut Self>, other: Self) { +/// let this = self.project_replace(other); +/// let _: U = this.unpinned_field; +/// let _: PhantomData<T> = this.pinned_field; +/// } +/// } +/// ``` +/// +/// By passing the value to the `project_replace` argument, you can name the +/// returned type of the `project_replace` method. This is necessary whenever +/// destructuring the return type of the `project_replace` method, and work in exactly +/// the same way as the `project` and `project_ref` arguments. +/// +/// ```rust +/// use pin_project::pin_project; +/// +/// #[pin_project(project_replace = EnumProjOwn)] +/// enum Enum<T, U> { +/// A { +/// #[pin] +/// pinned_field: T, +/// unpinned_field: U, +/// }, +/// B, +/// } +/// +/// let mut x = Box::pin(Enum::A { pinned_field: 42, unpinned_field: "hello" }); +/// +/// match x.as_mut().project_replace(Enum::B) { +/// EnumProjOwn::A { unpinned_field, .. } => assert_eq!(unpinned_field, "hello"), +/// EnumProjOwn::B => unreachable!(), +/// } +/// ``` +/// +/// [`PhantomData`]: core::marker::PhantomData +/// [`PhantomPinned`]: core::marker::PhantomPinned +/// [`Pin::as_mut`]: core::pin::Pin::as_mut +/// [`Pin::set`]: core::pin::Pin::set +/// [`Pin`]: core::pin::Pin +/// [`UnsafeUnpin`]: https://docs.rs/pin-project/1/pin_project/trait.UnsafeUnpin.html +/// [drop-guarantee]: core::pin#drop-guarantee +/// [pin-projection]: core::pin#projections-and-structural-pinning +/// [pinned-drop]: macro@pin_project#pinned_drop +/// [repr-packed]: https://doc.rust-lang.org/nomicon/other-reprs.html#reprpacked +/// [undefined-behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html +/// [unsafe-unpin]: macro@pin_project#unsafeunpin +#[proc_macro_attribute] +pub fn pin_project(args: TokenStream, input: TokenStream) -> TokenStream { + pin_project::attribute(&args.into(), input.into()).into() +} + +/// An attribute used for custom implementations of [`Drop`]. +/// +/// This attribute is used in conjunction with the `PinnedDrop` argument to +/// the [`#[pin_project]`][macro@pin_project] attribute. +/// +/// The impl block annotated with this attribute acts just like a normal +/// [`Drop`] impl, except for the following two: +/// +/// - `drop` method takes [`Pin`]`<&mut Self>` +/// - Name of the trait is `PinnedDrop`. +/// +/// ```rust +/// # use std::pin::Pin; +/// pub trait PinnedDrop { +/// fn drop(self: Pin<&mut Self>); +/// } +/// ``` +/// +/// `#[pin_project]` implements the actual [`Drop`] trait via `PinnedDrop` you +/// implemented. To drop a type that implements `PinnedDrop`, use the [`drop`] +/// function just like dropping a type that directly implements [`Drop`]. +/// +/// In particular, it will never be called more than once, just like +/// [`Drop::drop`]. +/// +/// # Examples +/// +/// ```rust +/// use std::pin::Pin; +/// +/// use pin_project::{pin_project, pinned_drop}; +/// +/// #[pin_project(PinnedDrop)] +/// struct PrintOnDrop { +/// #[pin] +/// field: u8, +/// } +/// +/// #[pinned_drop] +/// impl PinnedDrop for PrintOnDrop { +/// fn drop(self: Pin<&mut Self>) { +/// println!("Dropping: {}", self.field); +/// } +/// } +/// +/// fn main() { +/// let _x = PrintOnDrop { field: 50 }; +/// } +/// ``` +/// +/// See also ["pinned-drop" section of `#[pin_project]` attribute][pinned-drop]. +/// +/// # Why `#[pinned_drop]` attribute is needed? +/// +/// Implementing `PinnedDrop::drop` is safe, but calling it is not safe. +/// This is because destructors can be called multiple times in safe code and +/// [double dropping is unsound][rust-lang/rust#62360]. +/// +/// Ideally, it would be desirable to be able to forbid manual calls in +/// the same way as [`Drop::drop`], but the library cannot do it. So, by using +/// macros and replacing them with private traits like the following, +/// this crate prevent users from calling `PinnedDrop::drop` in safe code. +/// +/// ```rust +/// # use std::pin::Pin; +/// pub trait PinnedDrop { +/// unsafe fn drop(self: Pin<&mut Self>); +/// } +/// ``` +/// +/// This allows implementing [`Drop`] safely using `#[pinned_drop]`. +/// Also by using the [`drop`] function just like dropping a type that directly +/// implements [`Drop`], can drop safely a type that implements `PinnedDrop`. +/// +/// [rust-lang/rust#62360]: https://github.com/rust-lang/rust/pull/62360 +/// [`Pin`]: core::pin::Pin +/// [pinned-drop]: macro@pin_project#pinned_drop +#[proc_macro_attribute] +pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { + let input = syn::parse_macro_input!(input); + pinned_drop::attribute(&args.into(), input).into() +} + +// Not public API. +#[doc(hidden)] +#[proc_macro_derive(__PinProjectInternalDerive, attributes(pin))] +pub fn __pin_project_internal_derive(input: TokenStream) -> TokenStream { + pin_project::derive(input.into()).into() +} diff --git a/third_party/rust/pin-project-internal/src/pin_project/args.rs b/third_party/rust/pin-project-internal/src/pin_project/args.rs new file mode 100644 index 0000000000..d0d4f362f5 --- /dev/null +++ b/third_party/rust/pin-project-internal/src/pin_project/args.rs @@ -0,0 +1,254 @@ +use proc_macro2::{Span, TokenStream}; +use quote::quote; +use syn::{ + parse::{Parse, ParseStream}, + spanned::Spanned, + Attribute, Error, Ident, Result, Token, +}; + +use super::PIN; +use crate::utils::{ParseBufferExt, SliceExt}; + +pub(super) fn parse_args(attrs: &[Attribute]) -> Result<Args> { + // `(__private(<args>))` -> `<args>` + struct Input(Option<TokenStream>); + + impl Parse for Input { + fn parse(input: ParseStream<'_>) -> Result<Self> { + Ok(Self((|| { + let content = input.parenthesized().ok()?; + let private = content.parse::<Ident>().ok()?; + if private == "__private" { + content.parenthesized().ok()?.parse::<TokenStream>().ok() + } else { + None + } + })())) + } + } + + if let Some(attr) = attrs.find("pin_project") { + bail!(attr, "duplicate #[pin_project] attribute"); + } + + let mut attrs = attrs.iter().filter(|attr| attr.path.is_ident(PIN)); + + let prev = if let Some(attr) = attrs.next() { + (attr, syn::parse2::<Input>(attr.tokens.clone()).unwrap().0) + } else { + // This only fails if another macro removes `#[pin]`. + bail!(TokenStream::new(), "#[pin_project] attribute has been removed"); + }; + + if let Some(attr) = attrs.next() { + let (prev_attr, prev_res) = &prev; + // As the `#[pin]` attribute generated by `#[pin_project]` + // has the same span as `#[pin_project]`, it is possible + // that a useless error message will be generated. + // So, use the span of `prev_attr` if it is not a valid attribute. + let res = syn::parse2::<Input>(attr.tokens.clone()).unwrap().0; + let span = match (prev_res, res) { + (Some(_), _) => attr, + (None, _) => prev_attr, + }; + bail!(span, "duplicate #[pin] attribute"); + } + // This `unwrap` only fails if another macro removes `#[pin]` and inserts own `#[pin]`. + syn::parse2(prev.1.unwrap()) +} + +pub(super) struct Args { + /// `PinnedDrop` argument. + pub(super) pinned_drop: Option<Span>, + /// `UnsafeUnpin` or `!Unpin` argument. + pub(super) unpin_impl: UnpinImpl, + /// `project = <ident>` argument. + pub(super) project: Option<Ident>, + /// `project_ref = <ident>` argument. + pub(super) project_ref: Option<Ident>, + /// `project_replace [= <ident>]` argument. + pub(super) project_replace: ProjReplace, +} + +impl Parse for Args { + fn parse(input: ParseStream<'_>) -> Result<Self> { + mod kw { + syn::custom_keyword!(Unpin); + } + + /// Parses `= <value>` in `<name> = <value>` and returns value and span of name-value pair. + fn parse_value( + input: ParseStream<'_>, + name: &Ident, + has_prev: bool, + ) -> Result<(Ident, TokenStream)> { + if input.is_empty() { + bail!(name, "expected `{0} = <identifier>`, found `{0}`", name); + } + let eq_token: Token![=] = input.parse()?; + if input.is_empty() { + let span = quote!(#name #eq_token); + bail!(span, "expected `{0} = <identifier>`, found `{0} =`", name); + } + let value: Ident = input.parse()?; + let span = quote!(#name #value); + if has_prev { + bail!(span, "duplicate `{}` argument", name); + } + Ok((value, span)) + } + + let mut pinned_drop = None; + let mut unsafe_unpin = None; + let mut not_unpin = None; + let mut project = None; + let mut project_ref = None; + let mut project_replace_value = None; + let mut project_replace_span = None; + + while !input.is_empty() { + if input.peek(Token![!]) { + let bang: Token![!] = input.parse()?; + if input.is_empty() { + bail!(bang, "expected `!Unpin`, found `!`"); + } + let unpin: kw::Unpin = input.parse()?; + let span = quote!(#bang #unpin); + if not_unpin.replace(span.span()).is_some() { + bail!(span, "duplicate `!Unpin` argument"); + } + } else { + let token = input.parse::<Ident>()?; + match &*token.to_string() { + "PinnedDrop" => { + if pinned_drop.replace(token.span()).is_some() { + bail!(token, "duplicate `PinnedDrop` argument"); + } + } + "UnsafeUnpin" => { + if unsafe_unpin.replace(token.span()).is_some() { + bail!(token, "duplicate `UnsafeUnpin` argument"); + } + } + "project" => { + project = Some(parse_value(input, &token, project.is_some())?.0); + } + "project_ref" => { + project_ref = Some(parse_value(input, &token, project_ref.is_some())?.0); + } + "project_replace" => { + if input.peek(Token![=]) { + let (value, span) = + parse_value(input, &token, project_replace_span.is_some())?; + project_replace_value = Some(value); + project_replace_span = Some(span.span()); + } else if project_replace_span.is_some() { + bail!(token, "duplicate `project_replace` argument"); + } else { + project_replace_span = Some(token.span()); + } + } + "Replace" => { + bail!( + token, + "`Replace` argument was removed, use `project_replace` argument instead" + ); + } + _ => bail!(token, "unexpected argument: {}", token), + } + } + + if input.is_empty() { + break; + } + let _: Token![,] = input.parse()?; + } + + if project.is_some() || project_ref.is_some() { + if project == project_ref { + bail!( + project_ref, + "name `{}` is already specified by `project` argument", + project_ref.as_ref().unwrap() + ); + } + if let Some(ident) = &project_replace_value { + if project == project_replace_value { + bail!(ident, "name `{}` is already specified by `project` argument", ident); + } else if project_ref == project_replace_value { + bail!(ident, "name `{}` is already specified by `project_ref` argument", ident); + } + } + } + + if let Some(span) = pinned_drop { + if project_replace_span.is_some() { + return Err(Error::new( + span, + "arguments `PinnedDrop` and `project_replace` are mutually exclusive", + )); + } + } + let project_replace = match (project_replace_span, project_replace_value) { + (None, _) => ProjReplace::None, + (Some(span), Some(ident)) => ProjReplace::Named { ident, span }, + (Some(span), None) => ProjReplace::Unnamed { span }, + }; + let unpin_impl = match (unsafe_unpin, not_unpin) { + (None, None) => UnpinImpl::Default, + (Some(span), None) => UnpinImpl::Unsafe(span), + (None, Some(span)) => UnpinImpl::Negative(span), + (Some(span), Some(_)) => { + return Err(Error::new( + span, + "arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive", + )); + } + }; + + Ok(Self { pinned_drop, unpin_impl, project, project_ref, project_replace }) + } +} + +/// `UnsafeUnpin` or `!Unpin` argument. +#[derive(Clone, Copy)] +pub(super) enum UnpinImpl { + Default, + /// `UnsafeUnpin`. + Unsafe(Span), + /// `!Unpin`. + Negative(Span), +} + +/// `project_replace [= <ident>]` argument. +pub(super) enum ProjReplace { + None, + /// `project_replace`. + Unnamed { + span: Span, + }, + /// `project_replace = <ident>`. + #[allow(dead_code)] // false positive that fixed in Rust 1.38 + Named { + span: Span, + ident: Ident, + }, +} + +impl ProjReplace { + /// Return the span of this argument. + pub(super) fn span(&self) -> Option<Span> { + match self { + Self::None => None, + Self::Named { span, .. } | Self::Unnamed { span, .. } => Some(*span), + } + } + + pub(super) fn ident(&self) -> Option<&Ident> { + if let Self::Named { ident, .. } = self { + Some(ident) + } else { + None + } + } +} diff --git a/third_party/rust/pin-project-internal/src/pin_project/attribute.rs b/third_party/rust/pin-project-internal/src/pin_project/attribute.rs new file mode 100644 index 0000000000..7adfc07d59 --- /dev/null +++ b/third_party/rust/pin-project-internal/src/pin_project/attribute.rs @@ -0,0 +1,65 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::{ + parse::{Parse, ParseStream}, + Attribute, Result, Token, Visibility, +}; + +use super::PIN; +use crate::utils::SliceExt; + +// To generate the correct `Unpin` implementation and the projection methods, +// we need to collect the types of the pinned fields. +// However, since proc-macro-attribute is applied before `cfg` and `cfg_attr` +// on fields, we cannot be collecting field types properly at this timing. +// So instead of generating the `Unpin` implementation and the projection +// methods here, delegate their processing to proc-macro-derive. +// +// At this stage, only attributes are parsed and the following attributes are +// added to the attributes of the item. +// - `#[derive(InternalDerive)]` - An internal helper macro that does the above +// processing. +// - `#[pin(__private(#args))]` - Pass the argument of `#[pin_project]` to +// proc-macro-derive (`InternalDerive`). + +pub(super) fn parse_attribute(args: &TokenStream, input: TokenStream) -> Result<TokenStream> { + let Input { attrs, body } = syn::parse2(input)?; + + Ok(quote! { + #(#attrs)* + #[derive(::pin_project::__private::__PinProjectInternalDerive)] + // Use `__private` to prevent users from trying to control `InternalDerive` + // manually. `__private` does not guarantee compatibility between patch + // versions, so it should be sufficient for this purpose in most cases. + #[pin(__private(#args))] + #body + }) +} + +#[allow(dead_code)] // false positive that fixed in Rust 1.39 +struct Input { + attrs: Vec<Attribute>, + body: TokenStream, +} + +impl Parse for Input { + fn parse(input: ParseStream<'_>) -> Result<Self> { + let attrs = input.call(Attribute::parse_outer)?; + + let ahead = input.fork(); + let _vis: Visibility = ahead.parse()?; + if !ahead.peek(Token![struct]) && !ahead.peek(Token![enum]) { + // If we check this only on proc-macro-derive, it may generate unhelpful error + // messages. So it is preferable to be able to detect it here. + bail!( + input.parse::<TokenStream>()?, + "#[pin_project] attribute may only be used on structs or enums" + ); + } else if let Some(attr) = attrs.find(PIN) { + bail!(attr, "#[pin] attribute may only be used on fields of structs or variants"); + } else if let Some(attr) = attrs.find("pin_project") { + bail!(attr, "duplicate #[pin_project] attribute"); + } + Ok(Self { attrs, body: input.parse()? }) + } +} diff --git a/third_party/rust/pin-project-internal/src/pin_project/derive.rs b/third_party/rust/pin-project-internal/src/pin_project/derive.rs new file mode 100644 index 0000000000..fd2375dc3b --- /dev/null +++ b/third_party/rust/pin-project-internal/src/pin_project/derive.rs @@ -0,0 +1,1127 @@ +use proc_macro2::{Delimiter, Group, Span, TokenStream}; +use quote::{format_ident, quote, quote_spanned, ToTokens}; +use syn::{ + parse_quote, token, visit_mut::VisitMut, Attribute, Data, DataEnum, DeriveInput, Error, Field, + Fields, FieldsNamed, FieldsUnnamed, Generics, Ident, Index, Lifetime, LifetimeDef, Meta, + MetaList, MetaNameValue, NestedMeta, Result, Token, Type, Variant, Visibility, WhereClause, +}; + +use super::{ + args::{parse_args, Args, ProjReplace, UnpinImpl}, + PIN, +}; +use crate::utils::{ + determine_lifetime_name, determine_visibility, insert_lifetime_and_bound, ReplaceReceiver, + SliceExt, Variants, +}; + +pub(super) fn parse_derive(input: TokenStream) -> Result<TokenStream> { + let mut input: DeriveInput = syn::parse2(input)?; + + let mut cx; + let mut generate = GenerateTokens::default(); + + let ident = &input.ident; + let ty_generics = input.generics.split_for_impl().1; + let self_ty = parse_quote!(#ident #ty_generics); + let mut visitor = ReplaceReceiver(&self_ty); + visitor.visit_generics_mut(&mut input.generics); + visitor.visit_data_mut(&mut input.data); + + match &input.data { + Data::Struct(data) => { + cx = Context::new(&input.attrs, &input.vis, ident, &mut input.generics, Struct)?; + parse_struct(&mut cx, &data.fields, &mut generate)?; + } + Data::Enum(data) => { + cx = Context::new(&input.attrs, &input.vis, ident, &mut input.generics, Enum)?; + parse_enum(&mut cx, data, &mut generate)?; + } + Data::Union(_) => { + bail!(input, "#[pin_project] attribute may only be used on structs or enums"); + } + } + + Ok(generate.into_tokens(&cx)) +} + +#[derive(Default)] +struct GenerateTokens { + exposed: TokenStream, + scoped: TokenStream, +} + +impl GenerateTokens { + fn extend(&mut self, expose: bool, tokens: TokenStream) { + if expose { + self.exposed.extend(tokens); + } else { + self.scoped.extend(tokens); + } + } + + fn into_tokens(self, cx: &Context<'_>) -> TokenStream { + let mut tokens = self.exposed; + let scoped = self.scoped; + + let unpin_impl = make_unpin_impl(cx); + let drop_impl = make_drop_impl(cx); + let allowed_lints = global_allowed_lints(); + + tokens.extend(quote! { + // All items except projected types are generated inside a `const` scope. + // This makes it impossible for user code to refer to these types. + // However, this prevents Rustdoc from displaying docs for any + // of our types. In particular, users cannot see the + // automatically generated `Unpin` impl for the '__UnpinStruct' types + // + // Previously, we provided a flag to correctly document the + // automatically generated `Unpin` impl by using def-site hygiene, + // but it is now removed. + // + // Refs: + // - https://github.com/rust-lang/rust/issues/63281 + // - https://github.com/taiki-e/pin-project/pull/53#issuecomment-525906867 + // - https://github.com/taiki-e/pin-project/pull/70 + #allowed_lints + #[allow(unused_qualifications)] + #[allow(clippy::semicolon_if_nothing_returned)] + #[allow(clippy::use_self)] + #[allow(clippy::used_underscore_binding)] + const _: () = { + #[allow(unused_extern_crates)] + extern crate pin_project as _pin_project; + #scoped + #unpin_impl + #drop_impl + }; + }); + tokens + } +} + +/// Returns attributes that should be applied to all generated code. +fn global_allowed_lints() -> TokenStream { + quote! { + #[allow(box_pointers)] // This lint warns use of the `Box` type. + #[allow(deprecated)] + #[allow(explicit_outlives_requirements)] // https://github.com/rust-lang/rust/issues/60993 + #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 + #[allow(unreachable_pub)] // This lint warns `pub` field in private struct. + #[allow(unused_tuple_struct_fields)] + // This lint warns of `clippy::*` generated by external macros. + // We allow this lint for compatibility with older compilers. + #[allow(clippy::unknown_clippy_lints)] + #[allow(clippy::pattern_type_mismatch)] + #[allow(clippy::redundant_pub_crate)] // This lint warns `pub(crate)` field in private struct. + #[allow(clippy::type_repetition_in_bounds)] // https://github.com/rust-lang/rust-clippy/issues/4326 + } +} + +/// Returns attributes used on projected types. +fn proj_allowed_lints(cx: &Context<'_>) -> (TokenStream, TokenStream, TokenStream) { + let large_enum_variant = if cx.kind == Enum { + Some(quote! { + #[allow(variant_size_differences)] + #[allow(clippy::large_enum_variant)] + }) + } else { + None + }; + let global_allowed_lints = global_allowed_lints(); + let proj_mut_allowed_lints = if cx.project { Some(&global_allowed_lints) } else { None }; + let proj_mut = quote! { + #proj_mut_allowed_lints + #[allow(dead_code)] // This lint warns unused fields/variants. + #[allow(clippy::mut_mut)] // This lint warns `&mut &mut <ty>`. + }; + let proj_ref_allowed_lints = if cx.project_ref { Some(&global_allowed_lints) } else { None }; + let proj_ref = quote! { + #proj_ref_allowed_lints + #[allow(dead_code)] // This lint warns unused fields/variants. + #[allow(clippy::ref_option_ref)] // This lint warns `&Option<&<ty>>`. + }; + let proj_own_allowed_lints = + if cx.project_replace.ident().is_some() { Some(&global_allowed_lints) } else { None }; + let proj_own = quote! { + #proj_own_allowed_lints + #[allow(dead_code)] // This lint warns unused fields/variants. + #large_enum_variant + }; + (proj_mut, proj_ref, proj_own) +} + +struct Context<'a> { + /// The original type. + orig: OriginalType<'a>, + /// The projected types. + proj: ProjectedType, + /// Types of the pinned fields. + pinned_fields: Vec<&'a Type>, + /// Kind of the original type: struct or enum + kind: TypeKind, + + /// `PinnedDrop` argument. + pinned_drop: Option<Span>, + /// `UnsafeUnpin` or `!Unpin` argument. + unpin_impl: UnpinImpl, + /// `project` argument. + project: bool, + /// `project_ref` argument. + project_ref: bool, + /// `project_replace [= <ident>]` argument. + project_replace: ProjReplace, +} + +impl<'a> Context<'a> { + fn new( + attrs: &'a [Attribute], + vis: &'a Visibility, + ident: &'a Ident, + generics: &'a mut Generics, + kind: TypeKind, + ) -> Result<Self> { + let Args { pinned_drop, unpin_impl, project, project_ref, project_replace } = + parse_args(attrs)?; + + if let Some(name) = [project.as_ref(), project_ref.as_ref(), project_replace.ident()] + .iter() + .filter_map(Option::as_ref) + .find(|name| **name == ident) + { + bail!(name, "name `{}` is the same as the original type name", name); + } + + let mut lifetime_name = String::from("'pin"); + determine_lifetime_name(&mut lifetime_name, generics); + let lifetime = Lifetime::new(&lifetime_name, Span::call_site()); + + let ty_generics = generics.split_for_impl().1; + let ty_generics_as_generics = parse_quote!(#ty_generics); + let mut proj_generics = generics.clone(); + let pred = insert_lifetime_and_bound( + &mut proj_generics, + lifetime.clone(), + &ty_generics_as_generics, + ident, + ); + let mut where_clause = generics.make_where_clause().clone(); + where_clause.predicates.push(pred); + + let own_ident = project_replace + .ident() + .cloned() + .unwrap_or_else(|| format_ident!("__{}ProjectionOwned", ident)); + + Ok(Self { + kind, + pinned_drop, + unpin_impl, + project: project.is_some(), + project_ref: project_ref.is_some(), + project_replace, + proj: ProjectedType { + vis: determine_visibility(vis), + mut_ident: project.unwrap_or_else(|| format_ident!("__{}Projection", ident)), + ref_ident: project_ref.unwrap_or_else(|| format_ident!("__{}ProjectionRef", ident)), + own_ident, + lifetime, + generics: proj_generics, + where_clause, + }, + orig: OriginalType { attrs, vis, ident, generics }, + pinned_fields: Vec::new(), + }) + } +} + +#[derive(Copy, Clone, Eq, PartialEq)] +enum TypeKind { + Enum, + Struct, +} + +use TypeKind::{Enum, Struct}; + +struct OriginalType<'a> { + /// Attributes of the original type. + attrs: &'a [Attribute], + /// Visibility of the original type. + vis: &'a Visibility, + /// Name of the original type. + ident: &'a Ident, + /// Generics of the original type. + generics: &'a Generics, +} + +struct ProjectedType { + /// Visibility of the projected types. + vis: Visibility, + /// Name of the projected type returned by `project` method. + mut_ident: Ident, + /// Name of the projected type returned by `project_ref` method. + ref_ident: Ident, + /// Name of the projected type returned by `project_replace` method. + own_ident: Ident, + /// Lifetime on the generated projected types. + lifetime: Lifetime, + /// Generics of the projected types. + generics: Generics, + /// `where` clause of the projected types. This has an additional + /// bound generated by `insert_lifetime_and_bound` + where_clause: WhereClause, +} + +struct ProjectedVariants { + proj_variants: TokenStream, + proj_ref_variants: TokenStream, + proj_own_variants: TokenStream, + proj_arms: TokenStream, + proj_ref_arms: TokenStream, + proj_own_arms: TokenStream, +} + +#[derive(Default)] +struct ProjectedFields { + proj_pat: TokenStream, + proj_body: TokenStream, + proj_own_body: TokenStream, + proj_fields: TokenStream, + proj_ref_fields: TokenStream, + proj_own_fields: TokenStream, +} + +fn validate_struct(ident: &Ident, fields: &Fields) -> Result<()> { + if fields.is_empty() { + let msg = "#[pin_project] attribute may not be used on structs with zero fields"; + if let Fields::Unit = fields { + bail!(ident, msg) + } + bail!(fields, msg) + } + Ok(()) +} + +fn validate_enum(brace_token: token::Brace, variants: &Variants) -> Result<()> { + if variants.is_empty() { + return Err(Error::new( + brace_token.span, + "#[pin_project] attribute may not be used on enums without variants", + )); + } + let has_field = variants.iter().try_fold(false, |has_field, v| { + if let Some((_, e)) = &v.discriminant { + bail!(e, "#[pin_project] attribute may not be used on enums with discriminants"); + } else if let Some(attr) = v.attrs.find(PIN) { + bail!(attr, "#[pin] attribute may only be used on fields of structs or variants"); + } else if v.fields.is_empty() { + Ok(has_field) + } else { + Ok(true) + } + })?; + if has_field { + Ok(()) + } else { + bail!(variants, "#[pin_project] attribute may not be used on enums with zero fields"); + } +} + +fn parse_struct<'a>( + cx: &mut Context<'a>, + fields: &'a Fields, + generate: &mut GenerateTokens, +) -> Result<()> { + // Do this first for a better error message. + let packed_check = ensure_not_packed(&cx.orig, Some(fields))?; + + validate_struct(cx.orig.ident, fields)?; + + let ProjectedFields { + proj_pat, + proj_body, + proj_fields, + proj_ref_fields, + proj_own_fields, + proj_own_body, + } = match fields { + Fields::Named(_) => visit_fields(cx, None, fields, Delimiter::Brace)?, + Fields::Unnamed(_) => visit_fields(cx, None, fields, Delimiter::Parenthesis)?, + Fields::Unit => unreachable!(), + }; + + let proj_ident = &cx.proj.mut_ident; + let proj_ref_ident = &cx.proj.ref_ident; + let proj_own_ident = &cx.proj.own_ident; + let vis = &cx.proj.vis; + let mut orig_generics = cx.orig.generics.clone(); + let orig_where_clause = orig_generics.where_clause.take(); + let proj_generics = &cx.proj.generics; + let proj_where_clause = &cx.proj.where_clause; + + // For tuple structs, we need to generate `(T1, T2) where Foo: Bar` + // For non-tuple structs, we need to generate `where Foo: Bar { field1: T }` + let (where_clause_fields, where_clause_ref_fields, where_clause_own_fields) = match fields { + Fields::Named(_) => ( + quote!(#proj_where_clause #proj_fields), + quote!(#proj_where_clause #proj_ref_fields), + quote!(#orig_where_clause #proj_own_fields), + ), + Fields::Unnamed(_) => ( + quote!(#proj_fields #proj_where_clause;), + quote!(#proj_ref_fields #proj_where_clause;), + quote!(#proj_own_fields #orig_where_clause;), + ), + Fields::Unit => unreachable!(), + }; + + let (proj_attrs, proj_ref_attrs, proj_own_attrs) = proj_allowed_lints(cx); + generate.extend(cx.project, quote! { + #proj_attrs + #vis struct #proj_ident #proj_generics #where_clause_fields + }); + generate.extend(cx.project_ref, quote! { + #proj_ref_attrs + #vis struct #proj_ref_ident #proj_generics #where_clause_ref_fields + }); + if cx.project_replace.span().is_some() { + generate.extend(cx.project_replace.ident().is_some(), quote! { + #proj_own_attrs + #vis struct #proj_own_ident #orig_generics #where_clause_own_fields + }); + } + + let proj_mut_body = quote! { + let Self #proj_pat = self.get_unchecked_mut(); + #proj_ident #proj_body + }; + let proj_ref_body = quote! { + let Self #proj_pat = self.get_ref(); + #proj_ref_ident #proj_body + }; + let proj_own_body = quote! { + let Self #proj_pat = &mut *__self_ptr; + #proj_own_body + }; + generate.extend(false, make_proj_impl(cx, &proj_mut_body, &proj_ref_body, &proj_own_body)); + + generate.extend(false, packed_check); + Ok(()) +} + +fn parse_enum<'a>( + cx: &mut Context<'a>, + DataEnum { brace_token, variants, .. }: &'a DataEnum, + generate: &mut GenerateTokens, +) -> Result<()> { + if let ProjReplace::Unnamed { span } = &cx.project_replace { + return Err(Error::new( + *span, + "`project_replace` argument requires a value when used on enums", + )); + } + + // #[repr(packed)] cannot be apply on enums and will be rejected by rustc. + // However, we should not rely on the behavior of rustc that rejects this. + // https://github.com/taiki-e/pin-project/pull/324#discussion_r612388001 + // + // Do this first for a better error message. + ensure_not_packed(&cx.orig, None)?; + + validate_enum(*brace_token, variants)?; + + let ProjectedVariants { + proj_variants, + proj_ref_variants, + proj_own_variants, + proj_arms, + proj_ref_arms, + proj_own_arms, + } = visit_variants(cx, variants)?; + + let proj_ident = &cx.proj.mut_ident; + let proj_ref_ident = &cx.proj.ref_ident; + let proj_own_ident = &cx.proj.own_ident; + let vis = &cx.proj.vis; + let mut orig_generics = cx.orig.generics.clone(); + let orig_where_clause = orig_generics.where_clause.take(); + let proj_generics = &cx.proj.generics; + let proj_where_clause = &cx.proj.where_clause; + + let (proj_attrs, proj_ref_attrs, proj_own_attrs) = proj_allowed_lints(cx); + if cx.project { + generate.extend(true, quote! { + #proj_attrs + #vis enum #proj_ident #proj_generics #proj_where_clause { + #proj_variants + } + }); + } + if cx.project_ref { + generate.extend(true, quote! { + #proj_ref_attrs + #vis enum #proj_ref_ident #proj_generics #proj_where_clause { + #proj_ref_variants + } + }); + } + if cx.project_replace.ident().is_some() { + generate.extend(true, quote! { + #proj_own_attrs + #vis enum #proj_own_ident #orig_generics #orig_where_clause { + #proj_own_variants + } + }); + } + + let proj_mut_body = quote! { + match self.get_unchecked_mut() { + #proj_arms + } + }; + let proj_ref_body = quote! { + match self.get_ref() { + #proj_ref_arms + } + }; + let proj_own_body = quote! { + match &mut *__self_ptr { + #proj_own_arms + } + }; + generate.extend(false, make_proj_impl(cx, &proj_mut_body, &proj_ref_body, &proj_own_body)); + + Ok(()) +} + +fn visit_variants<'a>(cx: &mut Context<'a>, variants: &'a Variants) -> Result<ProjectedVariants> { + let mut proj_variants = TokenStream::new(); + let mut proj_ref_variants = TokenStream::new(); + let mut proj_own_variants = TokenStream::new(); + let mut proj_arms = TokenStream::new(); + let mut proj_ref_arms = TokenStream::new(); + let mut proj_own_arms = TokenStream::new(); + + for Variant { ident, fields, .. } in variants { + let ProjectedFields { + proj_pat, + proj_body, + proj_fields, + proj_ref_fields, + proj_own_fields, + proj_own_body, + } = match fields { + Fields::Named(_) => visit_fields(cx, Some(ident), fields, Delimiter::Brace)?, + Fields::Unnamed(_) => visit_fields(cx, Some(ident), fields, Delimiter::Parenthesis)?, + Fields::Unit => ProjectedFields { + proj_own_body: proj_own_body(cx, Some(ident), None, &[]), + ..Default::default() + }, + }; + + let proj_ident = &cx.proj.mut_ident; + let proj_ref_ident = &cx.proj.ref_ident; + proj_variants.extend(quote! { + #ident #proj_fields, + }); + proj_ref_variants.extend(quote! { + #ident #proj_ref_fields, + }); + proj_own_variants.extend(quote! { + #ident #proj_own_fields, + }); + proj_arms.extend(quote! { + Self::#ident #proj_pat => #proj_ident::#ident #proj_body, + }); + proj_ref_arms.extend(quote! { + Self::#ident #proj_pat => #proj_ref_ident::#ident #proj_body, + }); + proj_own_arms.extend(quote! { + Self::#ident #proj_pat => { #proj_own_body } + }); + } + + Ok(ProjectedVariants { + proj_variants, + proj_ref_variants, + proj_own_variants, + proj_arms, + proj_ref_arms, + proj_own_arms, + }) +} + +fn visit_fields<'a>( + cx: &mut Context<'a>, + variant_ident: Option<&Ident>, + fields: &'a Fields, + delim: Delimiter, +) -> Result<ProjectedFields> { + fn surround(delim: Delimiter, tokens: TokenStream) -> TokenStream { + Group::new(delim, tokens).into_token_stream() + } + + let mut proj_pat = TokenStream::new(); + let mut proj_body = TokenStream::new(); + let mut proj_fields = TokenStream::new(); + let mut proj_ref_fields = TokenStream::new(); + let mut proj_own_fields = TokenStream::new(); + let mut proj_move = TokenStream::new(); + let mut pinned_bindings = Vec::with_capacity(fields.len()); + + for (i, Field { attrs, vis, ident, colon_token, ty }) in fields.iter().enumerate() { + let binding = ident.clone().unwrap_or_else(|| format_ident!("_{}", i)); + proj_pat.extend(quote!(#binding,)); + let lifetime = &cx.proj.lifetime; + if attrs.position_exact(PIN)?.is_some() { + proj_fields.extend(quote! { + #vis #ident #colon_token ::pin_project::__private::Pin<&#lifetime mut (#ty)>, + }); + proj_ref_fields.extend(quote! { + #vis #ident #colon_token ::pin_project::__private::Pin<&#lifetime (#ty)>, + }); + proj_own_fields.extend(quote! { + #vis #ident #colon_token ::pin_project::__private::PhantomData<#ty>, + }); + proj_body.extend(quote! { + #ident #colon_token _pin_project::__private::Pin::new_unchecked(#binding), + }); + proj_move.extend(quote! { + #ident #colon_token _pin_project::__private::PhantomData, + }); + + cx.pinned_fields.push(ty); + pinned_bindings.push(binding); + } else { + proj_fields.extend(quote! { + #vis #ident #colon_token &#lifetime mut (#ty), + }); + proj_ref_fields.extend(quote! { + #vis #ident #colon_token &#lifetime (#ty), + }); + proj_own_fields.extend(quote! { + #vis #ident #colon_token #ty, + }); + proj_body.extend(quote! { + #binding, + }); + proj_move.extend(quote! { + #ident #colon_token _pin_project::__private::ptr::read(#binding), + }); + } + } + + let proj_pat = surround(delim, proj_pat); + let proj_body = surround(delim, proj_body); + let proj_fields = surround(delim, proj_fields); + let proj_ref_fields = surround(delim, proj_ref_fields); + let proj_own_fields = surround(delim, proj_own_fields); + + let proj_move = Group::new(delim, proj_move); + let proj_own_body = proj_own_body(cx, variant_ident, Some(&proj_move), &pinned_bindings); + + Ok(ProjectedFields { + proj_pat, + proj_body, + proj_own_body, + proj_fields, + proj_ref_fields, + proj_own_fields, + }) +} + +/// Generates the processing that `project_replace` does for the struct or each variant. +/// +/// Note: `pinned_fields` must be in declaration order. +fn proj_own_body( + cx: &Context<'_>, + variant_ident: Option<&Ident>, + proj_move: Option<&Group>, + pinned_fields: &[Ident], +) -> TokenStream { + let ident = &cx.proj.own_ident; + let proj_own = match variant_ident { + Some(variant_ident) => quote!(#ident::#variant_ident), + None => quote!(#ident), + }; + + // The fields of the struct and the active enum variant are dropped + // in declaration order. + // Refs: https://doc.rust-lang.org/reference/destructors.html + let pinned_fields = pinned_fields.iter().rev(); + + quote! { + // First, extract all the unpinned fields. + let __result = #proj_own #proj_move; + + // Now create guards to drop all the pinned fields. + // + // Due to a compiler bug (https://github.com/rust-lang/rust/issues/47949) + // this must be in its own scope, or else `__result` will not be dropped + // if any of the destructors panic. + { + #( + let __guard = _pin_project::__private::UnsafeDropInPlaceGuard::new(#pinned_fields); + )* + } + + // Finally, return the result. + __result + } +} + +/// Creates `Unpin` implementation for the original type. +/// +/// The kind of `Unpin` impl generated depends on `unpin_impl` field: +/// - `UnpinImpl::Unsafe` - Implements `Unpin` via `UnsafeUnpin` impl. +/// - `UnpinImpl::Negative` - Generates `Unpin` impl with bounds that will never be true. +/// - `UnpinImpl::Default` - Generates `Unpin` impl that requires `Unpin` for all pinned fields. +fn make_unpin_impl(cx: &Context<'_>) -> TokenStream { + match cx.unpin_impl { + UnpinImpl::Unsafe(span) => { + let mut proj_generics = cx.proj.generics.clone(); + let orig_ident = cx.orig.ident; + let lifetime = &cx.proj.lifetime; + + // Make the error message highlight `UnsafeUnpin` argument. + proj_generics.make_where_clause().predicates.push(parse_quote_spanned! { span => + _pin_project::__private::Wrapper<#lifetime, Self>: _pin_project::UnsafeUnpin + }); + + let (impl_generics, _, where_clause) = proj_generics.split_for_impl(); + let ty_generics = cx.orig.generics.split_for_impl().1; + + quote_spanned! { span => + impl #impl_generics _pin_project::__private::Unpin for #orig_ident #ty_generics + #where_clause + { + } + } + } + UnpinImpl::Negative(span) => { + let mut proj_generics = cx.proj.generics.clone(); + let orig_ident = cx.orig.ident; + let lifetime = &cx.proj.lifetime; + + proj_generics.make_where_clause().predicates.push(parse_quote! { + _pin_project::__private::Wrapper< + #lifetime, _pin_project::__private::PhantomPinned + >: _pin_project::__private::Unpin + }); + + let (proj_impl_generics, _, proj_where_clause) = proj_generics.split_for_impl(); + let ty_generics = cx.orig.generics.split_for_impl().1; + + // For interoperability with `forbid(unsafe_code)`, `unsafe` token should be + // call-site span. + let unsafety = <Token![unsafe]>::default(); + quote_spanned! { span => + impl #proj_impl_generics _pin_project::__private::Unpin + for #orig_ident #ty_generics + #proj_where_clause + { + } + + // Generate a dummy impl of `UnsafeUnpin`, to ensure that the user cannot implement it. + // + // To ensure that users don't accidentally write a non-functional `UnsafeUnpin` + // impls, we emit one ourselves. If the user ends up writing an `UnsafeUnpin` + // impl, they'll get a "conflicting implementations of trait" error when + // coherence checks are run. + #[doc(hidden)] + #unsafety impl #proj_impl_generics _pin_project::UnsafeUnpin + for #orig_ident #ty_generics + #proj_where_clause + { + } + } + } + UnpinImpl::Default => { + let mut full_where_clause = cx.orig.generics.where_clause.clone().unwrap(); + + // Generate a field in our new struct for every + // pinned field in the original type. + let fields = cx.pinned_fields.iter().enumerate().map(|(i, ty)| { + let field_ident = format_ident!("__field{}", i); + quote!(#field_ident: #ty) + }); + + // We could try to determine the subset of type parameters + // and lifetimes that are actually used by the pinned fields + // (as opposed to those only used by unpinned fields). + // However, this would be tricky and error-prone, since + // it's possible for users to create types that would alias + // with generic parameters (e.g. 'struct T'). + // + // Instead, we generate a use of every single type parameter + // and lifetime used in the original struct. For type parameters, + // we generate code like this: + // + // ```rust + // struct AlwaysUnpin<T: ?Sized>(PhantomData<T>) {} + // impl<T: ?Sized> Unpin for AlwaysUnpin<T> {} + // + // ... + // _field: AlwaysUnpin<(A, B, C)> + // ``` + // + // This ensures that any unused type parameters + // don't end up with `Unpin` bounds. + let lifetime_fields = cx.orig.generics.lifetimes().enumerate().map( + |(i, LifetimeDef { lifetime, .. })| { + let field_ident = format_ident!("__lifetime{}", i); + quote!(#field_ident: &#lifetime ()) + }, + ); + + let orig_ident = cx.orig.ident; + let struct_ident = format_ident!("__{}", orig_ident); + let vis = cx.orig.vis; + let lifetime = &cx.proj.lifetime; + let type_params = cx.orig.generics.type_params().map(|t| &t.ident); + let proj_generics = &cx.proj.generics; + let (proj_impl_generics, proj_ty_generics, _) = proj_generics.split_for_impl(); + let (_, ty_generics, where_clause) = cx.orig.generics.split_for_impl(); + + full_where_clause.predicates.push(parse_quote! { + #struct_ident #proj_ty_generics: _pin_project::__private::Unpin + }); + + quote! { + // This needs to have the same visibility as the original type, + // due to the limitations of the 'public in private' error. + // + // Our goal is to implement the public trait `Unpin` for + // a potentially public user type. Because of this, rust + // requires that any types mentioned in the where clause of + // our `Unpin` impl also be public. This means that our generated + // `__UnpinStruct` type must also be public. + // However, we ensure that the user can never actually reference + // this 'public' type by creating this type in the inside of `const`. + #[allow(missing_debug_implementations)] + #vis struct #struct_ident #proj_generics #where_clause { + __pin_project_use_generics: _pin_project::__private::AlwaysUnpin< + #lifetime, (#(_pin_project::__private::PhantomData<#type_params>),*) + >, + + #(#fields,)* + #(#lifetime_fields,)* + } + + impl #proj_impl_generics _pin_project::__private::Unpin + for #orig_ident #ty_generics + #full_where_clause + { + } + + // Generate a dummy impl of `UnsafeUnpin`, to ensure that the user cannot implement it. + // + // To ensure that users don't accidentally write a non-functional `UnsafeUnpin` + // impls, we emit one ourselves. If the user ends up writing an `UnsafeUnpin` + // impl, they'll get a "conflicting implementations of trait" error when + // coherence checks are run. + #[doc(hidden)] + unsafe impl #proj_impl_generics _pin_project::UnsafeUnpin + for #orig_ident #ty_generics + #full_where_clause + { + } + } + } + } +} + +/// Creates `Drop` implementation for the original type. +/// +/// The kind of `Drop` impl generated depends on `pinned_drop` field: +/// - `Some` - implements `Drop` via `PinnedDrop` impl. +/// - `None` - generates code that ensures that `Drop` trait is not implemented, +/// instead of generating `Drop` impl. +fn make_drop_impl(cx: &Context<'_>) -> TokenStream { + let ident = cx.orig.ident; + let (impl_generics, ty_generics, where_clause) = cx.orig.generics.split_for_impl(); + + if let Some(span) = cx.pinned_drop { + // For interoperability with `forbid(unsafe_code)`, `unsafe` token should be + // call-site span. + let unsafety = <Token![unsafe]>::default(); + quote_spanned! { span => + impl #impl_generics _pin_project::__private::Drop for #ident #ty_generics + #where_clause + { + fn drop(&mut self) { + #unsafety { + // Safety - we're in 'drop', so we know that 'self' will + // never move again. + let __pinned_self = _pin_project::__private::Pin::new_unchecked(self); + // We call `pinned_drop` only once. Since `PinnedDrop::drop` + // is an unsafe method and a private API, it is never called again in safe + // code *unless the user uses a maliciously crafted macro*. + _pin_project::__private::PinnedDrop::drop(__pinned_self); + } + } + } + } + } else { + // If the user does not provide a `PinnedDrop` impl, + // we need to ensure that they don't provide a `Drop` impl of their + // own. + // Based on https://github.com/upsuper/assert-impl/blob/f503255b292ab0ba8d085b657f4065403cfa46eb/src/lib.rs#L80-L87 + // + // We create a new identifier for each struct, so that the traits + // for different types do not conflict with each other. + // + // Another approach would be to provide an empty Drop impl, + // which would conflict with a user-provided Drop impl. + // However, this would trigger the compiler's special handling + // of Drop types (e.g. fields cannot be moved out of a Drop type). + // This approach prevents the creation of needless Drop impls, + // giving users more flexibility. + let trait_ident = format_ident!("{}MustNotImplDrop", ident); + + quote! { + // There are two possible cases: + // 1. The user type does not implement Drop. In this case, + // the first blanked impl will not apply to it. This code + // will compile, as there is only one impl of MustNotImplDrop for the user type + // 2. The user type does impl Drop. This will make the blanket impl applicable, + // which will then conflict with the explicit MustNotImplDrop impl below. + // This will result in a compilation error, which is exactly what we want. + trait #trait_ident {} + #[allow(clippy::drop_bounds, drop_bounds)] + impl<T: _pin_project::__private::Drop> #trait_ident for T {} + impl #impl_generics #trait_ident for #ident #ty_generics #where_clause {} + + // Generate a dummy impl of `PinnedDrop`, to ensure that the user cannot implement it. + // Since the user did not pass `PinnedDrop` to `#[pin_project]`, any `PinnedDrop` + // impl will not actually be called. Unfortunately, we can't detect this situation + // directly from either the `#[pin_project]` or `#[pinned_drop]` attributes, since + // we don't know what other attributes/impl may exist. + // + // To ensure that users don't accidentally write a non-functional `PinnedDrop` + // impls, we emit one ourselves. If the user ends up writing a `PinnedDrop` impl, + // they'll get a "conflicting implementations of trait" error when coherence + // checks are run. + #[doc(hidden)] + impl #impl_generics _pin_project::__private::PinnedDrop for #ident #ty_generics + #where_clause + { + unsafe fn drop(self: _pin_project::__private::Pin<&mut Self>) {} + } + } + } +} + +/// Creates an implementation of the projection methods. +/// +/// On structs, both the `project` and `project_ref` methods are always generated, +/// and the `project_replace` method is only generated if `ProjReplace::span` is `Some`. +/// +/// On enums, only methods that the returned projected type is named will be generated. +fn make_proj_impl( + cx: &Context<'_>, + proj_body: &TokenStream, + proj_ref_body: &TokenStream, + proj_own_body: &TokenStream, +) -> TokenStream { + let vis = &cx.proj.vis; + let lifetime = &cx.proj.lifetime; + let orig_ident = cx.orig.ident; + let proj_ident = &cx.proj.mut_ident; + let proj_ref_ident = &cx.proj.ref_ident; + let proj_own_ident = &cx.proj.own_ident; + + let orig_ty_generics = cx.orig.generics.split_for_impl().1; + let proj_ty_generics = cx.proj.generics.split_for_impl().1; + let (impl_generics, ty_generics, where_clause) = cx.orig.generics.split_for_impl(); + // TODO: For enums and project_replace, dead_code warnings should not be + // allowed because methods are not generated unless explicitly specified. + // However, there is currently no good way to allow warnings for generated + // code, so we allow warnings for all methods for now. + let allow_dead_code = quote! { #[allow(dead_code)] }; + + let mut project = Some(quote! { + #allow_dead_code + #vis fn project<#lifetime>( + self: _pin_project::__private::Pin<&#lifetime mut Self>, + ) -> #proj_ident #proj_ty_generics { + unsafe { + #proj_body + } + } + }); + let mut project_ref = Some(quote! { + #allow_dead_code + #[allow(clippy::missing_const_for_fn)] + #vis fn project_ref<#lifetime>( + self: _pin_project::__private::Pin<&#lifetime Self>, + ) -> #proj_ref_ident #proj_ty_generics { + unsafe { + #proj_ref_body + } + } + }); + let mut project_replace = cx.project_replace.span().map(|span| { + // It is enough to only set the span of the signature. + let sig = quote_spanned! { span => + #allow_dead_code + #vis fn project_replace( + self: _pin_project::__private::Pin<&mut Self>, + __replacement: Self, + ) -> #proj_own_ident #orig_ty_generics + }; + quote! { + #sig { + unsafe { + let __self_ptr: *mut Self = self.get_unchecked_mut(); + + // Destructors will run in reverse order, so next create a guard to overwrite + // `self` with the replacement value without calling destructors. + let __guard = _pin_project::__private::UnsafeOverwriteGuard::new( + __self_ptr, + __replacement, + ); + + #proj_own_body + } + } + } + }); + + if cx.kind == Enum { + if !cx.project { + project = None; + } + if !cx.project_ref { + project_ref = None; + } + if cx.project_replace.ident().is_none() { + project_replace = None; + } + } + + quote! { + impl #impl_generics #orig_ident #ty_generics #where_clause { + #project + #project_ref + #project_replace + } + } +} + +/// Checks that the `[repr(packed)]` attribute is not included. +/// +/// This currently does two checks: +/// - Checks the attributes of structs to ensure there is no `[repr(packed)]`. +/// - Generates a function that borrows fields without an unsafe block and +/// forbidding `unaligned_references` lint. +fn ensure_not_packed(orig: &OriginalType<'_>, fields: Option<&Fields>) -> Result<TokenStream> { + for meta in orig.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { + if let Meta::List(list) = meta { + if list.path.is_ident("repr") { + for repr in list.nested.iter() { + match repr { + NestedMeta::Meta(Meta::Path(path)) + | NestedMeta::Meta(Meta::List(MetaList { path, .. })) + | NestedMeta::Meta(Meta::NameValue(MetaNameValue { path, .. })) => { + if path.is_ident("packed") { + let msg = if fields.is_none() { + // #[repr(packed)] cannot be apply on enums and will be rejected by rustc. + // However, we should not rely on the behavior of rustc that rejects this. + // https://github.com/taiki-e/pin-project/pull/324#discussion_r612388001 + "#[repr(packed)] attribute should be applied to a struct or union" + } else if let NestedMeta::Meta(Meta::NameValue(..)) = repr { + // #[repr(packed = "")] is not valid format of #[repr(packed)] and will be + // rejected by rustc. + // However, we should not rely on the behavior of rustc that rejects this. + // https://github.com/taiki-e/pin-project/pull/324#discussion_r612388001 + "#[repr(packed)] attribute should not be name-value pair" + } else { + "#[pin_project] attribute may not be used on #[repr(packed)] types" + }; + bail!(repr, msg); + } + } + NestedMeta::Lit(..) => {} + } + } + } + } + } + + let fields = match fields { + Some(fields) => fields, + None => return Ok(TokenStream::new()), + }; + + // Workaround for https://github.com/taiki-e/pin-project/issues/32 + // Through the tricky use of proc macros, it's possible to bypass + // the above check for the `repr` attribute. + // To ensure that it's impossible to use pin projections on a `#[repr(packed)]` + // struct, we generate code like this: + // + // ```rust + // #[forbid(unaligned_references)] + // fn assert_not_repr_packed(val: &MyStruct) { + // let _field1 = &val.field1; + // let _field2 = &val.field2; + // ... + // let _fieldn = &val.fieldn; + // } + // ``` + // + // Taking a reference to a packed field is UB, and applying + // `#[forbid(unaligned_references)]` makes sure that doing this is a hard error. + // + // If the struct ends up having `#[repr(packed)]` applied somehow, + // this will generate an (unfriendly) error message. Under all reasonable + // circumstances, we'll detect the `#[repr(packed)]` attribute, and generate + // a much nicer error above. + // + // There is one exception: If the type of a struct field has an alignment of 1 + // (e.g. u8), it is always safe to take a reference to it, even if the struct + // is `#[repr(packed)]`. If the struct is composed entirely of types of + // alignment 1, our generated method will not trigger an error if the + // struct is `#[repr(packed)]`. + // + // Fortunately, this should have no observable consequence - `#[repr(packed)]` + // is essentially a no-op on such a type. Nevertheless, we include a test + // to ensure that the compiler doesn't ever try to copy the fields on + // such a struct when trying to drop it - which is reason we prevent + // `#[repr(packed)]` in the first place. + // + // See also https://github.com/taiki-e/pin-project/pull/34. + // + // Note: + // - Lint-based tricks aren't perfect, but they're much better than nothing: + // https://github.com/taiki-e/pin-project-lite/issues/26 + // + // - Enable both unaligned_references and safe_packed_borrows lints + // because unaligned_references lint does not exist in older compilers: + // https://github.com/taiki-e/pin-project-lite/pull/55 + // https://github.com/rust-lang/rust/pull/82525 + let mut field_refs = vec![]; + match fields { + Fields::Named(FieldsNamed { named, .. }) => { + for Field { ident, .. } in named { + field_refs.push(quote!(&this.#ident)); + } + } + Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => { + for (index, _) in unnamed.iter().enumerate() { + let index = Index::from(index); + field_refs.push(quote!(&this.#index)); + } + } + Fields::Unit => {} + } + + let (impl_generics, ty_generics, where_clause) = orig.generics.split_for_impl(); + let ident = orig.ident; + Ok(quote! { + #[forbid(unaligned_references, safe_packed_borrows)] + fn __assert_not_repr_packed #impl_generics (this: &#ident #ty_generics) #where_clause { + #(let _ = #field_refs;)* + } + }) +} diff --git a/third_party/rust/pin-project-internal/src/pin_project/mod.rs b/third_party/rust/pin-project-internal/src/pin_project/mod.rs new file mode 100644 index 0000000000..2dce78f37f --- /dev/null +++ b/third_party/rust/pin-project-internal/src/pin_project/mod.rs @@ -0,0 +1,17 @@ +mod args; +mod attribute; +mod derive; + +use proc_macro2::TokenStream; +use syn::Error; + +/// The annotation for pinned type. +const PIN: &str = "pin"; + +pub(crate) fn attribute(args: &TokenStream, input: TokenStream) -> TokenStream { + attribute::parse_attribute(args, input).unwrap_or_else(Error::into_compile_error) +} + +pub(crate) fn derive(input: TokenStream) -> TokenStream { + derive::parse_derive(input).unwrap_or_else(Error::into_compile_error) +} diff --git a/third_party/rust/pin-project-internal/src/pinned_drop.rs b/third_party/rust/pin-project-internal/src/pinned_drop.rs new file mode 100644 index 0000000000..912989dd41 --- /dev/null +++ b/third_party/rust/pin-project-internal/src/pinned_drop.rs @@ -0,0 +1,226 @@ +use proc_macro2::TokenStream; +use quote::{format_ident, quote, ToTokens}; +use syn::{ + parse_quote, spanned::Spanned, visit_mut::VisitMut, Error, FnArg, GenericArgument, ImplItem, + ItemImpl, Pat, PatIdent, Path, PathArguments, Result, ReturnType, Signature, Token, Type, + TypePath, TypeReference, +}; + +use crate::utils::{parse_as_empty, prepend_underscore_to_self, ReplaceReceiver, SliceExt}; + +pub(crate) fn attribute(args: &TokenStream, mut input: ItemImpl) -> TokenStream { + let res = (|| -> Result<()> { + parse_as_empty(args)?; + validate_impl(&input)?; + expand_impl(&mut input); + Ok(()) + })(); + + if let Err(e) = res { + let mut tokens = e.to_compile_error(); + if let Type::Path(self_ty) = &*input.self_ty { + let (impl_generics, _, where_clause) = input.generics.split_for_impl(); + + // Generate a dummy impl of `PinnedDrop`. + // In many cases, `#[pinned_drop] impl` is declared after `#[pin_project]`. + // Therefore, if `pinned_drop` compile fails, you will also get an error + // about `PinnedDrop` not being implemented. + // This can be prevented to some extent by generating a dummy + // `PinnedDrop` implementation. + // We already know that we will get a compile error, so this won't + // accidentally compile successfully. + // + // However, if `input.self_ty` is not Type::Path, there is a high possibility that + // the type does not exist (since #[pin_project] can only be used on struct/enum + // definitions), so do not generate a dummy impl. + tokens.extend(quote! { + impl #impl_generics ::pin_project::__private::PinnedDrop for #self_ty + #where_clause + { + unsafe fn drop(self: ::pin_project::__private::Pin<&mut Self>) {} + } + }); + } + tokens + } else { + input.into_token_stream() + } +} + +/// Validates the signature of given `PinnedDrop` impl. +fn validate_impl(item: &ItemImpl) -> Result<()> { + const INVALID_ITEM: &str = + "#[pinned_drop] may only be used on implementation for the `PinnedDrop` trait"; + + if let Some(attr) = item.attrs.find("pinned_drop") { + bail!(attr, "duplicate #[pinned_drop] attribute"); + } + + if let Some((_, path, _)) = &item.trait_ { + if !path.is_ident("PinnedDrop") { + bail!(path, INVALID_ITEM); + } + } else { + bail!(item.self_ty, INVALID_ITEM); + } + + if item.unsafety.is_some() { + bail!(item.unsafety, "implementing the trait `PinnedDrop` is not unsafe"); + } + if item.items.is_empty() { + bail!(item, "not all trait items implemented, missing: `drop`"); + } + + match &*item.self_ty { + Type::Path(_) => {} + ty => { + bail!(ty, "implementing the trait `PinnedDrop` on this type is unsupported"); + } + } + + item.items.iter().enumerate().try_for_each(|(i, item)| match item { + ImplItem::Const(item) => { + bail!(item, "const `{}` is not a member of trait `PinnedDrop`", item.ident) + } + ImplItem::Type(item) => { + bail!(item, "type `{}` is not a member of trait `PinnedDrop`", item.ident) + } + ImplItem::Method(method) => { + validate_sig(&method.sig)?; + if i == 0 { + Ok(()) + } else { + bail!(method, "duplicate definitions with name `drop`") + } + } + _ => unreachable!("unexpected ImplItem"), + }) +} + +/// Validates the signature of given `PinnedDrop::drop` method. +/// +/// The correct signature is: `(mut) self: (<path>::)Pin<&mut Self>` +fn validate_sig(sig: &Signature) -> Result<()> { + fn get_ty_path(ty: &Type) -> Option<&Path> { + if let Type::Path(TypePath { qself: None, path }) = ty { + Some(path) + } else { + None + } + } + + const INVALID_ARGUMENT: &str = "method `drop` must take an argument `self: Pin<&mut Self>`"; + + if sig.ident != "drop" { + bail!(sig.ident, "method `{}` is not a member of trait `PinnedDrop", sig.ident,); + } + + if let ReturnType::Type(_, ty) = &sig.output { + match &**ty { + Type::Tuple(ty) if ty.elems.is_empty() => {} + _ => bail!(ty, "method `drop` must return the unit type"), + } + } + + match sig.inputs.len() { + 1 => {} + 0 => return Err(Error::new(sig.paren_token.span, INVALID_ARGUMENT)), + _ => bail!(sig.inputs, INVALID_ARGUMENT), + } + + if let Some(FnArg::Typed(arg)) = sig.receiver() { + // (mut) self: <path> + if let Some(path) = get_ty_path(&arg.ty) { + let ty = path.segments.last().unwrap(); + if let PathArguments::AngleBracketed(args) = &ty.arguments { + // (mut) self: (<path>::)<ty><&mut <elem>..> + if let Some(GenericArgument::Type(Type::Reference(TypeReference { + mutability: Some(_), + elem, + .. + }))) = args.args.first() + { + // (mut) self: (<path>::)Pin<&mut Self> + if args.args.len() == 1 + && ty.ident == "Pin" + && get_ty_path(elem).map_or(false, |path| path.is_ident("Self")) + { + if sig.unsafety.is_some() { + bail!(sig.unsafety, "implementing the method `drop` is not unsafe"); + } + return Ok(()); + } + } + } + } + } + + bail!(sig.inputs[0], INVALID_ARGUMENT) +} + +// from: +// +// fn drop(self: Pin<&mut Self>) { +// // ... +// } +// +// into: +// +// unsafe fn drop(self: Pin<&mut Self>) { +// fn __drop_inner<T>(__self: Pin<&mut Foo<'_, T>>) { +// fn __drop_inner() {} +// // ... +// } +// __drop_inner(self); +// } +// +fn expand_impl(item: &mut ItemImpl) { + fn get_arg_pat(arg: &mut FnArg) -> Option<&mut PatIdent> { + if let FnArg::Typed(arg) = arg { + if let Pat::Ident(ident) = &mut *arg.pat { + return Some(ident); + } + } + None + } + + // `PinnedDrop` is a private trait and should not appear in docs. + item.attrs.push(parse_quote!(#[doc(hidden)])); + + let path = &mut item.trait_.as_mut().unwrap().1; + *path = parse_quote_spanned! { path.span() => + ::pin_project::__private::PinnedDrop + }; + + let method = + if let ImplItem::Method(method) = &mut item.items[0] { method } else { unreachable!() }; + + // `fn drop(mut self: Pin<&mut Self>)` -> `fn __drop_inner<T>(mut __self: Pin<&mut Receiver>)` + let drop_inner = { + let mut drop_inner = method.clone(); + let ident = format_ident!("__drop_inner"); + // Add a dummy `__drop_inner` function to prevent users call outer `__drop_inner`. + drop_inner.block.stmts.insert(0, parse_quote!(fn #ident() {})); + drop_inner.sig.ident = ident; + drop_inner.sig.generics = item.generics.clone(); + let self_pat = get_arg_pat(&mut drop_inner.sig.inputs[0]).unwrap(); + prepend_underscore_to_self(&mut self_pat.ident); + let self_ty = if let Type::Path(ty) = &*item.self_ty { ty } else { unreachable!() }; + let mut visitor = ReplaceReceiver(self_ty); + visitor.visit_signature_mut(&mut drop_inner.sig); + visitor.visit_block_mut(&mut drop_inner.block); + drop_inner + }; + + // `fn drop(mut self: Pin<&mut Self>)` -> `unsafe fn drop(self: Pin<&mut Self>)` + method.sig.unsafety = Some(<Token![unsafe]>::default()); + let self_pat = get_arg_pat(&mut method.sig.inputs[0]).unwrap(); + self_pat.mutability = None; + let self_token = &self_pat.ident; + + method.block.stmts = parse_quote! { + #[allow(clippy::needless_pass_by_value)] // This lint does not warn the receiver. + #drop_inner + __drop_inner(#self_token); + }; +} diff --git a/third_party/rust/pin-project-internal/src/utils.rs b/third_party/rust/pin-project-internal/src/utils.rs new file mode 100644 index 0000000000..27373efbae --- /dev/null +++ b/third_party/rust/pin-project-internal/src/utils.rs @@ -0,0 +1,402 @@ +use std::{iter::FromIterator, mem}; + +use proc_macro2::{Group, Spacing, Span, TokenStream, TokenTree}; +use quote::{quote, quote_spanned, ToTokens}; +use syn::{ + parse::{Parse, ParseBuffer, ParseStream}, + parse_quote, + punctuated::Punctuated, + token, + visit_mut::{self, VisitMut}, + Attribute, ExprPath, ExprStruct, Generics, Ident, Item, Lifetime, LifetimeDef, Macro, PatPath, + PatStruct, PatTupleStruct, Path, PathArguments, PredicateType, QSelf, Result, Token, Type, + TypeParamBound, TypePath, Variant, Visibility, WherePredicate, +}; + +pub(crate) type Variants = Punctuated<Variant, Token![,]>; + +macro_rules! format_err { + ($span:expr, $msg:expr $(,)?) => { + syn::Error::new_spanned(&$span as &dyn quote::ToTokens, &$msg as &dyn std::fmt::Display) + }; + ($span:expr, $($tt:tt)*) => { + format_err!($span, format!($($tt)*)) + }; +} + +macro_rules! bail { + ($($tt:tt)*) => { + return Err(format_err!($($tt)*)) + }; +} + +macro_rules! parse_quote_spanned { + ($span:expr => $($tt:tt)*) => { + syn::parse2(quote::quote_spanned!($span => $($tt)*)).unwrap_or_else(|e| panic!("{}", e)) + }; +} + +/// Determines the lifetime names. Ensure it doesn't overlap with any existing +/// lifetime names. +pub(crate) fn determine_lifetime_name(lifetime_name: &mut String, generics: &mut Generics) { + struct CollectLifetimes(Vec<String>); + + impl VisitMut for CollectLifetimes { + fn visit_lifetime_def_mut(&mut self, def: &mut LifetimeDef) { + self.0.push(def.lifetime.to_string()); + } + } + + debug_assert!(lifetime_name.starts_with('\'')); + + let mut lifetimes = CollectLifetimes(Vec::new()); + lifetimes.visit_generics_mut(generics); + + while lifetimes.0.iter().any(|name| name.starts_with(&**lifetime_name)) { + lifetime_name.push('_'); + } +} + +/// Like `insert_lifetime`, but also generates a bound of the form +/// `OriginalType<A, B>: 'lifetime`. Used when generating the definition +/// of a projection type +pub(crate) fn insert_lifetime_and_bound( + generics: &mut Generics, + lifetime: Lifetime, + orig_generics: &Generics, + orig_ident: &Ident, +) -> WherePredicate { + insert_lifetime(generics, lifetime.clone()); + + let orig_type: Type = parse_quote!(#orig_ident #orig_generics); + let mut punct = Punctuated::new(); + punct.push(TypeParamBound::Lifetime(lifetime)); + + WherePredicate::Type(PredicateType { + lifetimes: None, + bounded_ty: orig_type, + colon_token: <Token![:]>::default(), + bounds: punct, + }) +} + +/// Inserts a `lifetime` at position `0` of `generics.params`. +pub(crate) fn insert_lifetime(generics: &mut Generics, lifetime: Lifetime) { + generics.lt_token.get_or_insert_with(<Token![<]>::default); + generics.gt_token.get_or_insert_with(<Token![>]>::default); + generics.params.insert(0, LifetimeDef::new(lifetime).into()); +} + +/// Determines the visibility of the projected types and projection methods. +/// +/// If given visibility is `pub`, returned visibility is `pub(crate)`. +/// Otherwise, returned visibility is the same as given visibility. +pub(crate) fn determine_visibility(vis: &Visibility) -> Visibility { + if let Visibility::Public(token) = vis { + parse_quote_spanned!(token.pub_token.span => pub(crate)) + } else { + vis.clone() + } +} + +/// Checks if `tokens` is an empty `TokenStream`. +/// +/// This is almost equivalent to `syn::parse2::<Nothing>()`, but produces +/// a better error message and does not require ownership of `tokens`. +pub(crate) fn parse_as_empty(tokens: &TokenStream) -> Result<()> { + if tokens.is_empty() { + Ok(()) + } else { + bail!(tokens, "unexpected token: `{}`", tokens) + } +} + +pub(crate) fn respan<T>(node: &T, span: Span) -> T +where + T: ToTokens + Parse, +{ + let tokens = node.to_token_stream(); + let respanned = respan_tokens(tokens, span); + syn::parse2(respanned).unwrap() +} + +fn respan_tokens(tokens: TokenStream, span: Span) -> TokenStream { + tokens + .into_iter() + .map(|mut token| { + token.set_span(span); + token + }) + .collect() +} + +// ================================================================================================= +// extension traits + +pub(crate) trait SliceExt { + fn position_exact(&self, ident: &str) -> Result<Option<usize>>; + fn find(&self, ident: &str) -> Option<&Attribute>; +} + +impl SliceExt for [Attribute] { + /// # Errors + /// + /// - There are multiple specified attributes. + /// - The `Attribute::tokens` field of the specified attribute is not empty. + fn position_exact(&self, ident: &str) -> Result<Option<usize>> { + self.iter() + .try_fold((0, None), |(i, mut prev), attr| { + if attr.path.is_ident(ident) { + if prev.replace(i).is_some() { + bail!(attr, "duplicate #[{}] attribute", ident); + } + parse_as_empty(&attr.tokens)?; + } + Ok((i + 1, prev)) + }) + .map(|(_, pos)| pos) + } + + fn find(&self, ident: &str) -> Option<&Attribute> { + self.iter().position(|attr| attr.path.is_ident(ident)).map(|i| &self[i]) + } +} + +pub(crate) trait ParseBufferExt<'a> { + fn parenthesized(self) -> Result<ParseBuffer<'a>>; +} + +impl<'a> ParseBufferExt<'a> for ParseStream<'a> { + fn parenthesized(self) -> Result<ParseBuffer<'a>> { + let content; + let _: token::Paren = syn::parenthesized!(content in self); + Ok(content) + } +} + +impl<'a> ParseBufferExt<'a> for ParseBuffer<'a> { + fn parenthesized(self) -> Result<ParseBuffer<'a>> { + let content; + let _: token::Paren = syn::parenthesized!(content in self); + Ok(content) + } +} + +// ================================================================================================= +// visitors + +// Replace `self`/`Self` with `__self`/`self_ty`. +// Based on: +// - https://github.com/dtolnay/async-trait/blob/0.1.35/src/receiver.rs +// - https://github.com/dtolnay/async-trait/commit/6029cbf375c562ca98fa5748e9d950a8ff93b0e7 + +pub(crate) struct ReplaceReceiver<'a>(pub(crate) &'a TypePath); + +impl ReplaceReceiver<'_> { + fn self_ty(&self, span: Span) -> TypePath { + respan(self.0, span) + } + + fn self_to_qself(&self, qself: &mut Option<QSelf>, path: &mut Path) { + if path.leading_colon.is_some() { + return; + } + + let first = &path.segments[0]; + if first.ident != "Self" || !first.arguments.is_empty() { + return; + } + + if path.segments.len() == 1 { + self.self_to_expr_path(path); + return; + } + + let span = first.ident.span(); + *qself = Some(QSelf { + lt_token: Token![<](span), + ty: Box::new(self.self_ty(span).into()), + position: 0, + as_token: None, + gt_token: Token![>](span), + }); + + path.leading_colon = Some(**path.segments.pairs().next().unwrap().punct().unwrap()); + + let segments = mem::replace(&mut path.segments, Punctuated::new()); + path.segments = segments.into_pairs().skip(1).collect(); + } + + fn self_to_expr_path(&self, path: &mut Path) { + if path.leading_colon.is_some() { + return; + } + + let first = &path.segments[0]; + if first.ident != "Self" || !first.arguments.is_empty() { + return; + } + + let self_ty = self.self_ty(first.ident.span()); + let variant = mem::replace(path, self_ty.path); + for segment in &mut path.segments { + if let PathArguments::AngleBracketed(bracketed) = &mut segment.arguments { + if bracketed.colon2_token.is_none() && !bracketed.args.is_empty() { + bracketed.colon2_token = Some(<Token![::]>::default()); + } + } + } + if variant.segments.len() > 1 { + path.segments.push_punct(<Token![::]>::default()); + path.segments.extend(variant.segments.into_pairs().skip(1)); + } + } + + fn visit_token_stream(&self, tokens: &mut TokenStream) -> bool { + let mut out = Vec::new(); + let mut modified = false; + let mut iter = tokens.clone().into_iter().peekable(); + while let Some(tt) = iter.next() { + match tt { + TokenTree::Ident(mut ident) => { + modified |= prepend_underscore_to_self(&mut ident); + if ident == "Self" { + modified = true; + let self_ty = self.self_ty(ident.span()); + match iter.peek() { + Some(TokenTree::Punct(p)) + if p.as_char() == ':' && p.spacing() == Spacing::Joint => + { + let next = iter.next().unwrap(); + match iter.peek() { + Some(TokenTree::Punct(p)) if p.as_char() == ':' => { + let span = ident.span(); + out.extend(quote_spanned!(span=> <#self_ty>)); + } + _ => out.extend(quote!(#self_ty)), + } + out.push(next); + } + _ => out.extend(quote!(#self_ty)), + } + } else { + out.push(TokenTree::Ident(ident)); + } + } + TokenTree::Group(group) => { + let mut content = group.stream(); + modified |= self.visit_token_stream(&mut content); + let mut new = Group::new(group.delimiter(), content); + new.set_span(group.span()); + out.push(TokenTree::Group(new)); + } + other => out.push(other), + } + } + if modified { + *tokens = TokenStream::from_iter(out); + } + modified + } +} + +impl VisitMut for ReplaceReceiver<'_> { + // `Self` -> `Receiver` + fn visit_type_mut(&mut self, ty: &mut Type) { + if let Type::Path(node) = ty { + if node.qself.is_none() && node.path.is_ident("Self") { + *ty = self.self_ty(node.path.segments[0].ident.span()).into(); + } else { + self.visit_type_path_mut(node); + } + } else { + visit_mut::visit_type_mut(self, ty); + } + } + + // `Self::Assoc` -> `<Receiver>::Assoc` + fn visit_type_path_mut(&mut self, ty: &mut TypePath) { + if ty.qself.is_none() { + self.self_to_qself(&mut ty.qself, &mut ty.path); + } + visit_mut::visit_type_path_mut(self, ty); + } + + // `Self::method` -> `<Receiver>::method` + fn visit_expr_path_mut(&mut self, expr: &mut ExprPath) { + if expr.qself.is_none() { + self.self_to_qself(&mut expr.qself, &mut expr.path); + } + visit_mut::visit_expr_path_mut(self, expr); + } + + fn visit_expr_struct_mut(&mut self, expr: &mut ExprStruct) { + self.self_to_expr_path(&mut expr.path); + visit_mut::visit_expr_struct_mut(self, expr); + } + + fn visit_pat_path_mut(&mut self, pat: &mut PatPath) { + if pat.qself.is_none() { + self.self_to_qself(&mut pat.qself, &mut pat.path); + } + visit_mut::visit_pat_path_mut(self, pat); + } + + fn visit_pat_struct_mut(&mut self, pat: &mut PatStruct) { + self.self_to_expr_path(&mut pat.path); + visit_mut::visit_pat_struct_mut(self, pat); + } + + fn visit_pat_tuple_struct_mut(&mut self, pat: &mut PatTupleStruct) { + self.self_to_expr_path(&mut pat.path); + visit_mut::visit_pat_tuple_struct_mut(self, pat); + } + + fn visit_path_mut(&mut self, path: &mut Path) { + if path.segments.len() == 1 { + // Replace `self`, but not `self::function`. + prepend_underscore_to_self(&mut path.segments[0].ident); + } + for segment in &mut path.segments { + self.visit_path_arguments_mut(&mut segment.arguments); + } + } + + fn visit_item_mut(&mut self, item: &mut Item) { + match item { + // Visit `macro_rules!` because locally defined macros can refer to `self`. + Item::Macro(item) if item.mac.path.is_ident("macro_rules") => { + self.visit_macro_mut(&mut item.mac); + } + // Otherwise, do not recurse into nested items. + _ => {} + } + } + + fn visit_macro_mut(&mut self, mac: &mut Macro) { + // We can't tell in general whether `self` inside a macro invocation + // refers to the self in the argument list or a different self + // introduced within the macro. Heuristic: if the macro input contains + // `fn`, then `self` is more likely to refer to something other than the + // outer function's self argument. + if !contains_fn(mac.tokens.clone()) { + self.visit_token_stream(&mut mac.tokens); + } + } +} + +fn contains_fn(tokens: TokenStream) -> bool { + tokens.into_iter().any(|tt| match tt { + TokenTree::Ident(ident) => ident == "fn", + TokenTree::Group(group) => contains_fn(group.stream()), + _ => false, + }) +} + +pub(crate) fn prepend_underscore_to_self(ident: &mut Ident) -> bool { + let modified = ident == "self"; + if modified { + *ident = Ident::new("__self", ident.span()); + } + modified +} |