Description
Came across this while working on inlining enum constructors but I could reproduce on master as well:
#![allow(dead_code)]
struct Foo {
a: int
}
impl Drop for Foo {
fn drop(&mut self) {
println!("Dropping Foo: {}", self.a);
}
}
struct Bar {
x: Foo,
y: int
}
fn foo() -> Bar {
return Bar {
x: Foo { a: 23 },
y: return Bar {
x: Foo { a: 32 },
y: 0
}
};
}
fn main() {
unsafe {
std::mem::forget(foo());
}
}
When compiled and run this will print out:
Dropping Foo: 32
This is wrong since the Foo
we're actually dropping at that point (before returning in foo
) is the one with it's field set to 23. The problem is that we translate return expressions to write directly into the return pointer (either a hidden outpointer or a temporary alloca which we'll return from). So with the outer return we start writing out the struct but then we encounter another return in one of the fields and overwrite the previous result so now when we pass a pointer (which is just some offset into the return pointer) to the drop glue for what should be the initial Foo
we're actually calling drop on the second one.