Closed
Description
#![feature(generators, generator_trait)]
use std::ops::Generator;
struct Foo([u8; 1024]);
impl Drop for Foo {
fn drop(&mut self) {}
}
fn simple() -> impl Generator<Yield = (), Return = ()> {
static || {
let first = Foo([0; 1024]);
let _second = first;
yield;
}
}
fn complex() -> impl Generator<Yield = (), Return = ()> {
static || {
let first = Foo([0; 1024]);
{ foo(); fn foo() {} }
let _second = first;
yield;
}
}
fn main() {
dbg!(std::mem::size_of_val(&simple()));
dbg!(std::mem::size_of_val(&complex()));
}
The two generators returned by simple
and complex
should be equivalent, but complex
takes twice as much space:
[foo.rs:29] std::mem::size_of_val(&simple()) = 1028
[foo.rs:30] std::mem::size_of_val(&complex()) = 2056
Dumping out the MIR (with rustc 1.34.0-nightly (f66e4697a 2019-02-20)
) shows an issue with how unwinding from foo
interacts with the two stack slots for first
and _second
, using a dynamic drop flag means that first
is "live" through the path that goes through the yield, even though the drop flag is guaranteed to be false. (The below graph shows the basic blocks, with the psuedo-code run in them and which variables are alive when exiting the block):