Home Explore Blog CI



rustc

src/ty_module/binders.md
3d88e92dd7dd88c447dd90b31ec2bac05bcfcbea6a70e3910000000300001063
# `Binder` and Higher ranked regions

Sometimes we define generic parameters not on an item but as part of a type or a where clause. As an example the type `for<'a> fn(&amp;'a u32)` or the where clause `for<'a> T: Trait<'a>` both introduce a generic lifetime named `'a`. Currently there is no stable syntax for `for<T>` or `for<const N: usize>` but on nightly `feature(non_lifetime_binders)` can be used to write where clauses (but not types) using `for<T>`/`for<const N: usize>`.

The `for` is referred to as a "binder" because it brings new names into scope. In rustc we use the `Binder` type to track where these parameters are introduced and what the parameters are (i.e. how many and whether the parameter is a type/const/region). A type such as `for<'a> fn(&amp;'a u32)` would be
represented in rustc as:
```
Binder(
    fn(&amp;RegionKind::Bound(DebruijnIndex(0), BoundVar(0)) u32) -> (),
    &amp;[BoundVariableKind::Region(...)],
)
```

Usages of these parameters is represented by the `RegionKind::Bound` (or `TyKind::Bound`/`ConstKind::Bound` variants). These bound regions/types/consts are composed of two main pieces of data:
- A [DebruijnIndex](../appendix/background.md#what-is-a-de-bruijn-index) to specify which binder we are referring to.
- A [`BoundVar`] which specifies which of the parameters that the `Binder` introduces we are referring to.

We also sometimes store some extra information for diagnostics reasons via the [`BoundTyKind`]/[`BoundRegionKind`] but this is not important for type equality or more generally the semantics of `Ty`. (omitted from the above example)

In debug output (and also informally when talking to each other) we tend to write these bound variables in the format of `^DebruijnIndex_BoundVar`. The above example would instead be written as `Binder(fn(&amp;'^0_0), &amp;[BoundVariableKind::Region])`. Sometimes when the `DebruijnIndex` is `0` we just omit it and would write `^0`.

Another concrete example, this time a mixture of `for<'a>` in a where clause and a type:
```
where
    for<'a> Foo<for<'b> fn(&amp;'a &amp;'b T)>: Trait,
```
This would be represented as
```
Binder(
    Foo<Binder(
        fn(&amp;'^1_0 &amp;'^0 T/#0),
        [BoundVariableKind::Region(...)]
    )>: Trait,
    [BoundVariableKind::Region(...)]
)
```

Note how the `'^1_0` refers to the `'a` parameter. We use a `DebruijnIndex` of `1` to refer to the binder one level up from the innermost one, and a var of `0` to refer to the first parameter bound which is `'a`. We also use `'^0` to refer to the `'b` parameter, the `DebruijnIndex` is `0` (referring to the innermost binder) so we omit it, leaving only the boundvar of `0` referring to the first parameter bound which is `'b`.

We did not always explicitly track the set of bound vars introduced by each `Binder`, this caused a number of bugs (read: ICEs [#81193](https://github.com/rust-lang/rust/issues/81193), [#79949](https://github.com/rust-lang/rust/issues/79949), [#83017](https://github.com/rust-lang/rust/issues/83017)). By tracking these explicitly we can assert when constructing higher ranked where clauses/types that there are no escaping bound variables or variables from a different binder. See the following example of an invalid type inside of a binder:
```
Binder(
    fn(&amp;'^1_0 &amp;'^1 T/#0),
    &amp;[BoundVariableKind::Region(...)],
)
```
This would cause all kinds of issues as the region `'^1_0` refers to a binder at a higher level than the outermost binder i.e. it is an escaping bound var. The `'^1` region (also writeable as `'^0_1`) is also ill formed as the binder it refers to does not introduce a second parameter. Modern day rustc will ICE when constructing this binder due to both of those reasons, in the past we would have simply allowed this to work and then ran into issues in other parts of the codebase. 


Chunks
36d31ffc (1st chunk of `src/ty_module/binders.md`)
Title: Understanding `Binder` and Higher-Ranked Regions in Rust
Summary
This section explains the concept of `Binder` in Rust, which is used to track generic parameters introduced by `for<>` syntax in types and where clauses. It details how bound regions, types, and consts are represented using `DebruijnIndex` and `BoundVar` to refer to specific parameters within a binder. The text also highlights the importance of explicitly tracking bound variables to prevent issues like escaping bound variables and provides examples of valid and invalid higher-ranked types.