Clarifications and edits to hrtb chapter

This commit is contained in:
Mark Mansi 2018-02-12 17:22:23 -06:00
parent cbd05bc786
commit e5aca6a8ec
2 changed files with 25 additions and 17 deletions

View File

@ -51,4 +51,3 @@ and draw finer-grained distinctions, but that led to a serious of
annoying and weird bugs like #22019 and #18290. This simple rule seems annoying and weird bugs like #22019 and #18290. This simple rule seems
to be pretty clearly safe and also still retains a very high hit rate to be pretty clearly safe and also still retains a very high hit rate
(~95% when compiling rustc). (~95% when compiling rustc).

View File

@ -1,14 +1,13 @@
# Higher-ranked trait bounds # Higher-ranked trait bounds
One of the more subtle concepts at work are *higher-ranked trait 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>`. bounds*. An example of such a bound is `for<'a> MyTrait<&'a isize>`.
Let's walk through how selection on higher-ranked trait references Let's walk through how selection on higher-ranked trait references
works. works.
## Basic matching and skolemization leaks ## Basic matching and skolemization leaks
Let's walk through the test `compile-fail/hrtb-just-for-static.rs` to see Suppose we have a trait `Foo`:
how it works. The test starts with the trait `Foo`:
```rust ```rust
trait Foo<X> { trait Foo<X> {
@ -33,25 +32,34 @@ impl<'a> Foo<&'a isize> for AnyInt { }
And the question is, does `AnyInt : for<'a> Foo<&'a isize>`? We want the 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 answer to be yes. The algorithm for figuring it out is closely related
to the subtyping for higher-ranked types (which is described in to the subtyping for higher-ranked types (which is described in [here][hrsubtype]
`middle::infer::higher_ranked::doc`, but also in a [paper by SPJ] that and also in a [paper by SPJ]. If you wish to understand higher-ranked
I recommend you read). subtyping, we recommend you read the paper). There are a few parts:
1. Skolemize the obligation. **TODO**: We should define _skolemize_.
1. _Skolemize_ the obligation.
2. Match the impl against the skolemized obligation. 2. Match the impl against the skolemized obligation.
3. Check for skolemization leaks. 3. Check for _skolemization leaks_.
[hrsubtype]: https://github.com/rust-lang/rust/tree/master/src/librustc/infer/higher_ranked/README.md
[paper by SPJ]: http://research.microsoft.com/en-us/um/people/simonpj/papers/higher-rank/ [paper by SPJ]: http://research.microsoft.com/en-us/um/people/simonpj/papers/higher-rank/
So let's work through our example. The first thing we would do is to So let's work through our example.
1. The first thing we would do is to
skolemize the obligation, yielding `AnyInt : Foo<&'0 isize>` (here `'0` skolemize the obligation, yielding `AnyInt : Foo<&'0 isize>` (here `'0`
represents skolemized region #0). Note that now have no quantifiers; represents skolemized region #0). Note that we now have no quantifiers;
in terms of the compiler type, this changes from a `ty::PolyTraitRef` in terms of the compiler type, this changes from a `ty::PolyTraitRef`
to a `TraitRef`. We would then create the `TraitRef` from the impl, to a `TraitRef`. We would then create the `TraitRef` from the impl,
using fresh variables for it's bound regions (and thus getting using fresh variables for it's bound regions (and thus getting
`Foo<&'$a isize>`, where `'$a` is the inference variable for `'a`). Next `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 we relate the two trait refs, yielding a graph with the constraint
that `'0 == '$a`. Finally, we check for skolemization "leaks" a that `'0 == '$a`.
3. Finally, we check for skolemization "leaks" a
leak is basically any attempt to relate a skolemized region to another leak is basically any attempt to relate a skolemized region to another
skolemized region, or to any region that pre-existed the impl match. skolemized region, or to any region that pre-existed the impl match.
The leak check is done by searching from the skolemized region to find The leak check is done by searching from the skolemized region to find
@ -75,6 +83,8 @@ skolemized to `'0` and the impl trait reference is instantiated to
like `'static == '0`. This means that the taint set for `'0` is `{'0, like `'static == '0`. This means that the taint set for `'0` is `{'0,
'static}`, which fails the leak check. '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 ## Higher-ranked trait obligations
Once the basic matching is done, we get to another interesting topic: Once the basic matching is done, we get to another interesting topic:
@ -96,9 +106,9 @@ impl<X,F> Foo<X> for F
} }
``` ```
Now let's say we have a obligation `for<'a> Foo<&'a isize>` and we match Now let's say we have a obligation `Baz: for<'a> Foo<&'a isize>` and we match
this impl. What obligation is generated as a result? We want to get this impl. What obligation is generated as a result? We want to get
`for<'a> Bar<&'a isize>`, but how does that happen? `Baz: for<'a> Bar<&'a isize>`, but how does that happen?
After the matching, we are in a position where we have a skolemized After the matching, we are in a position where we have a skolemized
substitution like `X => &'0 isize`. If we apply this substitution to the substitution like `X => &'0 isize`. If we apply this substitution to the
@ -112,5 +122,4 @@ from. (This is done in `higher_ranked::plug_leaks`). We know that the
leak check passed, so this taint set consists solely of the skolemized leak check passed, so this taint set consists solely of the skolemized
region itself plus various intermediate region variables. We then walk region itself plus various intermediate region variables. We then walk
the trait-reference and convert every region in that taint set back to 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 `for<'a> F : a late-bound region, so in this case we'd wind up with `Baz: for<'a> Bar<&'a isize>`.
Bar<&'a isize>`.