Accepting nested method calls with an `&mut self` receiver

So I was chatting with @arielb1 and wanted to summarize the proposal he floated in this earlier thread (which @cuviper also cited). It is a more far-reaching alternative that does address this issue, but which also makes changes to the existing evaluation order that may be significant (interestingly, it seems plausible to test this). It has some benefits and some drawbacks.

The proposal is:

Evaluation order for a Rust expression is two pass:

- first evaluate all rvalue subexpressions (left to right)
- then evaluate all lvalue subexpressions (left to right)
    - here, derefs and index impls are assumed to be "pure enough" 
        - *but* index *arguments are evaluated in first pass
    - crucially, autoderefs and autoref are also applied in this second pass

So, for vec.push(vec.len()), there are two subexpressions, vec and vec.len(). vec is an lvalue, so it is not evaluated in the first pass, but vec.len() (an rvalue) is. Then we “evaluate” vec and apply the autoref, and hence we get:

let tmp1 = vec.len(); // naturally this would be recursively expanded
let tmp0 = &mut vec;
Vec::push(tmp0, tmp1)

Note that desugaring to Vec::push(&mut vec, vec.len()) does not work, unless you make the model a bit more complex to treat borrow expressions and things differently.

Index lvalues provide for some subtlety. For example, if x=0 and you evaluate vec[x].push({x += 1}), are you pushing onto vec[0] or vec[1]? Seems like yes, because index arguments are always treated as rvalues, but that’s also kind of surprising.

This is backwards incompatible. For example, if you have x=0 and you evaluate x + { x += 1; 1}, then the block is evaluated first, and hence the final result is 2. That is, we desugar that to:

let tmp1 = { x += 1; 1 };
let tmp0 = x; // just an lvalue, eval during 2nd pass
tmp0 + tmp1

It does however mean that x += ... and x = x + ... are equivalent (not true today).

Interestingly, we could test for backwards compatibility, at least in theory, by generating the MIR both ways and applying some filters. This could be used to detect the impact of said changes.

3 Likes