Stream: t-compiler/major changes

Topic: Add placement by return - compiler-team#330

Olivier FAURE (Jul 07 2020 at 09:16, on Zulip):

Hanna Kruppe said:

More precisely in terms of MIR: let mut p = &raw mut <something>; loop { let mut x = ptr::replace(p, 1); p = &raw mut x; }

Leaving scopes aside from a second, that seems like a clear stacked borrows violation to me. If we imagine a similar example where we hoist x out of the loop, the violation becomes clear:

let mut p = &raw mut <something>;
let mut x = 42;
loop {
    mutate_first_arg(&mut x, p, 1);
    p = &raw mut x;

In the more general case, as long as emplacement is interpreted as taking a mut/unique reference to its destination, I don't see how a well-formed program could alias that reference with input refs, unsafe code or not.

Bad unsafe code could alias emplacement memory with input memory if it wrongly assumes that any undefined memory is automatically valid for emplacement, but that's a documentation problem, not a soundness problem.

Hanna Kruppe (Jul 07 2020 at 12:36, on Zulip):

Gary Guo said:

Hanna Kruppe Something I really don't understand about the discussion, why would variables declared within a loop be treated differently? How about

loop {{ let mut x == ...; p = &raw mut x; }}

We've introduced a new scope, does your argument still apply?

My arguments would apply unaltered, because they revolve around "what do multiple StorageLive/StorageDead pairs on the same local do?". It's just that loops are the main (only?) way that you can get into that situation using surface Rust (as opposed to writing MIR by hand).

Hanna Kruppe (Jul 07 2020 at 12:40, on Zulip):

Olivier FAURE said:

Technically the proposal doesn't even require GCE to elide the final copy in the x = ... case; in that case the return chain could be collapsed to a single emplacement into a temporary, followed by a move, with no risk of aliasing.

Frankly, if we can't get rid of all the temporaries reliably, then that's not worth much: we already get down to one temporary in many cases, and it's still a problem in practice. Likewise, if you can't handle re-assignments or have other arbitrary limitations that natural code hits often, then the value for users is rather limited.

Hanna Kruppe (Jul 07 2020 at 12:44, on Zulip):

And yes, stacked borrows might rule out my earlier example for other reasons, as already noted earlier. But I'm not sure this addresses all problems of this sort, and in any case (again) limiting yourself only to initialization is not enough.

Gary Guo (Jul 07 2020 at 16:07, on Zulip):

I think we should not think about MIR when we discuss if something's UB or not. In surface Rust a value should not be accessed when it leaves scope and that's should be an enough argument to make these code UB.

Jonas Schievink (Jul 07 2020 at 16:12, on Zulip):

The plan is to define Rust's semantics in terms of MIR, and define a lowering from surface Rust to MIR, so MIR is very relevant as it will define the entire operational semantics of Rust.

Gary Guo (Jul 07 2020 at 16:32, on Zulip):

Okay, I see the point now. Thanks for the clarification.

RalfJ (Jul 12 2020 at 10:28, on Zulip):

comex said:

This is a weird situation. I wish we could do return value optimization for reassigning an existing variable, but we can't in general because x = foo(&mut x); is allowed.

I think we can (and do) have that rule on the MIR level though. Also see:

RalfJ (Jul 12 2020 at 10:29, on Zulip):

Beyond that I am not sure what your question is, do you have a concrete example?

Last update: May 07 2021 at 06:30UTC