Part of #9. Phase 3 — native dataflow. Contract: references/dataflow-graphs.md; method:
references/dataflow-construction.md (work the stages in order; each gate before the next).
Learning goals
THE Rust graph-ownership lesson (TRPL Smart Pointers): you cannot write a doubly-linked graph
with plain references. Choose eyes-open between an index-based arena (Vec<Node> + NodeId(u32)
— recommended; fast, serde-friendly, teaches you why rustc itself does this) and Rc<RefCell<_>>
(teaches interior mutability and why you'll regret it at scale). Also: the ? operator seen from
BELOW — you'll now lower the sugar you've been writing.
Task
Per callable, a statement-level CFG: synthetic ENTRY/EXIT, multi-exit normalized (every
return/panic/fall-off gets an EXIT edge), edge kinds true/false/loop_back/switch_case/exception.
The Rust lowering checklist — each construct gets a documented rule + a fixture exercising it:
? operator → implicit early-return branch (Ok-continue / Err-to-EXIT-or-handler);
match with guards (guard = extra branch node), or-patterns;
if/match/loop/blocks as EXPRESSIONS (values flow out of arms — affects issue 13);
loop/while/for, break-with-value, LABELED break/continue;
panic!/unwrap/expect → exception edge to EXIT (over-approximate: calls that may panic);
- return-position
! (never type) divergence;
- drop order at scope exit — Rust's
defer analog: document the rule even if the MVP models
drops as implicit end-of-scope nodes only;
- closures → separate CFG per closure (capture is a stage-3 concern);
.await → decide the MVP
stance (sync-body is acceptable; record it).
Emission comes in issue 16 — for now the CFG lives in memory + tests.
Gate (CFG gate)
- Every node maps to a real source span; single ENTRY/EXIT; every node reachable from ENTRY and
reaching EXIT; node count stable across two runs.
- Each checklist construct produces its documented edges — exact expected node/edge sets per
fixture snippet, hand-written in the tests.
Part of #9. Phase 3 — native dataflow. Contract:
references/dataflow-graphs.md; method:references/dataflow-construction.md(work the stages in order; each gate before the next).Learning goals
THE Rust graph-ownership lesson (TRPL Smart Pointers): you cannot write a doubly-linked graph
with plain references. Choose eyes-open between an index-based arena (
Vec<Node>+NodeId(u32)— recommended; fast, serde-friendly, teaches you why rustc itself does this) and
Rc<RefCell<_>>(teaches interior mutability and why you'll regret it at scale). Also: the
?operator seen fromBELOW — you'll now lower the sugar you've been writing.
Task
Per callable, a statement-level CFG: synthetic ENTRY/EXIT, multi-exit normalized (every
return/panic/fall-off gets an EXIT edge), edge kinds
true/false/loop_back/switch_case/exception.The Rust lowering checklist — each construct gets a documented rule + a fixture exercising it:
?operator → implicit early-return branch (Ok-continue / Err-to-EXIT-or-handler);matchwith guards (guard = extra branch node), or-patterns;if/match/loop/blocks as EXPRESSIONS (values flow out of arms — affects issue 13);loop/while/for,break-with-value, LABELED break/continue;panic!/unwrap/expect→ exception edge to EXIT (over-approximate: calls that may panic);!(never type) divergence;deferanalog: document the rule even if the MVP modelsdrops as implicit end-of-scope nodes only;
.await→ decide the MVPstance (sync-body is acceptable; record it).
Emission comes in issue 16 — for now the CFG lives in memory + tests.
Gate (CFG gate)
reaching EXIT; node count stable across two runs.
fixture snippet, hand-written in the tests.