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, Item, PathSegment, Type, TypePath, Visibility, WhereClause,
11};
12
13use crate::diagnostics::{DiagCtxt, ErrorGuaranteed};
14
15pub(crate) mod kw {
16 syn::custom_keyword!(PinnedDrop);
17}
18
19pub(crate) enum Args {
20 Nothing(Nothing),
21 #[allow(dead_code)]
22 PinnedDrop(kw::PinnedDrop),
23}
24
25impl 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
38impl 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
47struct FieldInfo<'a> {
48 field: &'a Field,
49 pinned: bool,
50}
51
52pub(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 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 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 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 const _: () = {
189 #the_pin_data
190 #unpin_impl
191 #drop_impl
192 };
193 })
194}
195
196fn is_phantom_pinned(ty: &Type) -> bool {
197 match ty {
198 Type::Path(TypePath { qself: None, path }) => {
199 if path.segments.len() > 3 {
201 return false;
202 }
203 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
220fn 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 #[allow(
251 dead_code, non_snake_case )]
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
272fn 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 if has_pinned_drop {
278 quote! {
280 impl #impl_generics ::core::ops::Drop for #ident #ty_generics
281 #whr
282 {
283 fn drop(&mut self) {
284 let pinned = unsafe { ::core::pin::Pin::new_unchecked(self) };
287 let token = unsafe { ::pin_init::__internal::OnlyCallFromDrop::new() };
290 ::pin_init::PinnedDrop::drop(pinned, token);
291 }
292 }
293 }
294 } else {
295 quote! {
297 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 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
319fn 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 #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 #[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 #(#[doc = #structurally_pinned_fields_docs])*
391 #(#[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 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
409fn 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 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 #[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 unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).#field_name) }
454 }
455 }
456 })
457 .collect::<TokenStream>();
458 quote! {
459 #[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)] impl #impl_generics __ThePinData #ty_generics
481 #whr
482 {
483 #[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 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
511struct SelfReplacer(PathSegment);
512
513impl 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 }
537}