@@ -237,8 +237,63 @@ r[const-eval.const-expr.if-match]
237237r[ const-eval.const-expr.final-value-provenance]
238238The representation of the final value of a [ constant] [ constant initializer ] or [ static initializer] must only contain bytes with provenance in whole-pointer groups. If a byte has provenance but is not part of an adjacent group of bytes that form an entire pointer, compilation will fail.
239239
240+ ``` rust,compile_fail
241+ # use core::mem::MaybeUninit;
242+ #
243+ #[repr(C)]
244+ struct Pair {
245+ x: u128,
246+ y: MaybeUninit<u64>,
247+ // 8 bytes of padding at offset 24.
248+ }
249+
250+ const C: Pair = unsafe {
251+ let mut m = MaybeUninit::<Pair>::uninit();
252+ // Store pointer half-way into trailing padding.
253+ m.as_mut_ptr().byte_add(20).cast::<&i32>().write_unaligned(&0);
254+ // Initialize fields.
255+ (*m.as_mut_ptr()).x = 0;
256+ (*m.as_mut_ptr()).y = MaybeUninit::new(0);
257+ // Now `m` contains a pointer fragment in the padding.
258+ m.assume_init()
259+ };
260+ ```
261+
262+ > [ !EXAMPLE]
263+ > Manually initializing (zeroing) the padding bytes ensures the final value is accepted:
264+ >
265+ > ``` rust
266+ > # use std :: mem :: MaybeUninit ;
267+ > # #[repr(C )]
268+ > # struct Pair {
269+ > # x : u128 ,
270+ > # y : MaybeUninit <u64 >,
271+ > # }
272+ > const C : Pair = unsafe {
273+ > let mut m = MaybeUninit :: <Pair >:: uninit ();
274+ > // ... setup like above ...
275+ > # m . as_mut_ptr (). byte_add (20 ). cast :: <& i32 >(). write_unaligned (& 0 );
276+ > # (* m . as_mut_ptr ()). x = 0 ;
277+ > # (* m . as_mut_ptr ()). y = MaybeUninit :: new (0 );
278+ >
279+ > // Explicitly zero the padding.
280+ > m . as_mut_ptr (). byte_add (24 ). cast :: <u64 >(). write_unaligned (0 );
281+ > m . assume_init ()
282+ > };
283+ > ```
284+
240285> [! NOTE ]
241286> If a byte in the representation of the final value is uninitialized , then it * may * end up having provenance , which can cause compilation to fail . `rustc ` tries (but does not guarantee ) to only actually fail if the initializer copies or overwrites parts of a pointer and those bytes appear in the final value .
287+ >
288+ > For example , `rustc ` currently accepts this , even though the padding bytes are uninitialized :
289+ > ```rust
290+ > # #[repr(C )]
291+ > # struct Pair { x : u128 , y : u64 }
292+ > // The padding bytes are uninitialized.
293+ > const ALLOWED : Pair = Pair { x : 0 , y : 0 };
294+ > ```
295+ >
296+ > Constant evaluation makes the details of typed copies observable : depending on whether a copy is performed field - by - field or as a memory - block copy , provenance in padding bytes might be discarded or preserved . Because the semantics of typed copies are not yet fully defined in Rust , and to preserve the ability to change how they work in the future (for example , to always set the padding bytes to uninitialized ), the language allows the compiler to reject any initializer with an uninitialized padding byte . Since the compiler cannot currently guarantee that an uninitialized byte does not contain a pointer fragment without a full model of typed copies , this allowance is necessary to avoid relying on undefined details .
242297
243298r [const - eval . const - context ]
244299## Const context
0 commit comments