| # `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(&'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(&'a u32)` would be |
| represented in rustc as: |
| ``` |
| Binder( |
| fn(&RegionKind::Bound(DebruijnIndex(0), BoundVar(0)) u32) -> (), |
| &[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(&'^0_0), &[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(&'a &'b T)>: Trait, |
| ``` |
| This would be represented as |
| ``` |
| Binder( |
| Foo<Binder( |
| fn(&'^1_0 &'^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`, |
| and 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(&'^1_0 &'^1 T/#0), |
| &[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. |
| |
| [`Binder`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Binder.html |
| [`BoundVar`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.BoundVar.html |
| [`BoundRegionKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.BoundRegionKind.html |
| [`BoundTyKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.BoundTyKind.html |