Skip to content

Padding considered uninitialized data when type is transmuted in const fn #131569

Closed as not planned
@jean-airoldie

Description

@jean-airoldie

For some reason the compiler treats some value cast to bytes via mem::transmute as uninitialized memory in a const function context.

use static_assertions::assert_eq_size;

struct Buf<const N: usize> {
    bytes: [u8; N],
    cursor: usize,
}

impl<const N: usize> Buf<N> {
    const fn new() -> Self {
        Self {
            bytes: [0u8; N],
            cursor: 0,
        }
    }

    const fn push_u32_slice(&mut self, slice: &[u32]) {
        if self.bytes.len() - self.cursor < slice.len() {
            panic!("exceeded capacity");
        }

        let mut i = 0;
        while i < slice.len() {
            let array = slice[i].to_le_bytes();
            let mut j = 0;
            while j < slice.len() {
                self.bytes[self.cursor + i] = array[j];
                j += 1;
            }
            i += 1;
        }
        self.cursor += slice.len();
    }
}

#[repr(u8)]
enum Frame {
    First(u16),
    Second,
}

assert_eq_size!(Frame, u32);

impl Frame {
    const fn cast_slice(slice: &[Frame]) -> &[u32] {
        // SAFETY We know the slice is valid and casting to bytes should
        // always be valid, even if repr(rust) isn't stable yet.
        unsafe { std::mem::transmute(slice) }
    }
}

const FRAMES: &[Frame] = &[Frame::First(8), Frame::Second];
const NB_BYTES: usize = FRAMES.len() * std::mem::size_of::<Frame>();
const SERIALIZED_FRAMES: [u8; NB_BYTES] = {
    let mut buf = Buf::<NB_BYTES>::new();
    let bytes = Frame::cast_slice(FRAMES);
    buf.push_u32_slice(&bytes);
    buf.bytes
};

Here's a link to the repo with this code.

Meta

rustc --version --verbose:

rustc 1.83.0-nightly (52fd99839 2024-10-10)
binary: rustc
commit-hash: 52fd9983996d9fcfb719749838336be66dee68f9
commit-date: 2024-10-10
host: x86_64-unknown-linux-gnu
release: 1.83.0-nightly
LLVM version: 19.1.1

Error output

error[E0080]: evaluation of constant value failed
  --> src/lib.rs:23:25
   |
23 |             let array = slice[i].to_le_bytes();
   |                         ^^^^^^^^ accessing memory based on pointer with alignment 2, but alignment 4 is required
   |
note: inside `Buf::<8>::push_u32_slice`
  --> src/lib.rs:23:25
   |
23 |             let array = slice[i].to_le_bytes();
   |                         ^^^^^^^^
note: inside `SERIALIZED_FRAMES`
  --> src/lib.rs:56:5
   |
56 |     buf.push_u32_slice(&bytes);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^

For more information about this error, try `rustc --explain E0080`.
error: could not compile `const_uninit_bug` (lib) due to 1 previous error

Edit1: Updated example to cast to u32 before casting to bytes for clarity

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-gubCategory: the reverse of a compiler bug is generally UB

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions