You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This section of the reference has been oversimplistic for some time (rust-lang#1018
and rust-lang#1534) and various rewrites have been attempted (e.g. rust-lang#1394, rust-lang#1432).
Here's another attempt!
My approach here is:
* Stop trying to keep it short and concise
* Document what actually happens in the code, step by step
This does result in a long explanation, because we're trying to document
nearly 2400 lines of code in `probe.rs`, but doing otherwise feels
as though we'll continue to run into criticisms of oversimplification.
This rewrite documents the post-arbitrary-self-types v2 situation,
i.e. it assumes rust-lang/rust#135881 has
landed. We should not merge this until or unless that lands.
This PR was inspired by discussion in rust-lang#1699. If we go ahead with this
approach, rust-lang#1699 becomes irrelevant. There was also discussion at
rust-lang/cargo#15117 (comment)
Copy file name to clipboardexpand all lines: src/expressions/method-call-expr.md
+144-19
Original file line number
Diff line number
Diff line change
@@ -15,22 +15,155 @@ let log_pi = pi.unwrap_or(1.0).log(2.72);
15
15
16
16
When looking up a method call, the receiver may be automatically dereferenced or borrowed in order to call a method.
17
17
This requires a more complex lookup process than for other functions, since there may be a number of possible methods to call.
18
-
The following procedure is used:
19
18
20
-
The first step is to build a list of candidate receiver types.
21
-
Obtain these by repeatedly [dereferencing][dereference] the receiver expression's type, adding each type encountered to the list, then finally attempting an [unsized coercion] at the end, and adding the result type if that is successful.
22
-
Then, for each candidate `T`, add `&T` and `&mut T` to the list immediately after `T`.
19
+
The following procedure is used:
23
20
24
-
For instance, if the receiver has type `Box<[i32;2]>`, then the candidate types will be `Box<[i32;2]>`, `&Box<[i32;2]>`, `&mut Box<[i32;2]>`, `[i32; 2]` (by dereferencing), `&[i32; 2]`, `&mut [i32; 2]`, `[i32]` (by unsized coercion), `&[i32]`, and finally `&mut [i32]`.
21
+
## Determining candidate types
22
+
23
+
First, a list of "candidate types" is assembled.
24
+
25
+
These types are found by taking the receiver type and iterating, following either:
26
+
27
+
* The built-in [dereference]; or
28
+
*`<T as Receiver>::Target`
29
+
30
+
to the next type. (If a step involved following the `Receiver` target, we also
31
+
note whether it would have been reachable by following `<T as Deref::Target>`
32
+
- this information is used later).
33
+
34
+
At the end, an additional candidate step may be added for
35
+
an [unsized coercion].
36
+
37
+
Each step of this chain provides a possible `self` type for methods that
38
+
might be called. The list will be used in two different ways:
39
+
40
+
* To find types that might have methods. This is used in the
41
+
"determining candidate methods" step, described below. This considers
42
+
the full list.
43
+
* To find types to which the receiver can be converted. This is used in the
44
+
"picking a method from the candidates" step, also described below - in this
45
+
case, we only consider the types reachable via `Deref` or built-in
46
+
dereferencing.
47
+
48
+
There is a built-in implementation of `Receiver` for all `T: Deref`, so
49
+
most of the time, every step can be reached through either mechanism.
50
+
Sometimes, more types can be reached via the `Receiver` chain, and so
51
+
more types will be considered for the former usage than the latter usage.
52
+
53
+
For instance, if the receiver has type `Box<[i32;2]>`, then the candidate types
54
+
will be `Box<[i32;2]>`,`[i32; 2]` (by dereferencing), and `[i32]` (by unsized
55
+
coercion).
56
+
57
+
If `SmartPtr<T>: Receiver<Target=T>`, and the receiver type is `&SmartPtr<Foo>`,
58
+
then the candidate types would be `&SmartPtr<Foo>`, `SmartPtr<Foo>` and `Foo`.
59
+
60
+
## Determining candidate methods
61
+
62
+
This list of candidate types is then converted to a list of candidate methods.
63
+
For each step, the `self` type is used to determine what searches to perform:
64
+
65
+
* For a trait object, there is first a search for for inherent candidates for
66
+
the object, then inherent impl candidates for the type.
67
+
* For a struct, enum, or foreign type, there is a search for inherent
68
+
impl candidates for the type.
69
+
* For a type param, there's a search for for inherent candidates on the param.
70
+
* For other tyings (e.g. bools, chars) there's a search for inherent candidates
71
+
for the incoherent type.
72
+
* After any of these, there's a further search for extension candidates for
73
+
traits in scope.
74
+
75
+
These searches contribute to list of all the candidate methods found;
76
+
separate lists are maintained for inherent and extension candidates. Only
77
+
[visible] candidates are included.
78
+
79
+
(For diagnostic purposes, the search may be performed slightly differently, for
80
+
instance searching all traits not just those in scope, or also noting
81
+
inaccessible candidates.)
82
+
83
+
## Picking a method from the candidates
84
+
85
+
Once the list of candidate methods is assembled, the "picking" process
86
+
starts.
87
+
88
+
Once again, the candidate types are iterated. This time, only those types
89
+
are iterated which can be reached via the `Deref` trait or built-in derefs;
90
+
as noted above, this may be a shorter list than those that can be reached
91
+
using the `Receiver` trait.
92
+
93
+
For each step, picking is attempted in this order:
94
+
95
+
* First, a by-value method, where the `self` type precisely matches
96
+
* First for inherent methods
97
+
* Then for extension methods
98
+
* Then, a method where `self` is received by immutable reference (`&T`)
99
+
* First for inherent methods
100
+
* Then for extension methods
101
+
* Then, a method where `self` is received by mutable reference (`&mut T`)
102
+
* First for inherent methods
103
+
* Then for extension methods
104
+
* Then, a method where the `self` type is a `*const T` - this is only considered
105
+
if the self type is `*mut T`)
106
+
* First for inherent methods
107
+
* Then for extension methods
108
+
* And finally, a method with a `Pin` that's reborrowed, if the `pin_ergonomics`
109
+
feature is enabled.
110
+
* First for inherent methods
111
+
* Then for extension methods
112
+
113
+
For each of those searches, if exactly one candidate is identified,
114
+
it's picked, and the search stops. If this results in multiple possible candidates,
115
+
then it is an error, and the receiver must be [converted][disambiguate call]
116
+
to an appropriate receiver type to make the method call.
117
+
118
+
With the example above of `SmartPtr<T>: Receiver<Target=T>`, and the receiver
119
+
type `&SmartPtr<Foo>`, this mechanism would pick:
120
+
121
+
```rust,ignore
122
+
impl Foo {
123
+
fn method(self: &SmartPtr<Foo>) {}
124
+
}
125
+
```
25
126
26
-
Then, for each candidate type `T`, search for a [visible] method with a receiver of that type in the following places:
127
+
but would not pick
27
128
28
-
1.`T`'s inherent methods (methods implemented directly on `T`).
29
-
1. Any of the methods provided by a [visible] trait implemented by `T`.
30
-
If `T` is a type parameter, methods provided by trait bounds on `T` are looked up first.
31
-
Then all remaining methods in scope are looked up.
129
+
```rust,ignore
130
+
impl Foo {
131
+
fn method(self: &Foo) {}
132
+
}
133
+
```
32
134
33
-
> Note: the lookup is done for each type in order, which can occasionally lead to surprising results.
135
+
because the receiver could not be converted to `&Foo` using the `Deref` chain,
136
+
only the `Receiver` chain.
137
+
138
+
## Extra details
139
+
140
+
There are a few details not considered in this overview:
141
+
142
+
* The search for candidate methods will also consider searches for
143
+
incoherent types if `rustc_has_incoherent_inherent_impls` is active for
144
+
a `dyn`, struct, enum, or foreign type.
145
+
* If there are multiple candidates from traits, they may in fact be
146
+
identical, and the picking operation collapses them to a single pick to avoid
147
+
reporting conflicts.
148
+
* Extra searches are performed to spot "shadowing" of pointee methods
149
+
by smart pointer methods, during the picking process. If a by-value pick
150
+
is going to be returned, an extra search is performed for a `&T` or
151
+
`&mut T` method. Similarly, if a `&T` method is to be returned, an extra
152
+
search is performed for `&mut T` methods. These extra searches consider
153
+
only inherent methods, where `T` is identical, but the method is
154
+
found from a step further along the `Receiver` chain. If any such method
155
+
is found, an ambiguity error is emitted.
156
+
* An error is emitted if we reached a recursion limit.
157
+
* The picking process emits some adjustments which must be made to the
158
+
receiver type in order to get to the correct `self` type. This includes
159
+
a number of dereferences, a possible autoreferencing, a conversion from
160
+
a mutable pointer to a constant pointer, or a pin reborrow.
161
+
* Extra lists are maintained for diagnostic purposes:
162
+
unstable candidates, unsatisfied predicates, and static candidates.
163
+
164
+
## Net results
165
+
166
+
> The lookup is done for each type in order, which can occasionally lead to surprising results.
34
167
> The below code will print "In trait impl!", because `&self` methods are looked up first, the trait method is found before the struct's `&mut self` method is found.
35
168
>
36
169
> ```rust
@@ -58,14 +191,6 @@ Then, for each candidate type `T`, search for a [visible] method with a receiver
0 commit comments