Change:
<!-- Thank you for filing a bug report! Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug. --I tried this code: I expected to see this happen: since a claims to act like a including for the purposes of borrowck/dropck then this should absolutely compile-fail. just as it does if you use a , or directly! Instead, this happened: it doesn't! this is clearly unsound. <!-- If you're using the stable version of the compiler, you should also check if the bug also exists in the beta or nightly versions. --: <!-- Include a backtrace in the code block by setting in your environment. E.g. . --<details<summaryBacktrace</summary<p</p</details
It isn't clear to me though. I don't see how this could ever be exploited to create unsoundness. It seems the error happens because actually drops , which counts as a use of the , while doesn't, so it compiles.
see also (maybe we should just ship it with the and let y'all deal with it instead. but we hear that counts as "releasing exploits into the wild" and some ppl are very litigious about that stuff and we don't really wanna deal with that just yet.) it's also worth noting that it fails to compile if you use a directly, and is supposed to act like a , is it not? so why does it fail to act like a ?
It's supposed to make a struct act as if it contains a , it doesn't act itself as a .
why can't/shouldn't it act itself as a when used directly?
is less like and more like . Using your example, this compiles: I haven't stared at this long enough to decide whether this is correct behavior for , but I think it's a bit of a stretch to call it "unsound" without qualification- it's clearly not an "unsound" API in the sense of "we wrap something but still cause UB in some cases" which is usual in Rust.
it's unsound as in "if you use this with unsafe, you'll have soundness issues". and the worst part is that there's no appropriate alternative that can be used on stable. there's no "PhantomDrop", no "PhantomBox", no anything like that.
AFAIK, are special markers to place in structs to aid on doing some analysis. When it is used by itself, it should really do nothing as it is a zero sized type, so should be optimized away. So for this, I really don't get the point. For the first sentence, are you saying that using in any unsafe code is UB? For the second, are you recommending new types that "act" as if they are (which I have no idea what it would mean)?
a that acts like a except for size. which should be just but for some reason isn't. acts like a for borrowck, dropck, impl, /, projections, etc etc.
would be the motivation for such type? such type ever be sound? Since it has size 0, in actual compilation it will be optimized away, and in actual execution there will be nothing. For example if you had a and called on it to get the inner value, what would the value of this at runtime? Anything related to such will need to be make sure to be no-ops at runtime, and I don't know if that is possible or even needed. Although this itself is slightly off-topic to the original question. Regarding it, I think you are trying to give semantic requirements it was never meant to have, hence why you thought the above example was unsound.
The difference is solely because does not implement .
the actual receiver is not so you can't actually call on it unless it explicitly supported it. but the point is that it should work just like a for some except this one happens to be zero-sized for any (including ). it needs the like has, otherwise it would have the wrong semantics. (are we allowed to break the rule for ? because that'd be awesome!)
Anyway, is not unsound, but surely it is not doing what the document suggests. You can't produce UB without unsafe code, it's just that your unsafe code depends on the property described in the document. I think it'll be a breaking change to modify the behaviour of does though, because if were truly behave like , the need of a drop glue will make things containing ineligible to NLL. Probably we should take this an opportunity to stablise or some alternative instead.
Note that 's documentation specifically talks about using it as a field, not by itself like in this issue. Quoting from the documentation (emphasis mine): Does this issue also affect when used as a field?
Ok I think I get your point, in that it should act like a struct with a T field, but be zero sized at runtime. Still, is that intended for PhantomData? IDK about the full technical details but my understanding has been that PhantomData is just a marker to properly able certain compiler analysis for certain types, and nothing really more.
Maybe we should change the doc to say that act like .
and so anyone who inadvertently (without testing it) relied on the as-documented behaviour is left with no usable alternative and an unsound crate. why not... fix ?
I’m going to nominate this for a look by T-lang. The lang team seems like the best candidate to make a call here as to how the should be behaving and what the implications of that decision may be.
This changed between 1.35 and 1.36 in the 2015 edition:
We discussed this in the . Some things we realized: is , which in NLL implies that dropping it has no effect. The docs as written do state that however that acts "as though" is dropped (and I at least expected that), which is incompatible with that. If you modify the example that that the type requires drop (e.g., by making it a new type that implements Drop, or which has a field that does), then plays its expected role -- it makes the drop code for be considered to drop , even if no other fields own data of type . So the more correct summary seems to be: when a struct owns a and either has or has fields which require , the drop code for will be assumed to potential drop values of type . As , this is more like saying "it is similar to ". That is the current behavior, at least. The question is whether you can adjust your code to be sound given those semantics?
I think the answer to the last question is yes. For the selfref crate, the : can be implemented as
honestly we'd lean towards considering this behaviour of NLL incorrect in the presence of PhantomData, because PhantomData is just that special. but if we were to do that, then how do you even fix that? we mean, what would dropck for Copy types even translate to in NLL terms? maybe it creates more problems than it solves... huh. interesting. edit: tho the meeting notes seem a bit inconsistent. specifically: wouldn't that be "the drop glue"? which is really weird/surprising btw! if this is gonna be a thing then there should definitely be tests for it. (that is to say: can we rely on it?)
also it's not similar to ManuallyDrop:
even more surprising is the behaviour of 0-length arrays: with drop sibling: without drop sibling: note that 0-length arrays are not copy:

