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