1use proc_macro2::TokenStream;
4use quote::{format_ident, quote, ToTokens};
5use syn::{
6 parse::{End, Nothing, Parse},
7 parse_quote, parse_quote_spanned,
8 punctuated::Punctuated,
9 spanned::Spanned,
10 visit_mut::VisitMut,
11 Field, Fields, Generics, Index, Item, ItemStruct, Member, PathSegment, Type, TypePath,
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 {
52 field: Field,
53 member: Member,
54 pinned: bool,
55}
56
57struct StructInfo {
58 args: Args,
59 struct_: ItemStruct,
60 fields: Vec<FieldInfo>,
61 is_tuple_struct: bool,
62}
63
64pub(crate) fn expand_with_cfg(
65 args: Args,
66 input: Item,
67 dcx: &mut DiagCtxt,
68) -> Result<TokenStream, ErrorGuaranteed> {
69 let mut struct_ = match input {
70 Item::Struct(struct_) => struct_,
71 Item::Enum(enum_) => {
72 return Err(dcx.error(
73 enum_.enum_token,
74 "`#[pin_data]` only supports structs for now",
75 ));
76 }
77 Item::Union(union) => {
78 return Err(dcx.error(
79 union.union_token,
80 "`#[pin_data]` only supports structs for now",
81 ));
82 }
83 rest => {
84 return Err(dcx.error(
85 rest,
86 "`#[pin_data]` can only be applied to struct, enum and union definitions",
87 ));
88 }
89 };
90
91 for (field_idx, field) in struct_.fields.iter_mut().enumerate() {
96 let cfg = field.attrs.extract_cfg_attrs();
97 if cfg.is_empty() {
98 continue;
99 }
100
101 let cfg_true_struct = quote!(#struct_);
102
103 let punctuated = match &mut struct_.fields {
104 Fields::Named(fields) => &mut fields.named,
105 Fields::Unnamed(fields) => &mut fields.unnamed,
106 Fields::Unit => unreachable!(),
107 };
108 *punctuated = std::mem::take(punctuated)
109 .into_pairs()
110 .enumerate()
111 .filter(|&(i, _)| i != field_idx)
112 .map(|(_, p)| p)
113 .collect();
114 let cfg_false_struct = quote!(#struct_);
115
116 return Ok(quote!(
120 #[cfg(all(#(#cfg,)*))]
121 #[::pin_init::pin_data(#args)]
122 #cfg_true_struct
123
124 #[cfg(not(all(#(#cfg,)*)))]
125 #[::pin_init::pin_data(#args)]
126 #cfg_false_struct
127 ));
128 }
129
130 expand(args, struct_, dcx)
131}
132
133fn expand(
134 args: Args,
135 mut struct_: ItemStruct,
136 dcx: &mut DiagCtxt,
137) -> Result<TokenStream, ErrorGuaranteed> {
138 let mut replacer = {
142 let name = &struct_.ident;
143 let (_, ty_generics, _) = struct_.generics.split_for_impl();
144 SelfReplacer(parse_quote!(#name #ty_generics))
145 };
146 replacer.visit_generics_mut(&mut struct_.generics);
147 replacer.visit_fields_mut(&mut struct_.fields);
148
149 let is_tuple_struct = matches!(struct_.fields, Fields::Unnamed(_));
150 let fields: Vec<FieldInfo> = struct_
151 .fields
152 .into_iter()
153 .enumerate()
154 .map(|(index, mut field)| {
155 let pinned = field.attrs.extract_single_attr(dcx, "pin").is_some();
156
157 assert!(
158 !field.attrs.iter().any(|a| a.path().is_ident("cfg")),
159 "cfgs should be all resolved at this point"
160 );
161 let member = match &field.ident {
162 Some(ident) => Member::Named(ident.clone()),
163 None => Member::Unnamed(Index {
164 index: index as u32,
165 span: field.span(),
166 }),
167 };
168
169 FieldInfo {
170 field,
171 member,
172 pinned,
173 }
174 })
175 .collect();
176
177 struct_.fields = Fields::Unit;
178 let info = StructInfo {
179 args,
180 struct_,
181 fields,
182 is_tuple_struct,
183 };
184
185 for field in &info.fields {
186 if !field.pinned && is_phantom_pinned(&field.field.ty) {
187 dcx.warn(
188 &field.field,
189 format!(
190 "The field {} of type `PhantomPinned` only has an effect \
191 if it has the `#[pin]` attribute",
192 field.member.display_name(),
193 ),
194 );
195 }
196 }
197
198 let struct_def = generate_struct_def(&info);
199 let unpin_impl = generate_unpin_impl(&info);
200 let drop_impl = generate_drop_impl(&info);
201 let projections = generate_projections(&info);
202 let the_pin_data = generate_the_pin_data(&info);
203
204 Ok(quote! {
205 #struct_def
206 const _: () = {
209 #projections
210 #the_pin_data
211 #unpin_impl
212 #drop_impl
213 };
214 })
215}
216
217fn is_phantom_pinned(ty: &Type) -> bool {
218 match ty {
219 Type::Path(TypePath { qself: None, path }) => {
220 if path.segments.len() > 3 {
222 return false;
223 }
224 if path.leading_colon.is_some() && path.segments.len() != 3 {
227 return false;
228 }
229 let expected: Vec<&[&str]> = vec![&["PhantomPinned"], &["marker"], &["core", "std"]];
230 for (actual, expected) in path.segments.iter().rev().zip(expected) {
231 if !actual.arguments.is_empty() || expected.iter().all(|e| actual.ident != e) {
232 return false;
233 }
234 }
235 true
236 }
237 _ => false,
238 }
239}
240
241fn generate_struct_def(info: &StructInfo) -> TokenStream {
242 let ItemStruct {
243 attrs,
244 vis,
245 struct_token,
246 ident,
247 generics,
248 fields: _,
249 semi_token,
250 } = &info.struct_;
251
252 let generated_fields = info.fields.iter().map(|field| {
253 let Field {
254 attrs,
255 vis,
256 mutability: _,
257 ident,
258 colon_token,
259 ty,
260 } = &field.field;
261
262 quote! {
263 #(#attrs)* #vis #ident #colon_token #ty
264 }
265 });
266
267 let whr = &generics.where_clause;
268
269 if info.is_tuple_struct {
270 quote!(
271 #(#attrs)*
272 #vis
273 #struct_token #ident #generics (#(#generated_fields,)*) #whr
274 #semi_token
275 )
276 } else {
277 quote!(
278 #(#attrs)*
279 #vis
280 #struct_token #ident #generics #whr {
281 #(#generated_fields,)*
282 }
283 #semi_token
284 )
285 }
286}
287
288fn generate_unpin_impl(info: &StructInfo) -> TokenStream {
289 let ItemStruct {
290 generics, ident, ..
291 } = &info.struct_;
292 let (impl_generics, ty_generics, whr) = generics.split_for_impl();
293 let predicates = whr
294 .map(|x| &x.predicates)
295 .unwrap_or(const { &Punctuated::new() });
296
297 let pinned_fields = info.fields.iter().filter(|f| f.pinned).map(|f| {
298 let ident = f.member.as_ident();
299 let ty = &f.field.ty;
300 quote!(
301 #ident: #ty
302 )
303 });
304 quote! {
305 #[allow(
308 dead_code, non_snake_case )]
311 struct __Unpin #generics #whr
312 {
313 __phantom: ::pin_init::__internal::PhantomInvariant<#ident #ty_generics>,
314 #(#pinned_fields),*
315 }
316
317 #[doc(hidden)]
318 impl #impl_generics ::core::marker::Unpin for #ident #ty_generics
319 where
320 for<'__dummy> __Unpin #ty_generics: ::core::marker::Unpin,
323 #predicates
324 {}
325 }
326}
327
328fn generate_drop_impl(info: &StructInfo) -> TokenStream {
329 let ItemStruct {
330 generics, ident, ..
331 } = &info.struct_;
332 let (impl_generics, ty_generics, whr) = generics.split_for_impl();
333 let has_pinned_drop = matches!(info.args, Args::PinnedDrop(_));
334 if has_pinned_drop {
337 quote! {
339 impl #impl_generics ::core::ops::Drop for #ident #ty_generics
340 #whr
341 {
342 fn drop(&mut self) {
343 let pinned = unsafe { ::core::pin::Pin::new_unchecked(self) };
346 let token = unsafe { ::pin_init::__internal::OnlyCallFromDrop::new() };
349 ::pin_init::PinnedDrop::drop(pinned, token);
350 }
351 }
352 }
353 } else {
354 quote! {
356 trait MustNotImplDrop {}
360 impl<T: ::core::ops::Drop + ?::core::marker::Sized> MustNotImplDrop for T {}
361 impl #impl_generics MustNotImplDrop for #ident #ty_generics
362 #whr
363 {}
364 trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {}
368 impl<T: ::pin_init::PinnedDrop + ?::core::marker::Sized>
369 UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {}
370 impl #impl_generics
371 UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for #ident #ty_generics
372 #whr
373 {}
374 }
375 }
376}
377
378fn generate_projections(info: &StructInfo) -> TokenStream {
379 let ItemStruct {
380 vis,
381 ident,
382 generics,
383 ..
384 } = &info.struct_;
385 let this_lt_generics: Generics = parse_quote!(<'__this>);
386 let generics_with_this_lt = CombinedGenerics(vec![&this_lt_generics, generics]);
387
388 let (impl_generics, ty_generics, whr) = generics.split_for_impl();
389 let (_, ty_generics_with_this_lt, _) = generics_with_this_lt.split_for_impl();
390
391 let this = format_ident!("this");
392
393 let (fields_decl, fields_proj): (Vec<_>, Vec<_>) = info
394 .fields
395 .iter()
396 .map(|field| {
397 let Field { vis, ty, .. } = &field.field;
398 let member = &field.member;
399 let name = (!info.is_tuple_struct).then(|| {
402 let ident = field.member.as_ident();
403 quote!(#ident:)
404 });
405
406 if field.pinned {
407 (
408 quote!(
409 #vis #name ::core::pin::Pin<&'__this mut #ty>,
410 ),
411 quote!(
412 #name unsafe { ::core::pin::Pin::new_unchecked(&mut #this.#member) },
414 ),
415 )
416 } else {
417 (
418 quote!(
419 #vis #name &'__this mut #ty,
420 ),
421 quote!(
422 #name &mut #this.#member,
423 ),
424 )
425 }
426 })
427 .collect();
428 let structurally_pinned_fields_docs = info
429 .fields
430 .iter()
431 .filter(|f| f.pinned)
432 .map(|f| format!(" - {}", f.member.display_name()));
433 let not_structurally_pinned_fields_docs = info
434 .fields
435 .iter()
436 .filter(|f| !f.pinned)
437 .map(|f| format!(" - {}", f.member.display_name()));
438 let docs = format!(" Pin-projections of [`{ident}`]");
439 let (projection_def, projection_init) = if info.is_tuple_struct {
440 (
441 quote! {
442 #vis struct __Projection #generics_with_this_lt (
443 #(#fields_decl)*
444 ::core::marker::PhantomData<&'__this mut #ident #ty_generics>,
445 ) #whr;
446 },
447 quote! {
448 __Projection(
449 #(#fields_proj)*
450 ::core::marker::PhantomData,
451 )
452 },
453 )
454 } else {
455 (
456 quote! {
457 #vis struct __Projection #generics_with_this_lt
458 #whr
459 {
460 #(#fields_decl)*
461 __this: ::core::marker::PhantomData<&'__this mut #ident #ty_generics>,
462 }
463 },
464 quote! {
465 __Projection {
466 #(#fields_proj)*
467 __this: ::core::marker::PhantomData,
468 }
469 },
470 )
471 };
472 quote! {
473 #[doc = #docs]
474 #[allow(dead_code, non_snake_case)]
477 #[doc(hidden)]
478 #projection_def
479
480 impl #impl_generics #ident #ty_generics
481 #whr
482 {
483 #(#[doc = #structurally_pinned_fields_docs])*
487 #(#[doc = #not_structurally_pinned_fields_docs])*
490 #[inline]
491 #vis fn project<'__this>(
492 self: ::core::pin::Pin<&'__this mut Self>,
493 ) -> __Projection #ty_generics_with_this_lt {
494 let #this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) };
496 #projection_init
497 }
498 }
499 }
500}
501
502fn generate_the_pin_data(info: &StructInfo) -> TokenStream {
503 let ItemStruct {
504 vis,
505 ident: struct_name,
506 generics,
507 ..
508 } = &info.struct_;
509 let (impl_generics, ty_generics, whr) = generics.split_for_impl();
510
511 let field_accessors = info
516 .fields
517 .iter()
518 .map(|f| {
519 let Field { vis, ty, .. } = &f.field;
520 let field_name = f.member.as_ident();
521 let member = &f.member;
522 let pin_marker = if f.pinned {
523 quote!(Pinned)
524 } else {
525 quote!(Unpinned)
526 };
527 quote! {
528 #[allow(non_snake_case)]
537 #[inline(always)]
538 #vis unsafe fn #field_name(
539 self,
540 slot: *mut #struct_name #ty_generics,
541 ) -> ::pin_init::__internal::Slot<::pin_init::__internal::#pin_marker, #ty> {
542 unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).#member) }
547 }
548 }
549 })
550 .collect::<TokenStream>();
551 quote! {
552 #[doc(hidden)]
555 #vis struct __ThePinData #generics
556 #whr
557 {
558 __phantom: ::pin_init::__internal::PhantomInvariant<#struct_name #ty_generics>,
559 }
560
561 impl #impl_generics ::core::clone::Clone for __ThePinData #ty_generics
562 #whr
563 {
564 #[inline]
565 fn clone(&self) -> Self { *self }
566 }
567
568 impl #impl_generics ::core::marker::Copy for __ThePinData #ty_generics
569 #whr
570 {}
571
572 #[allow(dead_code)] impl #impl_generics __ThePinData #ty_generics
574 #whr
575 {
576 #[inline(always)]
578 #vis fn __make_closure<__F, __E>(self, f: __F) -> __F
579 where
580 __F: FnOnce(*mut #struct_name #ty_generics) ->
581 ::core::result::Result<::pin_init::__internal::InitOk, __E>,
582 {
583 f
584 }
585
586 #field_accessors
587 }
588
589 unsafe impl #impl_generics ::pin_init::__internal::HasPinData for #struct_name #ty_generics
592 #whr
593 {
594 type PinData = __ThePinData #ty_generics;
595
596 #[inline]
597 fn __pin_data(_: ::pin_init::__internal::InitData<Self>) -> Self::PinData {
598 __ThePinData { __phantom: ::pin_init::__internal::PhantomInvariant::new() }
599 }
600 }
601 }
602}
603
604struct SelfReplacer(PathSegment);
605
606impl VisitMut for SelfReplacer {
607 fn visit_path_mut(&mut self, i: &mut syn::Path) {
608 if i.is_ident("Self") {
609 let span = i.span();
610 let seg = &self.0;
611 *i = parse_quote_spanned!(span=> #seg);
612 } else {
613 syn::visit_mut::visit_path_mut(self, i);
614 }
615 }
616
617 fn visit_path_segment_mut(&mut self, seg: &mut PathSegment) {
618 if seg.ident == "Self" {
619 let span = seg.span();
620 let this = &self.0;
621 *seg = parse_quote_spanned!(span=> #this);
622 } else {
623 syn::visit_mut::visit_path_segment_mut(self, seg);
624 }
625 }
626
627 fn visit_item_mut(&mut self, _: &mut Item) {
628 }
630}