Late bound lifetimes (HRTB related)


#1

@nikomatsakis 's post 5 years ago explained the resonale behind late bound lifetimes:

I will call this a late-bound lifetime parameter , because we don’t need to substitute a specific lifetime right away, but rather we can wait until the function is called.

or the following post quote:

  • Lifetime parameters on fns or methods that appear within a type bound are considered early bound . Those that do not are considered late bound .

The following recently found example code that causes ICE

trait Lt<'a> {
    type T;
}
impl<'a> Lt<'a> for () {
    type T = ();
}

fn main() {
    let _:fn(<() as Lt<'_>>::T) = |()| {};
}

however, put a serious question in front of us:

If the function were called, but no lifetime can be concluded, what should the compiler do?

Specifically, in the example above, <() as Lt<'_>>::T is late bound because the lifetime '_ cannot be bound until we know () must be a supertype of <() as Lt<'_>>::T. But even then, we don’t know what '_ should be.

Right now, the compiler cannot handle this case and get confused: "<() as Lt<'_>>::T have no relation with () and this should be rejected earlier" whiles the decision was just being postponed.

As this is ICE in STABLE we now have to make a decision:

What should we do?

EDIT

The following

trait Lt<'a> {
    type T;
}
impl<'a> Lt<'a> for () {
    type T = ();
}

fn test<'a>() {
    let _:fn(<() as Lt<'a>>::T) = |()| {};
}

fn main() {
    test();
}

Is working!, however if we remove the 'a lifetime from test and replace Lt<'a> with Lt<'_> it triggers ICE. I think this means the code to justify "<() as Lt<'_>>::T is a subtype of ()" failed to pick up a brand new lifetime parameter, so it didn’t fall back to the case above.


#2

Taking the hat of a language lawyer, let me quote the rules:

  • Whenever a type- or lifetime-parameterized path is referenced, users have the option of either:
    • supplying no values at all, in which case defaults/inference will be used for all parameteters.
  • Defaults, whether explicit or implicit, are only permitted in the following contexts:
    • In a function signature, values for lifetimes parameters may be defaulted. All such lifetimes will occur in types or trait references, and hence are early bound. They are replaced by anonymous bound lifetime parameters as today.
    • If these missing values appear in the list of arguments, the resulting parameters will be late bound. If the missing values appear in within a type bound, the resulting parameters will be early bound.
    • In a function body, values for types may be defaulted. They will be replaced with fresh type variable.
    • In a function body, values for lifetimes may be defaulted.
    • If the lifetime parameter is early bound, a fresh lifetime variable is substituted.
    • If the lifetime parameter is late bound, then it must appear as a parameter to a fn item, and results in a bound parameter scoped to the resulting fn type.

In the example given, there were no values supplied at all. So defaults/inference will be used.

Then "defaults are only permitted in " 6 context rules.

Rule 1: “… all such lifetimes will occur in types or trait references, and hence are early bound…” so either the life time should be early bound instead, or this rule does not apply.

Rule 2: Does not apply as the missing values are not appear anywhere.

Rule 3,4: the function body is empty and so nothing can be defaulted.

Rule 5: The lifetime were considered late bound, not applicable.

Rule 6: Seems this is the only rule that is applicable? Then how can we calculate an associate type when the lifetime parameter is bound?


#3

I examined the working version with -Zdump-mir, and figured out that all the MIR generated do have <() as Lt<'a>>::T resolved to ().

By the definition of “late bound lifetime”, in either the working or breaking version the lifetime of Lt<> is late bound.

what I can conclude from this is that associate types should be resolved Before MIR generation. So looking at MIR does not help.