Change:
/// 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]

Discussion:
<!-- 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:

Consider this code: I believe it should not compile (by failing dropcheck). It, however, compiles and runs on 1.42 and 1.31. On 1.24 it indeed fails as I expect. The equivalent code, where is replaced with rightfully fails to compiles: . So, dropchk somehow observes the difference between and ? Either I misunderstand how is supposed to work, or this is a stable-to-stable regression and a soundless hole.
It fails to compile with the 2015 edition on 1.31, so it's probably caused by NLL. I suspect the issue is something like: never "needs drop", so your struct also doesn't, so no terminator is created that could access the borrowed value.
Adding an unrelated field that has a destructor makes the error appear again
Regression from 1.36:
cc
Assigning to this bug and leaving the nomination, this could have been too. This was .
Should this error, though, if *no code actually runs? .
We definitely need to check into this, but I think is correct that the special role of only comes into play when actually implements ; otherwise, the compiler is allowed to know that dropping is a no-op (except in so far as it drops the contents of ).
Injected between +nightly-2019-04-22 and +nightly-2019-04-23 So current guess is that this was fallout from PR
And I have now confirmed that if you enable edition=2018, you will see this behavior going all the way back to Rust version 1.31.0 (i.e. back when we first deployed stable support for the 2018 edition). Ugh, said exactly this ; sorry for overlooking that! So now I just want to confirm the running hypothesis that this is logical according to NLL.
At this point I agree with the logic put forth by and up above: There is no destructor operating on the type that holds the (i.e. in this example), so it is sound to treat dropping as a no-op. I do worry a little bit about the user's mental model for this case, however. The claim " is just like a " doesn't quite hold up.

(not that it ever did ...) More specifically, here is the current text from the std library docs for , section : So, it seems at the very least we have a documentation bug to resolve here...
Does play any role whatsoever when there is no for a type containing one? The way I understand it, and if I'm right, that's a solution for the documentation, is that, when there is no field containing an actual or a type that may drop a , there are three cases: no explicit impl no instance of type may be used by the drop glue, hence it is fine for to dangle. Although it could, there is no reason for to play any role explicit impl, which is thus generic over (thus leading to being part of the drop glue). In that case, if dangles, there will be errors, whether there is a or not (by that I mean that one can replace by or and there will still be errors). So plays no role here explicit impl, which is thus generic over , but this time the type parameter has the (and unstable) annotation on it. In that case, if does dangle, errors will happen if and only if there is an "owned" in the type definition, and is counted as owned. . So here is the situation where interacts with . So I think the simpler phrasing w.r.t. the + interaction, is that, instead, a field interacts with . and the difference between the field + maydangle, and a classic drop impl is that the classic drop impl does not allow a dangling , even when has no drop glue (e.g., ) whereas the + does allow it. But both do deny . __ So, back to the OP, the question is: could / should the case be merged into the case ? And when phrased like that, it looks like there is no special reason for it. In stable Rust we can always go from (and ) to by having a classic impl (even an empty one), but it is true that going from to (and thus from to ) requires nightly Rust.
Yes. First of all, it is also relevant for variance. Secondly, it is relevant for a "normal" even without , because then the compiler assumes that this will call and dropck's accordingly.

For example: This is unsound because of the lack of , no matter whether the drop impl is or not.
Could you provide an example of an implementation being unsound that does not use ? The following (case from my previous post), for instance, fails to compile: _ The way I see it, was created to allow this kind of situations, where, since has no drop glue, it is sound to it. So, the way to express to Rust that the only usage of a type that happens in an explicit impl is , is to: first, -annotate the type parameter ("Hey Rust, I am not using ..."),then, ensure the type contains a field that -owns a , such as a ("... except for my -ing it") So, without step , I see no difference between and .
Hm... okay things don't work entirely as I thought they would. To rephrase what you said, a drop may not call any methods on except for dropping it, and moreover if it drops it we also need the so that rustc can check that? But without I may call 's drop even if I don't have a ? Interesting. I still have an idea for a counterexample but it's getting tricky: a type not having in its parameters at all (some kind oft type erasure going on) would have to still drop a . The would be remembered by an outer wrapper type that however does not have a destructor. This needs some awful unsafe code to extract the destructor from the vtable... I don't think I'll write all this right now, not sure what that would accomplish.
Yeah that actually sounds like an interesting and potentially useful data structure (a "view" into untyped memory responsible for calling destructors on it before handing it back). Beyond the soundness issue, I also don't like this because it makes the rules more complicated. "treat PhantomData<Tlike T during structural recursion" is a simple rule that applies to other "auto traits" (to the extent that inherent drop impls are auto trait implementations), and I was hoping to use it for dropck as well.
Are you sure dropck isn't using that rule, though? Your demonstrates that dropck works field-by-field. Isn't it just doing that here, too?

(EDIT: Hm okay, in the original example replacing by changes behavior... so I guess it is violating that rule, I am just still confused about how exactly dropck works.) Arguably, dropck needs a special case for because otherwise it would never have any effect, given that it never needs dropping...
Or special case for to depend on the inner . is documented to be only an optimization, so changing it's value to true is allowed:
I think I have managed to implement what you were talking about: I have crafted a custom that works as except for it expressing that it owns and thus may drop a independently of whether the containing struct impls any whatsoever (I have achieved this by making by a containing struct by itself, with its own dummy ).I have attempted to write an unsound program, which ought to be sound with the expected ownership semantics described in this thread (which carries, but which does not), and the former fails to compile, whereas the latter does not.I find it unclear whether it is the library's design fault of it is 's, so I'll let you be the judge.
Yeah, something like that is what I was imagining... though your code still has on the same type that also has the . I think I was imagining putting the function pointer and impl down into another type, so that and are on two different types. I am not sure why would be needed?
I'm still confused about: This is unsound because of the lack of , no matter whether the drop impl is or not. From my understanding of dropck, considering impl no Drop: If it construct without , the dropck would be: (strictly) with : where If you want to construct a ub from the former, it is equivalent to finding a that satisfies but not , and causes ub when it destructs. But I tried to construct such a and failed. I can't even find a counterexample for . And I'm not sure how this violates dropck:
Wow this is an ancient thread, and I lost all context. Could you link to the context where I said that? Also I recently opened , does that help?
Re-reading the thread above, it seems to be about the same fundamental question as Curious. My PR fixes the documentation paragraph that quoted above They also wrote which is still true and would require pretty fundamental changes to NLL at this point.

I know only works on needsdrop types dropping. I'm just curious how the following code causes ub, and how it violates dropck rules:
Ah, you mean . A link would have been helpful. :) Note that I go on saying clarifies the misunderstanding me and many others had -- is never needed for unless you use .
Oh, I will note that :D. But as the example in , still works for "normal" dropping.(without )
Yeah I read that thread after this one.^^ The summary is "it's complicated, hopefully we'll figure it out in ". Let's not re-post everything in two spots. :)