@@ -249,84 +249,45 @@ fn init_fields(
249249 } ) ;
250250 // Again span for better diagnostics
251251 let write = quote_spanned ! ( ident. span( ) => :: core:: ptr:: write) ;
252- // NOTE: the field accessor ensures that the initialized field is properly aligned.
253- // Unaligned fields will cause the compiler to emit E0793. We do not support
254- // unaligned fields since `Init::__init` requires an aligned pointer; the call to
255- // `ptr::write` below has the same requirement.
256- let accessor = if pinned {
257- let project_ident = format_ident ! ( "__project_{ident}" ) ;
258- quote ! {
259- // SAFETY: TODO
260- unsafe { #data. #project_ident( & mut ( * #slot) . #ident) }
261- }
262- } else {
263- quote ! {
264- // SAFETY: TODO
265- unsafe { & mut ( * #slot) . #ident }
266- }
267- } ;
268252 quote ! {
269253 #( #attrs) *
270254 {
271255 #value_prep
272256 // SAFETY: TODO
273257 unsafe { #write( & raw mut ( * #slot) . #ident, #value_ident) } ;
274258 }
275- #( #cfgs) *
276- #[ allow( unused_variables) ]
277- let #ident = #accessor;
278259 }
279260 }
280261 InitializerKind :: Init { ident, value, .. } => {
281262 // Again span for better diagnostics
282263 let init = format_ident ! ( "init" , span = value. span( ) ) ;
283- // NOTE: the field accessor ensures that the initialized field is properly aligned.
284- // Unaligned fields will cause the compiler to emit E0793. We do not support
285- // unaligned fields since `Init::__init` requires an aligned pointer; the call to
286- // `ptr::write` below has the same requirement.
287- let ( value_init, accessor) = if pinned {
288- let project_ident = format_ident ! ( "__project_{ident}" ) ;
289- (
290- quote ! {
291- // SAFETY:
292- // - `slot` is valid, because we are inside of an initializer closure, we
293- // return when an error/panic occurs.
294- // - We also use `#data` to require the correct trait (`Init` or `PinInit`)
295- // for `#ident`.
296- unsafe { #data. #ident( & raw mut ( * #slot) . #ident, #init) ? } ;
297- } ,
298- quote ! {
299- // SAFETY: TODO
300- unsafe { #data. #project_ident( & mut ( * #slot) . #ident) }
301- } ,
302- )
264+ let value_init = if pinned {
265+ quote ! {
266+ // SAFETY:
267+ // - `slot` is valid, because we are inside of an initializer closure, we
268+ // return when an error/panic occurs.
269+ // - We also use `#data` to require the correct trait (`Init` or `PinInit`)
270+ // for `#ident`.
271+ unsafe { #data. #ident( & raw mut ( * #slot) . #ident, #init) ? } ;
272+ }
303273 } else {
304- (
305- quote ! {
306- // SAFETY: `slot` is valid, because we are inside of an initializer
307- // closure, we return when an error/panic occurs.
308- unsafe {
309- :: pin_init:: Init :: __init(
310- #init,
311- & raw mut ( * #slot) . #ident,
312- ) ?
313- } ;
314- } ,
315- quote ! {
316- // SAFETY: TODO
317- unsafe { & mut ( * #slot) . #ident }
318- } ,
319- )
274+ quote ! {
275+ // SAFETY: `slot` is valid, because we are inside of an initializer
276+ // closure, we return when an error/panic occurs.
277+ unsafe {
278+ :: pin_init:: Init :: __init(
279+ #init,
280+ & raw mut ( * #slot) . #ident,
281+ ) ?
282+ } ;
283+ }
320284 } ;
321285 quote ! {
322286 #( #attrs) *
323287 {
324288 let #init = #value;
325289 #value_init
326290 }
327- #( #cfgs) *
328- #[ allow( unused_variables) ]
329- let #ident = #accessor;
330291 }
331292 }
332293 InitializerKind :: Code { block : value, .. } => quote ! {
@@ -339,18 +300,41 @@ fn init_fields(
339300 if let Some ( ident) = kind. ident ( ) {
340301 // `mixed_site` ensures that the guard is not accessible to the user-controlled code.
341302 let guard = format_ident ! ( "__{ident}_guard" , span = Span :: mixed_site( ) ) ;
303+
304+ // NOTE: The reference is derived from the guard so that it only lives as long as the
305+ // guard does and cannot escape the scope. If it's created via `&mut (*#slot).#ident`
306+ // like the unaligned field guard, it will become effectively `'static`.
307+ let accessor = if pinned {
308+ let project_ident = format_ident ! ( "__project_{ident}" ) ;
309+ quote ! {
310+ // SAFETY: the initialization is pinned.
311+ unsafe { #data. #project_ident( #guard. let_binding( ) ) }
312+ }
313+ } else {
314+ quote ! {
315+ #guard. let_binding( )
316+ }
317+ } ;
318+
342319 res. extend ( quote ! {
343320 #( #cfgs) *
344- // Create the drop guard:
321+ // Create the drop guard.
345322 //
346- // We rely on macro hygiene to make it impossible for users to access this local
347- // variable.
348- // SAFETY: We forget the guard later when initialization has succeeded.
349- let #guard = unsafe {
323+ // SAFETY:
324+ // - `&raw mut (*slot).#ident` is valid.
325+ // - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned.
326+ // - `(*slot).#ident` has been initialized above.
327+ // - We only need the ownership to the pointee back when initialization has
328+ // succeeded, where we `forget` the guard.
329+ let mut #guard = unsafe {
350330 :: pin_init:: __internal:: DropGuard :: new(
351331 & raw mut ( * slot) . #ident
352332 )
353333 } ;
334+
335+ #( #cfgs) *
336+ #[ allow( unused_variables) ]
337+ let #ident = #accessor;
354338 } ) ;
355339 guards. push ( guard) ;
356340 guard_attrs. push ( cfgs) ;
@@ -367,49 +351,49 @@ fn init_fields(
367351 }
368352}
369353
370- /// Generate the check for ensuring that every field has been initialized.
354+ /// Generate the check for ensuring that every field has been initialized and aligned .
371355fn make_field_check (
372356 fields : & Punctuated < InitializerField , Token ! [ , ] > ,
373357 init_kind : InitKind ,
374358 path : & Path ,
375359) -> TokenStream {
376- let field_attrs = fields
360+ let field_attrs: Vec < _ > = fields
377361 . iter ( )
378- . filter_map ( |f| f. kind . ident ( ) . map ( |_| & f. attrs ) ) ;
379- let field_name = fields . iter ( ) . filter_map ( |f| f . kind . ident ( ) ) ;
380- match init_kind {
381- InitKind :: Normal => quote ! {
382- // We use unreachable code to ensure that all fields have been mentioned exactly once ,
383- // this struct initializer will still be type-checked and complain with a very natural
384- // error message if a field is forgotten/mentioned more than once.
385- # [ allow ( unreachable_code , clippy :: diverging_sub_expression ) ]
386- // SAFETY: this code is never executed.
387- let _ = || unsafe {
388- :: core :: ptr :: write ( slot , #path {
389- # (
390- # ( #field_attrs ) *
391- #field_name : :: core :: panic! ( ) ,
392- ) *
393- } )
394- } ;
395- } ,
396- InitKind :: Zeroing => quote ! {
397- // We use unreachable code to ensure that all fields have been mentioned at most once.
398- // Since the user specified `..Zeroable::zeroed()` at the end, all missing fields will
399- // be zeroed. This struct initializer will still be type-checked and complain with a
400- // very natural error message if a field is mentioned more than once, or doesn't exist.
401- # [ allow ( unreachable_code , clippy :: diverging_sub_expression , unused_assignments ) ]
402- // SAFETY: this code is never executed.
403- let _ = || unsafe {
404- :: core :: ptr :: write ( slot , #path {
405- # (
406- # ( #field_attrs ) *
407- #field_name : :: core :: panic! ( ) ,
408- ) *
409- .. :: core :: mem :: zeroed ( )
410- } )
411- } ;
412- } ,
362+ . filter_map ( |f| f. kind . ident ( ) . map ( |_| & f. attrs ) )
363+ . collect ( ) ;
364+ let field_name : Vec < _ > = fields . iter ( ) . filter_map ( |f| f . kind . ident ( ) ) . collect ( ) ;
365+ let zeroing_trailer = match init_kind {
366+ InitKind :: Normal => None ,
367+ InitKind :: Zeroing => Some ( quote ! {
368+ .. :: core :: mem :: zeroed ( )
369+ } ) ,
370+ } ;
371+ quote ! {
372+ # [ allow ( unreachable_code , clippy :: diverging_sub_expression ) ]
373+ // We use unreachable code to perform field checks. They're still checked by the compiler.
374+ // SAFETY: this code is never executed.
375+ let _ = || unsafe {
376+ // Create references to ensure that the initialized field is properly aligned.
377+ // Unaligned fields will cause the compiler to emit E0793. We do not support
378+ // unaligned fields since `Init::__init` requires an aligned pointer; the call to
379+ // `ptr::write` for value-initialization case has the same requirement.
380+ # (
381+ # ( #field_attrs ) *
382+ let _ = & ( * slot ) . #field_name ;
383+ ) *
384+
385+ // If the zeroing trailer is not present, this checks that all fields have been
386+ // mentioned exactly once. If the zeroing trailer is present, all missing fields will be
387+ // zeroed, so this checks that all fields have been mentioned at most once. The use of
388+ // struct initializer will still generate very natural error messages for any misuse.
389+ :: core :: ptr :: write ( slot , #path {
390+ # (
391+ # ( #field_attrs ) *
392+ #field_name : :: core :: panic! ( ) ,
393+ ) *
394+ #zeroing_trailer
395+ } )
396+ } ;
413397 }
414398}
415399
0 commit comments