reference icon indicating copy to clipboard operation
reference copied to clipboard

Method call reference: major rewrite

Open adetaylor opened this issue 11 months ago • 11 comments

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

Tracking:

  • https://github.com/rust-lang/rust/issues/44874

adetaylor avatar Jan 30 '25 15:01 adetaylor

Overall, this is fantastic. Very much an improvement. Thanks @adetaylor.

traviscross avatar Jan 31 '25 00:01 traviscross

:umbrella: The latest upstream changes (possibly 4249fb411dd27f945e2881eb0378044b94cee06f) made this pull request unmergeable. Please resolve the merge conflicts.

rustbot avatar Jan 31 '25 03:01 rustbot

Note there's another description of the method lookup process in the rustc-dev-guide.

mattheww avatar Feb 03 '25 20:02 mattheww

One thing that makes this business difficult to document is that method lookup happens when type inference is not yet complete (and the results of method lookup participate in type inference).

All the writeups I've seen gloss over this by writing "the receiver type" and then proceeding to describe what happens as if that type was fully determined.

I'm not sure how much detail it makes sense to go into on this page as long as the Reference has very little to say in general about type inference. But I think it might be worth mentioning that:

  • At this point the receiver type might be something like Vec<?>, which I think explains how the "If there are multiple candidates from traits, they may in fact be identical" case can happen.

  • If the type isn't constrained at all by the code coming before the method call, method resolution will just give up (even if there's only one possible type for which the method call could work).

mattheww avatar Feb 03 '25 21:02 mattheww

It might also be worth noting explicitly that the types of parameters in the method call expression aren't taken into account in method resolution. Even the number of parameters is ignored. So the following won't compile, because the method from NoParameter is found earlier in the picking process even though it's "obviously" the wrong one.

trait NoParameter {
    fn method(self);
}

trait OneParameter {
    fn method(&self, jj: i32);
}

impl NoParameter for char {
    fn method(self) {}
}

impl OneParameter for char {
    fn method(&self, jj: i32) {}
}

fn f() {
    'x'.method(123);
}

mattheww avatar Feb 03 '25 21:02 mattheww

Wow, this example is very surprising to me!

With my "cargo-semver-checks maintainer" hat on, it's extremely useful to have someplace where these details are written up with examples like this. The better I can understand these nuances, the better our rules for discovering breakage will be.

I will leave to you all the question of whether the reference or some other location is the best place to document these behaviors.

obi1kenobi avatar Feb 04 '25 00:02 obi1kenobi

It might also be worth noting explicitly that the types of parameters in the method call expression aren't taken into account in method resolution.

Agreed - I added your example in 687a52b.

adetaylor avatar Feb 07 '25 14:02 adetaylor

@mattheww regarding

One thing that makes this business difficult to document is that method lookup happens when type inference is not yet complete (and the results of method lookup participate in type inference).

I agree that it would be good to explain that here, but I'd prefer it be done in a separate PR - this one is already ambitious enough, and I don't think it makes that situation worse.

adetaylor avatar Feb 07 '25 14:02 adetaylor

Yes, it would be better to get this merged and think about improving it later.

I think with the Reference in its current state there's an argument that this part should just be omitted (if it can't happen in a model where the full type of the receiver is known):

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

But I'd certainly have no objection to leaving that question for another time.

mattheww avatar Feb 08 '25 18:02 mattheww

I'm not sure whether that's the only case where the candidates may turn out to be identical - I'll try to work it out.

adetaylor avatar Feb 08 '25 22:02 adetaylor

@mattheww says:

Yes, it would be better to get this merged and think about improving it later.

I think with the Reference in its current state there's an argument that this part should just be omitted (if it can't happen in a model where the full type of the receiver is known):

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

But I'd certainly have no objection to leaving that question for another time.

I didn't want to decide to keep or remove this bullet until I was certain whether this "pick collapsing" effect only occurs in situations where the full type of the receiver isn't known. So, I tried disabling this bit of code to find out what broke. It is used in other circumstances including some not even involving a receiver, e.g. the I::From call in this example seems to hit it. In cases with a receiver I'm still not sure if the "full type of receiver isn't known" case is the only one. But in any case, I would definitely like to avoid making things more complex and leave any additional tweaks here for a future change. I personally don't feel this bullet does any harm, since it's buried in a list of "here be dragons" further nuances, so I'm going to keep it unless you strongly want it removed. Follow up edit PRs would be great once this lands, of course.

adetaylor avatar Mar 07 '25 16:03 adetaylor