|
| 1 | +# Method lookup |
| 2 | + |
| 3 | +Method lookup can be rather complex due to the interaction of a number |
| 4 | +of factors, such as self types, autoderef, trait lookup, etc. This |
| 5 | +file provides an overview of the process. More detailed notes are in |
| 6 | +the code itself, naturally. |
| 7 | + |
| 8 | +One way to think of method lookup is that we convert an expression of |
| 9 | +the form: |
| 10 | + |
| 11 | +```rust |
| 12 | +receiver.method(...) |
| 13 | +``` |
| 14 | + |
| 15 | +into a more explicit UFCS form: |
| 16 | + |
| 17 | +```rust |
| 18 | +Trait::method(ADJ(receiver), ...) // for a trait call |
| 19 | +ReceiverType::method(ADJ(receiver), ...) // for an inherent method call |
| 20 | +``` |
| 21 | + |
| 22 | +Here `ADJ` is some kind of adjustment, which is typically a series of |
| 23 | +autoderefs and then possibly an autoref (e.g., `&**receiver`). However |
| 24 | +we sometimes do other adjustments and coercions along the way, in |
| 25 | +particular unsizing (e.g., converting from `[T; n]` to `[T]`). |
| 26 | + |
| 27 | +Method lookup is divided into two major phases: |
| 28 | + |
| 29 | +1. Probing ([`probe.rs`][probe]). The probe phase is when we decide what method |
| 30 | + to call and how to adjust the receiver. |
| 31 | +2. Confirmation ([`confirm.rs`][confirm]). The confirmation phase "applies" |
| 32 | + this selection, updating the side-tables, unifying type variables, and |
| 33 | + otherwise doing side-effectful things. |
| 34 | + |
| 35 | +One reason for this division is to be more amenable to caching. The |
| 36 | +probe phase produces a "pick" (`probe::Pick`), which is designed to be |
| 37 | +cacheable across method-call sites. Therefore, it does not include |
| 38 | +inference variables or other information. |
| 39 | + |
| 40 | +[probe]: https://github.com/rust-lang/rust/blob/master/src/librustc_typeck/check/method/probe.rs |
| 41 | +[confirm]: https://github.com/rust-lang/rust/blob/master/src/librustc_typeck/check/method/confirm.rs |
| 42 | + |
| 43 | +## The Probe phase |
| 44 | + |
| 45 | +### Steps |
| 46 | + |
| 47 | +The first thing that the probe phase does is to create a series of |
| 48 | +*steps*. This is done by progressively dereferencing the receiver type |
| 49 | +until it cannot be deref'd anymore, as well as applying an optional |
| 50 | +"unsize" step. So if the receiver has type `Rc<Box<[T; 3]>>`, this |
| 51 | +might yield: |
| 52 | + |
| 53 | +```rust |
| 54 | +Rc<Box<[T; 3]>> |
| 55 | +Box<[T; 3]> |
| 56 | +[T; 3] |
| 57 | +[T] |
| 58 | +``` |
| 59 | + |
| 60 | +### Candidate assembly |
| 61 | + |
| 62 | +We then search along those steps to create a list of *candidates*. A |
| 63 | +`Candidate` is a method item that might plausibly be the method being |
| 64 | +invoked. For each candidate, we'll derive a "transformed self type" |
| 65 | +that takes into account explicit self. |
| 66 | + |
| 67 | +Candidates are grouped into two kinds, inherent and extension. |
| 68 | + |
| 69 | +**Inherent candidates** are those that are derived from the |
| 70 | +type of the receiver itself. So, if you have a receiver of some |
| 71 | +nominal type `Foo` (e.g., a struct), any methods defined within an |
| 72 | +impl like `impl Foo` are inherent methods. Nothing needs to be |
| 73 | +imported to use an inherent method, they are associated with the type |
| 74 | +itself (note that inherent impls can only be defined in the same |
| 75 | +module as the type itself). |
| 76 | + |
| 77 | +FIXME: Inherent candidates are not always derived from impls. If you |
| 78 | +have a trait object, such as a value of type `Box<ToString>`, then the |
| 79 | +trait methods (`to_string()`, in this case) are inherently associated |
| 80 | +with it. Another case is type parameters, in which case the methods of |
| 81 | +their bounds are inherent. However, this part of the rules is subject |
| 82 | +to change: when DST's "impl Trait for Trait" is complete, trait object |
| 83 | +dispatch could be subsumed into trait matching, and the type parameter |
| 84 | +behavior should be reconsidered in light of where clauses. |
| 85 | + |
| 86 | +TODO: Is this FIXME still accurate? |
| 87 | + |
| 88 | +**Extension candidates** are derived from imported traits. If I have |
| 89 | +the trait `ToString` imported, and I call `to_string()` on a value of |
| 90 | +type `T`, then we will go off to find out whether there is an impl of |
| 91 | +`ToString` for `T`. These kinds of method calls are called "extension |
| 92 | +methods". They can be defined in any module, not only the one that |
| 93 | +defined `T`. Furthermore, you must import the trait to call such a |
| 94 | +method. |
| 95 | + |
| 96 | +So, let's continue our example. Imagine that we were calling a method |
| 97 | +`foo` with the receiver `Rc<Box<[T; 3]>>` and there is a trait `Foo` |
| 98 | +that defines it with `&self` for the type `Rc<U>` as well as a method |
| 99 | +on the type `Box` that defines `Foo` but with `&mut self`. Then we |
| 100 | +might have two candidates: |
| 101 | + |
| 102 | + &Rc<Box<[T; 3]>> from the impl of `Foo` for `Rc<U>` where `U=Box<T; 3]> |
| 103 | + &mut Box<[T; 3]>> from the inherent impl on `Box<U>` where `U=[T; 3]` |
| 104 | + |
| 105 | +### Candidate search |
| 106 | + |
| 107 | +Finally, to actually pick the method, we will search down the steps, |
| 108 | +trying to match the receiver type against the candidate types. At |
| 109 | +each step, we also consider an auto-ref and auto-mut-ref to see whether |
| 110 | +that makes any of the candidates match. We pick the first step where |
| 111 | +we find a match. |
| 112 | + |
| 113 | +In the case of our example, the first step is `Rc<Box<[T; 3]>>`, |
| 114 | +which does not itself match any candidate. But when we autoref it, we |
| 115 | +get the type `&Rc<Box<[T; 3]>>` which does match. We would then |
| 116 | +recursively consider all where-clauses that appear on the impl: if |
| 117 | +those match (or we cannot rule out that they do), then this is the |
| 118 | +method we would pick. Otherwise, we would continue down the series of |
| 119 | +steps. |
0 commit comments