Skip to content

Adjust receiver lookup to match the actual impl #190

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 66 additions & 36 deletions src/expressions/method-call-expr.md
Original file line number Diff line number Diff line change
@@ -1,51 +1,80 @@
# Method-call expressions

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

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

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

1. Inherent methods, with receiver of type `A`, `&A`, `&mut A`.
1. Trait methods with receiver of type `A`.
1. Trait methods with receiver of type `&A`.
1. Trait methods with receiver of type `&mut A`.
1. If it's possible, Rust will then repeat steps 1-5 with
`<A as std::ops::Deref>::Target`, and insert a dereference operator.
1. If `A` is now an [array] type, then repeat steps 1-4 with the corresponding
slice type.
The first step is to build a list of candidate receiver types. 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. Then,
for each candidate `T`, add `&T` and `&mut T` to the list immediately after `T`.

Note: In steps 1-4, the receiver is used, not the type of `Self` nor the
type of `A`. For example:
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]`.

```rust,ignore
// `Self` is `&A`, receiver is `&A`.
impl<'a> Trait for &'a A {
fn method(self) {}
}
// If `A` is `&B`, then `Self` is `B` and the receiver is `A`.
impl B {
fn method(&self) {}
}
```
Then, for each candidate type `T`, search for a [visible] method with
a receiver of that type in the following places:

1. `T`'s inherent methods (methods implemented directly on `T`).
1. Any of the methods provided by a [visible] trait implemented by `T`. If `T`
is a type parameter, methods provided by trait bounds on `T` are looked up
first. Then all remaining methods in scope are looked up.

> Note: the lookup is done for each type in order, which can occasionally lead
> to surprising results. 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.
>
> ```rust
> struct Foo {}
>
> trait Bar {
> fn bar(&self);
> }
>
> impl Foo {
> fn bar(&mut self) {
> println!("In struct impl!")
> }
> }
>
> impl Bar for Foo {
> fn bar(&self) {
> println!("In trait impl!")
> }
> }
>
> fn main() {
> let mut f = Foo{};
> f.bar();
> }
> ```

If this results in multiple possible candidates, then it is an error, and the
receiver must be [converted][disambiguate call] to an appropriate receiver type
to make the method call.

Another note: this process does not use the mutability or lifetime of the
receiver, or whether `unsafe` methods can currently be called to resolve
methods. These constraints instead lead to compiler errors.
This process does not take into account the mutability or lifetime of the
receiver, or whether a method is `unsafe`. Once a method is looked up, if it
can't be called for one (or more) of those reasons, the result is a compiler
error.

If a step is reached where there is more than one possible method, such as where
generic methods or traits are considered the same, then it is a compiler
Expand All @@ -64,4 +93,5 @@ and function invocation.
[visible]: visibility-and-privacy.html
[array]: types.html#array-and-slice-types
[trait objects]: types.html#trait-objects
[disambiguating function call syntax]: expressions/call-expr.html#disambiguating-function-calls
[disambiguate call]: expressions/call-expr.html#disambiguating-function-calls
[dereference]: expressions/operator-expr.html#the-dereference-operator
45 changes: 40 additions & 5 deletions src/type-coercions.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ sites are:
}
```

For method calls, the receiver (`self` parameter) can only take advantage
of [unsized coercions](#unsafe-coercions).

* Instantiations of struct or variant fields

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

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

* TyCtor(`T`) to TyCtor(coerce_inner(`T`)), where TyCtor(`T`) is one of
* TyCtor(`T`) to TyCtor(`U`), where TyCtor(`T`) is one of
- `&T`
- `&mut T`
- `*const T`
- `*mut T`
- `Box<T>`

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

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

* Non capturing closures to `fn` pointers

### Unsized Coercions

The following coercions are called `unsized coercions`, since they
relate to converting sized types to unsized types, and are permitted in a few
cases where other coercions are not, as described above. They can still happen
anywhere else a coercion can occur.

Two traits, [`Unsize`](Unsize) and [`CoerceUnsized`](CoerceUnsized), are used
to assist in this process and expose it for library use. The compiler following
coercions are built-in and, if `T` can be coerced to `U` with one of the, then
the compiler will provide an implementation of `Unsize<U>` for `T`:

* `[T; n]` to `[T]`.

* `T` to `U`, when `U` is a trait object type and either `T` implements `U` or
`T` is a trait object for a subtrait of `U`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also the third case in the Unsize docs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left that out since it isn't yet stabilized. Should I put it in anyway?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The coercion is stable, it's just the trait that isn't.


* `Foo<..., T, ...>` to `Foo<..., U, ...>`, when:
* `Foo` is a struct.
* `T` implements `Unsize<U>`.
* The last field of `Foo` has a type involving `T`.
* If that field has type `Bar<T>`, then `Bar<T>` implements `Unsized<Bar<U>>`.
* T is not part of the type of any other fields.

Additionally, a type `Foo<T>` can implement `CoerceUnsized<Foo<U>>` when `T`
implements `Unsize<U>` or `CoerceUnsized<Foo<U>>`. This allows it to provide a
unsized coercion to `Foo<U>`.

> Note: While the definition of the unsized coercions and their implementation
> has been stabilized, the traits themselves are not yet stable and therefore
> can't be used directly in stable Rust.

[Unsize]: ../std/marker/trait.Unsize.html
[CoerceUnsized]: ../std/ops/trait.CoerceUnsized.html