Home Explore Blog CI



rustc

4th chunk of `src/ty_module/instantiating_binders.md`
28de0b638506cc3218da4dbea4deb59e3cf9af62a5702cd10000000100000ebf
Fundamentally all of this complexity is because `Bound` ty/const/regions have a different representation for a given parameter on a `Binder` depending on how many other `Binder`s there are between the binder introducing the parameter, and its usage. For example given the following code:
```rust
fn foo<T>()
where
    for<'a> T: Trait<'a, for<'b> fn(&'b T, &'a u32)>
{ ... }
```
That where clause would be written as:  
`for<'a> T: Trait<'^0, for<'b> fn(&'^0 T, &'^1_0 u32)>`  
Despite there being two references to the `'a` parameter they are both represented differently: `^0` and `^1_0`, due to the fact that the latter usage is nested under a second `Binder` for the inner function pointer type.

This is in contrast to `Placeholder` ty/const/regions which do not have this limitation due to the fact that `Universe`s are specific to the current `InferCtxt` not the usage site of the parameter.

It is trivially possible to instantiate `EarlyBinder`s and unify inference variables with existing `Placeholder`s as no matter what context the `Placeholder` is in, it will have the same representation. As an example if we were to instantiate the binder on the higher ranked where clause from above, it would be represented like so:  
`T: Trait<'!1_0, for<'b> fn(&'^0 T, &'!1_0 u32)>`  
the `RePlaceholder` representation for both usages of `'a` are the same despite one being underneath another `Binder`.

If we were to then instantiate the binder on the function pointer we would get a type such as:  
`fn(&'!2_0 T, ^'!1_0 u32)`  
the `RePlaceholder` for the `'b` parameter is in a higher universe to track the fact that its binder was instantiated after the binder for `'a`.

## Instantiating with `ReLateParam`

As discussed in [the chapter about representing types][representing-types], `RegionKind` has two variants for representing generic parameters, `ReLateParam` and `ReEarlyParam`.
`ReLateParam` is conceptually a `Placeholder` that is always in the root universe (`U0`). It is used when instantiating late bound parameters of functions/closures while inside of them. Its actual representation is relatively different from both `ReEarlyParam` and `RePlaceholder`:
- A `DefId` for the item that introduced the late bound generic parameter
- A [`BoundRegionKind`] which either specifies the `DefId` of the generic parameter and its name (via a `Symbol`), or that this placeholder is representing the anonymous lifetime of a `Fn`/`FnMut` closure's self borrow. There is also a variant for `BrAnon` but this is not used for `ReLateParam`.

For example, given the following code:
```rust,ignore
impl Trait for Whatever {
    fn foo<'a>(a: &'a u32) -> &'a u32 {
        let b: &'a u32 = a;
        b
    }
}
``` 
the lifetime `'a` in the type `&'a u32` in the function body would be represented as: 
```
ReLateParam(
    {impl#0}::foo,
    BoundRegionKind::BrNamed({impl#0}::foo::'a, "'a")
)
```

In this specific case of referencing late bound generic parameters of a function from inside the body this is done implicitly during `hir_ty_lowering` rather than explicitly when instantiating a `Binder` somewhere. In some cases however, we do explicitly instantiate a `Binder` with `ReLateParam`s.

Generally whenever we have a `Binder` for late bound parameters on a function/closure and we are conceptually inside of the binder already, we use [`liberate_late_bound_regions`] to instantiate it with `ReLateParam`s. That makes this operation the `Binder` equivalent to `EarlyBinder`'s `instantiate_identity`.

As a concrete example, accessing the signature of a function we are type checking will be represented as `EarlyBinder<Binder<FnSig>>`. As we are already "inside" of these binders, we would call `instantiate_identity` followed by `liberate_late_bound_regions`.


Title: Bound vs. Placeholder Representations and ReLateParam
Summary
This section contrasts the representations of bound variables using `Bound` and `Placeholder` types, and introduces `ReLateParam`. `Bound` types have different representations depending on the nesting of binders, unlike `Placeholder` types, which use `Universe`s for consistent representation. `ReLateParam` is a `Placeholder` in the root universe, used when instantiating late bound parameters of functions/closures. It's represented by a `DefId` and a `BoundRegionKind`, and is implicitly used during `hir_ty_lowering` when referencing late bound generic parameters in a function body. `liberate_late_bound_regions` is used to instantiate `Binder`s with `ReLateParam`s when already inside the binder, similar to `EarlyBinder`'s `instantiate_identity`.