-
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
[MIR] double drop with slice patterns #34708
Comments
Should this go on the mir milestone? |
|
Fixing the issue for arrays would be quite easy. The primary problem is that you can move out of slices #![feature(slice_patterns, advanced_slice_patterns, box_syntax, box_patterns)]
fn cond() -> bool { false }
fn make() -> Box<[Box<[Box<[String]>]>]> {
box [box [box ["hello".to_string(), "world".to_string()]]]
}
fn main() {
let b3 = make();
match (b3, cond()) {
(box [box [box [s, ..], ..], ..], true) => {
println!("{}", s);
}
(box [.., box [.., box [.., s]]], _) => {
println!("{}", s);
}
_ => {}
}
} Here we must drop only "hello" in the first branch, and only "world" in the second branch, but I do not see any reliable way of doing it. |
This is not blocking the move to MIR, as @arielb1 notes. |
Still an issue in |
Note that since #36353 prohibits moves out of slices, we could presumably fix things for arrays alone at this point without too much trouble. |
This is the last thing that is stopping slice patterns from being stabilized, so I think we should go forward with it. I would like to mentor this issue so that I will not be the sole maintainer of the drop elaboration code. Mentoring NotesThe issue is that drop elaboration does not handle arrays correctly. BackgroundDuring MIR generation, #![feature(slice_patterns)]
struct NoisyDrop;
impl Drop for NoisyDrop {
fn drop(&mut self) { println!("splat!"); }
}
fn main() {
let [x] = [NoisyDrop];
} generates the following MIR:
To ensure that locals that have already been moved aren't dropped again, Rust uses flag-based dynamic drop. The compiler tracks which locals and parts of locals have been moved out, so a This is done by drop elaboration, which is implemented in rustc_mir::util::elaborate_drops (the codegen part for each individual drop), rustc_mir::transform::elaborate_drops (the transform sequence), and rustc_mir::dataflow (the analysis). Drop elaboration uses local variable "drop flags" to track the initialization state of each local fragment (called a "move path"), and rewrites each MIR drop into code that checks these drop flags and only drops what is initialized. The move path analysis is currently not implemented for arrays, so array fragments are currently shortcutted. This causes the double-drop - moves out of array elements are ignored when checking drop flags. Implementation notesSo, in order to remove the shortcut, we have to implement move paths for arrays. The code for handling move paths at rustc_mir::dataflow::move_paths::builder needs to be adapted, and the code at open_drop_for_array needs to be changed to consume the new move paths and only drop what is initialized (see e.g. the code for I think that a first implementation that works like structs, and creates a field for every array index, would work. Before we stabilize slice patterns, I would like to modify the implementation to consolidate ranges of indexes and generate only a single entry for them. Afterwards, I would like to see a good amount of tests in the dynamic-drop test using the runner there (see the existing tests - this tests that drops are handled correctly, including in panic paths), including tests for patterns matching from both ends with potential overlap - e.g. if maybe {
let [_, ..b] = array;
} else {
let [..b, _, _x] = array;
} The drop elaboration code is subtle code that can easily cause miscompilations, so the more tests, the better (if you find something that looks suspicious, adding a test and potentionally a bugfix would be very cool). Bonus Points - Slice PatternsI'll note that move paths off slices, which can have several lengths, aren't supported, because I have no good idea of how to track drop flags for something like this (you can match slices for both ends, and for small lengths you might have overlap): fn foo(x: Box<[Box<[Box<u32>]>]>) {
match rand() {
0 => match x {
box [box [x, ..], ..] => use(x)
},
1 => match x {
box [.., box [x, ..]] => use(x)
},
2 => match x {
box [_, box [_, x, ..], ..] => use(x)
}
// etc.
}
} Because I expect this sort of thing to be rare, for bonus points I would like it if you find some "brute-force" way of handling this that is exponential at these ugly cases but linear in sane situations. |
triage: P-medium @rust-lang/lang team agrees that when this is done we could consider stabilizing slice patterns. |
…omatsakis add transform for uniform array move out reworked second step for fix #34708 previous try #46686 r? @nikomatsakis
Version
STR
Expected Result
NoisyDrop
gets dropped onceActual Result
NoisyDrop
is dropped twiceThe text was updated successfully, but these errors were encountered: