src/doc/rustc-dev-guide/src/traits/hrtb.md MARKDOWN 128 lines View on github.com → Search inside
1# Higher-ranked trait bounds23One of the more subtle concepts in trait resolution is *higher-ranked trait4bounds*. An example of such a bound is `for<'a> MyTrait<&'a isize>`.5Let's walk through how selection on higher-ranked trait references6works.78## Basic matching and placeholder leaks910Suppose we have a trait `Foo`:1112```rust13trait Foo<X> {14    fn foo(&self, x: X) { }15}16```1718Let's say we have a function `want_hrtb` that wants a type which19implements `Foo<&'a isize>` for any `'a`:2021```rust,ignore22fn want_hrtb<T>() where T : for<'a> Foo<&'a isize> { ... }23```2425Now we have a struct `AnyInt` that implements `Foo<&'a isize>` for any26`'a`:2728```rust,ignore29struct AnyInt;30impl<'a> Foo<&'a isize> for AnyInt { }31```3233And the question is, does `AnyInt : for<'a> Foo<&'a isize>`? We want the34answer to be yes. The algorithm for figuring it out is closely related35to the subtyping for higher-ranked types (which is described [here][hrsubtype]36and also in a [paper by SPJ]. If you wish to understand higher-ranked37subtyping, we recommend you read the paper). There are a few parts:38391. Replace bound regions in the obligation with placeholders.402. Match the impl against the [placeholder] obligation.413. Check for _placeholder leaks_.4243[hrsubtype]: ./hrtb.md44[placeholder]: ../appendix/glossary.html#placeholder45[paper by SPJ]: https://www.microsoft.com/en-us/research/publication/practical-type-inference-for-arbitrary-rank-types4647So let's work through our example.48491. The first thing we would do is to50replace the bound region in the obligation with a placeholder, yielding 51`AnyInt : Foo<&'0 isize>` (here `'0` represents placeholder region #0). 52Note that we now have no quantifiers;53in terms of the compiler type, this changes from a `ty::PolyTraitRef`54to a `TraitRef`. We would then create the `TraitRef` from the impl,55using fresh variables for it's bound regions (and thus getting56`Foo<&'$a isize>`, where `'$a` is the inference variable for `'a`).57582. Next59we relate the two trait refs, yielding a graph with the constraint60that `'0 == '$a`.61623. Finally, we check for placeholder "leaks"  a63leak is basically any attempt to relate a placeholder region to another64placeholder region, or to any region that pre-existed the impl match.65The leak check is done by searching from the placeholder region to find66the set of regions that it is related to in any way. This is called67the "taint" set. To pass the check, that set must consist *solely* of68itself and region variables from the impl. If the taint set includes69any other region, then the match is a failure. In this case, the taint70set for `'0` is `{'0, '$a}`, and hence the check will succeed.7172Let's consider a failure case. Imagine we also have a struct7374```rust,ignore75struct StaticInt;76impl Foo<&'static isize> for StaticInt;77```7879We want the obligation `StaticInt : for<'a> Foo<&'a isize>` to be80considered unsatisfied. The check begins just as before. `'a` is81replaced with a placeholder `'0` and the impl trait reference is instantiated to82`Foo<&'static isize>`. When we relate those two, we get a constraint83like `'static == '0`. This means that the taint set for `'0` is `{'0,84'static}`, which fails the leak check.8586**TODO**: This is because `'static` is not a region variable but is in the87taint set, right?8889## Higher-ranked trait obligations9091Once the basic matching is done, we get to another interesting topic:92how to deal with impl obligations. I'll work through a simple example93here. Imagine we have the traits `Foo` and `Bar` and an associated impl:9495```rust96trait Foo<X> {97    fn foo(&self, x: X) { }98}99100trait Bar<X> {101    fn bar(&self, x: X) { }102}103104impl<X,F> Foo<X> for F105    where F : Bar<X>106{107}108```109110Now let's say we have an obligation `Baz: for<'a> Foo<&'a isize>` and we match111this impl. What obligation is generated as a result? We want to get112`Baz: for<'a> Bar<&'a isize>`, but how does that happen?113114After the matching, we are in a position where we have a placeholder115substitution like `X => &'0 isize`. If we apply this substitution to the116impl obligations, we get `F : Bar<&'0 isize>`. Obviously this is not117directly usable because the placeholder region `'0` cannot leak out of118our computation.119120What we do is to create an inverse mapping from the taint set of `'0`121back to the original bound region (`'a`, here) that `'0` resulted122from. (This is done in `higher_ranked::plug_leaks`). We know that the123leak check passed, so this taint set consists solely of the placeholder124region itself plus various intermediate region variables. We then walk125the trait-reference and convert every region in that taint set back to126a late-bound region, so in this case we'd wind up with127`Baz: for<'a> Bar<&'a isize>`.

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.