Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

type-alias-impl-trait implicitly captures lifetime params #96996

Closed
aliemjay opened this issue May 12, 2022 · 3 comments · Fixed by #102417
Closed

type-alias-impl-trait implicitly captures lifetime params #96996

aliemjay opened this issue May 12, 2022 · 3 comments · Fixed by #102417
Labels
C-bug Category: This is a bug. F-type_alias_impl_trait `#[feature(type_alias_impl_trait)]` requires-nightly This issue requires a nightly compiler in some way. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue.

Comments

@aliemjay
Copy link
Member

#![feature(type_alias_impl_trait)]
trait Service {
    type Output;
    fn call(self) -> Self::Output;
}
impl<'a> Service for &'a str {
    type Output = impl Sized;
    fn call(self) -> Self::Output { self }
}

This compiles now but it shouldn't. Explicit annotation for captured lifetimes in the opaque type impl Sized + 'a is required by the RFC.

I guess this is the root cause of #91601.

@rustbot label F-type_alias_impl_trait T-compiler

@aliemjay aliemjay added the C-bug Category: This is a bug. label May 12, 2022
@rustbot rustbot added F-type_alias_impl_trait `#[feature(type_alias_impl_trait)]` T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels May 12, 2022
@cjgillot cjgillot self-assigned this May 14, 2022
@aliemjay
Copy link
Member Author

Another place where lifetime parameters are captured implicitly is in type aliases:

#![feature(type_alias_impl_trait)]
type StrRef<'a> = impl AsRef<str>;
fn define(s: &str) -> StrRef<'_> { s }

This shouldn't pass as it violates the RFC.

I can see several examples in the wild that rely on this behavior (disclosure: I do!). And I expect this fix to be painful for some. The main reason is that there's no easy way to indicate the an opaque type captures a lifetime paramter. The usual way to do so

- type StrRef<'a> = impl AsRef<str>;
+ type StrRef<'a> = impl AsRef<str> + 'a;

is just wrong; it changes the semantics and adds a probably unnecessary bounds on other captured lifetime/type parameters. See how this is a problem with return-position-impl-trait #49431.

It's only solution now is to use the Captures trait hack mentioned in #49431. But it looks somewhat ugly as I expect this to be supported at language level. Here's a sample from https://github.com/HFQR/xitca-web:

impl<'r, S, State, Res, Err> Service<WebRequest<'r, State>> for MiddlewareService<S>
where
    S: for<'r2> Service<WebRequest<'r2, State>, Response = Res>,
{
    type Response = Res;
    type Future<'f> = impl Future<Output = Res> + Captures<'r> + Captures<'f> where Self: 'f;

    fn call(&self, mut req: WebRequest<'r, State>) -> Self::Future<'_> {
        async move { self.0.call(req).await }
    }
}

@aliemjay aliemjay changed the title type-alias-impl-trait in impls implicitly captures lifetime params type-alias-impl-trait implicitly captures lifetime params May 15, 2022
@cjgillot cjgillot removed their assignment May 27, 2022
@inquisitivecrystal inquisitivecrystal added requires-nightly This issue requires a nightly compiler in some way. T-types Relevant to the types team, which will review and decide on the PR/issue. labels Jun 4, 2022
@oli-obk
Copy link
Contributor

oli-obk commented Jul 5, 2022

I'm wondering if we could change the meaning of + 'a on type alias impl trait. Right now it's a no-op, as all lifetimes from the type alias are captured. But the convenient thing would be to make it mean what Captures means, as I'm rather certain almost no one will be aware of the Captures workaround

@QuineDot
Copy link

I'm wondering if we could change the meaning of + 'a on type alias impl trait. Right now it's a no-op, as all lifetimes from the type alias are captured. But the convenient thing would be to make it mean what Captures means, as I'm rather certain almost no one will be aware of the Captures workaround

It's not a no-op, as the implicit capturing is intersection based and + 'a [ + 'b ...] is union-based. For example, the following compiles but errors if you uncomment the + 'future:

type FutureT<'iter, 'future> = impl Future<Output = It<'iter>> /*+ 'future */;
fn get_iter<'iter, 'future>(this: &'iter FutIt, arg: &'future ()) -> FutureT<'iter, 'future> {
    async move {
        let _a = &arg;
        It { _inner: this }
    }
}

Adapted from this forum post.

@oli-obk oli-obk moved this from Todo to In Progress in type alias impl trait stabilization Sep 28, 2022
@oli-obk oli-obk moved this from In Progress to Done in type alias impl trait stabilization Oct 19, 2022
@bors bors closed this as completed in cb94675 Oct 20, 2022
Aaron1011 pushed a commit to Aaron1011/rust that referenced this issue Jan 6, 2023
Require lifetime bounds for opaque types in order to allow hidden types to capture said lifetimes

fixes rust-lang#96996

cc `@aliemjay`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug. F-type_alias_impl_trait `#[feature(type_alias_impl_trait)]` requires-nightly This issue requires a nightly compiler in some way. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue.
6 participants