| 1 | |
| 2 |
|
| 3 | use proc_macro2::TokenStream;
|
| 4 | use quote::{format_ident, quote, ToTokens};
|
| 5 | use syn::{
|
| 6 | parse::{End, Nothing, Parse},
|
| 7 | parse_quote, parse_quote_spanned,
|
| 8 | spanned::Spanned,
|
| 9 | visit_mut::VisitMut,
|
| 10 | Field, Fields, Generics, Ident, Item, PathSegment, Type, TypePath, Visibility, WhereClause,
|
| 11 | };
|
| 12 |
|
| 13 | use crate::diagnostics::{DiagCtxt, ErrorGuaranteed};
|
| 14 |
|
| 15 | pub(crate) mod kw {
|
| 16 | syn::custom_keyword!(PinnedDrop);
|
| 17 | }
|
| 18 |
|
| 19 | pub(crate) enum Args {
|
| 20 | Nothing(Nothing),
|
| 21 | #[allow(dead_code)]
|
| 22 | PinnedDrop(kw::PinnedDrop),
|
| 23 | }
|
| 24 |
|
| 25 | impl Parse for Args {
|
| 26 | fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
|
| 27 | let lh = input.lookahead1();
|
| 28 | if lh.peek(End) {
|
| 29 | input.parse().map(Self::Nothing)
|
| 30 | } else if lh.peek(kw::PinnedDrop) {
|
| 31 | input.parse().map(Self::PinnedDrop)
|
| 32 | } else {
|
| 33 | Err(lh.error())
|
| 34 | }
|
| 35 | }
|
| 36 | }
|
| 37 |
|
| 38 | impl ToTokens for Args {
|
| 39 | fn to_tokens(&self, tokens: &mut TokenStream) {
|
| 40 | match self {
|
| 41 | Self::Nothing(_) => (),
|
| 42 | Self::PinnedDrop(kw) => kw.to_tokens(tokens),
|
| 43 | }
|
| 44 | }
|
| 45 | }
|
| 46 |
|
| 47 | struct FieldInfo<'a> {
|
| 48 | field: &'a Field,
|
| 49 | pinned: bool,
|
| 50 | }
|
| 51 |
|
| 52 | pub(crate) fn pin_data(
|
| 53 | args: Args,
|
| 54 | input: Item,
|
| 55 | dcx: &mut DiagCtxt,
|
| 56 | ) -> Result<TokenStream, ErrorGuaranteed> {
|
| 57 | let mut struct_ = match input {
|
| 58 | Item::Struct(struct_) => struct_,
|
| 59 | Item::Enum(enum_) => {
|
| 60 | return Err(dcx.error(
|
| 61 | enum_.enum_token,
|
| 62 | "`#[pin_data]` only supports structs for now",
|
| 63 | ));
|
| 64 | }
|
| 65 | Item::Union(union) => {
|
| 66 | return Err(dcx.error(
|
| 67 | union.union_token,
|
| 68 | "`#[pin_data]` only supports structs for now",
|
| 69 | ));
|
| 70 | }
|
| 71 | rest => {
|
| 72 | return Err(dcx.error(
|
| 73 | rest,
|
| 74 | "`#[pin_data]` can only be applied to struct, enum and union definitions",
|
| 75 | ));
|
| 76 | }
|
| 77 | };
|
| 78 |
|
| 79 | |
| 80 | |
| 81 | |
| 82 | |
| 83 | for (field_idx, field) in struct_.fields.iter_mut().enumerate() {
|
| 84 | let cfg: Vec<_> = field
|
| 85 | .attrs
|
| 86 | .iter()
|
| 87 | .filter(|a| a.path().is_ident("cfg"))
|
| 88 | .map(|a| {
|
| 89 | a.parse_args::<TokenStream>()
|
| 90 | .expect("parse as token stream cannot fail")
|
| 91 | })
|
| 92 | .collect();
|
| 93 |
|
| 94 | if cfg.is_empty() {
|
| 95 | continue;
|
| 96 | }
|
| 97 |
|
| 98 | field.attrs.retain(|a| !a.path().is_ident("cfg"));
|
| 99 | let cfg_true_struct = quote!(#struct_);
|
| 100 |
|
| 101 | let punctuated = match &mut struct_.fields {
|
| 102 | Fields::Named(fields) => &mut fields.named,
|
| 103 | Fields::Unnamed(fields) => &mut fields.unnamed,
|
| 104 | Fields::Unit => unreachable!(),
|
| 105 | };
|
| 106 | *punctuated = std::mem::take(punctuated)
|
| 107 | .into_pairs()
|
| 108 | .enumerate()
|
| 109 | .filter(|&(i, _)| i != field_idx)
|
| 110 | .map(|(_, p)| p)
|
| 111 | .collect();
|
| 112 | let cfg_false_struct = quote!(#struct_);
|
| 113 |
|
| 114 | |
| 115 | |
| 116 | |
| 117 | return Ok(quote!(
|
| 118 | #[cfg(all(#(#cfg,)*))]
|
| 119 | #[::pin_init::pin_data(#args)]
|
| 120 | #cfg_true_struct
|
| 121 |
|
| 122 | #[cfg(not(all(#(#cfg,)*)))]
|
| 123 | #[::pin_init::pin_data(#args)]
|
| 124 | #cfg_false_struct
|
| 125 | ));
|
| 126 | }
|
| 127 |
|
| 128 | |
| 129 | |
| 130 | |
| 131 | let mut replacer = {
|
| 132 | let name = &struct_.ident;
|
| 133 | let (_, ty_generics, _) = struct_.generics.split_for_impl();
|
| 134 | SelfReplacer(parse_quote!(#name #ty_generics))
|
| 135 | };
|
| 136 | replacer.visit_generics_mut(&mut struct_.generics);
|
| 137 | replacer.visit_fields_mut(&mut struct_.fields);
|
| 138 |
|
| 139 | let fields: Vec<FieldInfo<'_>> = struct_
|
| 140 | .fields
|
| 141 | .iter_mut()
|
| 142 | .map(|field| {
|
| 143 | let len = field.attrs.len();
|
| 144 | field.attrs.retain(|a| !a.path().is_ident("pin"));
|
| 145 | let pinned_count = len - field.attrs.len();
|
| 146 | if pinned_count > 1 {
|
| 147 | dcx.error(&field, "#[pin] attribute specified more than once");
|
| 148 | }
|
| 149 |
|
| 150 | assert!(
|
| 151 | !field.attrs.iter().any(|a| a.path().is_ident("cfg")),
|
| 152 | "cfgs should be all resolved at this point"
|
| 153 | );
|
| 154 |
|
| 155 | FieldInfo {
|
| 156 | field: &*field,
|
| 157 | pinned: pinned_count != 0,
|
| 158 | }
|
| 159 | })
|
| 160 | .collect();
|
| 161 |
|
| 162 | for field in &fields {
|
| 163 | let ident = field.field.ident.as_ref().unwrap();
|
| 164 |
|
| 165 | if !field.pinned && is_phantom_pinned(&field.field.ty) {
|
| 166 | dcx.warn(
|
| 167 | field.field,
|
| 168 | format!(
|
| 169 | "The field `{ident}` of type `PhantomPinned` only has an effect \
|
| 170 | if it has the `#[pin]` attribute",
|
| 171 | ),
|
| 172 | );
|
| 173 | }
|
| 174 | }
|
| 175 |
|
| 176 | let unpin_impl = generate_unpin_impl(&struct_.ident, &struct_.generics, &fields);
|
| 177 | let drop_impl = generate_drop_impl(&struct_.ident, &struct_.generics, args);
|
| 178 | let projections =
|
| 179 | generate_projections(&struct_.vis, &struct_.ident, &struct_.generics, &fields);
|
| 180 | let the_pin_data =
|
| 181 | generate_the_pin_data(&struct_.vis, &struct_.ident, &struct_.generics, &fields);
|
| 182 |
|
| 183 | Ok(quote! {
|
| 184 | #struct_
|
| 185 | #projections
|
| 186 | |
| 187 | |
| 188 | const _: () = {
|
| 189 | #the_pin_data
|
| 190 | #unpin_impl
|
| 191 | #drop_impl
|
| 192 | };
|
| 193 | })
|
| 194 | }
|
| 195 |
|
| 196 | fn is_phantom_pinned(ty: &Type) -> bool {
|
| 197 | match ty {
|
| 198 | Type::Path(TypePath { qself: None, path }) => {
|
| 199 | |
| 200 | if path.segments.len() > 3 {
|
| 201 | return false;
|
| 202 | }
|
| 203 | |
| 204 | |
| 205 | if path.leading_colon.is_some() && path.segments.len() != 3 {
|
| 206 | return false;
|
| 207 | }
|
| 208 | let expected: Vec<&[&str]> = vec![&["PhantomPinned"], &["marker"], &["core", "std"]];
|
| 209 | for (actual, expected) in path.segments.iter().rev().zip(expected) {
|
| 210 | if !actual.arguments.is_empty() || expected.iter().all(|e| actual.ident != e) {
|
| 211 | return false;
|
| 212 | }
|
| 213 | }
|
| 214 | true
|
| 215 | }
|
| 216 | _ => false,
|
| 217 | }
|
| 218 | }
|
| 219 |
|
| 220 | fn generate_unpin_impl(
|
| 221 | ident: &Ident,
|
| 222 | generics: &Generics,
|
| 223 | fields: &[FieldInfo<'_>],
|
| 224 | ) -> TokenStream {
|
| 225 | let (_, ty_generics, _) = generics.split_for_impl();
|
| 226 | let mut generics_with_pin_lt = generics.clone();
|
| 227 | generics_with_pin_lt.params.insert(0, parse_quote!('__pin));
|
| 228 | generics_with_pin_lt.make_where_clause();
|
| 229 | let (
|
| 230 | impl_generics_with_pin_lt,
|
| 231 | ty_generics_with_pin_lt,
|
| 232 | Some(WhereClause {
|
| 233 | where_token,
|
| 234 | predicates,
|
| 235 | }),
|
| 236 | ) = generics_with_pin_lt.split_for_impl()
|
| 237 | else {
|
| 238 | unreachable!()
|
| 239 | };
|
| 240 | let pinned_fields = fields.iter().filter(|f| f.pinned).map(|f| {
|
| 241 | let ident = f.field.ident.as_ref().unwrap();
|
| 242 | let ty = &f.field.ty;
|
| 243 | quote!(
|
| 244 | #ident: #ty
|
| 245 | )
|
| 246 | });
|
| 247 | quote! {
|
| 248 | |
| 249 | |
| 250 | #[allow(
|
| 251 | dead_code, // The fields below are never used.
|
| 252 | non_snake_case // The warning will be emitted on the struct definition.
|
| 253 | )]
|
| 254 | struct __Unpin #generics_with_pin_lt
|
| 255 | #where_token
|
| 256 | #predicates
|
| 257 | {
|
| 258 | __phantom_pin: ::pin_init::__internal::PhantomInvariantLifetime<'__pin>,
|
| 259 | __phantom: ::pin_init::__internal::PhantomInvariant<#ident #ty_generics>,
|
| 260 | #(#pinned_fields),*
|
| 261 | }
|
| 262 |
|
| 263 | #[doc(hidden)]
|
| 264 | impl #impl_generics_with_pin_lt ::core::marker::Unpin for #ident #ty_generics
|
| 265 | #where_token
|
| 266 | __Unpin #ty_generics_with_pin_lt: ::core::marker::Unpin,
|
| 267 | #predicates
|
| 268 | {}
|
| 269 | }
|
| 270 | }
|
| 271 |
|
| 272 | fn generate_drop_impl(ident: &Ident, generics: &Generics, args: Args) -> TokenStream {
|
| 273 | let (impl_generics, ty_generics, whr) = generics.split_for_impl();
|
| 274 | let has_pinned_drop = matches!(args, Args::PinnedDrop(_));
|
| 275 | |
| 276 | |
| 277 | if has_pinned_drop {
|
| 278 | |
| 279 | quote! {
|
| 280 | impl #impl_generics ::core::ops::Drop for #ident #ty_generics
|
| 281 | #whr
|
| 282 | {
|
| 283 | fn drop(&mut self) {
|
| 284 | |
| 285 | |
| 286 | let pinned = unsafe { ::core::pin::Pin::new_unchecked(self) };
|
| 287 | |
| 288 | |
| 289 | let token = unsafe { ::pin_init::__internal::OnlyCallFromDrop::new() };
|
| 290 | ::pin_init::PinnedDrop::drop(pinned, token);
|
| 291 | }
|
| 292 | }
|
| 293 | }
|
| 294 | } else {
|
| 295 | |
| 296 | quote! {
|
| 297 | |
| 298 | |
| 299 | |
| 300 | trait MustNotImplDrop {}
|
| 301 | impl<T: ::core::ops::Drop + ?::core::marker::Sized> MustNotImplDrop for T {}
|
| 302 | impl #impl_generics MustNotImplDrop for #ident #ty_generics
|
| 303 | #whr
|
| 304 | {}
|
| 305 | |
| 306 | |
| 307 | |
| 308 | trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {}
|
| 309 | impl<T: ::pin_init::PinnedDrop + ?::core::marker::Sized>
|
| 310 | UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {}
|
| 311 | impl #impl_generics
|
| 312 | UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for #ident #ty_generics
|
| 313 | #whr
|
| 314 | {}
|
| 315 | }
|
| 316 | }
|
| 317 | }
|
| 318 |
|
| 319 | fn generate_projections(
|
| 320 | vis: &Visibility,
|
| 321 | ident: &Ident,
|
| 322 | generics: &Generics,
|
| 323 | fields: &[FieldInfo<'_>],
|
| 324 | ) -> TokenStream {
|
| 325 | let (impl_generics, ty_generics, _) = generics.split_for_impl();
|
| 326 | let mut generics_with_pin_lt = generics.clone();
|
| 327 | generics_with_pin_lt.params.insert(0, parse_quote!('__pin));
|
| 328 | let (_, ty_generics_with_pin_lt, whr) = generics_with_pin_lt.split_for_impl();
|
| 329 | let projection = format_ident!("{ident}Projection");
|
| 330 | let this = format_ident!("this");
|
| 331 |
|
| 332 | let (fields_decl, fields_proj): (Vec<_>, Vec<_>) = fields
|
| 333 | .iter()
|
| 334 | .map(|field| {
|
| 335 | let Field { vis, ident, ty, .. } = &field.field;
|
| 336 |
|
| 337 | let ident = ident
|
| 338 | .as_ref()
|
| 339 | .expect("only structs with named fields are supported");
|
| 340 | if field.pinned {
|
| 341 | (
|
| 342 | quote!(
|
| 343 | #vis #ident: ::core::pin::Pin<&'__pin mut #ty>,
|
| 344 | ),
|
| 345 | quote!(
|
| 346 | |
| 347 | #ident: unsafe { ::core::pin::Pin::new_unchecked(&mut #this.#ident) },
|
| 348 | ),
|
| 349 | )
|
| 350 | } else {
|
| 351 | (
|
| 352 | quote!(
|
| 353 | #vis #ident: &'__pin mut #ty,
|
| 354 | ),
|
| 355 | quote!(
|
| 356 | #ident: &mut #this.#ident,
|
| 357 | ),
|
| 358 | )
|
| 359 | }
|
| 360 | })
|
| 361 | .collect();
|
| 362 | let structurally_pinned_fields_docs = fields
|
| 363 | .iter()
|
| 364 | .filter(|f| f.pinned)
|
| 365 | .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap()));
|
| 366 | let not_structurally_pinned_fields_docs = fields
|
| 367 | .iter()
|
| 368 | .filter(|f| !f.pinned)
|
| 369 | .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap()));
|
| 370 | let docs = format!(" Pin-projections of [`{ident}`]");
|
| 371 | quote! {
|
| 372 | #[doc = #docs]
|
| 373 | |
| 374 | |
| 375 | #[allow(dead_code, non_snake_case)]
|
| 376 | #[doc(hidden)]
|
| 377 | #vis struct #projection #generics_with_pin_lt
|
| 378 | #whr
|
| 379 | {
|
| 380 | #(#fields_decl)*
|
| 381 | ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>,
|
| 382 | }
|
| 383 |
|
| 384 | impl #impl_generics #ident #ty_generics
|
| 385 | #whr
|
| 386 | {
|
| 387 | |
| 388 | |
| 389 | |
| 390 | #(#[doc = #structurally_pinned_fields_docs])*
|
| 391 | |
| 392 | |
| 393 | #(#[doc = #not_structurally_pinned_fields_docs])*
|
| 394 | #[inline]
|
| 395 | #vis fn project<'__pin>(
|
| 396 | self: ::core::pin::Pin<&'__pin mut Self>,
|
| 397 | ) -> #projection #ty_generics_with_pin_lt {
|
| 398 | |
| 399 | let #this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) };
|
| 400 | #projection {
|
| 401 | #(#fields_proj)*
|
| 402 | ___pin_phantom_data: ::core::marker::PhantomData,
|
| 403 | }
|
| 404 | }
|
| 405 | }
|
| 406 | }
|
| 407 | }
|
| 408 |
|
| 409 | fn generate_the_pin_data(
|
| 410 | vis: &Visibility,
|
| 411 | struct_name: &Ident,
|
| 412 | generics: &Generics,
|
| 413 | fields: &[FieldInfo<'_>],
|
| 414 | ) -> TokenStream {
|
| 415 | let (impl_generics, ty_generics, whr) = generics.split_for_impl();
|
| 416 |
|
| 417 | |
| 418 | |
| 419 | |
| 420 | |
| 421 | let field_accessors = fields
|
| 422 | .iter()
|
| 423 | .map(|f| {
|
| 424 | let Field { vis, ident, ty, .. } = f.field;
|
| 425 |
|
| 426 | let field_name = ident
|
| 427 | .as_ref()
|
| 428 | .expect("only structs with named fields are supported");
|
| 429 | let pin_marker = if f.pinned {
|
| 430 | quote!(Pinned)
|
| 431 | } else {
|
| 432 | quote!(Unpinned)
|
| 433 | };
|
| 434 | quote! {
|
| 435 | |
| 436 | |
| 437 | |
| 438 | |
| 439 | |
| 440 | |
| 441 | |
| 442 | |
| 443 | #[allow(non_snake_case)]
|
| 444 | #[inline(always)]
|
| 445 | #vis unsafe fn #field_name(
|
| 446 | self,
|
| 447 | slot: *mut #struct_name #ty_generics,
|
| 448 | ) -> ::pin_init::__internal::Slot<::pin_init::__internal::#pin_marker, #ty> {
|
| 449 | |
| 450 | |
| 451 | |
| 452 | |
| 453 | unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).#field_name) }
|
| 454 | }
|
| 455 | }
|
| 456 | })
|
| 457 | .collect::<TokenStream>();
|
| 458 | quote! {
|
| 459 | |
| 460 | |
| 461 | #[doc(hidden)]
|
| 462 | #vis struct __ThePinData #generics
|
| 463 | #whr
|
| 464 | {
|
| 465 | __phantom: ::pin_init::__internal::PhantomInvariant<#struct_name #ty_generics>,
|
| 466 | }
|
| 467 |
|
| 468 | impl #impl_generics ::core::clone::Clone for __ThePinData #ty_generics
|
| 469 | #whr
|
| 470 | {
|
| 471 | #[inline]
|
| 472 | fn clone(&self) -> Self { *self }
|
| 473 | }
|
| 474 |
|
| 475 | impl #impl_generics ::core::marker::Copy for __ThePinData #ty_generics
|
| 476 | #whr
|
| 477 | {}
|
| 478 |
|
| 479 | #[allow(dead_code)] |
| 480 | impl #impl_generics __ThePinData #ty_generics
|
| 481 | #whr
|
| 482 | {
|
| 483 | |
| 484 | #[inline(always)]
|
| 485 | #vis fn __make_closure<__F, __E>(self, f: __F) -> __F
|
| 486 | where
|
| 487 | __F: FnOnce(*mut #struct_name #ty_generics) ->
|
| 488 | ::core::result::Result<::pin_init::__internal::InitOk, __E>,
|
| 489 | {
|
| 490 | f
|
| 491 | }
|
| 492 |
|
| 493 | #field_accessors
|
| 494 | }
|
| 495 |
|
| 496 | |
| 497 | |
| 498 | unsafe impl #impl_generics ::pin_init::__internal::HasPinData for #struct_name #ty_generics
|
| 499 | #whr
|
| 500 | {
|
| 501 | type PinData = __ThePinData #ty_generics;
|
| 502 |
|
| 503 | #[inline]
|
| 504 | unsafe fn __pin_data() -> Self::PinData {
|
| 505 | __ThePinData { __phantom: ::pin_init::__internal::PhantomInvariant::new() }
|
| 506 | }
|
| 507 | }
|
| 508 | }
|
| 509 | }
|
| 510 |
|
| 511 | struct SelfReplacer(PathSegment);
|
| 512 |
|
| 513 | impl VisitMut for SelfReplacer {
|
| 514 | fn visit_path_mut(&mut self, i: &mut syn::Path) {
|
| 515 | if i.is_ident("Self") {
|
| 516 | let span = i.span();
|
| 517 | let seg = &self.0;
|
| 518 | *i = parse_quote_spanned!(span=> #seg);
|
| 519 | } else {
|
| 520 | syn::visit_mut::visit_path_mut(self, i);
|
| 521 | }
|
| 522 | }
|
| 523 |
|
| 524 | fn visit_path_segment_mut(&mut self, seg: &mut PathSegment) {
|
| 525 | if seg.ident == "Self" {
|
| 526 | let span = seg.span();
|
| 527 | let this = &self.0;
|
| 528 | *seg = parse_quote_spanned!(span=> #this);
|
| 529 | } else {
|
| 530 | syn::visit_mut::visit_path_segment_mut(self, seg);
|
| 531 | }
|
| 532 | }
|
| 533 |
|
| 534 | fn visit_item_mut(&mut self, _: &mut Item) {
|
| 535 | |
| 536 | }
|
| 537 | }
|