Skip to content

const-eval: load of partially initialized scalar produces entirely uninitialized result #69488

Closed
@HeroicKatora

Description

@HeroicKatora

The constant evaluation of a move of a union differs from the semantics of non-constant evaluation. The union instance is not regarded as a bag-of-bits as aluded to in the reference and also this unsafe-code-guidelines issue. This occurs only if the unions has a single scalar scalar field and otherwise ZSTs, which is pretty specific but occurs for MaybeUninit if the type parameter is a scalar. If the field is of other types such as arrays ([scalar; N]) or if the other field is not a ZST then the semantics seem fine.

I tried this code:

#![feature(const_if_match)] // Only related to make_1u8_bag, all of them set by Miri
#![feature(const_fn)]
#![feature(const_mut_refs)]
#![feature(const_panic)]
#![feature(const_raw_ptr_deref)]

// Or, equivalently: `MaybeUninit`.
pub union BagOfBits<T: Copy> {
    uninit: (),
    _storage: T,
}

pub const fn make_1u8_bag<T: Copy>() -> BagOfBits<T> {
    assert!(core::mem::size_of::<T>() >= 1);
    let mut bag = BagOfBits { uninit: () };
    unsafe { (&mut bag as *mut _ as *mut u8).write(1); };
    bag
}

pub fn check_bag<T: Copy>(bag: &BagOfBits<T>) {
    let val = unsafe { (bag as *const _ as *const u8).read() };
    assert_eq!(val, 1);
}

fn main() {
    check_bag(&make_1u8_bag::<[usize; 1]>()); // Fine
    check_bag(&make_1u8_bag::<usize>()); // Fine

    const CONST_ARRAY_BAG: BagOfBits<[usize; 1]> = make_1u8_bag();
    check_bag(&CONST_ARRAY_BAG); // Fine.
    const CONST_USIZE_BAG: BagOfBits<usize> = make_1u8_bag();
    check_bag(&CONST_USIZE_BAG); // Panics
}

Playground

I expected to see this happen: All assertions succeed.

Instead, this happened: The check where a union of type BagOfBits<usize> is moved during constant evaluation, panics. When putting everything through Miri it panics with the message of trying to read an uninitialized byte. The check where the union of type BagOfBits<[usize; 1]> is used instead runs fine in both cases.

error: Miri evaluation error: attempted to read undefined bytes
  --> src/main.rs:21:5
   |
21 |     assert_eq!(val, 1);
   |     ^^^^^^^^^^^^^^^^^^^ attempted to read undefined bytes

As far as I am aware, when constant evaluation reads scalars then it propagates uninitialized bytes. If that were to happen then it would explain the observed behaviour but it seems weird why the union copy would use a scalar read.

cc @RalfJung

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-const-evalArea: Constant evaluation, covers all const contexts (static, const fn, ...)C-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions