rust-lang/rust

`impl Trait` Lifetime Elision

cramertj opened this issue · 3 comments

The following ICEs:
fn foo(x: &bool) -> impl Into<&bool> { x }

The message is: error: internal compiler error: /checkout/src/librustc_typeck/check/mod.rs:618: escaping regions in predicate Obligation(predicate=Binder(TraitPredicate(<_ as std::convert::Into<&bool>>)),depth=0) --> src/main.rs:6:21.

Should we explicitly disallow lifetime elision in impl Trait?

eddyb commented

The ICE should not be fixed, but rather elision be disabled, unless someone can point me to an accepted RFC allowing this kind of elision. cc @rust-lang/lang

So @cramertj and I discussed this over IRC today. The plan is to support elision as follows:

  • Lifetimes can be elided, but if you just write impl Trait it does not capture &self. To capture, you must write impl Trait + '_.
  • We will effectively desugar by mapping '_ to an add'l lifetime parameter on the abstract type that is being created. So we would have:
fn foo(x: &u32) -> impl Trait<'_> { ... }

desugaring to something like:

abstract type Foo<'a>: Trait<'a>;
fn foo(x: &u32) -> Foo<'_> { ... }

To implement this, we have to first modify HIR lowerring to detect elided lifetimes when it is scraping the impl trait bounds for additional parameters. We can then add a special parameter (perhaps naming it '_?).

Then we have to modify name resolution as follows. When we are lifetime-resolving the trait bounds (Trait<'a> in the above example):

self.with(scope, |_old_scope, this| {

we would insert an elision scope:

/// A scope which either determines unspecified lifetimes or errors
/// on them (e.g. due to ambiguity). For more details, see `Elide`.
Elision {
elide: Elide,
s: ScopeRef<'a>
},

with Elide::Exact set to a reference to this (early-bound) extra parameter:

/// Always use this one lifetime.
Exact(Region),

We will also insert a reference to the elided lifetime into the Foo<'_> reference, which can just resolve as normal.

An example from #46565:

struct Parent
{
    children: Option<Vec<String>>
}

impl Parent {
    pub fn children() -> impl Iterator<Item=&str> {
        self.children.unwrap_or(Vec::new()).iter()
    }
}