Expand description
Per-body control-flow narrowing (Phase-6 Workstream 2).
A pure forward dataflow over a lowered Body (no engine API, no &dyn Db, no types — types
are layered by the checker in crate::infer consulting these facts). For each reachable
statement it records the FlowFacts that hold before it; the checker installs those as the
active narrowing environment, replacing the old lexical narrowing map. It also computes
reachability — statements after a return/break/continue (or where every branch diverges)
are dead, feeding UNREACHABLE_CODE (Workstream 1 owns the emission).
Soundness over precision (the 1.0 invariant). When unsure we widen (drop a fact), never
narrow wrongly: a join is an intersection, a reassignment / opaque call invalidates, a loop body
is entered with its assignments widened (no back-edge fixpoint). A wrong narrowing would hide a
real UNSAFE_* or assert an absent member — both worse than over-warning. The checker keeps the
load-bearing is_uninformative + widen-only gate when it consumes a fact (M1).
GDScript’s control flow is fully structured (reducible: the only non-local edges are
break/continue), so this recursive dataflow is equivalent to — and simpler + less
error-prone than — an explicit basic-block graph. It produces exactly the contract the checker
(M1) and the warning layer (M3 UNREACHABLE_*) consume: per-statement entry facts +
reachability.
Structs§
- Assigned
Analysis - The locals definitely assigned a value at each statement’s entry — the intersection over all incoming control-flow paths. A typed-no-initializer local read while absent here is a read-before-assign. The sound dual of narrowing: grow-only + intersect-at-merge, and simpler — a callee cannot assign a caller’s function-scoped local, so there is no opaque-call / aliasing / self-rooted handling (do not copy those arms from the narrowing analyzer).
- Flow
Analysis - The result of flowing a body: per-statement entry facts + the statements proven unreachable.
- Flow
Facts - The narrowing facts in force at a program point (a
Place → NarrowedTyenvironment).
Enums§
- Narrowed
Ty - A narrowing fact that holds at a program point (a place narrowed to a type or proven non-null).
The type-test variants carry an
AstPtrto theTypeRef, resolved lazily by the checker against the engine model — exactly like the oldapply_narrowingresolved itsptr. - Place
- A narrowable place: a local/param, or a (shallow) dotted access rooted at a local or
self. Deliberately shallow — we narrowx,x.y,self.ybut not arbitrary call results (f().y), array indices (a[i].y), or anything whose identity isn’t stable under re-evaluation. Shallowness is what keeps narrowing sound under mutation/aliasing (the 1.0 cut).
Functions§
- analyze
- Run the forward dataflow over a lowered body, producing per-statement entry facts + reachability.
- analyze_
assigned - Run definite-assignment over
body’s top-level statements (recursing intoif/for/while/matchblocks but not lambda bodies — those get a fresh scope and are left unchecked), seeded with the function’sparams(always assigned). - condition_
facts - The narrowing facts a condition establishes on its truthy (or falsy) edge — exposed so the
checker can apply
and/orshort-circuit narrowing within a condition expression (the RHS ofa and bis typed undera’s then-facts,a or b’s undera’s else-facts).