Expand description

Code that handles “type-outlives” constraints like T: 'a. This is based on the push_outlives_components function defined in rustc_infer, but it adds a bit of heuristics on top, in particular to deal with associated types and projections.

When we process a given T: 'a obligation, we may produce two kinds of constraints for the region inferencer:

  • Relationships between inference variables and other regions. For example, if we have &'?0 u32: 'a, then we would produce a constraint that 'a <= '?0.
  • “Verifys” that must be checked after inferencing is done. For example, if we know that, for some type parameter T, T: 'a + 'b, and we have a requirement that T: '?1, then we add a “verify” that checks that '?1 <= 'a || '?1 <= 'b.
    • Note the difference with the previous case: here, the region variable must be less than something else, so this doesn’t affect how inference works (it finds the smallest region that will do); it’s just a post-condition that we have to check.

The key point is that once this function is done, we have reduced all of our “type-region outlives” obligations into relationships between individual regions.

One key input to this function is the set of “region-bound pairs”. These are basically the relationships between type parameters and regions that are in scope at the point where the outlives obligation was incurred. When type-checking a function, particularly in the face of closures, this is not known until regionck runs! This is because some of those bounds come from things we have yet to infer.

Consider:

fn bar<T>(a: T, b: impl for<'a> Fn(&'a T)) {}
fn foo<T>(x: T) {
    bar(x, |y| { /* ... */})
         // ^ closure arg
}

Here, the type of y may involve inference variables and the like, and it may also contain implied bounds that are needed to type-check the closure body (e.g., here it informs us that T outlives the late-bound region 'a).

Note that by delaying the gathering of implied bounds until all inference information is known, we may find relationships between bound regions and other regions in the environment. For example, when we first check a closure like the one expected as argument to foo:

fn foo<U, F: for<'a> FnMut(&'a U)>(_f: F) {}

the type of the closure’s first argument would be &'a ?U. We might later infer ?U to something like &'b u32, which would imply that 'b: 'a.

Structs

The TypeOutlives struct has the job of “lowering” a T: 'a obligation into a series of 'a: 'b constraints and “verify“s, as described on the module comment. The final constraints are emitted via a “delegate” of type D – this is usually the infcx, which accrues them into the region_obligations code, but for NLL we use something else.

Traits