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