Background
PR #114 added Interpreter::assert_no_streams_in_env (debug builds only) which walks the template env at fork time and panics if it finds a Value::Stream. The walker correctly recurses into containers (Array/Tuple/Set/Map/Object/Result/Some/Frozen) but uses _ => {} to skip Value::Function and Value::Lambda bodies.
That comment in the code said "closure scopes are walked separately by deep_clone_isolated" -- but those two walks have different purposes. deep_clone_isolated walks for Arc-dedup and closure isolation; the Stream-detection walker is checking for a runtime invariant. A Stream value reachable only via a captured closure escapes the assert.
Example pattern:
let inner = (fn() {
let s = stream([1,2,3])
return fn() { s.next() }
})()
inner is a Lambda whose closure captures s: Value::Stream. Pre-PR-#114 this was an invisible footgun; post-PR it's still an invisible footgun because the assert doesn't descend into closure bodies.
What to add
Have the walker also descend into Value::Function::closure and Value::Lambda::closure (locking the Lambda's Arc and walking its scopes), with a scope_map-style cycle break to avoid re-walking shared scopes.
~30 lines, one extra micro-allocation per fork.
Origin: rust-expert review on PR #114, follow-up #1.
Background
PR #114 added
Interpreter::assert_no_streams_in_env(debug builds only) which walks the template env at fork time and panics if it finds aValue::Stream. The walker correctly recurses into containers (Array/Tuple/Set/Map/Object/Result/Some/Frozen) but uses_ => {}to skipValue::FunctionandValue::Lambdabodies.That comment in the code said "closure scopes are walked separately by
deep_clone_isolated" -- but those two walks have different purposes.deep_clone_isolatedwalks for Arc-dedup and closure isolation; the Stream-detection walker is checking for a runtime invariant. A Stream value reachable only via a captured closure escapes the assert.Example pattern:
inneris a Lambda whose closure capturess: Value::Stream. Pre-PR-#114 this was an invisible footgun; post-PR it's still an invisible footgun because the assert doesn't descend into closure bodies.What to add
Have the walker also descend into
Value::Function::closureandValue::Lambda::closure(locking the Lambda'sArcand walking its scopes), with ascope_map-style cycle break to avoid re-walking shared scopes.~30 lines, one extra micro-allocation per fork.
Origin: rust-expert review on PR #114, follow-up #1.