Unchecked dangling lifetimes

So we have this: Rust Playground

How does it look?

The basic idea is that you'd have something like

pub struct PluginEnvironment<'dangling, 'env> {
    //plugins: RefCell<Vec<Pin<Rc<Holder<'dangling, MockPluginK<'dangling, 'env>>>>>>,
    plugins: RefCell<Vec<Pin<Rc<MockPlugin<'dangling, 'env>>>>>,
    // priority-based hooks
    //hooks: RefCell<Vec<Pin<Rc<Holder<'dangling, MockHookK<'dangling, 'env>>>>>>,
    hooks: RefCell<Vec<Pin<Rc<MockHook<'dangling, 'env>>>>>,
    // fd/timer hooks
    //extra_hooks: RefCell<Vec<Pin<Rc<Holder<'dangling, MockHookK<'dangling, 'env>>>>>>,
    extra_hooks: RefCell<Vec<Pin<Rc<MockHook<'dangling, 'env>>>>>,
    depth: Cell<usize>,
}
impl<'dangling, 'env> SelfRef<'env> for PluginEnvironment<'dangling, 'env> {}

pub struct PluginEnvironmentK<'dangling> {
    _p: PhantomData<&'dangling ()>,
}
opaque! {
    impl['dangling] Opaque for PluginEnvironmentK<'dangling> {
        type Kind<'env> = PluginEnvironment<'dangling, 'env>;
    }
}

pub type GlobalPluginEnvironment<'dangling> = Holder<'dangling, PluginEnvironmentK<'dangling>>;

(yeah we don't really have any nested Holders here, but if we did they'd also use 'dangling and it'd all work great. we only put 'dangling everywhere to future-proof it for nested Holders and to show it off as being "dangling lifetimes".)

anyway, the reason this is "unchecked" is because the compiler doesn't check its dangling-ness. you can write it but there are no checks provided by the tools. you just have to hope you're getting it correct.

actually we were doing it wrong

actually, we can't even have nested holders. this (correctly) fails to compile:

struct Foo<'dangling, 'foo> {
    x: RefCell<Vec<Pin<Box<Holder<'dangling, BarK<'dangling, 'foo>>>>>>,
}
impl<'dangling, 'foo> SelfRef<'foo> for Foo<'dangling, 'foo> {
}

struct Bar<'dangling, 'foo, 'bar> {
    x: &'foo Foo<'dangling, 'foo>,
    y: Cell<&'bar Bar<'dangling, 'foo, 'bar>>,
}
impl<'dangling, 'foo, 'bar> SelfRef<'bar> for Bar<'dangling, 'foo, 'bar> {
}

struct FooK<'dangling> {
    _p: PhantomData<&'dangling ()>,
}
opaque! {
    impl['dangling] Opaque for FooK<'dangling> {
        type Kind<'foo> = Foo<'dangling, 'foo>;
    }
}

struct BarK<'dangling, 'foo> {
    _p: PhantomData<(&'dangling (), &'foo ())>,
}
opaque! {
    impl['dangling, 'foo] Opaque for BarK<'dangling, 'foo> {
        type Kind<'bar> = Bar<'dangling, 'foo, 'bar>;
    }
}

because of dropck borrowck.

it'd be nice if we had checked dangling lifetimes so that this would compile. but yay for ub_detect_helper for correctly catching UB?

Rust Playground

this fails in dropck (correctly): Rust Playground

but it builds if we remove Drop from Holder: Rust Playground

The opaque! macro fails for structs with type parameters (Rust Playground):

struct Foo;

impl<'a> SelfRef<'a> for Foo {}

struct Bar<T>(T);

opaque! {
    impl[T] Opaque for Bar<T> {
        type Kind<'a> = Foo;
    }
}
error[E0282]: type annotations needed
   --> src/lib.rs:104:17
    |
104 |                   ub_detect_helper(|f, g| {
    |                   ^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` declared on the function `ub_detect_helper`
...
168 | / opaque! {
169 | |     impl[T] Opaque for Bar<T> {
170 | |         type Kind<'a> = Foo;
171 | |     }
172 | | }
    | |_- in this macro invocation
    |
    = note: this error originates in the macro `opaque` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0282`.

Is this intended to be a supported use case?


Also, the pattern for the optional where bounds has a bug (Rust Playground):

struct Foo;

impl<'a> SelfRef<'a> for Foo {}

struct Bar<'a, 'b>(&'a (), &'b ());

opaque! {
    impl['a, 'b] Opaque for Bar<'a, 'b> {
        type Kind<'c> = Foo;
    }
    where
        'a: 'b,
}
error: no rules expected the token `:`
   --> src/lib.rs:173:11
    |
62  | macro_rules! opaque {
    | ------------------- when calling this macro
...
173 |         'a: 'b,
    |           ^ no rules expected this token in macro call

try this one: Rust Playground

also yeah no that's not meant to be used that way, because your Bar<T> is only a key/kind (it should be FooK<T>). It can be used like so:

struct Foo<T>(T);

impl<'a> SelfRef<'a> for Foo {}

struct Bar<T>(PhantomData<T>);

opaque! {
    impl[T] Opaque for Bar<T> {
        type Kind<'a> = Foo<T>;
    }
}

Opaques aren't supposed to have any data. They're just a good-enough workaround for a lack of full HKTs.

Tho hm. It seems this doesn't work: Rust Playground

we give up, but here's our last attempt: Rust Playground