#![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):

The two generators returned by
simpleandcomplexshould be equivalent, butcomplextakes twice as much space:Dumping out the MIR (with
rustc 1.34.0-nightly (f66e4697a 2019-02-20)) shows an issue with how unwinding fromfoointeracts with the two stack slots forfirstand_second, using a dynamic drop flag means thatfirstis "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):