Skip to content

Casting away lifetimes using closures #65700

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Open
alecmocatta opened this issue Oct 22, 2019 · 4 comments
Open

Casting away lifetimes using closures #65700

alecmocatta opened this issue Oct 22, 2019 · 4 comments
Labels
A-closures Area: Closures (`|…| { … }`) A-lifetimes Area: Lifetimes / regions C-bug Category: This is a bug. T-lang Relevant to the language team

Comments

@alecmocatta
Copy link
Contributor

The following code compiles on stable and nightly. I'm not sure it should as it's effectively casting a Struct::<'a> to a Struct::<'static> in safe code.

use std::{any::Any, marker::PhantomData};

fn main() {
    bad();
}

fn bad<'a>() -> Struct<'static> {
    make_static(|| Struct::<'a>(PhantomData))
}

struct Struct<'a>(PhantomData<&'a ()>);

fn make_static<A, B>(a: A) -> B
where
    A: FnOnce0 + 'static,
    B: 'static,
{
    let boxed: Box<dyn Any + 'static> = Box::new(a.call());
    *Box::<dyn Any + 'static>::downcast(boxed).unwrap()
}

trait FnOnce0 {
    type Output;
    fn call(self) -> Self::Output;
}

impl<F, O> FnOnce0 for F
where
    F: FnOnce() -> O,
{
    type Output = F::Output;
    fn call(self) -> Self::Output {
        self()
    }
}

(Playground)

It seems lifetime and type parameters that are used in the closure don't affect the lifetime of the closure itself. I stumbled upon this after noticing that || T::default() is 'static whereas T::default() is not.

@jonas-schievink
Copy link
Contributor

It seems lifetime and type parameters that are used in the closure don't affect the lifetime of the closure itself.

Should they, though? The lifetime of a closure is determined by the captured environment it contains.

Also note that it fails to compile when you try to explicitly specify 'a (citing late-bound lifetimes) or when you try to pass in the non-'static struct as an argument to bad. While I don't know the specific rules here (especially about when 'a is treated as late-bound), this doesn't look unsound to me.

@alecmocatta
Copy link
Contributor Author

Thanks @jonas-schievink. As far as I'm aware it doesn't enable unsoundness. However the conversion from Struct<'a> to a Struct<'static> was surprising to me, so I thought I'd seek a second opinion.

@matthewjasper
Copy link
Contributor

I tried fixing this in #60332, but it ends up breaking too much code.

@jonas-schievink jonas-schievink added A-closures Area: Closures (`|…| { … }`) A-lifetimes Area: Lifetimes / regions C-bug Category: This is a bug. T-lang Relevant to the language team labels Oct 22, 2019
@Spoonbender
Copy link

triage: no change

(code still compiles)

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
A-closures Area: Closures (`|…| { … }`) A-lifetimes Area: Lifetimes / regions C-bug Category: This is a bug. T-lang Relevant to the language team
Projects
None yet
Development

No branches or pull requests

4 participants