From dab6e70e03b569c72ad74328ad91f48a2e00e44f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 15 Dec 2014 05:44:59 -0500 Subject: [PATCH] Convert gigantic comment away from `//!` form. It is annoying to read (`//!` is intrusive) and annoying to edit (must maintain a prefix on every line). Since the only purpose of a `doc.rs` file is to have a bunch of text, using `/*!` and `*/` without indentations seems appropriate. --- src/librustc/middle/traits/doc.rs | 798 +++++++++++++++--------------- 1 file changed, 402 insertions(+), 396 deletions(-) diff --git a/src/librustc/middle/traits/doc.rs b/src/librustc/middle/traits/doc.rs index 62246b77ee9..b0addfa0bd9 100644 --- a/src/librustc/middle/traits/doc.rs +++ b/src/librustc/middle/traits/doc.rs @@ -8,399 +8,405 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! # TRAIT RESOLUTION -//! -//! This document describes the general process and points out some non-obvious -//! things. -//! -//! ## Major concepts -//! -//! Trait resolution is the process of pairing up an impl with each -//! reference to a trait. So, for example, if there is a generic function like: -//! -//! fn clone_slice(x: &[T]) -> Vec { ... } -//! -//! and then a call to that function: -//! -//! let v: Vec = clone_slice([1, 2, 3].as_slice()) -//! -//! it is the job of trait resolution to figure out (in which case) -//! whether there exists an impl of `int : Clone` -//! -//! Note that in some cases, like generic functions, we may not be able to -//! find a specific impl, but we can figure out that the caller must -//! provide an impl. To see what I mean, consider the body of `clone_slice`: -//! -//! fn clone_slice(x: &[T]) -> Vec { -//! let mut v = Vec::new(); -//! for e in x.iter() { -//! v.push((*e).clone()); // (*) -//! } -//! } -//! -//! The line marked `(*)` is only legal if `T` (the type of `*e`) -//! implements the `Clone` trait. Naturally, since we don't know what `T` -//! is, we can't find the specific impl; but based on the bound `T:Clone`, -//! we can say that there exists an impl which the caller must provide. -//! -//! We use the term *obligation* to refer to a trait reference in need of -//! an impl. -//! -//! ## Overview -//! -//! Trait resolution consists of three major parts: -//! -//! - SELECTION: Deciding how to resolve a specific obligation. For -//! example, selection might decide that a specific obligation can be -//! resolved by employing an impl which matches the self type, or by -//! using a parameter bound. In the case of an impl, Selecting one -//! obligation can create *nested obligations* because of where clauses -//! on the impl itself. It may also require evaluating those nested -//! obligations to resolve ambiguities. -//! -//! - FULFILLMENT: The fulfillment code is what tracks that obligations -//! are completely fulfilled. Basically it is a worklist of obligations -//! to be selected: once selection is successful, the obligation is -//! removed from the worklist and any nested obligations are enqueued. -//! -//! - COHERENCE: The coherence checks are intended to ensure that there -//! are never overlapping impls, where two impls could be used with -//! equal precedence. -//! -//! ## Selection -//! -//! Selection is the process of deciding whether an obligation can be -//! resolved and, if so, how it is to be resolved (via impl, where clause, etc). -//! The main interface is the `select()` function, which takes an obligation -//! and returns a `SelectionResult`. There are three possible outcomes: -//! -//! - `Ok(Some(selection))` -- yes, the obligation can be resolved, and -//! `selection` indicates how. If the impl was resolved via an impl, -//! then `selection` may also indicate nested obligations that are required -//! by the impl. -//! -//! - `Ok(None)` -- we are not yet sure whether the obligation can be -//! resolved or not. This happens most commonly when the obligation -//! contains unbound type variables. -//! -//! - `Err(err)` -- the obligation definitely cannot be resolved due to a -//! type error, or because there are no impls that could possibly apply, -//! etc. -//! -//! The basic algorithm for selection is broken into two big phases: -//! candidate assembly and confirmation. -//! -//! ### Candidate assembly -//! -//! Searches for impls/where-clauses/etc that might -//! possibly be used to satisfy the obligation. Each of those is called -//! a candidate. To avoid ambiguity, we want to find exactly one -//! candidate that is definitively applicable. In some cases, we may not -//! know whether an impl/where-clause applies or not -- this occurs when -//! the obligation contains unbound inference variables. -//! -//! The basic idea for candidate assembly is to do a first pass in which -//! we identify all possible candidates. During this pass, all that we do -//! is try and unify the type parameters. (In particular, we ignore any -//! nested where clauses.) Presuming that this unification succeeds, the -//! impl is added as a candidate. -//! -//! Once this first pass is done, we can examine the set of candidates. If -//! it is a singleton set, then we are done: this is the only impl in -//! scope that could possibly apply. Otherwise, we can winnow down the set -//! of candidates by using where clauses and other conditions. If this -//! reduced set yields a single, unambiguous entry, we're good to go, -//! otherwise the result is considered ambiguous. -//! -//! #### The basic process: Inferring based on the impls we see -//! -//! This process is easier if we work through some examples. Consider -//! the following trait: -//! -//! ``` -//! trait Convert { -//! fn convert(&self) -> Target; -//! } -//! ``` -//! -//! This trait just has one method. It's about as simple as it gets. It -//! converts from the (implicit) `Self` type to the `Target` type. If we -//! wanted to permit conversion between `int` and `uint`, we might -//! implement `Convert` like so: -//! -//! ```rust -//! impl Convert for int { ... } // int -> uint -//! impl Convert for uint { ... } // uint -> uint -//! ``` -//! -//! Now imagine there is some code like the following: -//! -//! ```rust -//! let x: int = ...; -//! let y = x.convert(); -//! ``` -//! -//! The call to convert will generate a trait reference `Convert<$Y> for -//! int`, where `$Y` is the type variable representing the type of -//! `y`. When we match this against the two impls we can see, we will find -//! that only one remains: `Convert for int`. Therefore, we can -//! select this impl, which will cause the type of `$Y` to be unified to -//! `uint`. (Note that while assembling candidates, we do the initial -//! unifications in a transaction, so that they don't affect one another.) -//! -//! There are tests to this effect in src/test/run-pass: -//! -//! traits-multidispatch-infer-convert-source-and-target.rs -//! traits-multidispatch-infer-convert-target.rs -//! -//! #### Winnowing: Resolving ambiguities -//! -//! But what happens if there are multiple impls where all the types -//! unify? Consider this example: -//! -//! ```rust -//! trait Get { -//! fn get(&self) -> Self; -//! } -//! -//! impl Get for T { -//! fn get(&self) -> T { *self } -//! } -//! -//! impl Get for Box { -//! fn get(&self) -> Box { box get_it(&**self) } -//! } -//! ``` -//! -//! What happens when we invoke `get_it(&box 1_u16)`, for example? In this -//! case, the `Self` type is `Box` -- that unifies with both impls, -//! because the first applies to all types, and the second to all -//! boxes. In the olden days we'd have called this ambiguous. But what we -//! do now is do a second *winnowing* pass that considers where clauses -//! and attempts to remove candidates -- in this case, the first impl only -//! applies if `Box : Copy`, which doesn't hold. After winnowing, -//! then, we are left with just one candidate, so we can proceed. There is -//! a test of this in `src/test/run-pass/traits-conditional-dispatch.rs`. -//! -//! #### Matching -//! -//! The subroutines that decide whether a particular impl/where-clause/etc -//! applies to a particular obligation. At the moment, this amounts to -//! unifying the self types, but in the future we may also recursively -//! consider some of the nested obligations, in the case of an impl. -//! -//! #### Lifetimes and selection -//! -//! Because of how that lifetime inference works, it is not possible to -//! give back immediate feedback as to whether a unification or subtype -//! relationship between lifetimes holds or not. Therefore, lifetime -//! matching is *not* considered during selection. This is reflected in -//! the fact that subregion assignment is infallible. This may yield -//! lifetime constraints that will later be found to be in error (in -//! contrast, the non-lifetime-constraints have already been checked -//! during selection and can never cause an error, though naturally they -//! may lead to other errors downstream). -//! -//! #### Where clauses -//! -//! Besides an impl, the other major way to resolve an obligation is via a -//! where clause. The selection process is always given a *parameter -//! environment* which contains a list of where clauses, which are -//! basically obligations that can assume are satisfiable. We will iterate -//! over that list and check whether our current obligation can be found -//! in that list, and if so it is considered satisfied. More precisely, we -//! want to check whether there is a where-clause obligation that is for -//! the same trait (or some subtrait) and for which the self types match, -//! using the definition of *matching* given above. -//! -//! Consider this simple example: -//! -//! trait A1 { ... } -//! trait A2 : A1 { ... } -//! -//! trait B { ... } -//! -//! fn foo { ... } -//! -//! Clearly we can use methods offered by `A1`, `A2`, or `B` within the -//! body of `foo`. In each case, that will incur an obligation like `X : -//! A1` or `X : A2`. The parameter environment will contain two -//! where-clauses, `X : A2` and `X : B`. For each obligation, then, we -//! search this list of where-clauses. To resolve an obligation `X:A1`, -//! we would note that `X:A2` implies that `X:A1`. -//! -//! ### Confirmation -//! -//! Confirmation unifies the output type parameters of the trait with the -//! values found in the obligation, possibly yielding a type error. If we -//! return to our example of the `Convert` trait from the previous -//! section, confirmation is where an error would be reported, because the -//! impl specified that `T` would be `uint`, but the obligation reported -//! `char`. Hence the result of selection would be an error. -//! -//! ### Selection during translation -//! -//! During type checking, we do not store the results of trait selection. -//! We simply wish to verify that trait selection will succeed. Then -//! later, at trans time, when we have all concrete types available, we -//! can repeat the trait selection. In this case, we do not consider any -//! where-clauses to be in scope. We know that therefore each resolution -//! will resolve to a particular impl. -//! -//! One interesting twist has to do with nested obligations. In general, in trans, -//! we only need to do a "shallow" selection for an obligation. That is, we wish to -//! identify which impl applies, but we do not (yet) need to decide how to select -//! any nested obligations. Nonetheless, we *do* currently do a complete resolution, -//! and that is because it can sometimes inform the results of type inference. That is, -//! we do not have the full substitutions in terms of the type varibales of the impl available -//! to us, so we must run trait selection to figure everything out. -//! -//! Here is an example: -//! -//! trait Foo { ... } -//! impl> Foo for Vec { ... } -//! -//! impl Bar for int { ... } -//! -//! After one shallow round of selection for an obligation like `Vec -//! : Foo`, we would know which impl we want, and we would know that -//! `T=int`, but we do not know the type of `U`. We must select the -//! nested obligation `int : Bar` to find out that `U=uint`. -//! -//! It would be good to only do *just as much* nested resolution as -//! necessary. Currently, though, we just do a full resolution. -//! -//! ## Method matching -//! -//! Method dispach follows a slightly different path than normal trait -//! selection. This is because it must account for the transformed self -//! type of the receiver and various other complications. The procedure is -//! described in `select.rs` in the "METHOD MATCHING" section. -//! -//! # Caching and subtle considerations therewith -//! -//! In general we attempt to cache the results of trait selection. This -//! is a somewhat complex process. Part of the reason for this is that we -//! want to be able to cache results even when all the types in the trait -//! reference are not fully known. In that case, it may happen that the -//! trait selection process is also influencing type variables, so we have -//! to be able to not only cache the *result* of the selection process, -//! but *replay* its effects on the type variables. -//! -//! ## An example -//! -//! The high-level idea of how the cache works is that we first replace -//! all unbound inference variables with skolemized versions. Therefore, -//! if we had a trait reference `uint : Foo<$1>`, where `$n` is an unbound -//! inference variable, we might replace it with `uint : Foo<%0>`, where -//! `%n` is a skolemized type. We would then look this up in the cache. -//! If we found a hit, the hit would tell us the immediate next step to -//! take in the selection process: i.e., apply impl #22, or apply where -//! clause `X : Foo`. Let's say in this case there is no hit. -//! Therefore, we search through impls and where clauses and so forth, and -//! we come to the conclusion that the only possible impl is this one, -//! with def-id 22: -//! -//! impl Foo for uint { ... } // Impl #22 -//! -//! We would then record in the cache `uint : Foo<%0> ==> -//! ImplCandidate(22)`. Next we would confirm `ImplCandidate(22)`, which -//! would (as a side-effect) unify `$1` with `int`. -//! -//! Now, at some later time, we might come along and see a `uint : -//! Foo<$3>`. When skolemized, this would yield `uint : Foo<%0>`, just as -//! before, and hence the cache lookup would succeed, yielding -//! `ImplCandidate(22)`. We would confirm `ImplCandidate(22)` which would -//! (as a side-effect) unify `$3` with `int`. -//! -//! ## Where clauses and the local vs global cache -//! -//! One subtle interaction is that the results of trait lookup will vary -//! depending on what where clauses are in scope. Therefore, we actually -//! have *two* caches, a local and a global cache. The local cache is -//! attached to the `ParameterEnvironment` and the global cache attached -//! to the `tcx`. We use the local cache whenever the result might depend -//! on the where clauses that are in scope. The determination of which -//! cache to use is done by the method `pick_candidate_cache` in -//! `select.rs`. -//! -//! There are two cases where we currently use the local cache. The -//! current rules are probably more conservative than necessary. -//! -//! ### Trait references that involve parameter types -//! -//! The most obvious case where you need the local environment is -//! when the trait reference includes parameter types. For example, -//! consider the following function: -//! -//! impl Vec { -//! fn foo(x: T) -//! where T : Foo -//! { ... } -//! -//! fn bar(x: T) -//! { ... } -//! } -//! -//! If there is an obligation `T : Foo`, or `int : Bar`, or whatever, -//! clearly the results from `foo` and `bar` are potentially different, -//! since the set of where clauses in scope are different. -//! -//! ### Trait references with unbound variables when where clauses are in scope -//! -//! There is another less obvious interaction which involves unbound variables -//! where *only* where clauses are in scope (no impls). This manifested as -//! issue #18209 (`run-pass/trait-cache-issue-18209.rs`). Consider -//! this snippet: -//! -//! ``` -//! pub trait Foo { -//! fn load_from() -> Box; -//! fn load() -> Box { -//! Foo::load_from() -//! } -//! } -//! ``` -//! -//! The default method will incur an obligation `$0 : Foo` from the call -//! to `load_from`. If there are no impls, this can be eagerly resolved to -//! `VtableParam(Self : Foo)` and cached. Because the trait reference -//! doesn't involve any parameters types (only the resolution does), this -//! result was stored in the global cache, causing later calls to -//! `Foo::load_from()` to get nonsense. -//! -//! To fix this, we always use the local cache if there are unbound -//! variables and where clauses in scope. This is more conservative than -//! necessary as far as I can tell. However, it still seems to be a simple -//! rule and I observe ~99% hit rate on rustc, so it doesn't seem to hurt -//! us in particular. -//! -//! Here is an example of the kind of subtle case that I would be worried -//! about with a more complex rule (although this particular case works -//! out ok). Imagine the trait reference doesn't directly reference a -//! where clause, but the where clause plays a role in the winnowing -//! phase. Something like this: -//! -//! ``` -//! pub trait Foo { ... } -//! pub trait Bar { ... } -//! impl Foo for T { ... } // Impl A -//! impl Foo for uint { ... } // Impl B -//! ``` -//! -//! Now, in some function, we have no where clauses in scope, and we have -//! an obligation `$1 : Foo<$0>`. We might then conclude that `$0=char` -//! and `$1=uint`: this is because for impl A to apply, `uint:Bar` would -//! have to hold, and we know it does not or else the coherence check -//! would have failed. So we might enter into our global cache: `$1 : -//! Foo<$0> => Impl B`. Then we come along in a different scope, where a -//! generic type `A` is around with the bound `A:Bar`. Now suddenly the -//! impl is viable. -//! -//! The flaw in this imaginary DOOMSDAY SCENARIO is that we would not -//! currently conclude that `$1 : Foo<$0>` implies that `$0 == uint` and -//! `$1 == char`, even though it is true that (absent type parameters) -//! there is no other type the user could enter. However, it is not -//! *completely* implausible that we *could* draw this conclusion in the -//! future; we wouldn't have to guess types, in particular, we could be -//! led by the impls. +/*! + +# TRAIT RESOLUTION + +This document describes the general process and points out some non-obvious +things. + +## Major concepts + +Trait resolution is the process of pairing up an impl with each +reference to a trait. So, for example, if there is a generic function like: + + fn clone_slice(x: &[T]) -> Vec { ... } + +and then a call to that function: + + let v: Vec = clone_slice([1, 2, 3].as_slice()) + +it is the job of trait resolution to figure out (in which case) +whether there exists an impl of `int : Clone` + +Note that in some cases, like generic functions, we may not be able to +find a specific impl, but we can figure out that the caller must +provide an impl. To see what I mean, consider the body of `clone_slice`: + + fn clone_slice(x: &[T]) -> Vec { + let mut v = Vec::new(); + for e in x.iter() { + v.push((*e).clone()); // (*) + } + } + +The line marked `(*)` is only legal if `T` (the type of `*e`) +implements the `Clone` trait. Naturally, since we don't know what `T` +is, we can't find the specific impl; but based on the bound `T:Clone`, +we can say that there exists an impl which the caller must provide. + +We use the term *obligation* to refer to a trait reference in need of +an impl. + +## Overview + +Trait resolution consists of three major parts: + +- SELECTION: Deciding how to resolve a specific obligation. For + example, selection might decide that a specific obligation can be + resolved by employing an impl which matches the self type, or by + using a parameter bound. In the case of an impl, Selecting one + obligation can create *nested obligations* because of where clauses + on the impl itself. It may also require evaluating those nested + obligations to resolve ambiguities. + +- FULFILLMENT: The fulfillment code is what tracks that obligations + are completely fulfilled. Basically it is a worklist of obligations + to be selected: once selection is successful, the obligation is + removed from the worklist and any nested obligations are enqueued. + +- COHERENCE: The coherence checks are intended to ensure that there + are never overlapping impls, where two impls could be used with + equal precedence. + +## Selection + +Selection is the process of deciding whether an obligation can be +resolved and, if so, how it is to be resolved (via impl, where clause, etc). +The main interface is the `select()` function, which takes an obligation +and returns a `SelectionResult`. There are three possible outcomes: + +- `Ok(Some(selection))` -- yes, the obligation can be resolved, and + `selection` indicates how. If the impl was resolved via an impl, + then `selection` may also indicate nested obligations that are required + by the impl. + +- `Ok(None)` -- we are not yet sure whether the obligation can be + resolved or not. This happens most commonly when the obligation + contains unbound type variables. + +- `Err(err)` -- the obligation definitely cannot be resolved due to a + type error, or because there are no impls that could possibly apply, + etc. + +The basic algorithm for selection is broken into two big phases: +candidate assembly and confirmation. + +### Candidate assembly + +Searches for impls/where-clauses/etc that might +possibly be used to satisfy the obligation. Each of those is called +a candidate. To avoid ambiguity, we want to find exactly one +candidate that is definitively applicable. In some cases, we may not +know whether an impl/where-clause applies or not -- this occurs when +the obligation contains unbound inference variables. + +The basic idea for candidate assembly is to do a first pass in which +we identify all possible candidates. During this pass, all that we do +is try and unify the type parameters. (In particular, we ignore any +nested where clauses.) Presuming that this unification succeeds, the +impl is added as a candidate. + +Once this first pass is done, we can examine the set of candidates. If +it is a singleton set, then we are done: this is the only impl in +scope that could possibly apply. Otherwise, we can winnow down the set +of candidates by using where clauses and other conditions. If this +reduced set yields a single, unambiguous entry, we're good to go, +otherwise the result is considered ambiguous. + +#### The basic process: Inferring based on the impls we see + +This process is easier if we work through some examples. Consider +the following trait: + +``` +trait Convert { + fn convert(&self) -> Target; +} +``` + +This trait just has one method. It's about as simple as it gets. It +converts from the (implicit) `Self` type to the `Target` type. If we +wanted to permit conversion between `int` and `uint`, we might +implement `Convert` like so: + +```rust +impl Convert for int { ... } // int -> uint +impl Convert for uint { ... } // uint -> uint +``` + +Now imagine there is some code like the following: + +```rust +let x: int = ...; +let y = x.convert(); +``` + +The call to convert will generate a trait reference `Convert<$Y> for +int`, where `$Y` is the type variable representing the type of +`y`. When we match this against the two impls we can see, we will find +that only one remains: `Convert for int`. Therefore, we can +select this impl, which will cause the type of `$Y` to be unified to +`uint`. (Note that while assembling candidates, we do the initial +unifications in a transaction, so that they don't affect one another.) + +There are tests to this effect in src/test/run-pass: + + traits-multidispatch-infer-convert-source-and-target.rs + traits-multidispatch-infer-convert-target.rs + +#### Winnowing: Resolving ambiguities + +But what happens if there are multiple impls where all the types +unify? Consider this example: + +```rust +trait Get { + fn get(&self) -> Self; +} + +impl Get for T { + fn get(&self) -> T { *self } +} + +impl Get for Box { + fn get(&self) -> Box { box get_it(&**self) } +} +``` + +What happens when we invoke `get_it(&box 1_u16)`, for example? In this +case, the `Self` type is `Box` -- that unifies with both impls, +because the first applies to all types, and the second to all +boxes. In the olden days we'd have called this ambiguous. But what we +do now is do a second *winnowing* pass that considers where clauses +and attempts to remove candidates -- in this case, the first impl only +applies if `Box : Copy`, which doesn't hold. After winnowing, +then, we are left with just one candidate, so we can proceed. There is +a test of this in `src/test/run-pass/traits-conditional-dispatch.rs`. + +#### Matching + +The subroutines that decide whether a particular impl/where-clause/etc +applies to a particular obligation. At the moment, this amounts to +unifying the self types, but in the future we may also recursively +consider some of the nested obligations, in the case of an impl. + +#### Lifetimes and selection + +Because of how that lifetime inference works, it is not possible to +give back immediate feedback as to whether a unification or subtype +relationship between lifetimes holds or not. Therefore, lifetime +matching is *not* considered during selection. This is reflected in +the fact that subregion assignment is infallible. This may yield +lifetime constraints that will later be found to be in error (in +contrast, the non-lifetime-constraints have already been checked +during selection and can never cause an error, though naturally they +may lead to other errors downstream). + +#### Where clauses + +Besides an impl, the other major way to resolve an obligation is via a +where clause. The selection process is always given a *parameter +environment* which contains a list of where clauses, which are +basically obligations that can assume are satisfiable. We will iterate +over that list and check whether our current obligation can be found +in that list, and if so it is considered satisfied. More precisely, we +want to check whether there is a where-clause obligation that is for +the same trait (or some subtrait) and for which the self types match, +using the definition of *matching* given above. + +Consider this simple example: + + trait A1 { ... } + trait A2 : A1 { ... } + + trait B { ... } + + fn foo { ... } + +Clearly we can use methods offered by `A1`, `A2`, or `B` within the +body of `foo`. In each case, that will incur an obligation like `X : +A1` or `X : A2`. The parameter environment will contain two +where-clauses, `X : A2` and `X : B`. For each obligation, then, we +search this list of where-clauses. To resolve an obligation `X:A1`, +we would note that `X:A2` implies that `X:A1`. + +### Confirmation + +Confirmation unifies the output type parameters of the trait with the +values found in the obligation, possibly yielding a type error. If we +return to our example of the `Convert` trait from the previous +section, confirmation is where an error would be reported, because the +impl specified that `T` would be `uint`, but the obligation reported +`char`. Hence the result of selection would be an error. + +### Selection during translation + +During type checking, we do not store the results of trait selection. +We simply wish to verify that trait selection will succeed. Then +later, at trans time, when we have all concrete types available, we +can repeat the trait selection. In this case, we do not consider any +where-clauses to be in scope. We know that therefore each resolution +will resolve to a particular impl. + +One interesting twist has to do with nested obligations. In general, in trans, +we only need to do a "shallow" selection for an obligation. That is, we wish to +identify which impl applies, but we do not (yet) need to decide how to select +any nested obligations. Nonetheless, we *do* currently do a complete resolution, +and that is because it can sometimes inform the results of type inference. That is, +we do not have the full substitutions in terms of the type varibales of the impl available +to us, so we must run trait selection to figure everything out. + +Here is an example: + + trait Foo { ... } + impl> Foo for Vec { ... } + + impl Bar for int { ... } + +After one shallow round of selection for an obligation like `Vec +: Foo`, we would know which impl we want, and we would know that +`T=int`, but we do not know the type of `U`. We must select the +nested obligation `int : Bar` to find out that `U=uint`. + +It would be good to only do *just as much* nested resolution as +necessary. Currently, though, we just do a full resolution. + +## Method matching + +Method dispach follows a slightly different path than normal trait +selection. This is because it must account for the transformed self +type of the receiver and various other complications. The procedure is +described in `select.rs` in the "METHOD MATCHING" section. + +# Caching and subtle considerations therewith + +In general we attempt to cache the results of trait selection. This +is a somewhat complex process. Part of the reason for this is that we +want to be able to cache results even when all the types in the trait +reference are not fully known. In that case, it may happen that the +trait selection process is also influencing type variables, so we have +to be able to not only cache the *result* of the selection process, +but *replay* its effects on the type variables. + +## An example + +The high-level idea of how the cache works is that we first replace +all unbound inference variables with skolemized versions. Therefore, +if we had a trait reference `uint : Foo<$1>`, where `$n` is an unbound +inference variable, we might replace it with `uint : Foo<%0>`, where +`%n` is a skolemized type. We would then look this up in the cache. +If we found a hit, the hit would tell us the immediate next step to +take in the selection process: i.e., apply impl #22, or apply where +clause `X : Foo`. Let's say in this case there is no hit. +Therefore, we search through impls and where clauses and so forth, and +we come to the conclusion that the only possible impl is this one, +with def-id 22: + + impl Foo for uint { ... } // Impl #22 + +We would then record in the cache `uint : Foo<%0> ==> +ImplCandidate(22)`. Next we would confirm `ImplCandidate(22)`, which +would (as a side-effect) unify `$1` with `int`. + +Now, at some later time, we might come along and see a `uint : +Foo<$3>`. When skolemized, this would yield `uint : Foo<%0>`, just as +before, and hence the cache lookup would succeed, yielding +`ImplCandidate(22)`. We would confirm `ImplCandidate(22)` which would +(as a side-effect) unify `$3` with `int`. + +## Where clauses and the local vs global cache + +One subtle interaction is that the results of trait lookup will vary +depending on what where clauses are in scope. Therefore, we actually +have *two* caches, a local and a global cache. The local cache is +attached to the `ParameterEnvironment` and the global cache attached +to the `tcx`. We use the local cache whenever the result might depend +on the where clauses that are in scope. The determination of which +cache to use is done by the method `pick_candidate_cache` in +`select.rs`. + +There are two cases where we currently use the local cache. The +current rules are probably more conservative than necessary. + +### Trait references that involve parameter types + +The most obvious case where you need the local environment is +when the trait reference includes parameter types. For example, +consider the following function: + + impl Vec { + fn foo(x: T) + where T : Foo + { ... } + + fn bar(x: T) + { ... } + } + +If there is an obligation `T : Foo`, or `int : Bar`, or whatever, +clearly the results from `foo` and `bar` are potentially different, +since the set of where clauses in scope are different. + +### Trait references with unbound variables when where clauses are in scope + +There is another less obvious interaction which involves unbound variables +where *only* where clauses are in scope (no impls). This manifested as +issue #18209 (`run-pass/trait-cache-issue-18209.rs`). Consider +this snippet: + +``` +pub trait Foo { + fn load_from() -> Box; + fn load() -> Box { + Foo::load_from() + } +} +``` + +The default method will incur an obligation `$0 : Foo` from the call +to `load_from`. If there are no impls, this can be eagerly resolved to +`VtableParam(Self : Foo)` and cached. Because the trait reference +doesn't involve any parameters types (only the resolution does), this +result was stored in the global cache, causing later calls to +`Foo::load_from()` to get nonsense. + +To fix this, we always use the local cache if there are unbound +variables and where clauses in scope. This is more conservative than +necessary as far as I can tell. However, it still seems to be a simple +rule and I observe ~99% hit rate on rustc, so it doesn't seem to hurt +us in particular. + +Here is an example of the kind of subtle case that I would be worried +about with a more complex rule (although this particular case works +out ok). Imagine the trait reference doesn't directly reference a +where clause, but the where clause plays a role in the winnowing +phase. Something like this: + +``` +pub trait Foo { ... } +pub trait Bar { ... } +impl Foo for T { ... } // Impl A +impl Foo for uint { ... } // Impl B +``` + +Now, in some function, we have no where clauses in scope, and we have +an obligation `$1 : Foo<$0>`. We might then conclude that `$0=char` +and `$1=uint`: this is because for impl A to apply, `uint:Bar` would +have to hold, and we know it does not or else the coherence check +would have failed. So we might enter into our global cache: `$1 : +Foo<$0> => Impl B`. Then we come along in a different scope, where a +generic type `A` is around with the bound `A:Bar`. Now suddenly the +impl is viable. + +The flaw in this imaginary DOOMSDAY SCENARIO is that we would not +currently conclude that `$1 : Foo<$0>` implies that `$0 == uint` and +`$1 == char`, even though it is true that (absent type parameters) +there is no other type the user could enter. However, it is not +*completely* implausible that we *could* draw this conclusion in the +future; we wouldn't have to guess types, in particular, we could be +led by the impls. + + + +*/