Description
check_if_reassignment_to_immutable_state
has the of handling assignments to immutable local variables. These are a bit tricky: you are allowed to assign to an immutable local variable, but only if it has never been assigned to before. Actually, the logic implemented in the NLL borrow checker is kind of wrong (or at least inconsistent with the AST checker). It's also just slow.
Here is an example of the sort of assignments that should be accepted (playground):
#![feature(nll)]
fn main() {
let x: (u32, u32);
if true {
x = (1, 2);
} else {
x = (3, 4);
}
drop(x);
}
This example is rightly rejected (but we should check to ensure we have a test) (playground):
#![feature(nll)]
fn main() {
let x: (u32, u32);
x = (1, 2);
x = (3, 4);
drop(x);
}
This example should be rejected but currently is not (playground):
#![feature(nll)]
fn main() {
let x: (u32, u32);
x.0 = 22;
}
In addition to being slightly wrong, the current implementation is also slow. It iterates over all initialized paths and checks that the path being assigned to (e.g., x.0
in the third example) is disjoint from all of them:
rust/src/librustc_mir/borrow_check/mod.rs
Lines 1612 to 1619 in 52c785b
I will write below how I think it should work.
Activity
nikomatsakis commentedon Aug 8, 2018
I think what we should do:
x = ...
) from any other place.self.access_place
method withLocalMutationIsAllowed::No
(unlike now, where we useExceptUpvars
).nikomatsakis commentedon Aug 8, 2018
As a bonus, I think we could remove
ExceptUpvars
entirely. It is only used in two places -- the one above, and here:rust/src/librustc_mir/borrow_check/nll/invalidation.rs
Lines 358 to 372 in 52c785b
and both should now be just
No
.nikomatsakis commentedon Aug 8, 2018
@spastorino would like to take this on.
pnkfelix commentedon Aug 8, 2018
At some point I thought we had considered allowing a structs fields to be initialized independently .... don’t remember how far that went at the moment ....
nikomatsakis commentedon Aug 8, 2018
@pnkfelix
We were considering it, but we don't presently allow it. Even with NLL this does not compile, for example:
nikomatsakis commentedon Aug 8, 2018
It's true though that the existing borrow checker is sort of inconsistent here -- if you do
let mut x
, then it permitsx.0 = 1
andx.1 = 2
but rejects usingx
as a whole (just like NLL does).nikomatsakis commentedon Aug 8, 2018
I was suggesting that we match the existing behavior because it's simple to do — I am not opposed to being smarter later on though.
I guess to do this the "right" way we would do something like:
Sound about right @pnkfelix ?
memoryruins commentedon Aug 8, 2018
Following a similar table format from #53114:
let x: (u32, u32); x.0 = 1; x.1 = 2;
let mut x: (u32, u32); x.0 = 1; x.1 = 2;
println!("{:?}", x);
9 remaining items