-
Notifications
You must be signed in to change notification settings - Fork 13k
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 / MIR-borrowck regression in getopts #48225
Comments
I'll need to investigate further, but I suspect that this may have the same root cause as #47680. In particular, this line: state = match (state, whitespace, limit) { seems to fit the pattern of 'borrow variable, manipulate borrow, assign manipulated borrow back to original' that appears in the many examples in the above issue. |
This minimized example seems to confirm it: (playground): #![feature(nll)]
enum State {
A
}
fn bar() {
let mut state = State::A;
let mut closure = || {
state = match state {
State::A => State::A,
};
};
while match state { State::A => false } {
closure();
}
}
fn main() {} Removing |
Hmm, I'm tagging this as NLL-deferred because #47680 is out of scope for the present borrow checker (tackling that is Polonius's job). However, it's interesting that this is a regression. My guess is that the old borrow checker was unsound here, but I'm not entirely sure. |
The minimized example from the playground works now? @Aaron1011 do you know off hand whether the problem in getopts is still arising today? And also, is it only exposed by With respect to #56754, marking as NLL-complete for now, as well as E-needs-test and P-medium. |
(also assigning to self to make the test and followup with @Aaron1011 ) |
@pnkfelix: Running |
Okay yes I can reproduce (atop getopts version 0.2.15, that is) |
Migrate mode saves us here w.r.t. the 2018 edition i.e. I see this output under migrate mode:
so this is not super super high priority to resolve. Just ... sort of high priority. Re-tagging as P-high. |
Its also worth revisiting @nikomatsakis 's note that the fact this compiled under AST-borrowck may represent unsoundness there. Its certainly suspicious that the closure bound to
|
ah I think the reduced test case compiles because it reduces the intervening "read" of If I put two variants into the #![feature(nll)]
enum State {
A,
B
}
fn bar() {
let mut state = State::A;
let state_ref = &mut state;
let mut closure = || {
*state_ref = match *state_ref {
_ => State::A,
};
};
while match state { State::A => true, State::B => false } {
closure();
}
}
fn main() {
bar();
} (and the compiler continues to flag an error even if you get rid of the |
@nikomatsakis do you actually think this is a duplicate of #47680 ? #47680 is a case where you have a factoring of an expression into a separate method. But there's no implicit captures from closures (I think) This (#48225) is, I think, describing a factoring into a closure that makes use of free variables rather than passing them anew. But I don't know if its reasonable to expect that to work...? In other words, this fails to compile under NLL (play): #![feature(nll)]
fn main() {
enum State { A, B }
let mut state = State::A;
let closure = || {
state = match state {
State::A => State::B,
State::B => State::A,
};
};
while match state { State::A => false, State::B => true } {
closure();
}
} but this variant below will compile and run fine, because instead of having a single mut borrow captured by the closure in a free variable, I now create free mutable borrows at each call (play): #![feature(nll)]
fn main() {
enum State { A, B }
let mut state = State::A;
let closure = |s: &mut _| {
*s = match *s {
State::A => State::B,
State::B => State::A,
};
};
while match state { State::A => false, State::B => true } {
closure(&mut state);
}
} |
nominating for discussion at next NLL meeting, in case @nikomatsakis and/or @rust-lang/wg-compiler-nll doesn't get around to resolving the question I posed in the previous comment before then. |
@pnkfelix Indeed, I do not see the "hallmark" of #47680, which I consider to be:
That said, the examples I see here look to me like they ought not to compile. Moreover, they don't compile, even in 2015 Rust. But this example does compile in 2015 but not 2018: (you also posted it) enum State {
A,
B
}
fn bar() {
let mut state = State::A;
let state_ref = &mut state;
let mut closure = || {
*state_ref = match *state_ref {
_ => State::A,
};
};
while match state { State::A => true, State::B => false } {
closure();
}
}
fn main() {
bar();
} This seems like a bug in 2015 Rust, though. |
I'm taking over this issue, but I think at this point it looks like it should just be closed as "no bug here". |
Nominating for discussion in the next NLL meeting. I think we likely should just close this. |
We discussed this in the NLL meeting today and decided everything was working as expected. Closing. |
Compiling
getopts
version0.2.15
withcargo build
succeeds. However, compiling the same version withRUSTFLAGS='-Z borrowck=mir -Z nll -Z two-phase-borrows' cargo build
fails with the following errors:The text was updated successfully, but these errors were encountered: