From b73a7240266db5e72c063f6e3063e8e782ff3ded Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Thu, 30 Jan 2025 13:57:21 +0000 Subject: [PATCH 01/18] Method call reference: major rewrite This section of the reference has been oversimplistic for some time (#1018 and #1534) and various rewrites have been attempted (e.g. #1394, #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 https://github.com/rust-lang/rust/pull/135881 has landed. We should not merge this until or unless that lands. This PR was inspired by discussion in #1699. If we go ahead with this approach, #1699 becomes irrelevant. There was also discussion at https://github.com/rust-lang/cargo/pull/15117#discussion_r1934734824 --- src/expressions/method-call-expr.md | 163 ++++++++++++++++++++++++---- 1 file changed, 144 insertions(+), 19 deletions(-) diff --git a/src/expressions/method-call-expr.md b/src/expressions/method-call-expr.md index 9535cd207..0b2b65cdd 100644 --- a/src/expressions/method-call-expr.md +++ b/src/expressions/method-call-expr.md @@ -15,22 +15,155 @@ let log_pi = pi.unwrap_or(1.0).log(2.72); 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: -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`. +The following procedure is used: -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]`. +## Determining candidate types + +First, a list of "candidate types" is assembled. + +These types are found by taking the receiver type and iterating, following either: + +* The built-in [dereference]; or +* `::Target` + +to the next type. (If a step involved following the `Receiver` target, we also +note whether it would have been reachable by following `` +- this information is used later). + +At the end, an additional candidate step may be added for +an [unsized coercion]. + +Each step of this chain provides a possible `self` type for methods that +might be called. The list will be used in two different ways: + +* To find types that might have methods. This is used in the + "determining candidate methods" step, described below. This considers + the full list. +* To find types to which the receiver can be converted. This is used in the + "picking a method from the candidates" step, also described below - in this + case, we only consider the types reachable via `Deref` or built-in + dereferencing. + +There is a built-in implementation of `Receiver` for all `T: Deref`, so +most of the time, every step can be reached through either mechanism. +Sometimes, more types can be reached via the `Receiver` chain, and so +more types will be considered for the former usage than the latter usage. + +For instance, if the receiver has type `Box<[i32;2]>`, then the candidate types +will be `Box<[i32;2]>`,`[i32; 2]` (by dereferencing), and `[i32]` (by unsized +coercion). + +If `SmartPtr: Receiver`, and the receiver type is `&SmartPtr`, +then the candidate types would be `&SmartPtr`, `SmartPtr` and `Foo`. + +## Determining candidate methods + +This list of candidate types is then converted to a list of candidate methods. +For each step, the `self` type is used to determine what searches to perform: + +* For a trait object, there is first a search for for inherent candidates for + the object, then inherent impl candidates for the type. +* For a struct, enum, or foreign type, there is a search for inherent + impl candidates for the type. +* For a type param, there's a search for for inherent candidates on the param. +* For other tyings (e.g. bools, chars) there's a search for inherent candidates + for the incoherent type. +* After any of these, there's a further search for extension candidates for + traits in scope. + +These searches contribute to list of all the candidate methods found; +separate lists are maintained for inherent and extension candidates. Only +[visible] candidates are included. + +(For diagnostic purposes, the search may be performed slightly differently, for +instance searching all traits not just those in scope, or also noting +inaccessible candidates.) + +## Picking a method from the candidates + +Once the list of candidate methods is assembled, the "picking" process +starts. + +Once again, the candidate types are iterated. This time, only those types +are iterated which can be reached via the `Deref` trait or built-in derefs; +as noted above, this may be a shorter list than those that can be reached +using the `Receiver` trait. + +For each step, picking is attempted in this order: + +* First, a by-value method, where the `self` type precisely matches + * First for inherent methods + * Then for extension methods +* Then, a method where `self` is received by immutable reference (`&T`) + * First for inherent methods + * Then for extension methods +* Then, a method where `self` is received by mutable reference (`&mut T`) + * First for inherent methods + * Then for extension methods +* Then, a method where the `self` type is a `*const T` - this is only considered + if the self type is `*mut T`) + * First for inherent methods + * Then for extension methods +* And finally, a method with a `Pin` that's reborrowed, if the `pin_ergonomics` + feature is enabled. + * First for inherent methods + * Then for extension methods + +For each of those searches, if exactly one candidate is identified, +it's picked, and the search stops. 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. + +With the example above of `SmartPtr: Receiver`, and the receiver +type `&SmartPtr`, this mechanism would pick: + +```rust,ignore +impl Foo { + fn method(self: &SmartPtr) {} +} +``` -Then, for each candidate type `T`, search for a [visible] method with a receiver of that type in the following places: +but would not pick -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. +```rust,ignore +impl Foo { + fn method(self: &Foo) {} +} +``` -> Note: the lookup is done for each type in order, which can occasionally lead to surprising results. +because the receiver could not be converted to `&Foo` using the `Deref` chain, +only the `Receiver` chain. + +## Extra details + +There are a few details not considered in this overview: + +* The search for candidate methods will also consider searches for + incoherent types if `rustc_has_incoherent_inherent_impls` is active for + a `dyn`, struct, enum, or foreign type. +* If there are multiple candidates from traits, they may in fact be + identical, and the picking operation collapses them to a single pick to avoid + reporting conflicts. +* Extra searches are performed to spot "shadowing" of pointee methods + by smart pointer methods, during the picking process. If a by-value pick + is going to be returned, an extra search is performed for a `&T` or + `&mut T` method. Similarly, if a `&T` method is to be returned, an extra + search is performed for `&mut T` methods. These extra searches consider + only inherent methods, where `T` is identical, but the method is + found from a step further along the `Receiver` chain. If any such method + is found, an ambiguity error is emitted. +* An error is emitted if we reached a recursion limit. +* The picking process emits some adjustments which must be made to the + receiver type in order to get to the correct `self` type. This includes + a number of dereferences, a possible autoreferencing, a conversion from + a mutable pointer to a constant pointer, or a pin reborrow. +* Extra lists are maintained for diagnostic purposes: + unstable candidates, unsatisfied predicates, and static candidates. + +## Net results + +> 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 @@ -58,14 +191,6 @@ Then, for each candidate type `T`, search for a [visible] method with a receiver > } > ``` -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. - -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 error. -These cases require a [disambiguating function call syntax] for method and function invocation. - > **Edition differences**: Before the 2021 edition, during the search for visible methods, if the candidate receiver type is an [array type], methods provided by the standard library [`IntoIterator`] trait are ignored. > > The edition used for this purpose is determined by the token representing the method name. From d86363f9e596671353e06a61d28f5b42431cc088 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Mon, 3 Feb 2025 13:37:37 +0000 Subject: [PATCH 02/18] Update src/expressions/method-call-expr.md Co-authored-by: Travis Cross --- src/expressions/method-call-expr.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/expressions/method-call-expr.md b/src/expressions/method-call-expr.md index 0b2b65cdd..24b104498 100644 --- a/src/expressions/method-call-expr.md +++ b/src/expressions/method-call-expr.md @@ -112,8 +112,8 @@ For each step, picking is attempted in this order: For each of those searches, if exactly one candidate is identified, it's picked, and the search stops. 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. +then it is an error, and the user must [disambiguate][disambiguate call] +the call and convert the receiver to an appropriate receiver type. With the example above of `SmartPtr: Receiver`, and the receiver type `&SmartPtr`, this mechanism would pick: From c52cfca3a58eeda8ed1471ca8527a7b7fde427e8 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Mon, 3 Feb 2025 13:37:46 +0000 Subject: [PATCH 03/18] Update src/expressions/method-call-expr.md Co-authored-by: Travis Cross --- src/expressions/method-call-expr.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/expressions/method-call-expr.md b/src/expressions/method-call-expr.md index 24b104498..974aad0a0 100644 --- a/src/expressions/method-call-expr.md +++ b/src/expressions/method-call-expr.md @@ -62,7 +62,7 @@ then the candidate types would be `&SmartPtr`, `SmartPtr` and `Foo`. This list of candidate types is then converted to a list of candidate methods. For each step, the `self` type is used to determine what searches to perform: -* For a trait object, there is first a search for for inherent candidates for +* For a trait object, there is first a search for inherent candidates for the object, then inherent impl candidates for the type. * For a struct, enum, or foreign type, there is a search for inherent impl candidates for the type. From 875073f7734d4ef41ea8d863c3298a13d58148c5 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Mon, 3 Feb 2025 13:44:38 +0000 Subject: [PATCH 04/18] Update src/expressions/method-call-expr.md Co-authored-by: Tyler Mandry --- src/expressions/method-call-expr.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/expressions/method-call-expr.md b/src/expressions/method-call-expr.md index 974aad0a0..5c60ab164 100644 --- a/src/expressions/method-call-expr.md +++ b/src/expressions/method-call-expr.md @@ -28,8 +28,8 @@ These types are found by taking the receiver type and iterating, following eithe * `::Target` to the next type. (If a step involved following the `Receiver` target, we also -note whether it would have been reachable by following `` -- this information is used later). +note whether it would have been reachable by following `` - this information is used later). At the end, an additional candidate step may be added for an [unsized coercion]. From ba023192926429d5c7adbc6d247d39b13b023f5d Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Mon, 3 Feb 2025 13:45:59 +0000 Subject: [PATCH 05/18] Update src/expressions/method-call-expr.md Co-authored-by: Tyler Mandry --- src/expressions/method-call-expr.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/expressions/method-call-expr.md b/src/expressions/method-call-expr.md index 5c60ab164..c922dd97b 100644 --- a/src/expressions/method-call-expr.md +++ b/src/expressions/method-call-expr.md @@ -60,7 +60,7 @@ then the candidate types would be `&SmartPtr`, `SmartPtr` and `Foo`. ## Determining candidate methods This list of candidate types is then converted to a list of candidate methods. -For each step, the `self` type is used to determine what searches to perform: +For each step, the candidate type is used to determine what searches to perform: * For a trait object, there is first a search for inherent candidates for the object, then inherent impl candidates for the type. From f545a85405913618299d43c583301184b59147d2 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Mon, 3 Feb 2025 13:46:39 +0000 Subject: [PATCH 06/18] Update src/expressions/method-call-expr.md Co-authored-by: Tyler Mandry --- src/expressions/method-call-expr.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/expressions/method-call-expr.md b/src/expressions/method-call-expr.md index c922dd97b..c76dc6e51 100644 --- a/src/expressions/method-call-expr.md +++ b/src/expressions/method-call-expr.md @@ -102,7 +102,7 @@ For each step, picking is attempted in this order: * First for inherent methods * Then for extension methods * Then, a method where the `self` type is a `*const T` - this is only considered - if the self type is `*mut T`) + if the self type is `*mut T` * First for inherent methods * Then for extension methods * And finally, a method with a `Pin` that's reborrowed, if the `pin_ergonomics` From 8e682a6cd6f43675997c9f3c2c4bb9febc572273 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Mon, 3 Feb 2025 13:42:02 +0000 Subject: [PATCH 07/18] Various code review comments. --- src/expressions/method-call-expr.md | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/expressions/method-call-expr.md b/src/expressions/method-call-expr.md index c76dc6e51..23a8c96e3 100644 --- a/src/expressions/method-call-expr.md +++ b/src/expressions/method-call-expr.md @@ -67,18 +67,18 @@ For each step, the candidate type is used to determine what searches to perform: * For a struct, enum, or foreign type, there is a search for inherent impl candidates for the type. * For a type param, there's a search for for inherent candidates on the param. -* For other tyings (e.g. bools, chars) there's a search for inherent candidates - for the incoherent type. +* For various simpler types (listed below) there's a search for inherent + candidates for the incoherent type. * After any of these, there's a further search for extension candidates for traits in scope. -These searches contribute to list of all the candidate methods found; -separate lists are maintained for inherent and extension candidates. Only -[visible] candidates are included. +"Various simpler types" currently means bool, char, all numbers, str, array, +slices, raw pointers, references, never and tuple. -(For diagnostic purposes, the search may be performed slightly differently, for -instance searching all traits not just those in scope, or also noting -inaccessible candidates.) +These searches contribute to list of all the candidate methods found; +separate lists are maintained for inherent and extension candidates +(that is, applicable candidates from traits). Only [visible] candidates +are included. ## Picking a method from the candidates @@ -94,21 +94,21 @@ For each step, picking is attempted in this order: * First, a by-value method, where the `self` type precisely matches * First for inherent methods - * Then for extension methods + * Then for extension (trait) methods * Then, a method where `self` is received by immutable reference (`&T`) * First for inherent methods - * Then for extension methods + * Then for extension (trait) methods * Then, a method where `self` is received by mutable reference (`&mut T`) * First for inherent methods - * Then for extension methods + * Then for extension (trait) methods * Then, a method where the `self` type is a `*const T` - this is only considered if the self type is `*mut T` * First for inherent methods - * Then for extension methods + * Then for extension (trait) methods * And finally, a method with a `Pin` that's reborrowed, if the `pin_ergonomics` feature is enabled. * First for inherent methods - * Then for extension methods + * Then for extension (trait) methods For each of those searches, if exactly one candidate is identified, it's picked, and the search stops. If this results in multiple possible candidates, @@ -160,6 +160,9 @@ There are a few details not considered in this overview: a mutable pointer to a constant pointer, or a pin reborrow. * Extra lists are maintained for diagnostic purposes: unstable candidates, unsatisfied predicates, and static candidates. +* For diagnostic purposes, the search may be performed slightly differently, + for instance searching all traits not just those in scope, or also noting + inaccessible candidates. ## Net results From 8c16081d02096ae1307e0b5947140ddc26336e60 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Mon, 3 Feb 2025 14:00:11 +0000 Subject: [PATCH 08/18] Update src/expressions/method-call-expr.md Co-authored-by: Tyler Mandry --- src/expressions/method-call-expr.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/expressions/method-call-expr.md b/src/expressions/method-call-expr.md index 23a8c96e3..94d70c8ff 100644 --- a/src/expressions/method-call-expr.md +++ b/src/expressions/method-call-expr.md @@ -66,7 +66,7 @@ For each step, the candidate type is used to determine what searches to perform: the object, then inherent impl candidates for the type. * For a struct, enum, or foreign type, there is a search for inherent impl candidates for the type. -* For a type param, there's a search for for inherent candidates on the param. +* For a type param, there's a search for inherent candidates on the param. * For various simpler types (listed below) there's a search for inherent candidates for the incoherent type. * After any of these, there's a further search for extension candidates for From a8c7502e8fdffa9372bd5bc8ecaadb0e68bc9b66 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Fri, 7 Feb 2025 11:04:01 +0000 Subject: [PATCH 09/18] Small edit. --- src/expressions/method-call-expr.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/expressions/method-call-expr.md b/src/expressions/method-call-expr.md index 94d70c8ff..7911d8c3f 100644 --- a/src/expressions/method-call-expr.md +++ b/src/expressions/method-call-expr.md @@ -69,8 +69,9 @@ For each step, the candidate type is used to determine what searches to perform: * For a type param, there's a search for inherent candidates on the param. * For various simpler types (listed below) there's a search for inherent candidates for the incoherent type. -* After any of these, there's a further search for extension candidates for - traits in scope. + +After these occur, there's a further search for extension candidates for +traits in scope. "Various simpler types" currently means bool, char, all numbers, str, array, slices, raw pointers, references, never and tuple. From 3d73f8f5eb4ef35ed0aca46e8c6bb8b8469edb67 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Fri, 7 Feb 2025 11:18:06 +0000 Subject: [PATCH 10/18] Slightly clarify inherent/extension. --- src/expressions/method-call-expr.md | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/expressions/method-call-expr.md b/src/expressions/method-call-expr.md index 7911d8c3f..a9fe24db1 100644 --- a/src/expressions/method-call-expr.md +++ b/src/expressions/method-call-expr.md @@ -62,11 +62,11 @@ then the candidate types would be `&SmartPtr`, `SmartPtr` and `Foo`. This list of candidate types is then converted to a list of candidate methods. For each step, the candidate type is used to determine what searches to perform: -* For a trait object, there is first a search for inherent candidates for - the object, then inherent impl candidates for the type. * For a struct, enum, or foreign type, there is a search for inherent impl candidates for the type. * For a type param, there's a search for inherent candidates on the param. +* For a trait object, there is first a search for inherent candidates for + the object, then inherent impl candidates for the type. * For various simpler types (listed below) there's a search for inherent candidates for the incoherent type. @@ -76,10 +76,14 @@ traits in scope. "Various simpler types" currently means bool, char, all numbers, str, array, slices, raw pointers, references, never and tuple. +"Inherent" means a candidate method that can be identified just from +the signature. For example, the `impl` blocks corresponding to a struct +or a trait. "Extension" means a candidate gathered by considering the +traits in scope. + These searches contribute to list of all the candidate methods found; -separate lists are maintained for inherent and extension candidates -(that is, applicable candidates from traits). Only [visible] candidates -are included. +separate lists are maintained for the inherent and extension candidates. +Only [visible] candidates are included. ## Picking a method from the candidates @@ -95,21 +99,21 @@ For each step, picking is attempted in this order: * First, a by-value method, where the `self` type precisely matches * First for inherent methods - * Then for extension (trait) methods + * Then for extension methods * Then, a method where `self` is received by immutable reference (`&T`) * First for inherent methods - * Then for extension (trait) methods + * Then for extension methods * Then, a method where `self` is received by mutable reference (`&mut T`) * First for inherent methods - * Then for extension (trait) methods + * Then for extension methods * Then, a method where the `self` type is a `*const T` - this is only considered if the self type is `*mut T` * First for inherent methods - * Then for extension (trait) methods + * Then for extension methods * And finally, a method with a `Pin` that's reborrowed, if the `pin_ergonomics` feature is enabled. * First for inherent methods - * Then for extension (trait) methods + * Then for extension methods For each of those searches, if exactly one candidate is identified, it's picked, and the search stops. If this results in multiple possible candidates, From 4f2125b78cd445b929b2bb732b55631f4463b602 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Fri, 7 Feb 2025 11:28:06 +0000 Subject: [PATCH 11/18] Explain trait object search. --- src/expressions/method-call-expr.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/expressions/method-call-expr.md b/src/expressions/method-call-expr.md index a9fe24db1..7fdcc446b 100644 --- a/src/expressions/method-call-expr.md +++ b/src/expressions/method-call-expr.md @@ -66,7 +66,9 @@ For each step, the candidate type is used to determine what searches to perform: impl candidates for the type. * For a type param, there's a search for inherent candidates on the param. * For a trait object, there is first a search for inherent candidates for - the object, then inherent impl candidates for the type. + the trait (for example in `impl Trait` blocks), then inherent impl + candidates for the trait object itself (for example found in `impl dyn Trait` + blocks) * For various simpler types (listed below) there's a search for inherent candidates for the incoherent type. From fb498948fd29db0af1c4bdcbcdae5fd185ce2279 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Fri, 7 Feb 2025 11:45:38 +0000 Subject: [PATCH 12/18] Add a couple of links. --- src/expressions/method-call-expr.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/expressions/method-call-expr.md b/src/expressions/method-call-expr.md index 7fdcc446b..95a92d1e0 100644 --- a/src/expressions/method-call-expr.md +++ b/src/expressions/method-call-expr.md @@ -78,10 +78,10 @@ traits in scope. "Various simpler types" currently means bool, char, all numbers, str, array, slices, raw pointers, references, never and tuple. -"Inherent" means a candidate method that can be identified just from -the signature. For example, the `impl` blocks corresponding to a struct -or a trait. "Extension" means a candidate gathered by considering the -traits in scope. +["Inherent"][inherent] means a candidate method from a block directly +corresponding to the type in the signature. For example, the `impl` blocks +corresponding to a struct or a trait. "Extension" means a candidate gathered +by considering [methods on traits] in scope. These searches contribute to list of all the candidate methods found; separate lists are maintained for the inherent and extension candidates. @@ -226,3 +226,5 @@ There are a few details not considered in this overview: [methods]: ../items/associated-items.md#methods [unsized coercion]: ../type-coercions.md#unsized-coercions [`IntoIterator`]: std::iter::IntoIterator +[inherent]: ../items/implementations.md#inherent-implementations +[methods on traits]: ../items/implementations.md#trait-implementations From f1a7dd7d2d9c5cedeac2fe9992c99c96531bb169 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Fri, 7 Feb 2025 11:52:44 +0000 Subject: [PATCH 13/18] Document incoherence. --- src/expressions/method-call-expr.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/expressions/method-call-expr.md b/src/expressions/method-call-expr.md index 95a92d1e0..9ea9032b8 100644 --- a/src/expressions/method-call-expr.md +++ b/src/expressions/method-call-expr.md @@ -62,15 +62,13 @@ then the candidate types would be `&SmartPtr`, `SmartPtr` and `Foo`. This list of candidate types is then converted to a list of candidate methods. For each step, the candidate type is used to determine what searches to perform: -* For a struct, enum, or foreign type, there is a search for inherent - impl candidates for the type. +* For a struct, enum, foreign type, or various simpler types (listed below) + there is a search for inherent impl candidates for the type. * For a type param, there's a search for inherent candidates on the param. * For a trait object, there is first a search for inherent candidates for the trait (for example in `impl Trait` blocks), then inherent impl candidates for the trait object itself (for example found in `impl dyn Trait` - blocks) -* For various simpler types (listed below) there's a search for inherent - candidates for the incoherent type. + blocks). After these occur, there's a further search for extension candidates for traits in scope. @@ -146,9 +144,10 @@ only the `Receiver` chain. There are a few details not considered in this overview: -* The search for candidate methods will also consider searches for - incoherent types if `rustc_has_incoherent_inherent_impls` is active for - a `dyn`, struct, enum, or foreign type. +* The search for candidate methods will search more widely (potentially + across crates) for certain [incoherent] types: that includes any of + the "various simpler types" listed above, and a `dyn`, struct, enum, or + foreign type if `rustc_has_incoherent_inherent_impls` is active. * If there are multiple candidates from traits, they may in fact be identical, and the picking operation collapses them to a single pick to avoid reporting conflicts. @@ -228,3 +227,4 @@ There are a few details not considered in this overview: [`IntoIterator`]: std::iter::IntoIterator [inherent]: ../items/implementations.md#inherent-implementations [methods on traits]: ../items/implementations.md#trait-implementations +[incoherent]: ../items/implementations.md#trait-implementation-coherence From e17b9b6e993373ad54ce74c98b754242c80b1e6c Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Fri, 7 Feb 2025 13:18:26 +0000 Subject: [PATCH 14/18] Explain rustc_has_incoherent_inherent_impls is niche. --- src/expressions/method-call-expr.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/expressions/method-call-expr.md b/src/expressions/method-call-expr.md index 9ea9032b8..2af9e4a72 100644 --- a/src/expressions/method-call-expr.md +++ b/src/expressions/method-call-expr.md @@ -146,8 +146,9 @@ There are a few details not considered in this overview: * The search for candidate methods will search more widely (potentially across crates) for certain [incoherent] types: that includes any of - the "various simpler types" listed above, and a `dyn`, struct, enum, or - foreign type if `rustc_has_incoherent_inherent_impls` is active. + the "various simpler types" listed above; and any `dyn`, struct, enum, or + foreign type where the standard-library-internal attribute + `rustc_has_incoherent_inherent_impls` is active. * If there are multiple candidates from traits, they may in fact be identical, and the picking operation collapses them to a single pick to avoid reporting conflicts. From 687a52ba1793f6b5e6fafb7ee33cf4c515908a8f Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Fri, 7 Feb 2025 14:00:43 +0000 Subject: [PATCH 15/18] Note another surprise. --- src/expressions/method-call-expr.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/expressions/method-call-expr.md b/src/expressions/method-call-expr.md index 2af9e4a72..d2ce256bd 100644 --- a/src/expressions/method-call-expr.md +++ b/src/expressions/method-call-expr.md @@ -201,6 +201,31 @@ There are a few details not considered in this overview: > } > ``` +The types and number of parameters in the method call expression aren't taken +into account in method resolution. So the following won't compile: + +```rust,nocompile +trait NoParameter { + fn method(self); +} + +trait OneParameter { + fn method(&self, jj: i32); +} + +impl NoParameter for char { + fn method(self) {} // found first and picked, but doesn't work +} + +impl OneParameter for char { + fn method(&self, jj: i32) {} // found second, thus ignored +} + +fn f() { + 'x'.method(123); +} +``` + > **Edition differences**: Before the 2021 edition, during the search for visible methods, if the candidate receiver type is an [array type], methods provided by the standard library [`IntoIterator`] trait are ignored. > > The edition used for this purpose is determined by the token representing the method name. From 41098a641e31e3f7b0a4d5838723f9434c65fb99 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Fri, 7 Mar 2025 16:14:20 +0000 Subject: [PATCH 16/18] Explicitly note need for matching method names. --- src/expressions/method-call-expr.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/expressions/method-call-expr.md b/src/expressions/method-call-expr.md index d2ce256bd..be0e16536 100644 --- a/src/expressions/method-call-expr.md +++ b/src/expressions/method-call-expr.md @@ -81,9 +81,11 @@ corresponding to the type in the signature. For example, the `impl` blocks corresponding to a struct or a trait. "Extension" means a candidate gathered by considering [methods on traits] in scope. +Each of these searches looks for [visible] candidates where the method name +matches. + These searches contribute to list of all the candidate methods found; separate lists are maintained for the inherent and extension candidates. -Only [visible] candidates are included. ## Picking a method from the candidates @@ -169,7 +171,7 @@ There are a few details not considered in this overview: unstable candidates, unsatisfied predicates, and static candidates. * For diagnostic purposes, the search may be performed slightly differently, for instance searching all traits not just those in scope, or also noting - inaccessible candidates. + inaccessible candidates, or noting those with similar names. ## Net results From 9d00834a6a2b59367a3f28f0f1a5e448c1d9a1eb Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Fri, 7 Mar 2025 16:25:07 +0000 Subject: [PATCH 17/18] Note on trait consideration. --- src/expressions/method-call-expr.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/expressions/method-call-expr.md b/src/expressions/method-call-expr.md index be0e16536..3948d7747 100644 --- a/src/expressions/method-call-expr.md +++ b/src/expressions/method-call-expr.md @@ -70,8 +70,9 @@ For each step, the candidate type is used to determine what searches to perform: candidates for the trait object itself (for example found in `impl dyn Trait` blocks). -After these occur, there's a further search for extension candidates for -traits in scope. +After these occur, there's a further search for extension candidates from +all traits in scope (irrespective of whether the trait is remotely relevant +to the `self` type - that's considered later). "Various simpler types" currently means bool, char, all numbers, str, array, slices, raw pointers, references, never and tuple. From 923094b35ddaca31355812237a23c060e26ce666 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Fri, 7 Mar 2025 16:39:42 +0000 Subject: [PATCH 18/18] Clarify unsized coercion applicability. --- src/expressions/method-call-expr.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/expressions/method-call-expr.md b/src/expressions/method-call-expr.md index 3948d7747..5339d1f29 100644 --- a/src/expressions/method-call-expr.md +++ b/src/expressions/method-call-expr.md @@ -31,8 +31,8 @@ to the next type. (If a step involved following the `Receiver` target, we also note whether it would have been reachable by following `` - this information is used later). -At the end, an additional candidate step may be added for -an [unsized coercion]. +If the final type is an array, an additional candidate type is added +by [unsized coercion] of that array. Each step of this chain provides a possible `self` type for methods that might be called. The list will be used in two different ways: