I didn't take the time to comprehend the code as a whole, so I can't say if this is truly a fix or not, but this change:
impl<'ignored, T> Holder<'ignored, T> where T: Opaque {
/// "Unwraps" the value in this holder, binding its lifetime to a new stack
/// frame.
pub fn operate_in<'i, F, R>(pinned_self: Pin<&'i Self>, f: F) -> R
- where F: for<'a> FnOnce(Pin<&'a T::Kind<'a>>) -> R {
+ where F: FnOnce(Pin<&'ignored T::Kind<'ignored>>) -> R, {
It might be easier to help with this if you could explain
how Opaque and Holder are intended to be used in the general case; as well as
how you believe your implementation (with ub_check) to be sound.
I've tried digging into this code before, but it's difficult without knowledge of the intent of it all. In particular, an earlier version of ub_check seemed to have possible issues, but I was unable to work around the compiler limitations of GATs to create a proper counterexample.
ub_check's only purpose is to prevent Drop impls that aren't #[may_dangle], because, ideally, Drop is the only time you'd be able to actually observe dangling references in self-referential structs. Indeed, if you impl Drop for any of these, without #[may_dangle], you get an error. It'd be nice if one were able to represent this in the type system (such as by T: '!) but this macro seems good enough for now.
Meanwhile Opaque itself is just a replacement for proper HKTs. This shouldn't need proper HKTs, as you control the whole types, so GATs should be enough.
And Holder doesn't... really do much, as you can probably tell. It mostly just requires Pin and tries to uphold the above invariants (no Drop, ideally no way to leak the self-ref and then dangle it... but clearly that last part isn't working.) But its main purpose is to allow moving a Box<SelfRef> (or, well, a Pin<Box<SelfRef>>... or rather a Pin<Box<Holder<SelfRef>>> but you get the point).