Description
We should experiment with "re-allocating" storage for a local after it's moved from if it gets re-initialized. This would enable more optimizations, but could have some potential fallout.
EDIT: See this comment for further explanation of what kinds of optimizations this would enable.
Quoth @RalfJung, from #59123 (comment):
Currently, the following (entirely safe) code will definitely return
true
:let mut x = String::new(); let addr_x = &x as *const _ as usize; drop(x); // later x = String::new(); let addr_x2 = &x as *const _ as usize; return addr_x == addr_x2;If we want to do optimizations like yours here (and I am totally sympathetic to that), we have to explain in the "Rust Abstract Machine" (and in Miri) why this program might return
false
. And the answer cannot be "there is UB", because this is safe code.This is a topic that @nikomatsakis, @eddyb and me have touched on several times already, without ever hashing out a full plan. But in the current state of affairs, the only mechanism we have to "defeat" pointer equality tests like the above is to make sure that this is not the same allocation any more.
So, one thing we might do is to do
StorageDead(x); StorageLive(x);
immediately after every move. This "re-allocates"x
and thus definitely kills any existing pointers and also "defeats" pointer comparisons. The immediateStorageLive
is to keep the liveness state in sync in both branches of a conditional (which might or might not be relevant -- unfortunately LLVM's semantics for these intrinsics is less than clear). I guess theStorageLive
could be moved down in cases where there is no merging control flow, which should give you your optimization in many cases.
It is possible to do a subset of the optimization discussed in #59123 without this, but this would help cover more cases, in this optimization and others.