Discussion:
/// /// ## Ownership and the drop check /// <del> /// Adding a field of type `PhantomData<T>` indicates that your /// type owns data of type `T`. This in turn implies that when your /// type is dropped, it may drop one or more instances of the type /// `T`. This has bearing on the Rust compiler's [drop check] /// analysis. </del> <ins> /// The exact interaction of `PhantomData` with drop check **may change in the future**. </ins> /// <del> /// If your struct does not in fact *own* the data of type `T`, it is /// better to use a reference type, like `PhantomData<&'a T>` /// (ideally) or `PhantomData<*const T>` (if no lifetime applies), so /// as not to indicate ownership. </del> <ins> /// Currently, adding a field of type `PhantomData<T>` indicates that your type *owns* data of type /// `T` in very rare circumstances. This in turn has effects on the Rust compiler's [drop check] /// analysis. For the exact rules, see the [drop check] documentation. </ins> /// /// ## Layout ///

/// * `size_of::<PhantomData<T>>() == 0` /// * `align_of::<PhantomData<T>>() == 1` /// <del> /// [drop check]: ../../nomicon/dropck.html </del> <ins> /// [drop check]: Drop#drop-check </ins> #[lang = "phantom_data"] #[stable(feature = "rust1", since = "1.0.0")] pub struct PhantomData<T: ?Sized>;

/// are `Copy` get implicitly duplicated by the compiler, making it very /// hard to predict when, and how often destructors will be executed. As such, /// these types cannot have destructors. <ins> /// /// ## Drop check /// /// Dropping interacts with the borrow checker in subtle ways: when a type `T` is being implicitly /// dropped as some variable of this type goes out of scope, the borrow checker needs to ensure that /// calling `T`'s destructor at this moment is safe. In particular, it also needs to be safe to /// recursively drop all the fields of `T`. For example, it is crucial that code like the following /// is being rejected: /// /// ```compile_fail,E0597 /// use std::cell::Cell; /// /// struct S<'a>(Cell<Option<&'a S<'a>>>, Box<i32>); /// impl Drop for S<'_> { ///     fn drop(&mut self) { ///         if let Some(r) = self.0.get() { ///             // Print the contents of the `Box` in `r`. ///             println!("{}", r.1); ///         } ///     } /// } /// /// fn main() { ///     // Set up two `S` that point to each other. ///     let s1 = S(Cell::new(None), Box::new(42)); ///     let s2 = S(Cell::new(Some(&s1)), Box::new(42)); ///     s1.0.set(Some(&s2)); ///     // Now they both get dropped. But whichever is the 2nd one ///     // to be dropped will access the `Box` in the first one, ///     // which is a use-after-free! /// } /// ``` /// /// The Nomicon discusses the need for [drop check in more detail][drop check]. /// /// To reject such code, the "drop check" analysis determines which types and lifetimes need to /// still be live when `T` gets dropped. The exact details of this analysis are not yet /// stably guaranteed and **subject to change**. Currently, the analysis works as follows: /// - If `T` has no drop glue, then trivially nothing is required to be live. This is the case if ///   neither `T` nor any of its (recursive) fields have a destructor (`impl Drop`). [`PhantomData`] ///   and [`ManuallyDrop`] are considered to never have a destructor, no matter their field type. /// - If `T` has drop glue, then, for all types `U` that are *owned* by any field of `T`, ///   recursively add the types and lifetimes that need to be live when `U` gets dropped. The set of ///   owned types is determined by recursively traversing `T`: ///   - Recursively descend through `PhantomData`, `Box`, tuples, and arrays (including arrays of ///     length 0). ///   - Stop at reference and raw pointer types as well as function pointers and function items; ///     they do not own anything. ///   - Stop at non-composite types (type parameters that remain generic in the current context and ///     base types such as integers and `bool`); these types are owned. ///   - When hitting an ADT with `impl Drop`, stop there; this type is owned. ///   - When hitting an ADT without `impl Drop`, recursively descend to its fields. (For an `enum`, ///     consider all fields of all variants.) /// - Furthermore, if `T` implements `Drop`, then all generic (lifetime and type) parameters of `T` ///   must be live. /// /// In the above example, the last clause implies that `'a` must be live when `S<'a>` is dropped, /// and hence the example is rejected. If we remove the `impl Drop`, the liveness requirement /// disappears and the example is accepted. /// /// There exists an unstable way for a type to opt-out of the last clause; this is called "drop /// check eyepatch" or `may_dangle`. For more details on this nightly-only feature, see the /// [discussion in the Nomicon][nomicon]. /// /// [`ManuallyDrop`]: crate::mem::ManuallyDrop /// [`PhantomData`]: crate::marker::PhantomData /// [drop check]: ../../nomicon/dropck.html /// [nomicon]: ../../nomicon/phantom-data.html#an-exception-the-special-case-of-the-standard-library-and-its-unstable-may_dangle </ins> #[lang = "drop"] #[stable(feature = "rust1", since = "1.0.0")] #[const_trait]

<ins> // check-pass //! This test checks that dropck knows that ManuallyDrop does not drop its field. #![feature(dropck_eyepatch)] use std::mem::ManuallyDrop; struct S<T>(ManuallyDrop<T>); unsafe impl<#[may_dangle] T> Drop for S<T> { fn drop(&mut self) {} } struct NonTrivialDrop<'a>(&'a str); impl<'a> Drop for NonTrivialDrop<'a> { fn drop(&mut self) {} } fn main() { let s = String::from("string"); let _t = S(ManuallyDrop::new(NonTrivialDrop(&s))); drop(s); } </ins>