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_count = len - field.attrs.len();
89 if pinned_count > 1 {
90 dcx.error(&field, "#[pin] attribute specified more than once");
91 }
92
93 let cfg_attrs = field
94 .attrs
95 .iter()
96 .filter(|a| a.path().is_ident("cfg"))
97 .collect();
98
99 FieldInfo {
100 field: &*field,
101 pinned: pinned_count != 0,
102 cfg_attrs,
103 }
104 })
105 .collect();
106
107 for field in &fields {
108 let ident = field.field.ident.as_ref().unwrap();
109
110 if !field.pinned && is_phantom_pinned(&field.field.ty) {
111 dcx.warn(
112 field.field,
113 format!(
114 "The field `{ident}` of type `PhantomPinned` only has an effect \
115 if it has the `#[pin]` attribute",
116 ),
117 );
118 }
119 }
120
121 let unpin_impl = generate_unpin_impl(&struct_.ident, &struct_.generics, &fields);
122 let drop_impl = generate_drop_impl(&struct_.ident, &struct_.generics, args);
123 let projections =
124 generate_projections(&struct_.vis, &struct_.ident, &struct_.generics, &fields);
125 let the_pin_data =
126 generate_the_pin_data(&struct_.vis, &struct_.ident, &struct_.generics, &fields);
127
128 Ok(quote! {
129 #struct_
130 #projections
131 const _: () = {
134 #the_pin_data
135 #unpin_impl
136 #drop_impl
137 };
138 })
139}
140
141fn is_phantom_pinned(ty: &Type) -> bool {
142 match ty {
143 Type::Path(TypePath { qself: None, path }) => {
144 if path.segments.len() > 3 {
146 return false;
147 }
148 if path.leading_colon.is_some() && path.segments.len() != 3 {
151 return false;
152 }
153 let expected: Vec<&[&str]> = vec![&["PhantomPinned"], &["marker"], &["core", "std"]];
154 for (actual, expected) in path.segments.iter().rev().zip(expected) {
155 if !actual.arguments.is_empty() || expected.iter().all(|e| actual.ident != e) {
156 return false;
157 }
158 }
159 true
160 }
161 _ => false,
162 }
163}
164
165fn generate_unpin_impl(
166 ident: &Ident,
167 generics: &Generics,
168 fields: &[FieldInfo<'_>],
169) -> TokenStream {
170 let (_, ty_generics, _) = generics.split_for_impl();
171 let mut generics_with_pin_lt = generics.clone();
172 generics_with_pin_lt.params.insert(0, parse_quote!('__pin));
173 generics_with_pin_lt.make_where_clause();
174 let (
175 impl_generics_with_pin_lt,
176 ty_generics_with_pin_lt,
177 Some(WhereClause {
178 where_token,
179 predicates,
180 }),
181 ) = generics_with_pin_lt.split_for_impl()
182 else {
183 unreachable!()
184 };
185 let pinned_fields = fields.iter().filter(|f| f.pinned).map(|f| {
186 let ident = f.field.ident.as_ref().unwrap();
187 let ty = &f.field.ty;
188 let cfg_attrs = &f.cfg_attrs;
189 quote!(
190 #(#cfg_attrs)*
191 #ident: #ty
192 )
193 });
194 quote! {
195 #[allow(
198 dead_code, non_snake_case )]
201 struct __Unpin #generics_with_pin_lt
202 #where_token
203 #predicates
204 {
205 __phantom_pin: ::pin_init::__internal::PhantomInvariantLifetime<'__pin>,
206 __phantom: ::pin_init::__internal::PhantomInvariant<#ident #ty_generics>,
207 #(#pinned_fields),*
208 }
209
210 #[doc(hidden)]
211 impl #impl_generics_with_pin_lt ::core::marker::Unpin for #ident #ty_generics
212 #where_token
213 __Unpin #ty_generics_with_pin_lt: ::core::marker::Unpin,
214 #predicates
215 {}
216 }
217}
218
219fn generate_drop_impl(ident: &Ident, generics: &Generics, args: Args) -> TokenStream {
220 let (impl_generics, ty_generics, whr) = generics.split_for_impl();
221 let has_pinned_drop = matches!(args, Args::PinnedDrop(_));
222 if has_pinned_drop {
225 quote! {
227 impl #impl_generics ::core::ops::Drop for #ident #ty_generics
228 #whr
229 {
230 fn drop(&mut self) {
231 let pinned = unsafe { ::core::pin::Pin::new_unchecked(self) };
234 let token = unsafe { ::pin_init::__internal::OnlyCallFromDrop::new() };
237 ::pin_init::PinnedDrop::drop(pinned, token);
238 }
239 }
240 }
241 } else {
242 quote! {
244 trait MustNotImplDrop {}
248 impl<T: ::core::ops::Drop + ?::core::marker::Sized> MustNotImplDrop for T {}
249 impl #impl_generics MustNotImplDrop for #ident #ty_generics
250 #whr
251 {}
252 trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {}
256 impl<T: ::pin_init::PinnedDrop + ?::core::marker::Sized>
257 UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {}
258 impl #impl_generics
259 UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for #ident #ty_generics
260 #whr
261 {}
262 }
263 }
264}
265
266fn generate_projections(
267 vis: &Visibility,
268 ident: &Ident,
269 generics: &Generics,
270 fields: &[FieldInfo<'_>],
271) -> TokenStream {
272 let (impl_generics, ty_generics, _) = generics.split_for_impl();
273 let mut generics_with_pin_lt = generics.clone();
274 generics_with_pin_lt.params.insert(0, parse_quote!('__pin));
275 let (_, ty_generics_with_pin_lt, whr) = generics_with_pin_lt.split_for_impl();
276 let projection = format_ident!("{ident}Projection");
277 let this = format_ident!("this");
278
279 let (fields_decl, fields_proj): (Vec<_>, Vec<_>) = fields
280 .iter()
281 .map(|field| {
282 let Field { vis, ident, ty, .. } = &field.field;
283 let cfg_attrs = &field.cfg_attrs;
284
285 let ident = ident
286 .as_ref()
287 .expect("only structs with named fields are supported");
288 if field.pinned {
289 (
290 quote!(
291 #(#cfg_attrs)*
292 #vis #ident: ::core::pin::Pin<&'__pin mut #ty>,
293 ),
294 quote!(
295 #(#cfg_attrs)*
296 #ident: unsafe { ::core::pin::Pin::new_unchecked(&mut #this.#ident) },
298 ),
299 )
300 } else {
301 (
302 quote!(
303 #(#cfg_attrs)*
304 #vis #ident: &'__pin mut #ty,
305 ),
306 quote!(
307 #(#cfg_attrs)*
308 #ident: &mut #this.#ident,
309 ),
310 )
311 }
312 })
313 .collect();
314 let structurally_pinned_fields_docs = fields
315 .iter()
316 .filter(|f| f.pinned)
317 .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap()));
318 let not_structurally_pinned_fields_docs = fields
319 .iter()
320 .filter(|f| !f.pinned)
321 .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap()));
322 let docs = format!(" Pin-projections of [`{ident}`]");
323 quote! {
324 #[doc = #docs]
325 #[allow(dead_code, non_snake_case)]
328 #[doc(hidden)]
329 #vis struct #projection #generics_with_pin_lt
330 #whr
331 {
332 #(#fields_decl)*
333 ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>,
334 }
335
336 impl #impl_generics #ident #ty_generics
337 #whr
338 {
339 #(#[doc = #structurally_pinned_fields_docs])*
343 #(#[doc = #not_structurally_pinned_fields_docs])*
346 #[inline]
347 #vis fn project<'__pin>(
348 self: ::core::pin::Pin<&'__pin mut Self>,
349 ) -> #projection #ty_generics_with_pin_lt {
350 let #this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) };
352 #projection {
353 #(#fields_proj)*
354 ___pin_phantom_data: ::core::marker::PhantomData,
355 }
356 }
357 }
358 }
359}
360
361fn generate_the_pin_data(
362 vis: &Visibility,
363 struct_name: &Ident,
364 generics: &Generics,
365 fields: &[FieldInfo<'_>],
366) -> TokenStream {
367 let (impl_generics, ty_generics, whr) = generics.split_for_impl();
368
369 let field_accessors = fields
374 .iter()
375 .map(|f| {
376 let Field { vis, ident, ty, .. } = f.field;
377 let cfg_attrs = &f.cfg_attrs;
378
379 let field_name = ident
380 .as_ref()
381 .expect("only structs with named fields are supported");
382 let pin_marker = if f.pinned {
383 quote!(Pinned)
384 } else {
385 quote!(Unpinned)
386 };
387 quote! {
388 #(#cfg_attrs)*
395 #[allow(non_snake_case)]
398 #[inline(always)]
399 #vis unsafe fn #field_name(
400 self,
401 slot: *mut #struct_name #ty_generics,
402 ) -> ::pin_init::__internal::Slot<::pin_init::__internal::#pin_marker, #ty> {
403 unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).#field_name) }
408 }
409 }
410 })
411 .collect::<TokenStream>();
412 quote! {
413 #[doc(hidden)]
416 #vis struct __ThePinData #generics
417 #whr
418 {
419 __phantom: ::pin_init::__internal::PhantomInvariant<#struct_name #ty_generics>,
420 }
421
422 impl #impl_generics ::core::clone::Clone for __ThePinData #ty_generics
423 #whr
424 {
425 fn clone(&self) -> Self { *self }
426 }
427
428 impl #impl_generics ::core::marker::Copy for __ThePinData #ty_generics
429 #whr
430 {}
431
432 #[allow(dead_code)] 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}