Skip to content

Commit 8cc56ae

Browse files
author
Alexis Hunt
committed
Adjust receiver lookup to match the actual impl
As a part of this, remove the reference to coerce_inner and replace it with a proper description of unsized coercions. That description will have to be updated later when more of the unsized work stabilizes. Closes #62.
1 parent 9adc9b5 commit 8cc56ae

File tree

2 files changed

+76
-34
lines changed

2 files changed

+76
-34
lines changed

src/expressions/method-call-expr.md

Lines changed: 60 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,38 +14,68 @@ let log_pi = pi.unwrap_or(1.0).log(2.72);
1414
# assert!(1.14 < log_pi && log_pi < 1.15)
1515
```
1616

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.
22-
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.
31-
32-
Note: In steps 1-4, the receiver is used, not the type of `Self` nor the
33-
type of `A`. For example:
34-
35-
```rust,ignore
36-
// `Self` is `&A`, receiver is `&A`.
37-
impl<'a> Trait for &'a A {
38-
fn method(self) {}
17+
When resolving method calls on an expression of type `A`, Rust looks up methods
18+
both on the type itself and the traits in implements. Additionally, unlike with
19+
non-method function calls, the `self` parameter is special and may be
20+
automatically dereferenced in order to resolve it. Rust uses the following
21+
process to resolve method calls.
22+
23+
First, Rust will attempt to build a list of candidate receiver types. It obtains
24+
these by repeatedly [dereferencing][dereference] the type, adding each type
25+
encountered to the list, then finally attempting an [unsized coercion] at the
26+
end, and adding the result type if that is successful. Then, for each candidate
27+
`T`, Rust adds `&T` and `&mut T` to the list immediately afterward.
28+
29+
So, for instance, if `A` is `Box<[i32;2]>`, then the candidate types will be
30+
`Box<[i32;2]>`, `&Box<[i32;2]>`, `&mut Box<[i32;2]>`, `[i32; 2]` (by
31+
dereferencing), `&[i32; 2]`, `&mut [i32; 2]`, `[i32]` (by unsized coercion),
32+
`&[i32]`, and finally `&mut [i32]`.
33+
34+
Then, for each candidate type `T`, Rust will search for a [visible] method with
35+
a receiver of that type in the following places:
36+
37+
1. `T`'s inherent methods (methods implemented directly on `T`).
38+
1. Any of the methods provided by a trait implemented by `T`. If `T` is
39+
a type parameter (including the `Self` parameter of a trait), then only
40+
methods from the trait constraints on `T` are available for lookup. If `T` is
41+
not, then methods from any in-scope trait are available.
42+
43+
Note that the lookup is done for each type in order, which can occasionally lead
44+
to surprising results. The below code will print "In trait impl!", because
45+
`&self` methods are looked up first, the trait method is found before the
46+
struct's `&mut self` method is found.
47+
48+
```rust
49+
struct Foo {}
50+
51+
trait Bar {
52+
fn bar(&self);
53+
}
54+
55+
impl Foo {
56+
fn bar(&mut self) {
57+
println!("In struct impl!")
58+
}
3959
}
40-
// If `A` is `&B`, then `Self` is `B` and the receiver is `A`.
41-
impl B {
42-
fn method(&self) {}
60+
61+
impl Bar for Foo {
62+
fn bar(&self) {
63+
println!("In trait impl!")
64+
}
65+
}
66+
67+
fn main() {
68+
let mut f = Foo{};
69+
f.bar();
4370
}
4471
```
4572

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.
73+
If this results in multiple possible candidates, then it is an error, and the
74+
receiver must be [converted][disambiguate call] to an appropriate receiver type
75+
to make the method call.
76+
77+
The lookup process does not take into account the mutability or lifetime of the
78+
receiver, or whether a method is `unsafe`. Once a method is looked up.
4979

5080
If a step is reached where there is more than one possible method, such as where
5181
generic methods or traits are considered the same, then it is a compiler
@@ -64,4 +94,5 @@ and function invocation.
6494
[visible]: visibility-and-privacy.html
6595
[array]: types.html#array-and-slice-types
6696
[trait objects]: types.html#trait-objects
67-
[disambiguating function call syntax]: expressions/call-expr.html#disambiguating-function-calls
97+
[disambiguate call]: expressions/call-expr.html#disambiguating-function-calls
98+
[dereference]: expressions/operator-expr.html#the-dereference-operator

src/type-coercions.md

Lines changed: 16 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,28 @@ 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 anywhere else a coercion can occur.
156+
157+
* `[T; n]` to `[T]`.
158+
159+
* `T` to `U`, when `U` is a trait object type and either `T` implements `U` or
160+
`T` is a trait object for a subtrait of `U`.

0 commit comments

Comments
 (0)