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