Change:
So it turns out that is a problem: The following code should be rejected by dropck, but . This is a nightly-only (because implementing for unions is unstable) soundness issue. I need unsafe code to exploit it, but above should be a perfectly safe to use type.
regression.
Not without guarantees on union validity :) Remember how third parties can legally write garbage into that's not a valid ? This means neither deref nor drop are safe operation in that interpretation.
No, that's not at all what I was saying. We were discussing basic layout invariants. These are invariants the compiler knows about and exploits for enum layout optimizations; violating them is insta-UB. For example, a reference must be non-NULL and aligned. Even a reference being valid is already not covered here as it cannot expressed in terms of just its bit pattern. However, for private fields, types may have arbitrary additional invariants. I hope we agree that the layout invariant for is simple: Just looking at the type , the only layout invariants we have is that none of the fields can be undef and that the first field must not be NULL. Still, a third party cannot legally write any garbage conforming with these invariants into those fields, like setting to ! Doing this is not insta-UB, but can easily lead to UB on the next call to . This is an example for an extra invariant that the compiler does not know about, that the data structure upholds through privacy and that unsafe code in this module can hence rely on. works the same way, its only field is private for this very reason.
Answered in to avoid offtopic here.

Discussion:
.all(|t| trivial_dropck_outlives(tcx, t)), ty::TyAdt(def, _) => { <del> if def.is_union() { // Unions never have a dtor. true } else if Some(def.did) == tcx.lang_items().manually_drop() { </del> <ins> if Some(def.did) == tcx.lang_items().manually_drop() { </ins> // `ManuallyDrop` never has a dtor. true } else { // Other types might. Moreover, PhantomData doesn't // have a dtor, but it is considered to own its <del> // content, so it is non-trivial. </del> <ins> // content, so it is non-trivial. Unions can have `impl Drop`, // and hence are non-trivial as well. </ins> false } }

<ins> error[E0597]: `v` does not live long enough --> $DIR/dropck-union.rs:49:18 | LL |     v.0.set(Some(&v)); //~ ERROR: `v` does not live long enough |                  ^^ borrowed value does not live long enough LL | } | - | | | `v` dropped here while still borrowed | borrow later used here, when `v` is dropped | = note: values in a scope are dropped in the opposite order they are defined error: aborting due to previous error For more information about this error, try `rustc --explain E0597`. </ins>

<ins> // Copyright 2018 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. #![feature(untagged_unions)] use std::cell::Cell; use std::ops::Deref; use std::mem::ManuallyDrop; union Wrap<T> { x: ManuallyDrop<T> } impl<T> Drop for Wrap<T>  { fn drop(&mut self) { unsafe { std::ptr::drop_in_place(&mut *self.x as *mut T); } } } impl<T> Wrap<T> { fn new(x: T) -> Self { Wrap { x: ManuallyDrop::new(x) } } } impl<T> Deref for Wrap<T> { type Target = T; #[inline] fn deref(&self) -> &Self::Target { unsafe { &self.x } } } struct C<'a>(Cell<Option<&'a C<'a>>>); impl<'a> Drop for C<'a> { fn drop(&mut self) {} } fn main() { let v : Wrap<C> = Wrap::new(C(Cell::new(None))); v.0.set(Some(&v)); //~ ERROR: `v` does not live long enough } </ins>

<ins> error[E0597]: `v` does not live long enough --> $DIR/dropck-union.rs:49:19 | LL |     v.0.set(Some(&v)); //~ ERROR: `v` does not live long enough |                   ^ borrowed value does not live long enough LL | } | - `v` dropped here while still borrowed | = note: values in a scope are dropped in the opposite order they are created error: aborting due to previous error For more information about this error, try `rustc --explain E0597`. </ins>