Skip to content

Commit b6acf26

Browse files
authored
Merge pull request rust-lang#190 from alercah/receiver-lookups
Adjust receiver lookup to match the actual impl
2 parents 3e52960 + 3122881 commit b6acf26

File tree

2 files changed

+106
-41
lines changed

2 files changed

+106
-41
lines changed

src/expressions/method-call-expr.md

Lines changed: 66 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,80 @@
11
# Method-call expressions
22

3-
A _method call_ consists of an expression followed by a single dot, an
4-
[identifier], and a parenthesized expression-list. Method
5-
calls are resolved to methods on specific traits, either statically dispatching
6-
to a method if the exact `self`-type of the left-hand-side is known, or
7-
dynamically dispatching if the left-hand-side expression is an indirect [trait
8-
object](types.html#trait-objects). Method call expressions will automatically
9-
take a shared or mutable borrow of the receiver if needed.
3+
A _method call_ consists of an expression (the *receiver*) followed by a single
4+
dot, an [identifier], and a parenthesized expression-list. Method calls are
5+
resolved to methods on specific traits, either statically dispatching to a
6+
method if the exact `self`-type of the left-hand-side is known, or dynamically
7+
dispatching if the left-hand-side expression is an indirect [trait
8+
object](types.html#trait-objects).
109

1110
```rust
1211
let pi: Result<f32, _> = "3.14".parse();
1312
let log_pi = pi.unwrap_or(1.0).log(2.72);
1413
# assert!(1.14 < log_pi && log_pi < 1.15)
1514
```
1615

17-
When resolving method calls on an expression of type `A`, Rust will use the
18-
following order, only looking at methods that are [visible]. If the type of `A`
19-
is a type parameter or `Self` in a trait definitition then steps 2-4 first
20-
consider traits from bounds on the type paramter, then the traits that are in
21-
scope. For other types, only the traits that are in scope are considered.
16+
When looking up a method call, the receiver may be automatically dereferenced or
17+
borrowed in order to call a method. This requires a more complex lookup process
18+
than for other functions, since there may be a number of possible methods to
19+
call. The following procedure is used:
2220

23-
1. Inherent methods, with receiver of type `A`, `&A`, `&mut A`.
24-
1. Trait methods with receiver of type `A`.
25-
1. Trait methods with receiver of type `&A`.
26-
1. Trait methods with receiver of type `&mut A`.
27-
1. If it's possible, Rust will then repeat steps 1-5 with
28-
`<A as std::ops::Deref>::Target`, and insert a dereference operator.
29-
1. If `A` is now an [array] type, then repeat steps 1-4 with the corresponding
30-
slice type.
21+
The first step is to build a list of candidate receiver types. Obtain
22+
these by repeatedly [dereferencing][dereference] the receiver expression's type,
23+
adding each type encountered to the list, then finally attempting an [unsized
24+
coercion] at the end, and adding the result type if that is successful. Then,
25+
for each candidate `T`, add `&T` and `&mut T` to the list immediately after `T`.
3126

32-
Note: In steps 1-4, the receiver is used, not the type of `Self` nor the
33-
type of `A`. For example:
27+
For instance, if the receiver has type `Box<[i32;2]>`, then the candidate types
28+
will be `Box<[i32;2]>`, `&Box<[i32;2]>`, `&mut Box<[i32;2]>`, `[i32; 2]` (by
29+
dereferencing), `&[i32; 2]`, `&mut [i32; 2]`, `[i32]` (by unsized coercion),
30+
`&[i32]`, and finally `&mut [i32]`.
3431

35-
```rust,ignore
36-
// `Self` is `&A`, receiver is `&A`.
37-
impl<'a> Trait for &'a A {
38-
fn method(self) {}
39-
}
40-
// If `A` is `&B`, then `Self` is `B` and the receiver is `A`.
41-
impl B {
42-
fn method(&self) {}
43-
}
44-
```
32+
Then, for each candidate type `T`, search for a [visible] method with
33+
a receiver of that type in the following places:
34+
35+
1. `T`'s inherent methods (methods implemented directly on `T`).
36+
1. Any of the methods provided by a [visible] trait implemented by `T`. If `T`
37+
is a type parameter, methods provided by trait bounds on `T` are looked up
38+
first. Then all remaining methods in scope are looked up.
39+
40+
> Note: the lookup is done for each type in order, which can occasionally lead
41+
> to surprising results. The below code will print "In trait impl!", because
42+
> `&self` methods are looked up first, the trait method is found before the
43+
> struct's `&mut self` method is found.
44+
>
45+
> ```rust
46+
> struct Foo {}
47+
>
48+
> trait Bar {
49+
> fn bar(&self);
50+
> }
51+
>
52+
> impl Foo {
53+
> fn bar(&mut self) {
54+
> println!("In struct impl!")
55+
> }
56+
> }
57+
>
58+
> impl Bar for Foo {
59+
> fn bar(&self) {
60+
> println!("In trait impl!")
61+
> }
62+
> }
63+
>
64+
> fn main() {
65+
> let mut f = Foo{};
66+
> f.bar();
67+
> }
68+
> ```
69+
70+
If this results in multiple possible candidates, then it is an error, and the
71+
receiver must be [converted][disambiguate call] to an appropriate receiver type
72+
to make the method call.
4573
46-
Another note: this process does not use the mutability or lifetime of the
47-
receiver, or whether `unsafe` methods can currently be called to resolve
48-
methods. These constraints instead lead to compiler errors.
74+
This process does not take into account the mutability or lifetime of the
75+
receiver, or whether a method is `unsafe`. Once a method is looked up, if it
76+
can't be called for one (or more) of those reasons, the result is a compiler
77+
error.
4978
5079
If a step is reached where there is more than one possible method, such as where
5180
generic methods or traits are considered the same, then it is a compiler
@@ -64,4 +93,5 @@ and function invocation.
6493
[visible]: visibility-and-privacy.html
6594
[array]: types.html#array-and-slice-types
6695
[trait objects]: types.html#trait-objects
67-
[disambiguating function call syntax]: expressions/call-expr.html#disambiguating-function-calls
96+
[disambiguate call]: expressions/call-expr.html#disambiguating-function-calls
97+
[dereference]: expressions/operator-expr.html#the-dereference-operator

src/type-coercions.md

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ sites are:
3838
}
3939
```
4040

41+
For method calls, the receiver (`self` parameter) can only take advantage
42+
of [unsized coercions](#unsafe-coercions).
43+
4144
* Instantiations of struct or variant fields
4245

4346
For example, `42` is coerced to have type `i8` in the following:
@@ -130,20 +133,52 @@ Coercion is allowed between the following types:
130133

131134
* `&mut T` to `&mut U` if `T` implements `DerefMut<Target = U>`.
132135

133-
* TyCtor(`T`) to TyCtor(coerce_inner(`T`)), where TyCtor(`T`) is one of
136+
* TyCtor(`T`) to TyCtor(`U`), where TyCtor(`T`) is one of
134137
- `&T`
135138
- `&mut T`
136139
- `*const T`
137140
- `*mut T`
138141
- `Box<T>`
139142

140-
and where
141-
- coerce_inner(`[T, ..n]`) = `[T]`
142-
- coerce_inner(`T`) = `U` where `T` is a concrete type which implements the
143-
trait `U`.
143+
and where `T` can obtained from `U` by [unsized coercion](#unsized-coercion).
144144

145145
<!--In the future, coerce_inner will be recursively extended to tuples and
146146
structs. In addition, coercions from sub-traits to super-traits will be
147147
added. See [RFC 401] for more details.-->
148148

149149
* Non capturing closures to `fn` pointers
150+
151+
### Unsized Coercions
152+
153+
The following coercions are called `unsized coercions`, since they
154+
relate to converting sized types to unsized types, and are permitted in a few
155+
cases where other coercions are not, as described above. They can still happen
156+
anywhere else a coercion can occur.
157+
158+
Two traits, [`Unsize`](Unsize) and [`CoerceUnsized`](CoerceUnsized), are used
159+
to assist in this process and expose it for library use. The compiler following
160+
coercions are built-in and, if `T` can be coerced to `U` with one of the, then
161+
the compiler will provide an implementation of `Unsize<U>` for `T`:
162+
163+
* `[T; n]` to `[T]`.
164+
165+
* `T` to `U`, when `U` is a trait object type and either `T` implements `U` or
166+
`T` is a trait object for a subtrait of `U`.
167+
168+
* `Foo<..., T, ...>` to `Foo<..., U, ...>`, when:
169+
* `Foo` is a struct.
170+
* `T` implements `Unsize<U>`.
171+
* The last field of `Foo` has a type involving `T`.
172+
* If that field has type `Bar<T>`, then `Bar<T>` implements `Unsized<Bar<U>>`.
173+
* T is not part of the type of any other fields.
174+
175+
Additionally, a type `Foo<T>` can implement `CoerceUnsized<Foo<U>>` when `T`
176+
implements `Unsize<U>` or `CoerceUnsized<Foo<U>>`. This allows it to provide a
177+
unsized coercion to `Foo<U>`.
178+
179+
> Note: While the definition of the unsized coercions and their implementation
180+
> has been stabilized, the traits themselves are not yet stable and therefore
181+
> can't be used directly in stable Rust.
182+
183+
[Unsize]: ../std/marker/trait.Unsize.html
184+
[CoerceUnsized]: ../std/ops/trait.CoerceUnsized.html

0 commit comments

Comments
 (0)