Stream: t-compiler/wg-nll

Topic: two-phase borrows with existing loans


RalfJ (Nov 26 2018 at 15:39, on Zulip):

@nikomatsakis Just chatted with @Matthew Jasper, and I learned that we accept code like

fn two_phase_overlapping1() {
    let mut x = vec![];
    let p = &x;
    x.push(p.len());
}

Can we, uh, not do that?^^ It would do quite horrible things to Stacked Borrows. Firstly, it'd kill the stack. More interestingly, to actually properly explain what happens (@Ariel Ben-Yehuda came up with some way more crazy examples), we'd probably have to generalize from a stack to a tree or some such thing

RalfJ (Nov 26 2018 at 15:40, on Zulip):

I think I have a fairly simple way of supporting two-phase borrows by immediately sharing the mutable reference, done. this works nicely together with the rule that reading from a mutable reference does not require popping of frozen/shared items.

RalfJ (Nov 26 2018 at 15:40, on Zulip):

but code like the above breaks that idea completely

nikomatsakis (Nov 26 2018 at 18:13, on Zulip):

@RalfJ hmm

nikomatsakis (Nov 26 2018 at 18:13, on Zulip):

we could

nikomatsakis (Nov 26 2018 at 18:13, on Zulip):

I'm not opposed to doing so per se, just thinking about the schedule

nikomatsakis (Nov 26 2018 at 18:14, on Zulip):

we might be able to get away with calling this a bug fix

nikomatsakis (Nov 26 2018 at 18:14, on Zulip):

I guess we can still backport to Rust 2018

nikomatsakis (Nov 26 2018 at 18:14, on Zulip):

@RalfJ can you file an issue?

Jake Goulding (Nov 26 2018 at 19:49, on Zulip):

Hmm. The code is actually safe, isn't it? It feels wrong to go out of the way to disallow safe code; the point of NLL is to reduce false negatives.

nikomatsakis (Nov 26 2018 at 20:06, on Zulip):

well, that's the question, isn't it

nikomatsakis (Nov 26 2018 at 20:07, on Zulip):

that is, the code is not safe according to the stacked borrows rules that @RalfJ has been working on, it's UB

nikomatsakis (Nov 26 2018 at 20:07, on Zulip):

but the question then is whether this makes the rules wrong

nikomatsakis (Nov 26 2018 at 20:07, on Zulip):

I'm inclined to try and tread carefully here

nikomatsakis (Nov 26 2018 at 20:08, on Zulip):

I'd still like to do this refactoring

nikomatsakis (Nov 26 2018 at 20:09, on Zulip):

which I think would also reject the example in question

nikomatsakis (Nov 26 2018 at 20:11, on Zulip):

opened https://github.com/rust-lang/rust/issues/56254

Jake Goulding (Nov 26 2018 at 20:19, on Zulip):

this makes the rules wrong

I think this is the crux for me — what is actually driving what Rust-the-language accepts? Is stacked borrows the true arbiter of what's safe?

nikomatsakis (Nov 26 2018 at 20:23, on Zulip):

it aims to be

nikomatsakis (Nov 26 2018 at 20:23, on Zulip):

we can of course decide that it should accept this code

nikomatsakis (Nov 26 2018 at 20:23, on Zulip):

there's a sort of balancing act

nikomatsakis (Nov 26 2018 at 20:23, on Zulip):

how complex are the rules to describe

nikomatsakis (Nov 26 2018 at 20:23, on Zulip):

(and understand)

nikomatsakis (Nov 26 2018 at 20:23, on Zulip):

vs what code they accept

nikomatsakis (Nov 26 2018 at 20:23, on Zulip):

(and what optimizations they permit)

Jake Goulding (Nov 26 2018 at 20:25, on Zulip):

As an outsider, it's interesting because it seems like stacked borrows is a post-hoc justification of what "the code" does. This makes it weird when the code is adjusted to fit the model (physicists everywhere are jealous)

