1use proc_macro2::{Delimiter, Group, Span, TokenStream};
2use quote::{format_ident, quote, quote_spanned, ToTokens};
3use syn::{
4 parse_quote, punctuated::Punctuated, token, visit_mut::VisitMut, Attribute, Data, DataEnum,
5 DeriveInput, Error, Field, Fields, FieldsNamed, FieldsUnnamed, Generics, Ident, Index,
6 Lifetime, LifetimeParam, Meta, Result, Token, Type, Variant, Visibility, WhereClause,
7};
8
9use super::{
10 args::{parse_args, Args, ProjReplace, UnpinImpl},
11 PIN,
12};
13use crate::utils::{
14 determine_lifetime_name, determine_visibility, insert_lifetime_and_bound, ReplaceReceiver,
15 SliceExt, Variants,
16};
17
18pub(super) fn parse_derive(input: TokenStream) -> Result<TokenStream> {
19 let mut input: DeriveInput = syn::parse2(input)?;
20
21 let mut cx;
22 let mut generate = GenerateTokens::default();
23
24 let ident = &input.ident;
25 let ty_generics = input.generics.split_for_impl().1;
26 let self_ty = parse_quote!(#ident #ty_generics);
27 let mut visitor = ReplaceReceiver(&self_ty);
28 visitor.visit_generics_mut(&mut input.generics);
29 visitor.visit_data_mut(&mut input.data);
30
31 match &input.data {
32 Data::Struct(data) => {
33 cx = Context::new(&input.attrs, &input.vis, ident, &mut input.generics, Struct)?;
34 parse_struct(&mut cx, &data.fields, &mut generate)?;
35 }
36 Data::Enum(data) => {
37 cx = Context::new(&input.attrs, &input.vis, ident, &mut input.generics, Enum)?;
38 parse_enum(&mut cx, data, &mut generate)?;
39 }
40 Data::Union(_) => {
41 bail!(input, "#[pin_project] attribute may only be used on structs or enums");
42 }
43 }
44
45 Ok(generate.into_tokens(&cx))
46}
47
48#[derive(Default)]
49struct GenerateTokens {
50 exposed: TokenStream,
51 scoped: TokenStream,
52}
53
54impl GenerateTokens {
55 fn extend(&mut self, expose: bool, tokens: TokenStream) {
56 if expose {
57 self.exposed.extend(tokens);
58 } else {
59 self.scoped.extend(tokens);
60 }
61 }
62
63 fn into_tokens(self, cx: &Context<'_>) -> TokenStream {
64 let mut tokens = self.exposed;
65 let scoped = self.scoped;
66
67 let unpin_impl = make_unpin_impl(cx);
68 let drop_impl = make_drop_impl(cx);
69 let allowed_lints = global_allowed_lints();
70
71 tokens.extend(quote! {
72 // All items except projected types are generated inside a `const` scope.
73 // This makes it impossible for user code to refer to these types.
74 // However, this prevents Rustdoc from displaying docs for any
75 // of our types. In particular, users cannot see the
76 // automatically generated `Unpin` impl for the '__UnpinStruct' types
77 //
78 // Previously, we provided a flag to correctly document the
79 // automatically generated `Unpin` impl by using def-site hygiene,
80 // but it is now removed.
81 //
82 // Refs:
83 // - https://github.com/rust-lang/rust/issues/63281
84 // - https://github.com/taiki-e/pin-project/pull/53#issuecomment-525906867
85 // - https://github.com/taiki-e/pin-project/pull/70
86 #allowed_lints
87 #[allow(unused_qualifications)]
88 #[allow(clippy::semicolon_if_nothing_returned)]
89 #[allow(clippy::use_self)]
90 #[allow(clippy::used_underscore_binding)]
91 const _: () = {
92 #[allow(unused_extern_crates)]
93 extern crate pin_project as _pin_project;
94 #scoped
95 #unpin_impl
96 #drop_impl
97 };
98 });
99 tokens
100 }
101}
102
103/// Returns attributes that should be applied to all generated code.
104fn global_allowed_lints() -> TokenStream {
105 quote! {
106 #[allow(box_pointers)] // This lint warns use of the `Box` type.
107 #[allow(deprecated)]
108 #[allow(explicit_outlives_requirements)] // https://github.com/rust-lang/rust/issues/60993
109 #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058
110 #[allow(unreachable_pub)] // This lint warns `pub` field in private struct.
111 #[allow(unused_tuple_struct_fields)]
112 // This lint warns of `clippy::*` generated by external macros.
113 // We allow this lint for compatibility with older compilers.
114 #[allow(clippy::unknown_clippy_lints)]
115 #[allow(clippy::pattern_type_mismatch)]
116 #[allow(clippy::redundant_pub_crate)] // This lint warns `pub(crate)` field in private struct.
117 #[allow(clippy::type_repetition_in_bounds)] // https://github.com/rust-lang/rust-clippy/issues/4326
118 }
119}
120
121/// Returns attributes used on projected types.
122fn proj_allowed_lints(cx: &Context<'_>) -> (TokenStream, TokenStream, TokenStream) {
123 let large_enum_variant = if cx.kind == Enum {
124 Some(quote! {
125 #[allow(variant_size_differences)]
126 #[allow(clippy::large_enum_variant)]
127 })
128 } else {
129 None
130 };
131 let global_allowed_lints = global_allowed_lints();
132 let proj_mut_allowed_lints = if cx.project { Some(&global_allowed_lints) } else { None };
133 let proj_mut = quote! {
134 #proj_mut_allowed_lints
135 #[allow(dead_code)] // This lint warns unused fields/variants.
136 #[allow(clippy::mut_mut)] // This lint warns `&mut &mut <ty>`.
137 };
138 let proj_ref_allowed_lints = if cx.project_ref { Some(&global_allowed_lints) } else { None };
139 let proj_ref = quote! {
140 #proj_ref_allowed_lints
141 #[allow(dead_code)] // This lint warns unused fields/variants.
142 #[allow(clippy::ref_option_ref)] // This lint warns `&Option<&<ty>>`.
143 };
144 let proj_own_allowed_lints =
145 if cx.project_replace.ident().is_some() { Some(&global_allowed_lints) } else { None };
146 let proj_own = quote! {
147 #proj_own_allowed_lints
148 #[allow(dead_code)] // This lint warns unused fields/variants.
149 #large_enum_variant
150 };
151 (proj_mut, proj_ref, proj_own)
152}
153
154struct Context<'a> {
155 /// The original type.
156 orig: OriginalType<'a>,
157 /// The projected types.
158 proj: ProjectedType,
159 /// Types of the pinned fields.
160 pinned_fields: Vec<&'a Type>,
161 /// Kind of the original type: struct or enum
162 kind: TypeKind,
163
164 /// `PinnedDrop` argument.
165 pinned_drop: Option<Span>,
166 /// `UnsafeUnpin` or `!Unpin` argument.
167 unpin_impl: UnpinImpl,
168 /// `project` argument.
169 project: bool,
170 /// `project_ref` argument.
171 project_ref: bool,
172 /// `project_replace [= <ident>]` argument.
173 project_replace: ProjReplace,
174}
175
176impl<'a> Context<'a> {
177 fn new(
178 attrs: &'a [Attribute],
179 vis: &'a Visibility,
180 ident: &'a Ident,
181 generics: &'a mut Generics,
182 kind: TypeKind,
183 ) -> Result<Self> {
184 let Args { pinned_drop, unpin_impl, project, project_ref, project_replace } =
185 parse_args(attrs)?;
186
187 if let Some(name) = [project.as_ref(), project_ref.as_ref(), project_replace.ident()]
188 .iter()
189 .filter_map(Option::as_ref)
190 .find(|name| **name == ident)
191 {
192 bail!(name, "name `{}` is the same as the original type name", name);
193 }
194
195 let mut lifetime_name = String::from("'pin");
196 determine_lifetime_name(&mut lifetime_name, generics);
197 let lifetime = Lifetime::new(&lifetime_name, Span::call_site());
198
199 let ty_generics = generics.split_for_impl().1;
200 let ty_generics_as_generics = parse_quote!(#ty_generics);
201 let mut proj_generics = generics.clone();
202 let pred = insert_lifetime_and_bound(
203 &mut proj_generics,
204 lifetime.clone(),
205 &ty_generics_as_generics,
206 ident,
207 );
208 let mut where_clause = generics.make_where_clause().clone();
209 where_clause.predicates.push(pred);
210
211 let own_ident = project_replace
212 .ident()
213 .cloned()
214 .unwrap_or_else(|| format_ident!("__{}ProjectionOwned", ident));
215
216 Ok(Self {
217 kind,
218 pinned_drop,
219 unpin_impl,
220 project: project.is_some(),
221 project_ref: project_ref.is_some(),
222 project_replace,
223 proj: ProjectedType {
224 vis: determine_visibility(vis),
225 mut_ident: project.unwrap_or_else(|| format_ident!("__{}Projection", ident)),
226 ref_ident: project_ref.unwrap_or_else(|| format_ident!("__{}ProjectionRef", ident)),
227 own_ident,
228 lifetime,
229 generics: proj_generics,
230 where_clause,
231 },
232 orig: OriginalType { attrs, vis, ident, generics },
233 pinned_fields: Vec::new(),
234 })
235 }
236}
237
238#[derive(Copy, Clone, PartialEq)]
239enum TypeKind {
240 Enum,
241 Struct,
242}
243
244use TypeKind::{Enum, Struct};
245
246struct OriginalType<'a> {
247 /// Attributes of the original type.
248 attrs: &'a [Attribute],
249 /// Visibility of the original type.
250 vis: &'a Visibility,
251 /// Name of the original type.
252 ident: &'a Ident,
253 /// Generics of the original type.
254 generics: &'a Generics,
255}
256
257struct ProjectedType {
258 /// Visibility of the projected types.
259 vis: Visibility,
260 /// Name of the projected type returned by `project` method.
261 mut_ident: Ident,
262 /// Name of the projected type returned by `project_ref` method.
263 ref_ident: Ident,
264 /// Name of the projected type returned by `project_replace` method.
265 own_ident: Ident,
266 /// Lifetime on the generated projected types.
267 lifetime: Lifetime,
268 /// Generics of the projected types.
269 generics: Generics,
270 /// `where` clause of the projected types. This has an additional
271 /// bound generated by `insert_lifetime_and_bound`
272 where_clause: WhereClause,
273}
274
275struct ProjectedVariants {
276 proj_variants: TokenStream,
277 proj_ref_variants: TokenStream,
278 proj_own_variants: TokenStream,
279 proj_arms: TokenStream,
280 proj_ref_arms: TokenStream,
281 proj_own_arms: TokenStream,
282}
283
284#[derive(Default)]
285struct ProjectedFields {
286 proj_pat: TokenStream,
287 proj_body: TokenStream,
288 proj_own_body: TokenStream,
289 proj_fields: TokenStream,
290 proj_ref_fields: TokenStream,
291 proj_own_fields: TokenStream,
292}
293
294fn validate_struct(ident: &Ident, fields: &Fields) -> Result<()> {
295 if fields.is_empty() {
296 let msg = "#[pin_project] attribute may not be used on structs with zero fields";
297 if let Fields::Unit = fields {
298 bail!(ident, msg)
299 }
300 bail!(fields, msg)
301 }
302 Ok(())
303}
304
305fn validate_enum(brace_token: token::Brace, variants: &Variants) -> Result<()> {
306 if variants.is_empty() {
307 return Err(Error::new(
308 brace_token.span.join(),
309 "#[pin_project] attribute may not be used on enums without variants",
310 ));
311 }
312 let has_field = variants.iter().try_fold(false, |has_field, v| {
313 if let Some((_, e)) = &v.discriminant {
314 bail!(e, "#[pin_project] attribute may not be used on enums with discriminants");
315 } else if let Some(attr) = v.attrs.find(PIN) {
316 bail!(attr, "#[pin] attribute may only be used on fields of structs or variants");
317 } else if v.fields.is_empty() {
318 Ok(has_field)
319 } else {
320 Ok(true)
321 }
322 })?;
323 if has_field {
324 Ok(())
325 } else {
326 bail!(variants, "#[pin_project] attribute may not be used on enums with zero fields");
327 }
328}
329
330fn parse_struct<'a>(
331 cx: &mut Context<'a>,
332 fields: &'a Fields,
333 generate: &mut GenerateTokens,
334) -> Result<()> {
335 // Do this first for a better error message.
336 let packed_check = ensure_not_packed(&cx.orig, Some(fields))?;
337
338 validate_struct(cx.orig.ident, fields)?;
339
340 let ProjectedFields {
341 proj_pat,
342 proj_body,
343 proj_fields,
344 proj_ref_fields,
345 proj_own_fields,
346 proj_own_body,
347 } = match fields {
348 Fields::Named(_) => visit_fields(cx, None, fields, Delimiter::Brace)?,
349 Fields::Unnamed(_) => visit_fields(cx, None, fields, Delimiter::Parenthesis)?,
350 Fields::Unit => unreachable!(),
351 };
352
353 let proj_ident = &cx.proj.mut_ident;
354 let proj_ref_ident = &cx.proj.ref_ident;
355 let proj_own_ident = &cx.proj.own_ident;
356 let vis = &cx.proj.vis;
357 let mut orig_generics = cx.orig.generics.clone();
358 let orig_where_clause = orig_generics.where_clause.take();
359 let proj_generics = &cx.proj.generics;
360 let proj_where_clause = &cx.proj.where_clause;
361
362 // For tuple structs, we need to generate `(T1, T2) where Foo: Bar`
363 // For non-tuple structs, we need to generate `where Foo: Bar { field1: T }`
364 let (where_clause_fields, where_clause_ref_fields, where_clause_own_fields) = match fields {
365 Fields::Named(_) => (
366 quote!(#proj_where_clause #proj_fields),
367 quote!(#proj_where_clause #proj_ref_fields),
368 quote!(#orig_where_clause #proj_own_fields),
369 ),
370 Fields::Unnamed(_) => (
371 quote!(#proj_fields #proj_where_clause;),
372 quote!(#proj_ref_fields #proj_where_clause;),
373 quote!(#proj_own_fields #orig_where_clause;),
374 ),
375 Fields::Unit => unreachable!(),
376 };
377
378 let (proj_attrs, proj_ref_attrs, proj_own_attrs) = proj_allowed_lints(cx);
379 generate.extend(cx.project, quote! {
380 #proj_attrs
381 #vis struct #proj_ident #proj_generics #where_clause_fields
382 });
383 generate.extend(cx.project_ref, quote! {
384 #proj_ref_attrs
385 #vis struct #proj_ref_ident #proj_generics #where_clause_ref_fields
386 });
387 if cx.project_replace.span().is_some() {
388 generate.extend(cx.project_replace.ident().is_some(), quote! {
389 #proj_own_attrs
390 #vis struct #proj_own_ident #orig_generics #where_clause_own_fields
391 });
392 }
393
394 let proj_mut_body = quote! {
395 let Self #proj_pat = self.get_unchecked_mut();
396 #proj_ident #proj_body
397 };
398 let proj_ref_body = quote! {
399 let Self #proj_pat = self.get_ref();
400 #proj_ref_ident #proj_body
401 };
402 let proj_own_body = quote! {
403 let Self #proj_pat = &mut *__self_ptr;
404 #proj_own_body
405 };
406 generate.extend(false, make_proj_impl(cx, &proj_mut_body, &proj_ref_body, &proj_own_body));
407
408 generate.extend(false, packed_check);
409 Ok(())
410}
411
412fn parse_enum<'a>(
413 cx: &mut Context<'a>,
414 DataEnum { brace_token, variants, .. }: &'a DataEnum,
415 generate: &mut GenerateTokens,
416) -> Result<()> {
417 if let ProjReplace::Unnamed { span } = &cx.project_replace {
418 return Err(Error::new(
419 *span,
420 "`project_replace` argument requires a value when used on enums",
421 ));
422 }
423
424 // #[repr(packed)] cannot be apply on enums and will be rejected by rustc.
425 // However, we should not rely on the behavior of rustc that rejects this.
426 // https://github.com/taiki-e/pin-project/pull/324#discussion_r612388001
427 //
428 // Do this first for a better error message.
429 ensure_not_packed(&cx.orig, None)?;
430
431 validate_enum(*brace_token, variants)?;
432
433 let ProjectedVariants {
434 proj_variants,
435 proj_ref_variants,
436 proj_own_variants,
437 proj_arms,
438 proj_ref_arms,
439 proj_own_arms,
440 } = visit_variants(cx, variants)?;
441
442 let proj_ident = &cx.proj.mut_ident;
443 let proj_ref_ident = &cx.proj.ref_ident;
444 let proj_own_ident = &cx.proj.own_ident;
445 let vis = &cx.proj.vis;
446 let mut orig_generics = cx.orig.generics.clone();
447 let orig_where_clause = orig_generics.where_clause.take();
448 let proj_generics = &cx.proj.generics;
449 let proj_where_clause = &cx.proj.where_clause;
450
451 let (proj_attrs, proj_ref_attrs, proj_own_attrs) = proj_allowed_lints(cx);
452 if cx.project {
453 generate.extend(true, quote! {
454 #proj_attrs
455 #vis enum #proj_ident #proj_generics #proj_where_clause {
456 #proj_variants
457 }
458 });
459 }
460 if cx.project_ref {
461 generate.extend(true, quote! {
462 #proj_ref_attrs
463 #vis enum #proj_ref_ident #proj_generics #proj_where_clause {
464 #proj_ref_variants
465 }
466 });
467 }
468 if cx.project_replace.ident().is_some() {
469 generate.extend(true, quote! {
470 #proj_own_attrs
471 #vis enum #proj_own_ident #orig_generics #orig_where_clause {
472 #proj_own_variants
473 }
474 });
475 }
476
477 let proj_mut_body = quote! {
478 match self.get_unchecked_mut() {
479 #proj_arms
480 }
481 };
482 let proj_ref_body = quote! {
483 match self.get_ref() {
484 #proj_ref_arms
485 }
486 };
487 let proj_own_body = quote! {
488 match &mut *__self_ptr {
489 #proj_own_arms
490 }
491 };
492 generate.extend(false, make_proj_impl(cx, &proj_mut_body, &proj_ref_body, &proj_own_body));
493
494 Ok(())
495}
496
497fn visit_variants<'a>(cx: &mut Context<'a>, variants: &'a Variants) -> Result<ProjectedVariants> {
498 let mut proj_variants = TokenStream::new();
499 let mut proj_ref_variants = TokenStream::new();
500 let mut proj_own_variants = TokenStream::new();
501 let mut proj_arms = TokenStream::new();
502 let mut proj_ref_arms = TokenStream::new();
503 let mut proj_own_arms = TokenStream::new();
504
505 for Variant { ident, fields, .. } in variants {
506 let ProjectedFields {
507 proj_pat,
508 proj_body,
509 proj_fields,
510 proj_ref_fields,
511 proj_own_fields,
512 proj_own_body,
513 } = match fields {
514 Fields::Named(_) => visit_fields(cx, Some(ident), fields, Delimiter::Brace)?,
515 Fields::Unnamed(_) => visit_fields(cx, Some(ident), fields, Delimiter::Parenthesis)?,
516 Fields::Unit => ProjectedFields {
517 proj_own_body: proj_own_body(cx, Some(ident), None, &[]),
518 ..Default::default()
519 },
520 };
521
522 let proj_ident = &cx.proj.mut_ident;
523 let proj_ref_ident = &cx.proj.ref_ident;
524 proj_variants.extend(quote! {
525 #ident #proj_fields,
526 });
527 proj_ref_variants.extend(quote! {
528 #ident #proj_ref_fields,
529 });
530 proj_own_variants.extend(quote! {
531 #ident #proj_own_fields,
532 });
533 proj_arms.extend(quote! {
534 Self::#ident #proj_pat => #proj_ident::#ident #proj_body,
535 });
536 proj_ref_arms.extend(quote! {
537 Self::#ident #proj_pat => #proj_ref_ident::#ident #proj_body,
538 });
539 proj_own_arms.extend(quote! {
540 Self::#ident #proj_pat => { #proj_own_body }
541 });
542 }
543
544 Ok(ProjectedVariants {
545 proj_variants,
546 proj_ref_variants,
547 proj_own_variants,
548 proj_arms,
549 proj_ref_arms,
550 proj_own_arms,
551 })
552}
553
554fn visit_fields<'a>(
555 cx: &mut Context<'a>,
556 variant_ident: Option<&Ident>,
557 fields: &'a Fields,
558 delim: Delimiter,
559) -> Result<ProjectedFields> {
560 fn surround(delim: Delimiter, tokens: TokenStream) -> TokenStream {
561 Group::new(delim, tokens).into_token_stream()
562 }
563
564 let mut proj_pat = TokenStream::new();
565 let mut proj_body = TokenStream::new();
566 let mut proj_fields = TokenStream::new();
567 let mut proj_ref_fields = TokenStream::new();
568 let mut proj_own_fields = TokenStream::new();
569 let mut proj_move = TokenStream::new();
570 let mut pinned_bindings = Vec::with_capacity(fields.len());
571
572 for (i, Field { attrs, vis, ident, colon_token, ty, mutability: _ }) in
573 fields.iter().enumerate()
574 {
575 let binding = ident.clone().unwrap_or_else(|| format_ident!("_{}", i));
576 proj_pat.extend(quote!(#binding,));
577 let lifetime = &cx.proj.lifetime;
578 if attrs.position_exact(PIN)?.is_some() {
579 proj_fields.extend(quote! {
580 #vis #ident #colon_token ::pin_project::__private::Pin<&#lifetime mut (#ty)>,
581 });
582 proj_ref_fields.extend(quote! {
583 #vis #ident #colon_token ::pin_project::__private::Pin<&#lifetime (#ty)>,
584 });
585 proj_own_fields.extend(quote! {
586 #vis #ident #colon_token ::pin_project::__private::PhantomData<#ty>,
587 });
588 proj_body.extend(quote! {
589 #ident #colon_token _pin_project::__private::Pin::new_unchecked(#binding),
590 });
591 proj_move.extend(quote! {
592 #ident #colon_token _pin_project::__private::PhantomData,
593 });
594
595 cx.pinned_fields.push(ty);
596 pinned_bindings.push(binding);
597 } else {
598 proj_fields.extend(quote! {
599 #vis #ident #colon_token &#lifetime mut (#ty),
600 });
601 proj_ref_fields.extend(quote! {
602 #vis #ident #colon_token &#lifetime (#ty),
603 });
604 proj_own_fields.extend(quote! {
605 #vis #ident #colon_token #ty,
606 });
607 proj_body.extend(quote! {
608 #binding,
609 });
610 proj_move.extend(quote! {
611 #ident #colon_token _pin_project::__private::ptr::read(#binding),
612 });
613 }
614 }
615
616 let proj_pat = surround(delim, proj_pat);
617 let proj_body = surround(delim, proj_body);
618 let proj_fields = surround(delim, proj_fields);
619 let proj_ref_fields = surround(delim, proj_ref_fields);
620 let proj_own_fields = surround(delim, proj_own_fields);
621
622 let proj_move = Group::new(delim, proj_move);
623 let proj_own_body = proj_own_body(cx, variant_ident, Some(&proj_move), &pinned_bindings);
624
625 Ok(ProjectedFields {
626 proj_pat,
627 proj_body,
628 proj_own_body,
629 proj_fields,
630 proj_ref_fields,
631 proj_own_fields,
632 })
633}
634
635/// Generates the processing that `project_replace` does for the struct or each variant.
636///
637/// Note: `pinned_fields` must be in declaration order.
638fn proj_own_body(
639 cx: &Context<'_>,
640 variant_ident: Option<&Ident>,
641 proj_move: Option<&Group>,
642 pinned_fields: &[Ident],
643) -> TokenStream {
644 let ident = &cx.proj.own_ident;
645 let proj_own = match variant_ident {
646 Some(variant_ident) => quote!(#ident::#variant_ident),
647 None => quote!(#ident),
648 };
649
650 // The fields of the struct and the active enum variant are dropped
651 // in declaration order.
652 // Refs: https://doc.rust-lang.org/reference/destructors.html
653 let pinned_fields = pinned_fields.iter().rev();
654
655 quote! {
656 // First, extract all the unpinned fields.
657 let __result = #proj_own #proj_move;
658
659 // Now create guards to drop all the pinned fields.
660 //
661 // Due to a compiler bug (https://github.com/rust-lang/rust/issues/47949)
662 // this must be in its own scope, or else `__result` will not be dropped
663 // if any of the destructors panic.
664 {
665 #(
666 let __guard = _pin_project::__private::UnsafeDropInPlaceGuard::new(#pinned_fields);
667 )*
668 }
669
670 // Finally, return the result.
671 __result
672 }
673}
674
675/// Creates `Unpin` implementation for the original type.
676///
677/// The kind of `Unpin` impl generated depends on `unpin_impl` field:
678/// - `UnpinImpl::Unsafe` - Implements `Unpin` via `UnsafeUnpin` impl.
679/// - `UnpinImpl::Negative` - Generates `Unpin` impl with bounds that will never be true.
680/// - `UnpinImpl::Default` - Generates `Unpin` impl that requires `Unpin` for all pinned fields.
681fn make_unpin_impl(cx: &Context<'_>) -> TokenStream {
682 match cx.unpin_impl {
683 UnpinImpl::Unsafe(span) => {
684 let mut proj_generics = cx.proj.generics.clone();
685 let orig_ident = cx.orig.ident;
686 let lifetime = &cx.proj.lifetime;
687
688 // Make the error message highlight `UnsafeUnpin` argument.
689 proj_generics.make_where_clause().predicates.push(parse_quote_spanned! { span =>
690 _pin_project::__private::Wrapper<#lifetime, Self>: _pin_project::UnsafeUnpin
691 });
692
693 let (impl_generics, _, where_clause) = proj_generics.split_for_impl();
694 let ty_generics = cx.orig.generics.split_for_impl().1;
695
696 quote_spanned! { span =>
697 impl #impl_generics _pin_project::__private::Unpin for #orig_ident #ty_generics
698 #where_clause
699 {
700 }
701 }
702 }
703 UnpinImpl::Negative(span) => {
704 let mut proj_generics = cx.proj.generics.clone();
705 let orig_ident = cx.orig.ident;
706 let lifetime = &cx.proj.lifetime;
707
708 proj_generics.make_where_clause().predicates.push(parse_quote! {
709 _pin_project::__private::Wrapper<
710 #lifetime, _pin_project::__private::PhantomPinned
711 >: _pin_project::__private::Unpin
712 });
713
714 let (proj_impl_generics, _, proj_where_clause) = proj_generics.split_for_impl();
715 let ty_generics = cx.orig.generics.split_for_impl().1;
716
717 // For interoperability with `forbid(unsafe_code)`, `unsafe` token should be
718 // call-site span.
719 let unsafety = <Token![unsafe]>::default();
720 quote_spanned! { span =>
721 #[doc(hidden)]
722 impl #proj_impl_generics _pin_project::__private::Unpin
723 for #orig_ident #ty_generics
724 #proj_where_clause
725 {
726 }
727
728 // Generate a dummy impl of `UnsafeUnpin`, to ensure that the user cannot implement it.
729 //
730 // To ensure that users don't accidentally write a non-functional `UnsafeUnpin`
731 // impls, we emit one ourselves. If the user ends up writing an `UnsafeUnpin`
732 // impl, they'll get a "conflicting implementations of trait" error when
733 // coherence checks are run.
734 #[doc(hidden)]
735 #unsafety impl #proj_impl_generics _pin_project::UnsafeUnpin
736 for #orig_ident #ty_generics
737 #proj_where_clause
738 {
739 }
740 }
741 }
742 UnpinImpl::Default => {
743 let mut full_where_clause = cx.orig.generics.where_clause.clone().unwrap();
744
745 // Generate a field in our new struct for every
746 // pinned field in the original type.
747 let fields = cx.pinned_fields.iter().enumerate().map(|(i, ty)| {
748 let field_ident = format_ident!("__field{}", i);
749 quote!(#field_ident: #ty)
750 });
751
752 // We could try to determine the subset of type parameters
753 // and lifetimes that are actually used by the pinned fields
754 // (as opposed to those only used by unpinned fields).
755 // However, this would be tricky and error-prone, since
756 // it's possible for users to create types that would alias
757 // with generic parameters (e.g. 'struct T').
758 //
759 // Instead, we generate a use of every single type parameter
760 // and lifetime used in the original struct. For type parameters,
761 // we generate code like this:
762 //
763 // ```rust
764 // struct AlwaysUnpin<T: ?Sized>(PhantomData<T>) {}
765 // impl<T: ?Sized> Unpin for AlwaysUnpin<T> {}
766 //
767 // ...
768 // _field: AlwaysUnpin<(A, B, C)>
769 // ```
770 //
771 // This ensures that any unused type parameters
772 // don't end up with `Unpin` bounds.
773 let lifetime_fields = cx.orig.generics.lifetimes().enumerate().map(
774 |(i, LifetimeParam { lifetime, .. })| {
775 let field_ident = format_ident!("__lifetime{}", i);
776 quote!(#field_ident: &#lifetime ())
777 },
778 );
779
780 let orig_ident = cx.orig.ident;
781 let struct_ident = format_ident!("__{}", orig_ident);
782 let vis = cx.orig.vis;
783 let lifetime = &cx.proj.lifetime;
784 let type_params = cx.orig.generics.type_params().map(|t| &t.ident);
785 let proj_generics = &cx.proj.generics;
786 let (proj_impl_generics, proj_ty_generics, _) = proj_generics.split_for_impl();
787 let (_, ty_generics, where_clause) = cx.orig.generics.split_for_impl();
788
789 full_where_clause.predicates.push(parse_quote! {
790 #struct_ident #proj_ty_generics: _pin_project::__private::Unpin
791 });
792
793 quote! {
794 // This needs to have the same visibility as the original type,
795 // due to the limitations of the 'public in private' error.
796 //
797 // Our goal is to implement the public trait `Unpin` for
798 // a potentially public user type. Because of this, rust
799 // requires that any types mentioned in the where clause of
800 // our `Unpin` impl also be public. This means that our generated
801 // `__UnpinStruct` type must also be public.
802 // However, we ensure that the user can never actually reference
803 // this 'public' type by creating this type in the inside of `const`.
804 #[allow(missing_debug_implementations)]
805 #vis struct #struct_ident #proj_generics #where_clause {
806 __pin_project_use_generics: _pin_project::__private::AlwaysUnpin<
807 #lifetime, (#(_pin_project::__private::PhantomData<#type_params>),*)
808 >,
809
810 #(#fields,)*
811 #(#lifetime_fields,)*
812 }
813
814 impl #proj_impl_generics _pin_project::__private::Unpin
815 for #orig_ident #ty_generics
816 #full_where_clause
817 {
818 }
819
820 // Generate a dummy impl of `UnsafeUnpin`, to ensure that the user cannot implement it.
821 //
822 // To ensure that users don't accidentally write a non-functional `UnsafeUnpin`
823 // impls, we emit one ourselves. If the user ends up writing an `UnsafeUnpin`
824 // impl, they'll get a "conflicting implementations of trait" error when
825 // coherence checks are run.
826 #[doc(hidden)]
827 unsafe impl #proj_impl_generics _pin_project::UnsafeUnpin
828 for #orig_ident #ty_generics
829 #full_where_clause
830 {
831 }
832 }
833 }
834 }
835}
836
837/// Creates `Drop` implementation for the original type.
838///
839/// The kind of `Drop` impl generated depends on `pinned_drop` field:
840/// - `Some` - implements `Drop` via `PinnedDrop` impl.
841/// - `None` - generates code that ensures that `Drop` trait is not implemented,
842/// instead of generating `Drop` impl.
843fn make_drop_impl(cx: &Context<'_>) -> TokenStream {
844 let ident = cx.orig.ident;
845 let (impl_generics, ty_generics, where_clause) = cx.orig.generics.split_for_impl();
846
847 if let Some(span) = cx.pinned_drop {
848 // For interoperability with `forbid(unsafe_code)`, `unsafe` token should be
849 // call-site span.
850 let unsafety = <Token![unsafe]>::default();
851 quote_spanned! { span =>
852 impl #impl_generics _pin_project::__private::Drop for #ident #ty_generics
853 #where_clause
854 {
855 fn drop(&mut self) {
856 #unsafety {
857 // Safety - we're in 'drop', so we know that 'self' will
858 // never move again.
859 let __pinned_self = _pin_project::__private::Pin::new_unchecked(self);
860 // We call `pinned_drop` only once. Since `PinnedDrop::drop`
861 // is an unsafe method and a private API, it is never called again in safe
862 // code *unless the user uses a maliciously crafted macro*.
863 _pin_project::__private::PinnedDrop::drop(__pinned_self);
864 }
865 }
866 }
867 }
868 } else {
869 // If the user does not provide a `PinnedDrop` impl,
870 // we need to ensure that they don't provide a `Drop` impl of their
871 // own.
872 // Based on https://github.com/upsuper/assert-impl/blob/f503255b292ab0ba8d085b657f4065403cfa46eb/src/lib.rs#L80-L87
873 //
874 // We create a new identifier for each struct, so that the traits
875 // for different types do not conflict with each other.
876 //
877 // Another approach would be to provide an empty Drop impl,
878 // which would conflict with a user-provided Drop impl.
879 // However, this would trigger the compiler's special handling
880 // of Drop types (e.g. fields cannot be moved out of a Drop type).
881 // This approach prevents the creation of needless Drop impls,
882 // giving users more flexibility.
883 let trait_ident = format_ident!("{}MustNotImplDrop", ident);
884
885 quote! {
886 // There are two possible cases:
887 // 1. The user type does not implement Drop. In this case,
888 // the first blanked impl will not apply to it. This code
889 // will compile, as there is only one impl of MustNotImplDrop for the user type
890 // 2. The user type does impl Drop. This will make the blanket impl applicable,
891 // which will then conflict with the explicit MustNotImplDrop impl below.
892 // This will result in a compilation error, which is exactly what we want.
893 trait #trait_ident {}
894 #[allow(clippy::drop_bounds, drop_bounds)]
895 impl<T: _pin_project::__private::Drop> #trait_ident for T {}
896 impl #impl_generics #trait_ident for #ident #ty_generics #where_clause {}
897
898 // Generate a dummy impl of `PinnedDrop`, to ensure that the user cannot implement it.
899 // Since the user did not pass `PinnedDrop` to `#[pin_project]`, any `PinnedDrop`
900 // impl will not actually be called. Unfortunately, we can't detect this situation
901 // directly from either the `#[pin_project]` or `#[pinned_drop]` attributes, since
902 // we don't know what other attributes/impl may exist.
903 //
904 // To ensure that users don't accidentally write a non-functional `PinnedDrop`
905 // impls, we emit one ourselves. If the user ends up writing a `PinnedDrop` impl,
906 // they'll get a "conflicting implementations of trait" error when coherence
907 // checks are run.
908 #[doc(hidden)]
909 impl #impl_generics _pin_project::__private::PinnedDrop for #ident #ty_generics
910 #where_clause
911 {
912 unsafe fn drop(self: _pin_project::__private::Pin<&mut Self>) {}
913 }
914 }
915 }
916}
917
918/// Creates an implementation of the projection methods.
919///
920/// On structs, both the `project` and `project_ref` methods are always generated,
921/// and the `project_replace` method is only generated if `ProjReplace::span` is `Some`.
922///
923/// On enums, only methods that the returned projected type is named will be generated.
924fn make_proj_impl(
925 cx: &Context<'_>,
926 proj_body: &TokenStream,
927 proj_ref_body: &TokenStream,
928 proj_own_body: &TokenStream,
929) -> TokenStream {
930 let vis = &cx.proj.vis;
931 let lifetime = &cx.proj.lifetime;
932 let orig_ident = cx.orig.ident;
933 let proj_ident = &cx.proj.mut_ident;
934 let proj_ref_ident = &cx.proj.ref_ident;
935 let proj_own_ident = &cx.proj.own_ident;
936
937 let orig_ty_generics = cx.orig.generics.split_for_impl().1;
938 let proj_ty_generics = cx.proj.generics.split_for_impl().1;
939 let (impl_generics, ty_generics, where_clause) = cx.orig.generics.split_for_impl();
940 // TODO: For enums and project_replace, dead_code warnings should not be
941 // allowed because methods are not generated unless explicitly specified.
942 // However, there is currently no good way to allow warnings for generated
943 // code, so we allow warnings for all methods for now.
944 let allow_dead_code = quote! { #[allow(dead_code)] };
945
946 let mut project = Some(quote! {
947 #allow_dead_code
948 #[inline]
949 #vis fn project<#lifetime>(
950 self: _pin_project::__private::Pin<&#lifetime mut Self>,
951 ) -> #proj_ident #proj_ty_generics {
952 unsafe {
953 #proj_body
954 }
955 }
956 });
957 let mut project_ref = Some(quote! {
958 #allow_dead_code
959 #[allow(clippy::missing_const_for_fn)]
960 #[inline]
961 #vis fn project_ref<#lifetime>(
962 self: _pin_project::__private::Pin<&#lifetime Self>,
963 ) -> #proj_ref_ident #proj_ty_generics {
964 unsafe {
965 #proj_ref_body
966 }
967 }
968 });
969 let mut project_replace = cx.project_replace.span().map(|span| {
970 // It is enough to only set the span of the signature.
971 let sig = quote_spanned! { span =>
972 #allow_dead_code
973 #[inline]
974 #vis fn project_replace(
975 self: _pin_project::__private::Pin<&mut Self>,
976 __replacement: Self,
977 ) -> #proj_own_ident #orig_ty_generics
978 };
979 quote! {
980 #sig {
981 unsafe {
982 let __self_ptr: *mut Self = self.get_unchecked_mut();
983
984 // Destructors will run in reverse order, so next create a guard to overwrite
985 // `self` with the replacement value without calling destructors.
986 let __guard = _pin_project::__private::UnsafeOverwriteGuard::new(
987 __self_ptr,
988 __replacement,
989 );
990
991 #proj_own_body
992 }
993 }
994 }
995 });
996
997 if cx.kind == Enum {
998 if !cx.project {
999 project = None;
1000 }
1001 if !cx.project_ref {
1002 project_ref = None;
1003 }
1004 if cx.project_replace.ident().is_none() {
1005 project_replace = None;
1006 }
1007 }
1008
1009 quote! {
1010 impl #impl_generics #orig_ident #ty_generics #where_clause {
1011 #project
1012 #project_ref
1013 #project_replace
1014 }
1015 }
1016}
1017
1018/// Checks that the `[repr(packed)]` attribute is not included.
1019///
1020/// This currently does two checks:
1021/// - Checks the attributes of structs to ensure there is no `[repr(packed)]`.
1022/// - Generates a function that borrows fields without an unsafe block and
1023/// forbidding `unaligned_references` lint.
1024fn ensure_not_packed(orig: &OriginalType<'_>, fields: Option<&Fields>) -> Result<TokenStream> {
1025 for attr in orig.attrs {
1026 if let Meta::List(ref list) = attr.meta {
1027 if list.path.is_ident("repr") {
1028 for repr in list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)? {
1029 if repr.path().is_ident("packed") {
1030 let msg = if fields.is_none() {
1031 // #[repr(packed)] cannot be apply on enums and will be rejected by rustc.
1032 // However, we should not rely on the behavior of rustc that rejects this.
1033 // https://github.com/taiki-e/pin-project/pull/324#discussion_r612388001
1034 "#[repr(packed)] attribute should be applied to a struct or union"
1035 } else if repr.require_name_value().is_ok() {
1036 // #[repr(packed = "")] is not valid format of #[repr(packed)] and will be
1037 // rejected by rustc.
1038 // However, we should not rely on the behavior of rustc that rejects this.
1039 // https://github.com/taiki-e/pin-project/pull/324#discussion_r612388001
1040 "#[repr(packed)] attribute should not be name-value pair"
1041 } else {
1042 "#[pin_project] attribute may not be used on #[repr(packed)] types"
1043 };
1044 bail!(repr, msg);
1045 }
1046 }
1047 }
1048 }
1049 }
1050
1051 let fields = match fields {
1052 Some(fields) => fields,
1053 None => return Ok(TokenStream::new()),
1054 };
1055
1056 // Workaround for https://github.com/taiki-e/pin-project/issues/32
1057 // Through the tricky use of proc macros, it's possible to bypass
1058 // the above check for the `repr` attribute.
1059 // To ensure that it's impossible to use pin projections on a `#[repr(packed)]`
1060 // struct, we generate code like this:
1061 //
1062 // ```rust
1063 // #[forbid(unaligned_references)]
1064 // fn assert_not_repr_packed(val: &MyStruct) {
1065 // let _field_1 = &val.field_1;
1066 // let _field_2 = &val.field_2;
1067 // ...
1068 // let _field_n = &val.field_n;
1069 // }
1070 // ```
1071 //
1072 // Taking a reference to a packed field is UB, and applying
1073 // `#[forbid(unaligned_references)]` makes sure that doing this is a hard error.
1074 //
1075 // If the struct ends up having `#[repr(packed)]` applied somehow,
1076 // this will generate an (unfriendly) error message. Under all reasonable
1077 // circumstances, we'll detect the `#[repr(packed)]` attribute, and generate
1078 // a much nicer error above.
1079 //
1080 // There is one exception: If the type of a struct field has an alignment of 1
1081 // (e.g. u8), it is always safe to take a reference to it, even if the struct
1082 // is `#[repr(packed)]`. If the struct is composed entirely of types of
1083 // alignment 1, our generated method will not trigger an error if the
1084 // struct is `#[repr(packed)]`.
1085 //
1086 // Fortunately, this should have no observable consequence - `#[repr(packed)]`
1087 // is essentially a no-op on such a type. Nevertheless, we include a test
1088 // to ensure that the compiler doesn't ever try to copy the fields on
1089 // such a struct when trying to drop it - which is reason we prevent
1090 // `#[repr(packed)]` in the first place.
1091 //
1092 // See also https://github.com/taiki-e/pin-project/pull/34.
1093 //
1094 // Note:
1095 // - Lint-based tricks aren't perfect, but they're much better than nothing:
1096 // https://github.com/taiki-e/pin-project-lite/issues/26
1097 //
1098 // - Enable both unaligned_references and safe_packed_borrows lints
1099 // because unaligned_references lint does not exist in older compilers:
1100 // https://github.com/taiki-e/pin-project-lite/pull/55
1101 // https://github.com/rust-lang/rust/pull/82525
1102 let mut field_refs = vec![];
1103 match fields {
1104 Fields::Named(FieldsNamed { named, .. }) => {
1105 for Field { ident, .. } in named {
1106 field_refs.push(quote!(&this.#ident));
1107 }
1108 }
1109 Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
1110 for (index, _) in unnamed.iter().enumerate() {
1111 let index = Index::from(index);
1112 field_refs.push(quote!(&this.#index));
1113 }
1114 }
1115 Fields::Unit => {}
1116 }
1117
1118 let (impl_generics, ty_generics, where_clause) = orig.generics.split_for_impl();
1119 let ident = orig.ident;
1120 Ok(quote! {
1121 #[forbid(unaligned_references, safe_packed_borrows)]
1122 fn __assert_not_repr_packed #impl_generics (this: &#ident #ty_generics) #where_clause {
1123 #(let _ = #field_refs;)*
1124 }
1125 })
1126}
1127