-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
NLL: Limit two-phase borrows to autoref-introduced borrows #47489
Conversation
(rust_highfive has picked a reviewer for you, use r? to override) |
(Incidentally, I keep toying with the idea of adding an opt-in way to write MIR directly, something analogous to how asm.js lets one write low-level operations using Javascript syntax.) |
☔ The latest upstream changes (presumably #47209) made this pull request unmergeable. Please resolve the merge conflicts. |
src/librustc_mir/hair/cx/expr.rs
Outdated
@@ -111,7 +112,7 @@ fn apply_adjustment<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, | |||
span, | |||
kind: ExprKind::Borrow { | |||
region: deref.region, | |||
borrow_kind: to_borrow_kind(deref.mutbl), | |||
borrow_kind: to_borrow_kind(deref.mutbl, true), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I expected us to push these flags even further back, to type check. I'm a bit wary that we may introduce adjustments for reasons that are not "autoref-autoderef in conjunction with method calls".
For example, does this type-check?
fn foo(x: &mut u32, y: u32) {
*x += y;
}
fn bar(x: &mut u32) {
foo(x, *x)
}
I think it might, because we get an implicit &mut *x
on the call, and that might be treated as two-phase. Similarly let x: &mut u32 = x
or something like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Actually, I suppose we could conceivably thread a bit of state down during HAIR lowering to indicate whether the expression being coerced is in receiver position.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will figure out how to tighten the restriction. (As you have pointed out there may be different paths to getting there.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Also that example you have does leak through in the current state of this PR, so I will also add it as a compile-fail test)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Though of course the example you give is sound; its just code that might surprise some if it passes, right? And anyway I agree that for our first deployment we should reject it)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, just surprising
src/librustc/ich/impls_mir.rs
Outdated
@@ -20,7 +20,8 @@ use std::mem; | |||
impl_stable_hash_for!(struct mir::GeneratorLayout<'tcx> { fields }); | |||
impl_stable_hash_for!(struct mir::SourceInfo { span, scope }); | |||
impl_stable_hash_for!(enum mir::Mutability { Mut, Not }); | |||
impl_stable_hash_for!(enum mir::BorrowKind { Shared, Unique, Mut }); | |||
impl_stable_hash_for!(struct mir::BorrowKind { mut_kind, introduced_via_autoref }); | |||
impl_stable_hash_for!(enum mir::BorrowMutKind { Shared, Unique, Mut }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bikeshed: name looks confusing, I would prefer BorrowMutability
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Hey look who it is!)
will do!
src/librustc/ich/impls_mir.rs
Outdated
@@ -20,7 +20,8 @@ use std::mem; | |||
impl_stable_hash_for!(struct mir::GeneratorLayout<'tcx> { fields }); | |||
impl_stable_hash_for!(struct mir::SourceInfo { span, scope }); | |||
impl_stable_hash_for!(enum mir::Mutability { Mut, Not }); | |||
impl_stable_hash_for!(enum mir::BorrowKind { Shared, Unique, Mut }); | |||
impl_stable_hash_for!(struct mir::BorrowKind { mut_kind, introduced_via_autoref }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bikeshed: I would prefer the "operational" always_allow_2phi
to the descriptive introduced_via_autoref
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please call it "two phase", but yes that's a good point. Operational is often better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay, this distinction doesn't matter to me, so I'll make this change.
(sorry this is taking so long; I keep hitting #47381 and so that is slowing my progress |
Marking as blocked by #47381. |
(I'm hoping that now that I've rebased atop a |
@shepmaster I assume that you switched it from S-waiting-on-author to S-blocked because of #47381 ? Just wanted to confirm, as after rebasing atop newer master, that bug is no longer blocking me. (And thus I am now switching it back to S-waiting-on-author) |
That's right! |
5154cc0
to
0c108b1
Compare
@pnkfelix at first glance, changes look good but appveyor won't run because of the merge conflict |
0c108b1
to
a9af49e
Compare
src/librustc_mir/hair/cx/expr.rs
Outdated
match *self { | ||
AutoBorrowMutability::Mutable { allow_two_phase_borrows } => | ||
BorrowKind { mut_kind: BorrowMutability::Mut, | ||
// FIXME ugh typo/inconsisten pluralization |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
heh I guess I should address this FIXME now that I have this working again
src/librustc_typeck/check/mod.rs
Outdated
// another kind of call"; perhaps it would | ||
// be more consistent to allow two-phase | ||
// borrows for .index() receivers here. | ||
allow_two_phase_borrows: false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I think the test illustrates the kind of code that is being filtered out by not allowing two-phase borrows for index operators... I'll leave another comment with my thoughts on that test itself.)
double_access(&mut a, &a); | ||
//[lxl]~^ ERROR cannot borrow `a` as immutable because it is also borrowed as mutable [E0502] | ||
//[nll]~^^ ERROR cannot borrow `a` as immutable because it is also borrowed as mutable [E0502] | ||
//[g2p]~^^^ ERROR cannot borrow `a` as immutable because it is also borrowed as mutable [E0502] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Important: even the generalized form tested by g2p
/-Z two-phase-beyond-autoref
(thus no longer connected to autoref) is still rejecting this example. Compare that against deref_coercion
above...
|
||
fn coerce_index_op() { | ||
let mut i = I(10); | ||
i[i[3]] = 4; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so the generalized (non-autoref-connected) g2p
form starts allowing things like i[i[3]] = ...;
I don't know about other people, but I think ... that seems okay to allow...? (As in, it seems like a reasonable extension to consider, if not in the immediate release of two-phase borrows, then at some point in the future?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It works for normal slices, so it seems ok to me.
028ca2b
to
5c8ff83
Compare
(skimming over the commit series, I realize now that some of the last minute refactorings I did yesterday brought the code into a state where I could get the diff much smaller. I'll take care of that now.) |
…o autoref. This is foundation for issue 46747 (limit two-phase borrows to method-call autorefs).
Added `-Z two-phase-beyond-autoref` to bring back old behavior (mainly to allow demonstration of desugared examples). Updated tests to use aforementioned flag when necessary. (But in each case where I added the flag, I made sure to also include a revision without the flag so that one can readily see what the actual behavior we expect is for the initial deployment of NLL.)
…se borrow info too. Namely, the mutable borrows also carries a flag indicating whether they should support two-phase borrows. This allows us to thread down, from the point of the borrow's introduction, whether the particular adjustment that created it is one that yields two-phase mutable borrows.
5c8ff83
to
b55cd8c
Compare
@nikomatsakis okay I am done polishing; you should be safe to do a review without me |
@bors r+ |
📌 Commit b55cd8c has been approved by |
NLL: Limit two-phase borrows to autoref-introduced borrows This imposes a restriction on two-phase borrows so that it only applies to autoref-introduced borrows. The goal is to ensure that our initial deployment of two-phase borrows is very conservative. We want it to still cover the `v.push(v.len());` example, but we do not want it to cover cases like `let imm = &v; let mu = &mut v; mu.push(imm.len());` (Why do we want it to be conservative? Because when you are not conservative, then the results you get, at least with the current analysis, are tightly coupled to details of the MIR construction that we would rather remain invisible to the end user.) Fix #46747 I decided, for this PR, to add a debug-flag `-Z two-phase-beyond-autoref`, to re-enable the more general approach. But my intention here is *not* that we would eventually turn on that debugflag by default; the main reason I added it was that I thought it was useful for writing tests to be able to write source that looks like desugared MIR.
☀️ Test successful - status-appveyor, status-travis |
This imposes a restriction on two-phase borrows so that it only applies to autoref-introduced borrows.
The goal is to ensure that our initial deployment of two-phase borrows is very conservative. We want it to still cover the
v.push(v.len());
example, but we do not want it to cover cases likelet imm = &v; let mu = &mut v; mu.push(imm.len());
(Why do we want it to be conservative? Because when you are not conservative, then the results you get, at least with the current analysis, are tightly coupled to details of the MIR construction that we would rather remain invisible to the end user.)
Fix #46747
I decided, for this PR, to add a debug-flag
-Z two-phase-beyond-autoref
, to re-enable the more general approach. But my intention here is not that we would eventually turn on that debugflag by default; the main reason I added it was that I thought it was useful for writing tests to be able to write source that looks like desugared MIR.