RalfJ (Nov 26 2018 at 21:24, on Zulip):

As an outsider, it's interesting because it seems like stacked borrows is a post-hoc justification of what "the code" does. This makes it weird when the code is adjusted to fit the model (physicists everywhere are jealous)

stacked borrows is a post-hoc justification of original borrowck and might serve as a guide for future borrowck evolution. also, it might uncover situations where borrowck is inconsistent or runs afoul of guarantees we might want to have. in this concrete case, for example, whether or not we allow such code has influence on how hard it will be for an optimization pass to analyze which shared reference can alias a given mutable reference.

RalfJ (Nov 26 2018 at 21:24, on Zulip):

but mostly, I'd like us to be able to have that discussion

RalfJ (Nov 26 2018 at 21:24, on Zulip):

and that means not stabilizing too much too early

RalfJ (Nov 26 2018 at 21:29, on Zulip):

physicists model an immutable reality, whereas modelling programming languages can hopefully contribute to improve the future evolution of that language ;)

Keith Yeung (Nov 26 2018 at 21:30, on Zulip):

think HTML but with a lot more formal verification :)

Keith Yeung (Nov 26 2018 at 21:30, on Zulip):

and of course, without the backwards compatibility issues

RalfJ (Nov 26 2018 at 21:30, on Zulip):

I'd actually claim that doing formal models during development will often lead to a better understanding of what is being developed, and thus a better language

nikomatsakis (Nov 26 2018 at 22:28, on Zulip):

looks @RalfJ like this might be a small diff

nikomatsakis (Nov 26 2018 at 22:28, on Zulip):

I guess we'll see what errors I get

nikomatsakis (Nov 26 2018 at 22:40, on Zulip):

hmm, I get a bunch :)

nikomatsakis (Nov 26 2018 at 22:40, on Zulip):

from the match desugaring, I think

nikomatsakis (Nov 26 2018 at 22:41, on Zulip):

ah, yes

nikomatsakis (Nov 26 2018 at 22:42, on Zulip):

I have to check, @Matthew Jasper might remember better, but I believe we are relying on the current behavior in match desugaring -- we create shared borrows of the various "discriminants" that we examine

nikomatsakis (Nov 26 2018 at 22:42, on Zulip):

then we make two-phase borrows for ref mut bindings

nikomatsakis (Nov 26 2018 at 22:42, on Zulip):

but they are not activated until we enter the arm

nikomatsakis (Nov 26 2018 at 22:43, on Zulip):

some of those have the peculiar characteristic that they are 2PB that never get activated

Matthew Jasper (Nov 26 2018 at 22:51, on Zulip):

We also create borrows of the places that we bind variables to, which is the more difficult one to fix, since they don't have a different borrow kind.

Matthew Jasper (Nov 26 2018 at 22:53, on Zulip):

I'll try to write up what I think is the best solution (with the time we have) tomorrow.

Matthew Jasper (Nov 27 2018 at 08:00, on Zulip):

