Details
One proposal that mostly maintains LTR and handles x[i] = x[j]
Definitions
An lvalue expression is basically the current rustc lvalue expression.
Unresolved Question: do we include overloaded index/deref in index/deref lvalues? this makes more code compile but could be confusing in some cases?
LVALUE_EXPR =
MUTABILITY RVALUE_EXPR - temporary
| LVALUE_EXPR . FIELD - field access
| LVALUE_EXPR [ RVALUE_EXPR ] - indexing (including overloaded?)
| *MUTABILITY LVALUE_EXPR - deref
| LOCAL - local variable
Evaluating an expression “to an lvalue” is just evaluating all the rvalue-expressions in it. Note that after an expression is evaluated to an lvalue, finalizing its evaluation can still read memory and possibly run user code (if we allow overloaded derefs/indexing).
Evaluating an expression “to an rvalue” is just standard evaluation.
To evaluate an expression with a receiver, that’s it “LHS = RHS”, “LHS OP= RHS”, “LHS.foo(ARG1, ARG2)”, first the pre-final-autoref receiver is evaluated to an lvalue, then the other operands are evaluated to rvalues, then the receiver evaluation is finalized and autoref-ed if needed.
Unresolved question: should this happen with by-value-self taking methods too? Provide an example for and against.
Examples
Simple assignment
x[I] = x[J];
// equiv
let i = I;
let rhs = x[J];
x[I] = rhs;
Simple function call
a.b.f(a.b.g(), a.b.h())
// equiv
let arg0 = a.b.g();
let arg1 = a.b.h();
a.b.f(arg0, arg1); // potentially with overloaded autoderef
Changing receiver
(*boxed).push({
mem::replace(&mut boxed, Box::new(vec![])).len()
})
// equiv
let arg0 = mem::replace(&mut boxed, Box::new(vec![])).len();
Vec::push(&mut *boxed, arg0); // this can be surprising, I guess.
Changing receiver, deep
let y = Vec::new();
(***boxed).get({boxed=&&&y; 42})
// equiv
let y = Vec::new();
let ix = {boxed=&&&y; 42};
<[u8]>::get(&<Vec<u8> as Deref>::deref(&***boxed), ix)
Cutting your own receiver
let boxed: Box<&[u8]> = get();
boxed.get({drop(boxed); 4})
// equiv
let boxed: Box<&[u8]> = get();
let ix = { drop(boxed); 4 };
<[u8]>::get(&**boxed) //~ ERROR use of moved value
Cutting your own receiver, by-value
let a: &mut [u32] = &mut [1,2];
let b: &mut [u32] = &mut [3,4];
let c;
a.get_mut({a=b; c=a; 1})
// equiv
let ix = {a=b; c=a; 1}
<&mut [u32]>::get_mut(a, ix) //~ ERROR use of moved value
Chained function call
a.b().c().d()
// equiv
let t0 = a.b(); // this is an rvalue
let t1 = t0.c();
let t2 = t1.d();
Pushing length
x[ix()].push(x[0].len());
// equiv
let index = ix();
let arg0 = x[0].len();
x[index].push(arg0);
Pushing length, via function
x.get_mut().push(x[0].len());
// equiv
let t0 = x.get_mut();
let len = x[0].len(); //~ ERROR cannot borrow
t0.push(len);
Simple assign-op
dirty |= SOMETHING_MODIFYING_DIRTY;
// equiv
let rhs = SOMETHING_MODIFYING_DIRTY;
dirty = dirty | rhs;
Advantages
Most code should compile and do something sane.
Disadvantages
Indexing/deref is handled somewhat differently from normal methods. If we don’t allow overloaded deref/indexing, they can behave differently from primitive deref/indexing. If we do, their order-of-evaluation can be somewhat confusing.
The handling of coercions needs to be thought about - if they can occur in the middle of an lvalue, they can break it. It would be nice if someone who knows them could provide an example.
This also changes order-of-evaluation, which could subtly break existing code.
Coercions
Could someone help me there (@eddyb, you know these best)
Lvalues and deref
With the borrow checker, overloaded deref as well as deref of &mut references is very similar to an operation on lvalues, turning an 'a Ptr<T> (or 'a &'b mut T) into an 'a T.
This is actually not totally precise: references can be “leaked”. A by-value &'a mut T can be transformed to an 'a mut T. This is similar to a turning a by-value Box<T> into an 'static mut T (the latter transformation is not automatic, as it has the unfortunate side-effect effect of leaking memory), but is a good model in many cases (this “leak” functionality is somewhat confusing).
Dereference of &T always uses the “leak” functionality, but that is less confusing as &T is Copy.