@@ -7,7 +7,7 @@ use syn::{
77 parse_quote, parse_quote_spanned,
88 spanned:: Spanned ,
99 visit_mut:: VisitMut ,
10- Field , Generics , Ident , Item , PathSegment , Type , TypePath , Visibility , WhereClause ,
10+ Field , Generics , Ident , Item , Member , PathSegment , Type , TypePath , Visibility , WhereClause ,
1111} ;
1212
1313use crate :: diagnostics:: { DiagCtxt , ErrorGuaranteed } ;
@@ -35,6 +35,52 @@ impl Parse for Args {
3535 }
3636}
3737
38+ struct FieldInfo < ' a > {
39+ pinned : bool ,
40+ field : & ' a Field ,
41+ member : Member ,
42+ proj_ident : Ident ,
43+ display_name : String ,
44+ }
45+
46+ impl < ' a > FieldInfo < ' a > {
47+ fn new ( field : & ' a mut Field , idx : Option < usize > ) -> Self {
48+ // Invariant: either it's a named field OR it's a tuple field with an index.
49+ debug_assert_eq ! ( field. ident. is_some( ) , idx. is_none( ) ) ;
50+
51+ // Strip #[pin] and remember whether it was present.
52+ let mut pinned = false ;
53+ field. attrs . retain ( |a| {
54+ let is_pin = a. path ( ) . is_ident ( "pin" ) ;
55+ pinned |= is_pin;
56+ !is_pin
57+ } ) ;
58+
59+ let ( member, proj_ident, display_name) = if let Some ( ident) = field. ident . as_ref ( ) {
60+ (
61+ Member :: Named ( ident. clone ( ) ) ,
62+ ident. clone ( ) ,
63+ format ! ( "`{ident}`" ) ,
64+ )
65+ } else {
66+ let i = idx. expect ( "tuple struct fields must have an index" ) ; // Invariant
67+ (
68+ Member :: Unnamed ( i. into ( ) ) ,
69+ format_ident ! ( "_{i}" ) ,
70+ format ! ( "index `{i}`" ) ,
71+ )
72+ } ;
73+
74+ Self {
75+ pinned,
76+ field,
77+ member,
78+ proj_ident,
79+ display_name,
80+ }
81+ }
82+ }
83+
3884pub ( crate ) fn pin_data (
3985 args : Args ,
4086 input : Item ,
@@ -73,27 +119,25 @@ pub(crate) fn pin_data(
73119 replacer. visit_generics_mut ( & mut struct_. generics ) ;
74120 replacer. visit_fields_mut ( & mut struct_. fields ) ;
75121
76- let fields: Vec < ( bool , & Field ) > = struct_
122+ let fields: Vec < FieldInfo > = struct_
77123 . fields
78124 . iter_mut ( )
79- . map ( |field| {
80- let len = field. attrs . len ( ) ;
81- field. attrs . retain ( |a| !a. path ( ) . is_ident ( "pin" ) ) ;
82- ( len != field. attrs . len ( ) , & * field)
83- } )
125+ . enumerate ( )
126+ . map ( |( i, field) | FieldInfo :: new ( field, field. ident . is_none ( ) . then_some ( i) ) )
84127 . collect ( ) ;
85128
86- for ( pinned, field) in & fields {
87- if !pinned && is_phantom_pinned ( & field. ty ) {
88- dcx. error (
89- field,
90- format ! (
91- "The field `{}` of type `PhantomPinned` only has an effect \
92- if it has the `#[pin]` attribute",
93- field. ident. as_ref( ) . unwrap( ) ,
94- ) ,
95- ) ;
96- }
129+ for field in fields
130+ . iter ( )
131+ . filter ( |f| !f. pinned && is_phantom_pinned ( & f. field . ty ) )
132+ {
133+ dcx. error (
134+ field. field ,
135+ format ! (
136+ "The field {} of type `PhantomPinned` only has an effect \
137+ if it has the `#[pin]` attribute",
138+ field. display_name
139+ ) ,
140+ ) ;
97141 }
98142
99143 let unpin_impl = generate_unpin_impl ( & struct_. ident , & struct_. generics , & fields) ;
@@ -140,11 +184,7 @@ fn is_phantom_pinned(ty: &Type) -> bool {
140184 }
141185}
142186
143- fn generate_unpin_impl (
144- ident : & Ident ,
145- generics : & Generics ,
146- fields : & [ ( bool , & Field ) ] ,
147- ) -> TokenStream {
187+ fn generate_unpin_impl ( ident : & Ident , generics : & Generics , fields : & [ FieldInfo ] ) -> TokenStream {
148188 let ( _, ty_generics, _) = generics. split_for_impl ( ) ;
149189 let mut generics_with_pin_lt = generics. clone ( ) ;
150190 generics_with_pin_lt. params . insert ( 0 , parse_quote ! ( ' __pin) ) ;
@@ -160,7 +200,17 @@ fn generate_unpin_impl(
160200 else {
161201 unreachable ! ( )
162202 } ;
163- let pinned_fields = fields. iter ( ) . filter_map ( |( b, f) | b. then_some ( f) ) ;
203+ let pinned_fields = fields. iter ( ) . filter ( |f| f. pinned ) . map (
204+ |FieldInfo {
205+ field, proj_ident, ..
206+ } | {
207+ let Field { attrs, vis, ty, .. } = field;
208+ quote ! (
209+ #( #attrs) *
210+ #vis #proj_ident: #ty
211+ )
212+ } ,
213+ ) ;
164214 quote ! {
165215 // This struct will be used for the unpin analysis. It is needed, because only structurally
166216 // pinned fields are relevant whether the struct should implement `Unpin`.
@@ -238,7 +288,7 @@ fn generate_projections(
238288 vis : & Visibility ,
239289 ident : & Ident ,
240290 generics : & Generics ,
241- fields : & [ ( bool , & Field ) ] ,
291+ fields : & [ FieldInfo ] ,
242292) -> TokenStream {
243293 let ( impl_generics, ty_generics, _) = generics. split_for_impl ( ) ;
244294 let mut generics_with_pin_lt = generics. clone ( ) ;
@@ -248,57 +298,55 @@ fn generate_projections(
248298 let this = format_ident ! ( "this" ) ;
249299
250300 let ( fields_decl, fields_proj) = collect_tuple ( fields. iter ( ) . map (
251- |(
252- pinned,
253- Field {
254- vis,
255- ident,
256- ty,
257- attrs,
258- ..
259- } ,
260- ) | {
261- let mut attrs = attrs. clone ( ) ;
301+ |FieldInfo {
302+ pinned,
303+ field,
304+ member,
305+ proj_ident,
306+ ..
307+ } | {
308+ let Field { vis, ty, .. } = field;
309+ let mut attrs = field. attrs . clone ( ) ;
262310 attrs. retain ( |a| !a. path ( ) . is_ident ( "pin" ) ) ;
263311 let mut no_doc_attrs = attrs. clone ( ) ;
264312 no_doc_attrs. retain ( |a| !a. path ( ) . is_ident ( "doc" ) ) ;
265- let ident = ident
266- . as_ref ( )
267- . expect ( "only structs with named fields are supported" ) ;
313+
268314 if * pinned {
269315 (
270316 quote ! (
271317 #( #attrs) *
272- #vis #ident : :: core:: pin:: Pin <& ' __pin mut #ty>,
318+ #vis #proj_ident : :: core:: pin:: Pin <& ' __pin mut #ty>,
273319 ) ,
274320 quote ! (
275321 #( #no_doc_attrs) *
276322 // SAFETY: this field is structurally pinned.
277- #ident : unsafe { :: core:: pin:: Pin :: new_unchecked( & mut #this. #ident ) } ,
323+ #proj_ident : unsafe { :: core:: pin:: Pin :: new_unchecked( & mut #this. #member ) } ,
278324 ) ,
279325 )
280326 } else {
281327 (
282328 quote ! (
283329 #( #attrs) *
284- #vis #ident : & ' __pin mut #ty,
330+ #vis #proj_ident : & ' __pin mut #ty,
285331 ) ,
286332 quote ! (
287333 #( #no_doc_attrs) *
288- #ident : & mut #this. #ident ,
334+ #proj_ident : & mut #this. #member ,
289335 ) ,
290336 )
291337 }
292338 } ,
293339 ) ) ;
294340 let structurally_pinned_fields_docs = fields
295341 . iter ( )
296- . filter_map ( |( pinned, field) | pinned. then_some ( field) )
297- . map ( |Field { ident, .. } | format ! ( " - `{}`" , ident. as_ref( ) . unwrap( ) ) ) ;
342+ . filter ( |f| f. pinned )
343+ . map ( |f| format ! ( " - {}" , f. display_name) ) ;
344+
298345 let not_structurally_pinned_fields_docs = fields
299346 . iter ( )
300- . filter_map ( |( pinned, field) | ( !pinned) . then_some ( field) )
301- . map ( |Field { ident, .. } | format ! ( " - `{}`" , ident. as_ref( ) . unwrap( ) ) ) ;
347+ . filter ( |f| !f. pinned )
348+ . map ( |f| format ! ( " - {}" , f. display_name) ) ;
349+
302350 let docs = format ! ( " Pin-projections of [`{ident}`]" ) ;
303351 quote ! {
304352 #[ doc = #docs]
@@ -338,7 +386,7 @@ fn generate_the_pin_data(
338386 vis : & Visibility ,
339387 ident : & Ident ,
340388 generics : & Generics ,
341- fields : & [ ( bool , & Field ) ] ,
389+ fields : & [ FieldInfo ] ,
342390) -> TokenStream {
343391 let ( impl_generics, ty_generics, whr) = generics. split_for_impl ( ) ;
344392
@@ -347,24 +395,24 @@ fn generate_the_pin_data(
347395 // not structurally pinned, then it can be initialized via `Init`.
348396 //
349397 // The functions are `unsafe` to prevent accidentally calling them.
350- fn handle_field (
351- Field {
352- vis,
353- ident,
354- ty,
355- attrs,
356- ..
357- } : & Field ,
358- struct_ident : & Ident ,
359- pinned : bool ,
360- ) -> TokenStream {
398+ fn handle_field ( field : & FieldInfo , struct_ident : & Ident ) -> TokenStream {
399+ let FieldInfo {
400+ pinned,
401+ field,
402+ member,
403+ proj_ident,
404+ display_name,
405+ } = field;
406+ let Field { attrs, vis, ty, .. } = field;
407+
361408 let mut attrs = attrs. clone ( ) ;
362409 attrs. retain ( |a| !a. path ( ) . is_ident ( "pin" ) ) ;
363- let ident = ident
364- . as_ref ( )
365- . expect ( "only structs with named fields are supported" ) ;
366- let project_ident = format_ident ! ( "__project_{ident}" ) ;
367- let ( init_ty, init_fn, project_ty, project_body, pin_safety) = if pinned {
410+
411+ let project_ident = match member {
412+ Member :: Named ( ident) => format_ident ! ( "__project_{ident}" ) ,
413+ Member :: Unnamed ( index) => format_ident ! ( "__project_{}" , index. index) ,
414+ } ;
415+ let ( init_ty, init_fn, project_ty, project_body, pin_safety) = if * pinned {
368416 (
369417 quote ! ( PinInit ) ,
370418 quote ! ( __pinned_init) ,
@@ -385,7 +433,7 @@ fn generate_the_pin_data(
385433 )
386434 } ;
387435 let slot_safety = format ! (
388- " `slot` points at the field `{ident}` inside of `{struct_ident}`, which is pinned." ,
436+ " `slot` points at the field {display_name} inside of `{struct_ident}`, which is pinned." ,
389437 ) ;
390438 quote ! {
391439 /// # Safety
@@ -395,7 +443,7 @@ fn generate_the_pin_data(
395443 /// to deallocate.
396444 #pin_safety
397445 #( #attrs) *
398- #vis unsafe fn #ident <E >(
446+ #vis unsafe fn #proj_ident <E >(
399447 self ,
400448 slot: * mut #ty,
401449 init: impl :: pin_init:: #init_ty<#ty, E >,
@@ -420,7 +468,7 @@ fn generate_the_pin_data(
420468
421469 let field_accessors = fields
422470 . iter ( )
423- . map ( |( pinned , field ) | handle_field ( field , ident, * pinned ) )
471+ . map ( |f | handle_field ( f , ident) )
424472 . collect :: < TokenStream > ( ) ;
425473 quote ! {
426474 // We declare this struct which will host all of the projection function for our type. It
0 commit comments