So the simplest thing that I think we can do is to add another borrow kind that doesn't conflict with reservations, but is otherwise a shared borrow. Then move all fake borrows to that kind (shallow borrows aren't compatible with niche layout optimisations) and remove the shallow borrow kind.
This would also allow us to do all post NLL cleanup in a single pass (we wouldn't backport that part).

Matthew Jasper (Nov 27 2018 at 08:03, on Zulip):

Having a different borrow kind isn't great, but fake borrows are strange and special anyway.

Matthew Jasper (Nov 27 2018 at 08:06, on Zulip):

some of those have the peculiar characteristic that they are 2PB that never get activated

They are activated as long as the arm is reachable, which doesn't seem any different to function call two phase borrows.

RalfJ (Nov 27 2018 at 10:12, on Zulip):

@Matthew Jasper what would be an example of a desugared match guard that relies on the existing-loans-are-preserved thing?

Matthew Jasper (Nov 27 2018 at 10:22, on Zulip):

Any match that has a mut ref binding with a match guard.

RalfJ (Nov 27 2018 at 11:22, on Zulip):

okay so let's say we have

fn foo(x: Option<String>) {
  match x {
    Some(mut ref s) if s.starts_with("hello") => s.push_str(" world!"),
    _ => {},
  }
}

what does this desugar to, with the fake refs?

Ariel Ben-Yehuda (Nov 27 2018 at 11:41, on Zulip):

@RalfJ so the problem is a conflict with the "borrow lock"

Ariel Ben-Yehuda (Nov 27 2018 at 11:41, on Zulip):

*the "discriminant lock" borrow

Ariel Ben-Yehuda (Nov 27 2018 at 11:42, on Zulip):

which borrows x immutably for the duration of the arms

Ariel Ben-Yehuda (Nov 27 2018 at 11:42, on Zulip):

(to prevent it from being modified in a way that breaks exhaustiveness)

Matthew Jasper (Nov 27 2018 at 11:42, on Zulip):
_fake1 = &shallow x;
_fake2 = &(x as Some).0;
// switch on discriminant
s_for_guard = &mut (x as Some).0;
s_for_guard_ref = &s_for_guard;
// guard, using *s_for_guard_ref instead of s
FakeRead(_fake1);
FakeRead (_fake2);
s = s_for_guard;
// Arm as usual
Ariel Ben-Yehuda (Nov 27 2018 at 11:43, on Zulip):

yea that

Matthew Jasper (Nov 27 2018 at 11:45, on Zulip):

Everything involving _fakeN is deleted shortly after NLL

nikomatsakis (Nov 27 2018 at 15:13, on Zulip):

Having a different borrow kind isn't great, but fake borrows are strange and special anyway.

this is what I was thinking

nikomatsakis (Nov 27 2018 at 15:13, on Zulip):

for one thing, these "borrow locks" are different in kind from other borrows

nikomatsakis (Nov 27 2018 at 15:13, on Zulip):

hmm, well, are they?

nikomatsakis (Nov 27 2018 at 15:14, on Zulip):

I think they are -- or at least could be

nikomatsakis (Nov 27 2018 at 15:14, on Zulip):

That is, they are there to enforce rules about what you can/can't do in match arms, guards, etc, because of our exhaustiveness rules. It potentially creates UB to violate those rules because you mess up our match desugaring.

nikomatsakis (Nov 27 2018 at 15:14, on Zulip):

However, it's not clear that stacked borrows cares about this

nikomatsakis (Nov 27 2018 at 15:14, on Zulip):

that is, if you used unsafe code to violate those loans, this may create UB, but not necessarily because of an aliasing violation

nikomatsakis (Nov 27 2018 at 15:15, on Zulip):

(it's more akin to creating UB by relying on the layout of a struct or some such thing, which we are free to change)

nikomatsakis (Nov 27 2018 at 15:15, on Zulip):

@Matthew Jasper did you do any work on that?

Matthew Jasper (Nov 27 2018 at 17:04, on Zulip):

I've done no work on this yet since I've been traveling. I should be able to get this done tonight if you don't want to take it.

nikomatsakis (Nov 27 2018 at 18:25, on Zulip):

I've not done anything, not feeling well today. Probably shoudn't be typing here now I'm supposed to be sleeping :P

memoryruins (Nov 27 2018 at 19:52, on Zulip):

using the original example, if i try to push twice, it errors with

error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
 --> src/lib.rs:4:5
  |
3 |     let p = &x;
  |             -- immutable borrow occurs here
4 |     x.push(p.len());
  |     ^^^^^^^^^^^^^^^ mutable borrow occurs here
5 |     x.push(p.len());
  |            - immutable borrow later used here

(posting ^ if it gives a bigger picture / current diagnostics. if I push once and debug print x, it works, printing [0])

Matthew Jasper (Nov 27 2018 at 22:29, on Zulip):

Ugh, we need Shallow borrows to allow matching on (*x).1 while (*x).0 is borrowed.

Matthew Jasper (Nov 27 2018 at 23:37, on Zulip):

{WIP] PR https://github.com/rust-lang/rust/pull/56301. Thinking about this some more, I'm not convinced that 2 phase borrows are the correct way of handling match guards at all. Notably, in match x { ref mut z if ..., x should be mutably borrowed for the guard, as well as for the match arm.

RalfJ (Nov 28 2018 at 08:10, on Zulip):

@Matthew Jasper I assume the &mut there is an &mut2phase?

RalfJ (Nov 28 2018 at 08:14, on Zulip):

@Ariel Ben-Yehuda @Matthew Jasper See https://github.com/rust-lang/rust/issues/56254#issuecomment-442357546: I don't understand why my desugaring based on transmute does not work. All pointer addresses are the same, from what I can see.

RalfJ (Nov 28 2018 at 08:18, on Zulip):

x should be mutably borrowed for the guard, as well as for the match arm.

by "mutably borrowed" you mean there should be an active mutable loan?

Matthew Jasper (Nov 28 2018 at 08:22, on Zulip):

Yes.

Matthew Jasper (Nov 28 2018 at 08:23, on Zulip):

Anyway, i think i have a way to avoid 2PB for matches.

RalfJ (Nov 28 2018 at 08:31, on Zulip):

Is that included in your PR above?

RalfJ (Nov 28 2018 at 09:03, on Zulip):

I now have an implementation of two-phase borrows without existing loans. It is trivial, as expected: When retagging for an &mut2phase, after doing the usual retag business, we simply immediately do a shared re-borrow of the mutable borrow we just created. done.

Matthew Jasper (Nov 28 2018 at 09:04, on Zulip):

No, it's a different approach. I'm still thinking it through.

RalfJ (Nov 28 2018 at 09:04, on Zulip):

We do not have to rewrite the other uses, because read uses of something shared do not invalidate mutable references that are ancestors of the shared one

RalfJ (Nov 28 2018 at 09:05, on Zulip):

as in, let x = &mut 2; let y = &*x; let _val1 = *x; let _val2 = *y; is fine because reading through x does not invalidate y

RalfJ (Nov 28 2018 at 09:06, on Zulip):

that also applies "further up the chain":

let x = &mut 42;
let y = &mut*x;
let z = &*y;
let _val1 = *x;
let _val2 = *y;

would be fine if it was accepted, because reading x is justified by z, and hence does not have to invalidate y

RalfJ (Nov 28 2018 at 09:08, on Zulip):

This works quite nicely for 2phase borrows (they are only a tiny extension of the model), but it enforces that there be no outstanding references when the 2phase borrow starts.

nikomatsakis (Nov 28 2018 at 14:02, on Zulip):

@RalfJ the transmute based desugaring was one of the first things we thought of, but 2PB seemed more elegant.

nikomatsakis (Nov 28 2018 at 14:04, on Zulip):

(IIRC)

nikomatsakis (Nov 28 2018 at 14:04, on Zulip):

I'll have to catch up on the comments though

nikomatsakis (Nov 28 2018 at 14:04, on Zulip):

I'm still feeling sort of sick today :( not sure how much I'll be up for working yet

pnkfelix (Nov 28 2018 at 14:06, on Zulip):

@Matthew Jasper out of curiosity, when you said:

Notably, in match x { ref mut z if ..., x should be mutably borrowed for the guard, as well as for the match arm.

what was the scenario that you are concerned about?

Unless I'm mistaken, the semantics for references to pattern variables in guards should prevent any mutable accesses from occurring... and therefore the mutable borrow need not be activated during the guard's execution, no?

nikomatsakis (Nov 28 2018 at 14:07, on Zulip):

@Matthew Jasper I don't quite follow why using 2PB is wrong for match guards though, particularly if we manage to move to the model @RalfJ is advocating. If I understand that means that a 2PB is equivalent to a &mut which is reborrowed to an &, right? (And the activation corresponds to the & going out of scope)

In that case, in the example:

match x { ref mut z if ...

I believe we would create a 2PB on z, so there would be an active mut borrow on x? (But one that is itself frozen)

I'm also not sure why it matters if there is one =)

nikomatsakis (Nov 28 2018 at 14:07, on Zulip):

(jinx)

RalfJ (Nov 28 2018 at 15:23, on Zulip):

If I understand that means that a 2PB is equivalent to a &mut which is reborrowed to an &, right? (And the activation corresponds to the & going out of scope)

Yes. Crucially, rewriting uses is not necessary, any read-only use of a variable that is a parent of the &mut can still be read from (this is unlike my original thoughts from months ago where I thought we'd also have to rewrite uses)

RalfJ (Nov 28 2018 at 15:24, on Zulip):

the problem with the current version is that it creates a new borrow but keeps the old ones alive "in parallel", so we have a tree more than a stack. a tree might be sound, might even be closer to what one needs to model LLVM's noalias, but it is much more complicated -- and everything except for this quirk in 2PB can be explained by just a stack.

RalfJ (Nov 28 2018 at 15:28, on Zulip):

don't quite follow why using 2PB is wrong for match guards though, particularly if we manage to move to the model @RalfJ is advocating.

that doesn't seem possible though? the desugaring inherently relies on outstanding shared borrows remaining intact. the PR by @Matthew Jasper introduces a new kind of borrow that will not conflict and makes sure those all disappear before we execute anything, but that's quite a hack. it means giving a semantics to the MIR that the analysis actually happens on still requires trees. it solves miri's problem, but it doesn't solve the problem of giving a proper spec to MIR -- to all variants of MIR, including the one NLL runs on.

RalfJ (Nov 28 2018 at 15:28, on Zulip):

the transmute based desugaring was one of the first things we thought of, but 2PB seemed more elegant.

you clearly have different taste than I do. :D or maybe you weren't aware that the transmute is actually generally sound, not just in some special cases?

nikomatsakis (Nov 28 2018 at 15:41, on Zulip):

I am aware of that

RalfJ (Nov 28 2018 at 15:52, on Zulip):

kk

nikomatsakis (Nov 28 2018 at 16:16, on Zulip):

it means giving a semantics to the MIR that the analysis actually happens on still requires trees.

ok, so you're saying that if we:

and hence just avoid 2PB in this desugaring at all, we just don't have any particular problems.

I think I agree with that.

nikomatsakis (Nov 28 2018 at 16:16, on Zulip):

(Sound correct @RalfJ ?)

nikomatsakis (Nov 28 2018 at 16:26, on Zulip):

Yes. Crucially, rewriting uses is not necessary, any read-only use of a variable that is a parent of the &mut can still be read from (this is unlike my original thoughts from months ago where I thought we'd also have to rewrite uses)

But I definitely need to work this through in my head to fully understand.

Thinking about it a bit more, I think the way the code is treating a 2PB now is roughly like an "upgradable read lock". That is, the initial borrow is shared, and then it is "activated" into an &mut. (But this activation is not presently explicit in the MIR, though we always know what it is.)

This also feels coherent but it still feels prudent to be more conservative for now.

RalfJ (Nov 28 2018 at 16:30, on Zulip):

(Sound correct @RalfJ ?)

yes

RalfJ (Nov 28 2018 at 16:31, on Zulip):

Thinking about it a bit more, I think the way the code is treating a 2PB now is roughly like an "upgradable read lock". That is, the initial borrow is shared, and then it is "activated" into an &mut. (But this activation is not presently explicit in the MIR, though we always know what it is.)

Hm, I see. Yeah Stacked Borrows is not currently prepared for a borrow to change its "type" (unique/shared)

RalfJ (Nov 28 2018 at 16:32, on Zulip):

I think doing that would require tracking the relationship of shared references more precisely, which would probably also need a tree.

Matthew Jasper (Nov 28 2018 at 17:15, on Zulip):

I'm not saying 2PB is unsound for match guards. I just don't think that it should be: allowing match x { ref mut z if x == 1 => ... is unexpected.

RalfJ (Nov 28 2018 at 17:48, on Zulip):

x == z seems even more unexpected

RalfJ (Nov 28 2018 at 17:48, on Zulip):

that is "using" two mutable references to the same location

nikomatsakis (Nov 28 2018 at 18:36, on Zulip):

my sense is we won't be able to change this for the initial Rust 2018 release

nikomatsakis (Nov 28 2018 at 18:36, on Zulip):

but we might be able to make changes here for the follow-on release, presuming low impact (as seems likely)

nikomatsakis (Nov 28 2018 at 18:36, on Zulip):

the clock is just running down

Matthew Jasper (Nov 28 2018 at 18:49, on Zulip):

That's probably the case.

RalfJ (Nov 28 2018 at 20:29, on Zulip):

if only I had been two weeks faster :(

nikomatsakis (Nov 28 2018 at 22:03, on Zulip):

something that occurs to me, @pnkfelix @Matthew Jasper @RalfJ, is that if we don't backport -- and I'm still not that keen on doing so because of the risk factor --

nikomatsakis (Nov 28 2018 at 22:03, on Zulip):

we still have NLL-sound things to land anyway

nikomatsakis (Nov 28 2018 at 22:03, on Zulip):

we can make a warning period here if we have to

nikomatsakis (Nov 28 2018 at 22:04, on Zulip):

there is already a kind of "NLL transition" taking place

nikomatsakis (Nov 28 2018 at 22:04, on Zulip):

and it feels like we can fit this into that

nikomatsakis (Nov 28 2018 at 22:04, on Zulip):

it's a bit different, of course, but so what

RalfJ (Nov 28 2018 at 22:04, on Zulip):

we still have NLL-sound things to land anyway

what does that means? soundness holes in NLL that must be fixed?

nikomatsakis (Nov 28 2018 at 22:04, on Zulip):

yes, there are some known soundness holes

nikomatsakis (Nov 28 2018 at 22:05, on Zulip):

mostly around user-supplied type annotations though

nikomatsakis (Nov 28 2018 at 22:05, on Zulip):

things judged to be pretty edge-casey

nikomatsakis (Nov 28 2018 at 22:05, on Zulip):

link to conversation with @Pietro Albini on discord

nikomatsakis (Nov 28 2018 at 22:06, on Zulip):

Personally, I'm leaning against backporting

nikomatsakis (Nov 28 2018 at 22:15, on Zulip):

@Matthew Jasper looking at the code in question from the new error:

        let mut next = || {
            let val = llvm::get_param(bx.llfn(), *idx as c_uint);
            *idx += 1;
            val
        };
        match self.mode {
            PassMode::Ignore => {},
            PassMode::Pair(..) => {
                OperandValue::Pair(next(), next()).store(bx, dst);
            }
            PassMode::Indirect(_, Some(_)) => {
                OperandValue::Ref(next(), Some(next()), self.layout.align.abi).store(bx, dst);
            }
            PassMode::Direct(_) | PassMode::Indirect(_, None) | PassMode::Cast(_) => {
                self.store(bx, next(), dst);
            }
        }
nikomatsakis (Nov 28 2018 at 22:15, on Zulip):

with the error:

error[E0502]: cannot borrow `*bx` as mutable because it is also borrowed as immutable_codegen_llvm
   --> src/librustc_codegen_llvm/abi.rs:279:28
    |
265 |         let mut next = || {
    |                        -- immutable borrow occurs here
266 |             let val = llvm::get_param(bx.llfn(), *idx as c_uint);
    |                                       -- first borrow occurs due to use of `bx` in closure
...
279 |                 self.store(bx, next(), dst);
    |                            ^^  ---- immutable borrow later used here
    |                            |
    |                            mutable borrow occurs here

error: aborting due to previous error
nikomatsakis (Nov 28 2018 at 22:16, on Zulip):

is the bx here a 2pb?

Matthew Jasper (Nov 28 2018 at 22:16, on Zulip):

Yes

nikomatsakis (Nov 28 2018 at 22:16, on Zulip):

sorry, which one, the bx.llfn()?

nikomatsakis (Nov 28 2018 at 22:16, on Zulip):

the self.store(bx, ...)?

nikomatsakis (Nov 28 2018 at 22:16, on Zulip):

if the latter, I .. feel a bit surprised, because I thought we didn't do it in that scenario.

nikomatsakis (Nov 28 2018 at 22:16, on Zulip):

the former seems wrong because it is fn llfn(&self)

nikomatsakis (Nov 28 2018 at 22:17, on Zulip):

I guess we extended to fn arguments?

nikomatsakis (Nov 28 2018 at 22:18, on Zulip):

I see, we do. Well... I was wrong about that

nikomatsakis (Nov 28 2018 at 22:18, on Zulip):

interesting

nikomatsakis (Nov 28 2018 at 22:18, on Zulip):

(I thought we did only method receivers and binary operators)

nikomatsakis (Nov 28 2018 at 22:19, on Zulip):

( ok, we definitely have to prioritize also writing up a complete NLL document :) which has been on my mind for some time anyway... but the match desugaring discussion and now this brought it to the fore again )

Matthew Jasper (Nov 28 2018 at 22:28, on Zulip):

OK, I'll try to write up the current state and our long-term options tomorrow, since we should have time to implement them.

nikomatsakis (Nov 28 2018 at 22:29, on Zulip):

Well, consensus from the release discussion was that a backport is off the table, which gives us a bit more time to settle on the right fix. I feel good about that.

Ariel Ben-Yehuda (Nov 28 2018 at 22:30, on Zulip):

sure doing a backport to the Rust 2018 release doesn't look like the best idea

Ariel Ben-Yehuda (Nov 28 2018 at 22:30, on Zulip):

we'll probably have to release a patch edition anyway

Ariel Ben-Yehuda (Nov 28 2018 at 22:31, on Zulip):

for NLL

nikomatsakis (Nov 28 2018 at 22:34, on Zulip):

I wouldn't be surprised, for one reason or another, anyway

Matthew Jasper (Dec 01 2018 at 14:31, on Zulip):

Some notes on two-phase borrows and some potential changes: https://gist.github.com/matthewjasper/1e48c300e96aaf69e9f56a2d9f969c87

nikomatsakis (Dec 19 2018 at 10:28, on Zulip):

@pnkfelix on the topic of implementing 2PB via rewriting, I see that this example compiles:

fn main() {
    let mut x = &mut vec![];
    let mut y = vec![];
    x.push((
        {
            if false { x = &mut y };
            22
        },
        x.len(),
    ));
}

though I suspect that this one doesn't occur a lot in the wild =)

nikomatsakis (Dec 19 2018 at 10:28, on Zulip):

(afk now)

pnkfelix (Dec 19 2018 at 11:09, on Zulip):

hmm. look at this: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2015&gist=b997316b973891850c7d457ac79b3ac5

pnkfelix (Dec 19 2018 at 11:10, on Zulip):

oh wait, duh

pnkfelix (Dec 19 2018 at 11:10, on Zulip):

that isn't surprising at all

pnkfelix (Dec 19 2018 at 11:12, on Zulip):

(had to lift the value being borrowed into a separate nameable thing to observe the effect, this way)

Last update: Nov 21 2019 at 13:05UTC