Rfcs: uninitialized/zeroed statics/consts

Created on 24 Oct 2014  Â·  18Comments  Â·  Source: rust-lang/rfcs

There should be a way to create consts, statics and static muts which are partially or fully uninitialized or zeroed.

One way to do this could be to create built-in unsafe generic constants ZEROED and UNDEF.

@eddyb suggested to make the existing intrinsics usable in statics

A crude solution could be to allow the initializer in a static mut to be left out, which should cause zeroing all it's memory.

T-compiler T-lang

Most helpful comment

@gnzlbg once https://github.com/rust-lang/rust/pull/46882 is merged, that is a trivial addition! (imo even uncontroversial, since you can write an unsafe const fn yourself that does the same thing via unions).

union Foo<T> {
    t: T,
    u: (),
}
unsafe const fn uninitialized<T>() -> T {
    Foo { u: () }.t
}

All 18 comments

All-bits-zero isn't special in Rust (up to dynamic drops, which are being removed). Allowing some kind of mem::uninitialized() would be nice, through.

Sounds like CTFE. See https://github.com/rust-lang/rfcs/issues/322 for another proposal that suggested limited CTFE for intrinsic functions.

@mahkoh

We _do_ have a limited version of CTFE. Currently it is limited to arithmetic and constructors. mem::uninitialized() won't increase its complexity so much .

However, adding size_of would complicate things more, because constants can be a part of type-checking, and this would make type-checking depend on LLVM. Because recursion in structs is so limited, this would still be tractable, but if we add traits etc. it would be a major mess.

I'm talking about stuff like:

trait Tr { fn doit(&self); }
impl Tr for [uint, ..std::mem::size_of::<uint>()] {
    fn doit(&self) { println!("called uint"); }
}
impl Tr for [uint, ..12-std::mem::size_of::<uint>()] {
    fn doit(&self) { println!("called 12-uint"); }
}

fn main() {
    let s = [0,1,2,3u];
    s.doit(); // which .doit is called depends on architecture
}

We could get around this by treating such values as "abstract values", so that they don't interfere with type-checking, and only expand them in trans. Of course this would create 2 kinds of constexprs, but otherwise we would be getting into what seems to me like a pretty deep dependent type hole.

C gets away with this because it "executes" declarations one-by-one.

@arielb1 you can already do that, using uint::BYTES or uint::BITS.

uint was just a simple example, whose size is a well-known constant – you could put more complicated types there so we need to put trans::adt-s size calculation code into rustc.

AFAIK, the size calculations are done by LLVM. We could have a LLVM context during type-checking, is there anything problematic with that? We should do that anyway instead of the hacks we've thrown around transmute.

@eddyb

They are done via LLVM and a big part of trans. I'll prefer keeping them separate. Actually, if we allow calling trait methods in constexprs then we'll have a total dependent type mess, and I strongly prefer not to go there, so we need to stop somewhere.

There is also the problem of struct -> constexpr recursion. Because there are only finitely many constants we can handle this with a DFS, but if we allow functions this could get more complicated.

A big part of trans? I find it's quite isolated, compared to anything which deals with Block, for example.

if we allow calling trait methods in constexprs

Nobody said anything about that. You couldn't even use a size_of intrinsic or associated constant, with a non-concrete type parameter, as the length of an array (e.g. impl<T> Cast<T> for [u8, ..size_of::<T>()]) because the length has to be constant before monomorphization, though that may change at some point.

Would it be possible to make mem::uninitialized a const fn ? (or mem::zeroed?)

@gnzlbg once https://github.com/rust-lang/rust/pull/46882 is merged, that is a trivial addition! (imo even uncontroversial, since you can write an unsafe const fn yourself that does the same thing via unions).

union Foo<T> {
    t: T,
    u: (),
}
unsafe const fn uninitialized<T>() -> T {
    Foo { u: () }.t
}

@oli-obk congrats on the merge of that PR :)

Any news about this? This issue comes up every now and then in stdsimd: https://github.com/rust-lang-nursery/stdsimd/issues/403#issuecomment-376354681

My feeling is that someone should just open a PR and we FCP it, because you can do zeroed/uninitialized with horrible hacks in constants, so why not allow the nice obvious ways, too!?

Implementation guide: grep for "size_of" (yes with quotes) in rustc, grep for "uninitialized" in the miri repository, copy over code from the miri repository to rustc.

@oli-obk can you give some more hints? I can't find any code for "uninitialized" in miri that looks like the const eval stuff in librustc_mir/interpret/const_eval. Also, would I need to add uninitialized/zeroed to the intrinsics?

So... I've been told that these intrinsics should not exist due to various unsafety rules not playing nice with them.

Instead of creating a static/const with one of these intrinsics, your static/const should be of Union type with a zst variant that you use to "initialize" it.

I'm not sure this issue can be acted on. Maybe we should close it?

cc @RalfJung

Seems like essentially a duplicate of https://github.com/rust-lang/rust/pull/50150 to me.

Instead, we should strive to stabilize MaybeUninit and make it usable from const context.

I've now read the [other issue] & the [MaybeUninit RFC] and it does seem like this is a duplicate of the other issue. Maybe close this so no-one tries to implement it?

Closing as ~"completed".

Was this page helpful?
0 / 5 - 0 ratings

Related issues

yongqli picture yongqli  Â·  3Comments

burdges picture burdges  Â·  3Comments

rudolfschmidt picture rudolfschmidt  Â·  3Comments

marinintim picture marinintim  Â·  3Comments

3442853561 picture 3442853561  Â·  3Comments