1use proc_macro2::TokenStream;
4use quote::{format_ident, quote};
5use syn::{
6 parse::{End, Nothing, Parse},
7 parse_quote, parse_quote_spanned,
8 spanned::Spanned,
9 visit_mut::VisitMut,
10 Attribute, Field, 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
38struct FieldInfo<'a> {
39 field: &'a Field,
40 pinned: bool,
41 cfg_attrs: Vec<&'a Attribute>,
42}
43
44pub(crate) fn pin_data(
45 args: Args,
46 input: Item,
47 dcx: &mut DiagCtxt,
48) -> Result<TokenStream, ErrorGuaranteed> {
49 let mut struct_ = match input {
50 Item::Struct(struct_) => struct_,
51 Item::Enum(enum_) => {
52 return Err(dcx.error(
53 enum_.enum_token,
54 "`#[pin_data]` only supports structs for now",
55 ));
56 }
57 Item::Union(union) => {
58 return Err(dcx.error(
59 union.union_token,
60 "`#[pin_data]` only supports structs for now",
61 ));
62 }
63 rest => {
64 return Err(dcx.error(
65 rest,
66 "`#[pin_data]` can only be applied to struct, enum and union definitions",
67 ));
68 }
69 };
70
71 let mut replacer = {
75 let name = &struct_.ident;
76 let (_, ty_generics, _) = struct_.generics.split_for_impl();
77 SelfReplacer(parse_quote!(#name #ty_generics))
78 };
79 replacer.visit_generics_mut(&mut struct_.generics);
80 replacer.visit_fields_mut(&mut struct_.fields);
81
82 let fields: Vec<FieldInfo<'_>> = struct_
83 .fields
84 .iter_mut()
85 .map(|field| {
86 let len = field.attrs.len();
87 field.attrs.retain(|a| !a.path().is_ident("pin"));
88 let pinned = len != field.attrs.len();
89
90 let cfg_attrs = field
91 .attrs
92 .iter()
93 .filter(|a| a.path().is_ident("cfg"))
94 .collect();
95
96 FieldInfo {
97 field: &*field,
98 pinned,
99 cfg_attrs,
100 }
101 })
102 .collect();
103
104 for field in &fields {
105 let ident = field.field.ident.as_ref().unwrap();
106
107 if !field.pinned && is_phantom_pinned(&field.field.ty) {
108 dcx.warn(
109 field.field,
110 format!(
111 "The field `{ident}` of type `PhantomPinned` only has an effect \
112 if it has the `#[pin]` attribute",
113 ),
114 );
115 }
116 }
117
118 let unpin_impl = generate_unpin_impl(&struct_.ident, &struct_.generics, &fields);
119 let drop_impl = generate_drop_impl(&struct_.ident, &struct_.generics, args);
120 let projections =
121 generate_projections(&struct_.vis, &struct_.ident, &struct_.generics, &fields);
122 let the_pin_data =
123 generate_the_pin_data(&struct_.vis, &struct_.ident, &struct_.generics, &fields);
124
125 Ok(quote! {
126 #struct_
127 #projections
128 const _: () = {
131 #the_pin_data
132 #unpin_impl
133 #drop_impl
134 };
135 })
136}
137
138fn is_phantom_pinned(ty: &Type) -> bool {
139 match ty {
140 Type::Path(TypePath { qself: None, path }) => {
141 if path.segments.len() > 3 {
143 return false;
144 }
145 if path.leading_colon.is_some() && path.segments.len() != 3 {
148 return false;
149 }
150 let expected: Vec<&[&str]> = vec![&["PhantomPinned"], &["marker"], &["core", "std"]];
151 for (actual, expected) in path.segments.iter().rev().zip(expected) {
152 if !actual.arguments.is_empty() || expected.iter().all(|e| actual.ident != e) {
153 return false;
154 }
155 }
156 true
157 }
158 _ => false,
159 }
160}
161
162fn generate_unpin_impl(
163 ident: &Ident,
164 generics: &Generics,
165 fields: &[FieldInfo<'_>],
166) -> TokenStream {
167 let (_, ty_generics, _) = generics.split_for_impl();
168 let mut generics_with_pin_lt = generics.clone();
169 generics_with_pin_lt.params.insert(0, parse_quote!('__pin));
170 generics_with_pin_lt.make_where_clause();
171 let (
172 impl_generics_with_pin_lt,
173 ty_generics_with_pin_lt,
174 Some(WhereClause {
175 where_token,
176 predicates,
177 }),
178 ) = generics_with_pin_lt.split_for_impl()
179 else {
180 unreachable!()
181 };
182 let pinned_fields = fields.iter().filter(|f| f.pinned).map(|f| {
183 let ident = f.field.ident.as_ref().unwrap();
184 let ty = &f.field.ty;
185 let cfg_attrs = &f.cfg_attrs;
186 quote!(
187 #(#cfg_attrs)*
188 #ident: #ty
189 )
190 });
191 quote! {
192 #[allow(
195 dead_code, non_snake_case )]
198 struct __Unpin #generics_with_pin_lt
199 #where_token
200 #predicates
201 {
202 __phantom_pin: ::pin_init::__internal::PhantomInvariantLifetime<'__pin>,
203 __phantom: ::pin_init::__internal::PhantomInvariant<#ident #ty_generics>,
204 #(#pinned_fields),*
205 }
206
207 #[doc(hidden)]
208 impl #impl_generics_with_pin_lt ::core::marker::Unpin for #ident #ty_generics
209 #where_token
210 __Unpin #ty_generics_with_pin_lt: ::core::marker::Unpin,
211 #predicates
212 {}
213 }
214}
215
216fn generate_drop_impl(ident: &Ident, generics: &Generics, args: Args) -> TokenStream {
217 let (impl_generics, ty_generics, whr) = generics.split_for_impl();
218 let has_pinned_drop = matches!(args, Args::PinnedDrop(_));
219 if has_pinned_drop {
222 quote! {
224 impl #impl_generics ::core::ops::Drop for #ident #ty_generics
225 #whr
226 {
227 fn drop(&mut self) {
228 let pinned = unsafe { ::core::pin::Pin::new_unchecked(self) };
231 let token = unsafe { ::pin_init::__internal::OnlyCallFromDrop::new() };
234 ::pin_init::PinnedDrop::drop(pinned, token);
235 }
236 }
237 }
238 } else {
239 quote! {
241 trait MustNotImplDrop {}
245 #[expect(drop_bounds)]
246 impl<T: ::core::ops::Drop + ?::core::marker::Sized> MustNotImplDrop for T {}
247 impl #impl_generics MustNotImplDrop for #ident #ty_generics
248 #whr
249 {}
250 #[expect(non_camel_case_types)]
254 trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {}
255 impl<T: ::pin_init::PinnedDrop + ?::core::marker::Sized>
256 UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {}
257 impl #impl_generics
258 UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for #ident #ty_generics
259 #whr
260 {}
261 }
262 }
263}
264
265fn generate_projections(
266 vis: &Visibility,
267 ident: &Ident,
268 generics: &Generics,
269 fields: &[FieldInfo<'_>],
270) -> TokenStream {
271 let (impl_generics, ty_generics, _) = generics.split_for_impl();
272 let mut generics_with_pin_lt = generics.clone();
273 generics_with_pin_lt.params.insert(0, parse_quote!('__pin));
274 let (_, ty_generics_with_pin_lt, whr) = generics_with_pin_lt.split_for_impl();
275 let projection = format_ident!("{ident}Projection");
276 let this = format_ident!("this");
277
278 let (fields_decl, fields_proj): (Vec<_>, Vec<_>) = fields
279 .iter()
280 .map(|field| {
281 let Field { vis, ident, ty, .. } = &field.field;
282 let cfg_attrs = &field.cfg_attrs;
283
284 let ident = ident
285 .as_ref()
286 .expect("only structs with named fields are supported");
287 if field.pinned {
288 (
289 quote!(
290 #(#cfg_attrs)*
291 #vis #ident: ::core::pin::Pin<&'__pin mut #ty>,
292 ),
293 quote!(
294 #(#cfg_attrs)*
295 #ident: unsafe { ::core::pin::Pin::new_unchecked(&mut #this.#ident) },
297 ),
298 )
299 } else {
300 (
301 quote!(
302 #(#cfg_attrs)*
303 #vis #ident: &'__pin mut #ty,
304 ),
305 quote!(
306 #(#cfg_attrs)*
307 #ident: &mut #this.#ident,
308 ),
309 )
310 }
311 })
312 .collect();
313 let structurally_pinned_fields_docs = fields
314 .iter()
315 .filter(|f| f.pinned)
316 .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap()));
317 let not_structurally_pinned_fields_docs = fields
318 .iter()
319 .filter(|f| !f.pinned)
320 .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap()));
321 let docs = format!(" Pin-projections of [`{ident}`]");
322 quote! {
323 #[doc = #docs]
324 #[allow(dead_code, non_snake_case)]
327 #[doc(hidden)]
328 #vis struct #projection #generics_with_pin_lt
329 #whr
330 {
331 #(#fields_decl)*
332 ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>,
333 }
334
335 impl #impl_generics #ident #ty_generics
336 #whr
337 {
338 #(#[doc = #structurally_pinned_fields_docs])*
342 #(#[doc = #not_structurally_pinned_fields_docs])*
345 #[inline]
346 #vis fn project<'__pin>(
347 self: ::core::pin::Pin<&'__pin mut Self>,
348 ) -> #projection #ty_generics_with_pin_lt {
349 let #this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) };
351 #projection {
352 #(#fields_proj)*
353 ___pin_phantom_data: ::core::marker::PhantomData,
354 }
355 }
356 }
357 }
358}
359
360fn generate_the_pin_data(
361 vis: &Visibility,
362 struct_name: &Ident,
363 generics: &Generics,
364 fields: &[FieldInfo<'_>],
365) -> TokenStream {
366 let (impl_generics, ty_generics, whr) = generics.split_for_impl();
367
368 let field_accessors = fields
373 .iter()
374 .map(|f| {
375 let Field { vis, ident, ty, .. } = f.field;
376 let cfg_attrs = &f.cfg_attrs;
377
378 let field_name = ident
379 .as_ref()
380 .expect("only structs with named fields are supported");
381 let pin_marker = if f.pinned {
382 quote!(Pinned)
383 } else {
384 quote!(Unpinned)
385 };
386 quote! {
387 #(#cfg_attrs)*
394 #[allow(non_snake_case)]
397 #[inline(always)]
398 #vis unsafe fn #field_name(
399 self,
400 slot: *mut #struct_name #ty_generics,
401 ) -> ::pin_init::__internal::Slot<::pin_init::__internal::#pin_marker, #ty> {
402 unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).#field_name) }
407 }
408 }
409 })
410 .collect::<TokenStream>();
411 quote! {
412 #[doc(hidden)]
415 #vis struct __ThePinData #generics
416 #whr
417 {
418 __phantom: ::pin_init::__internal::PhantomInvariant<#struct_name #ty_generics>,
419 }
420
421 impl #impl_generics ::core::clone::Clone for __ThePinData #ty_generics
422 #whr
423 {
424 fn clone(&self) -> Self { *self }
425 }
426
427 impl #impl_generics ::core::marker::Copy for __ThePinData #ty_generics
428 #whr
429 {}
430
431 #[allow(dead_code)] #[expect(clippy::missing_safety_doc)]
433 impl #impl_generics __ThePinData #ty_generics
434 #whr
435 {
436 #[inline(always)]
438 #vis fn __make_closure<__F, __E>(self, f: __F) -> __F
439 where
440 __F: FnOnce(*mut #struct_name #ty_generics) ->
441 ::core::result::Result<::pin_init::__internal::InitOk, __E>,
442 {
443 f
444 }
445
446 #field_accessors
447 }
448
449 unsafe impl #impl_generics ::pin_init::__internal::HasPinData for #struct_name #ty_generics
452 #whr
453 {
454 type PinData = __ThePinData #ty_generics;
455
456 unsafe fn __pin_data() -> Self::PinData {
457 __ThePinData { __phantom: ::pin_init::__internal::PhantomInvariant::new() }
458 }
459 }
460 }
461}
462
463struct SelfReplacer(PathSegment);
464
465impl VisitMut for SelfReplacer {
466 fn visit_path_mut(&mut self, i: &mut syn::Path) {
467 if i.is_ident("Self") {
468 let span = i.span();
469 let seg = &self.0;
470 *i = parse_quote_spanned!(span=> #seg);
471 } else {
472 syn::visit_mut::visit_path_mut(self, i);
473 }
474 }
475
476 fn visit_path_segment_mut(&mut self, seg: &mut PathSegment) {
477 if seg.ident == "Self" {
478 let span = seg.span();
479 let this = &self.0;
480 *seg = parse_quote_spanned!(span=> #this);
481 } else {
482 syn::visit_mut::visit_path_segment_mut(self, seg);
483 }
484 }
485
486 fn visit_item_mut(&mut self, _: &mut Item) {
487 }
489}