-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Drop is called more than once for the same object #16151
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
Comments
@zwarich reported this differs for him based on |
On my machine (OS X) the presence of
|
Discovered on rust @ 0582421. |
@dotdash Could this be caused by incorrect LLVM lifetime annotations? |
@brson The Rust we're using for the upgrade doesn't have the LLVM lifetime intrinsic changes. |
@metajack The example that differs for me depending on optimization settings was my further reduction, not the original: struct Fragment;
impl Drop for Fragment {
fn drop(&mut self) {
println!("drop");
}
}
fn main() {
let mut fragments = Vec::new();
fragments.push(Fragment);
fragments.push(Fragment);
fragments.move_iter()
.skip_while(|fragment| {
true
}).collect::<Vec<Fragment>>();
} |
@pnkfelix or @nick29581 do either of you have the bandwith to look into this? It's blocking servo. |
Adding some debug output, it looks like fragment 2 is dropped too early. use std::mem;
struct Fragment {
dummy: int
}
impl Fragment {
fn new(d: int) -> Fragment {
Fragment {
dummy: d,
}
}
}
impl Drop for Fragment {
fn drop(&mut self) {
println!("drop {}", self.dummy);
}
}
fn main() {
let mut fragments = vec!();
fragments.push(Fragment::new(1));
fragments.push(Fragment::new(2));
let new_fragments: Vec<Fragment> = mem::replace(&mut fragments, vec![])
.move_iter()
.skip_while(|fragment| {
println!("Skip {}", fragment.dummy);
true
}).collect();
std::io::println("End");
} Gives:
|
After reading through the LLVM IR, the issue is in Here's the source of fn next(&mut self) -> Option<A> {
let mut next = self.iter.next();
if self.flag {
next
} else {
loop {
match next {
Some(x) => {
if (self.predicate)(&x) {
next = self.iter.next();
continue
} else {
self.flag = true;
return Some(x)
}
}
None => return None
}
}
}
} In the match case I'm not quite sure what the expected sequence here is. In the I suppose a fix would be to actually move the value out of |
I strongly suspect this was caused by #15076, which removed the extra alloca slot that was used to move the value out of the llmatch. |
cc @luqmana |
Yeah, that makes sense, as that is the only place that does zero-after-drop, while explains the "Skip 0" in the debugging output. |
So, should we be trying to revert #15076 locally and everything that depends on it? Or is there a potential fix? |
I suspect it can be fixed by extending the There's probably some other fix that could be made that doesn't require the extra alloca, by changing when destructors are called, but that's probably complicated and may run the risk of being incorrect. |
No, that's not enough. If you use put |
Perhaps we should make allocas if you're matching and destructuring an lvalue which is mutated inside the match? Expr use visitor and mem cat should be able to tell you this. |
Perhaps borrow check or a separate pass could build up a table to tell codegen when this occurs. |
@larsbergstrom I suggest you work around it by just not using skip while. |
It occurs to me that the set of types that don't implement |
Yes, which is why I think we should not fix this with a sledgehammer. This is an important optimization which I want to keep. I believe this problem is specific to mutation of the matched lvalue. Rust has precise information about that, so let's use it to guide optimizations. |
@dotdash I'm not sure what you mean about However, since that effectively kills the optimization for almost all types, I agree that it's not the right fix. Of course, disabling the optimization for all |
@kballard We're already reusing the alloca when it's a by value binding where the type would move. The problem here is reassigning to the expression you're matching on in the body of the match. @pcwalton I'm working on this and have it working for a small no_std test. Just need to get through this ice while building libstd. Also, this is essentially a dupe of #15571. |
@luqmana If you can track any mutation of the original expression, not just reassignment, then the |
@kballard Ah, damn, sorry. I forgot to fix that to say |
@dotdash Ah. Well, I think |
@pcwalton Ok, so I added a check using ExprUseVisitor to see if we reassign to the expr we're matching on in the arm body and if so then use a new alloca slot. Here's the diff: https://gist.github.com/76a232453cec85c1c861 Now this works for my no_std test case: https://gist.github.com/37257dd0036a7fa20085
But this ICE's while building libstd, since while looking up type_contents it encounters a ty_param whose |
It occurs to me that just tracking mutable borrows would not be sufficient (not that @luqmana is doing that, but it was a suggestion), because |
@luqmana You may have to call monomorphize_type somewhere. |
@kballard Yeah, we might need to look at Share kind |
@pcwalton Well, the whole point of the suggestion was to drop the |
…barsky internal: Add minimal support for the 2024 edition CC rust-lang#16146
Produces the following output:
The text was updated successfully, but these errors were encountered: