← board

Three separate places decide a NilPy local's class identity

The three

site what it does
PyNoteLocalType (~2820) records one assignment's RHS against a local, widening via PyWiden
PyCollectLocalsAST harvest loop (~16900) harvests the trial parse's syms, widening via PyWidenBinding
PyCollectModuleLocalsAST harvest loop (~17600) the same loop again, for module scope

They disagree in detail: two use PyWidenBinding, one uses PyWiden; the class identity rule now has to be written out three times.

Why this is worth a ticket

root-cause-over-microfix's counting test says two mechanisms is a smell and three is a design flaw, and this one has already cost a session. Fixing "a local reassigned across two unrelated classes keeps one static class" in the two harvest loops looked complete — the two-assignment repro passed — and was silently undone by PyNoteLocalType, which read the cleared RecCi as "no class known yet" and re-adopted one. It took a three-assignment repro and a PXXDBG=n.locals reading (o tk=22o tk=6 rec=0) to see it.

The Poly flag now papers over that by being sticky and checked in all three. That works, but the next rule added to local typing has the same three-way duplication waiting for it.

Shape of the fix

One PyBindLocalClass(ci, tk, rec) that owns widening, class identity and the Poly decision; the three sites call it. The two harvest loops differ only in which constraint table they walk, so they should reduce to a shared helper parameterised by that.

Watch for the fixpoint contract: the harvest loops report changed and re-run, so the helper must return "did anything move" rather than setting a global, and must be idempotent — the original bug here was a rule that oscillated and would have hung the fixpoint if Poly had not been made sticky.

Gate

make test-nilpy + self-host byte-identical, plus the tests named in the parent ticket — especially test_nilpy_rebind_across_unrelated_classes (whose third assignment is what catches a regression in exactly this area) and test_nilpy_with_name_reuse.