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