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