Stream: t-lang/wg-unsafe-code-guidelines

Topic: questions from Stack Overflow


Jake Goulding (Mar 23 2019 at 13:50, on Zulip):

Is it sound to transmute a MaybeUninit<[T; N]> to [MaybeUninit<T>; N]? (https://stackoverflow.com/q/55313460/155423)

/cc @RalfJ

Jake Goulding (Mar 23 2019 at 13:51, on Zulip):

I think it's "yes", but I've spent all of 5 seconds thinking about it

Jake Goulding (Mar 23 2019 at 14:04, on Zulip):

my rationale (as much as I have) is because it's a union

rkruppe (Mar 23 2019 at 14:45, on Zulip):

With what we guarantee about arrays, this question boils down to "does MaybeUninit<T> have the same memory layout as T?". Everyone wants that but I don't think it is technically guaranteed yet.

Jake Goulding (Mar 23 2019 at 14:53, on Zulip):

@rkruppe so my wishful "cause it's a union" holds no water? I guess I shouldn't be surprised.

rkruppe (Mar 23 2019 at 14:54, on Zulip):

Well, MaybeUninit makes all the concerns about validity go away. But transmute also needs memory layout compatibility and that isn't specified for unions in general (only for repr(C) ones).

rkruppe (Mar 23 2019 at 14:55, on Zulip):

Well, the validity of unions in general is also not settled, but the entire point of MaybeUninit is to side-step validity based pitfalls, so you'll be fine in that respect.

JP Sugarbroad (Mar 23 2019 at 19:11, on Zulip):

#[repr(transparent)] to the rescue?

gnzlbg (Mar 25 2019 at 10:11, on Zulip):

@Jake Goulding while that is not guaranteed, this is how it is actually implemented

gnzlbg (Mar 25 2019 at 10:13, on Zulip):

transmute only works if both types have the same size - if the layout of the union were to change in such a way that this is broken, then I can't imagine a situation in which transmute wouldn't have to fail to compile

gnzlbg (Mar 25 2019 at 10:14, on Zulip):

since the field in the union must be properly aligned, so for the layout to change, the size would need to change

briansmith (Mar 26 2019 at 00:43, on Zulip):

The best way of resolving these kinds of questions is to put the relevant conversions in the standard library/compiler.

gnzlbg (Mar 26 2019 at 08:04, on Zulip):

These are unsafe conversions, I don't think we have a trait for this.

gnzlbg (Mar 26 2019 at 09:04, on Zulip):

Wait, I'm wrong, these aren't unsafe:

impl<T, N: const usize> MaybeUninit<[T; N]> {
    fn foo(self) -> [MaybeUninit<T>; N];
}
impl<T, N: const usize> [MaybeUninit<T>; N] {
    fn bar(self) -> MaybeUninit<[T; N]>;
}

so we can probably just use From/Into for these.

RalfJ (Mar 26 2019 at 09:18, on Zulip):

yeah I think this transmute is fine. it's part of "interaction of MaybeUninit and arrays/slices" which we'll get to eventually, I hope

RalfJ (Mar 26 2019 at 09:19, on Zulip):

With what we guarantee about arrays, this question boils down to "does MaybeUninit<T> have the same memory layout as T?". Everyone wants that but I don't think it is technically guaranteed yet.

I think while we do not guarantee it for unions the user writes, we do guarantee it for MaybeUninit

RalfJ (Mar 26 2019 at 09:19, on Zulip):

as_ptr and the other ptr/ref-getters of MaybeUninit basically rely on this

rkruppe (Mar 26 2019 at 10:43, on Zulip):

Technically the interface of those conversions could be implemented even if MaybeUninit had e.g. extra padding or larger alignment, right? They only need MaybeUninit to actually contain a T, not to contain only a T.

gnzlbg (Mar 26 2019 at 11:25, on Zulip):

hmm I am not sure

gnzlbg (Mar 26 2019 at 11:25, on Zulip):

if MaybeUnit had padding, then how do you get a pointer to its field without creating a reference ?

gnzlbg (Mar 26 2019 at 11:26, on Zulip):

right now all of this relies on "pointer to union == pointer to all fields", but that only holds if MaybeUninit is repr(C), which it is not (and making it repr(C) runs into what do ZSTs do in repr(C)-type of issues)

rkruppe (Mar 26 2019 at 18:02, on Zulip):

The implementation we have right now certainly assumes on "pointer to union == pointer to all fields" but that's not exposed by those interfaces (they could be implemented with unstable or rustc-specific intrinsics or knowledge, for example). None of this is intended to say that MaybeUninit should ever be layout incompatible, or that the implementation needs to be changes. I'm just saying, right now as an external user, you can't rules lawyer your way to the conclusion that MaybeUninit<T> has the same layout as T. We should fix that, of course.

Last update: Nov 20 2019 at 12:05UTC