Towards may_dangle stabilization*

* more like refinement but ok.

So uh, yeah, we guess we got unbanned from here after we wrote RFC PR 3390? This isn't a pre-RFC thread or anything like that, we just feel like we need somewhere to bring up discussion that isn't quite RFC-level discussion but also isn't ideal for text chat either. Uh anyway.

If you look through the commit history (which is why we haven't squashed these commits) you'll see we originally tried to do something rather complex with basically trying to define dropck as a kind of bounds. It was more of a starting point to talk about the idea with folks, figure out why it would (or wouldn't) work, figure out some of its limitations, and so on. So we wrote that, then we started bringing it up with folks, and then folks started encouraging us to go on, and we did. This was based around bounds like "alive", "safe to drop" and "dangling".

Then, folks mentioned how the borrow checker basically implicitly requires every type and lifetime to outlive the current function, using a sort of hidden lifetime for the function's scope, and how "alive" is simply a lifetime bound - T is alive in 'a if T: 'a holds.

How do we define "alive"? The most straightforward definition is probably to simply say an object is alive if every lifetime within it is in scope. If you could impl a lifetime, this would be something along the lines of:

impl<'self, 'a: 'self, 'b: 'self> 'self for Foo<'a, 'b> {}

Tho it does feel a bit silly to think about it this way. But anyway, since "alive" is just a lifetime bound, a generic function accepting some type T has to have a bound on that T that checks that it is alive for the scope of the function, in other words, that hidden lifetime, which we called 'fn in the RFC, is that bound: T: 'fn.

Further, the borrow checker does have the concept of dangling references and the like. They mostly exist thanks to the work on non-lexical lifetimes, but it's important to also consider how #[may_dangle] interacts with the borrow checker. The TL;DR is that #[may_dangle] effectively applies NLL logic to the caller, but not to the callee. In reality it's a bit more complicated than that (e.g. #[may_dangle] only applies to Drop), but in terms of observable behaviour, that's what it is.

Tho on that note, well, dropck mostly boils down to emitting drop glue (drop elaboration) and then just doing borrowck on a drop call. And that's where the #[may_dangle] interaction comes in.

Note that for most of this post, we haven't actually talked about Drop. We think it would be useful to refine the #[may_dangle] mechanism and extend it to the rest of the language. Importantly, we also noticed many ppl don't understand dropck, so boiling it down to just "we just call drop where it goes out of scope" might actually help with that. However, Drop is still rather special in that its safety contract is that it's safe to implement but unsafe to call, despite not using the unsafe keyword anywhere, so it's still gonna remain impossible to manually call Drop::drop.

Any thoughts?