| # Higher-ranked trait bounds | |
| One of the more subtle concepts in trait resolution is *higher-ranked trait | |
| bounds*. An example of such a bound is `for<'a> MyTrait<&'a isize>`. | |
| Let's walk through how selection on higher-ranked trait references | |
| works. | |
| ## Basic matching and placeholder leaks | |
| Suppose we have a trait `Foo`: | |
| ```rust | |
| trait Foo<X> { | |
| fn foo(&self, x: X) { } | |
| } | |
| ``` | |
| Let's say we have a function `want_hrtb` that wants a type which | |
| implements `Foo<&'a isize>` for any `'a`: | |
| ```rust,ignore | |
| fn want_hrtb<T>() where T : for<'a> Foo<&'a isize> { ... } | |
| ``` | |
| Now we have a struct `AnyInt` that implements `Foo<&'a isize>` for any | |
| `'a`: | |
| ```rust,ignore | |
| struct AnyInt; | |
| impl<'a> Foo<&'a isize> for AnyInt { } | |
| ``` | |
| And the question is, does `AnyInt : for<'a> Foo<&'a isize>`? We want the | |
| answer to be yes. The algorithm for figuring it out is closely related | |
| to the subtyping for higher-ranked types (which is described [here][hrsubtype] | |
| and also in a [paper by SPJ]. If you wish to understand higher-ranked | |
| subtyping, we recommend you read the paper). There are a few parts: | |
| 1. Replace bound regions in the obligation with placeholders. | |
| 2. Match the impl against the [placeholder] obligation. | |
| 3. Check for _placeholder leaks_. | |
| [hrsubtype]: ./hrtb.md | |
| [placeholder]: ../appendix/glossary.md#placeholder | |
| [paper by SPJ]: https://www.microsoft.com/en-us/research/publication/practical-type-inference-for-arbitrary-rank-types | |
| So let's work through our example. | |
| 1. The first thing we would do is to | |
| replace the bound region in the obligation with a placeholder, yielding | |
| `AnyInt : Foo<&'0 isize>` (here `'0` represents placeholder region #0). | |
| Note that we now have no quantifiers; | |
| in terms of the compiler type, this changes from a `ty::PolyTraitRef` | |
| to a `TraitRef`. We would then create the `TraitRef` from the impl, | |
| using fresh variables for it's bound regions (and thus getting | |
| `Foo<&'$a isize>`, where `'$a` is the inference variable for `'a`). | |
| 2. Next | |
| we relate the two trait refs, yielding a graph with the constraint | |
| that `'0 == '$a`. | |
| 3. Finally, we check for placeholder "leaks" – a | |
| leak is basically any attempt to relate a placeholder region to another | |
| placeholder region, or to any region that pre-existed the impl match. | |
| The leak check is done by searching from the placeholder region to find | |
| the set of regions that it is related to in any way. This is called | |
| the "taint" set. To pass the check, that set must consist *solely* of | |
| itself and region variables from the impl. If the taint set includes | |
| any other region, then the match is a failure. In this case, the taint | |
| set for `'0` is `{'0, '$a}`, and hence the check will succeed. | |
| Let's consider a failure case. Imagine we also have a struct | |
| ```rust,ignore | |
| struct StaticInt; | |
| impl Foo<&'static isize> for StaticInt; | |
| ``` | |
| We want the obligation `StaticInt : for<'a> Foo<&'a isize>` to be | |
| considered unsatisfied. The check begins just as before. `'a` is | |
| replaced with a placeholder `'0` and the impl trait reference is instantiated to | |
| `Foo<&'static isize>`. When we relate those two, we get a constraint | |
| like `'static == '0`. This means that the taint set for `'0` is `{'0, | |
| 'static}`, which fails the leak check. | |
| **TODO**: This is because `'static` is not a region variable but is in the | |
| taint set, right? | |
| ## Higher-ranked trait obligations | |
| Once the basic matching is done, we get to another interesting topic: | |
| how to deal with impl obligations. I'll work through a simple example | |
| here. Imagine we have the traits `Foo` and `Bar` and an associated impl: | |
| ```rust | |
| trait Foo<X> { | |
| fn foo(&self, x: X) { } | |
| } | |
| trait Bar<X> { | |
| fn bar(&self, x: X) { } | |
| } | |
| impl<X,F> Foo<X> for F | |
| where F : Bar<X> | |
| { | |
| } | |
| ``` | |
| Now let's say we have an obligation `Baz: for<'a> Foo<&'a isize>` and we match | |
| this impl. What obligation is generated as a result? We want to get | |
| `Baz: for<'a> Bar<&'a isize>`, but how does that happen? | |
| After the matching, we are in a position where we have a placeholder | |
| substitution like `X => &'0 isize`. If we apply this substitution to the | |
| impl obligations, we get `F : Bar<&'0 isize>`. Obviously this is not | |
| directly usable because the placeholder region `'0` cannot leak out of | |
| our computation. | |
| What we do is to create an inverse mapping from the taint set of `'0` | |
| back to the original bound region (`'a`, here) that `'0` resulted | |
| from. (This is done in `higher_ranked::plug_leaks`). We know that the | |
| leak check passed, so this taint set consists solely of the placeholder | |
| region itself plus various intermediate region variables. We then walk | |
| the trait-reference and convert every region in that taint set back to | |
| a late-bound region, so in this case we'd wind up with | |
| `Baz: for<'a> Bar<&'a isize>`. |