← board

pylib.pas calls eight helpers before defining them, with no forward declaration

The hazard, and why it is not theoretical

pylib.pas is ~11k lines. A routine called from ABOVE its body, with no entry in the unit's top declaration block and no forward;, does not fail to compile — it links to a plausible wrong address ([[project_bodyless_procaddr_links_to_entry_minus_one]]).

Two things make that worse than a style issue:

  1. Such a call can pass its tests. Three sites added earlier the same night (pystr_translate, pystr_startswith_any, pystr_endswith_any calling PyVarText) were CPython-diffed, green, and committed. They were forward uses the whole time.
  2. An unrelated edit elsewhere can turn them into hard errors. Adding one forward declaration in the middle of the implementation section made PySliceBounds and PyVarText start failing to compile in routines nobody had touched. That is a build break with a cause nowhere near the symptom — the next person will spend the time re-deriving it that this ticket exists to save.

The eight, measured

Called before their definition, with no declaration anywhere:

helper defined at called from (examples)
PySliceBounds 6373 PyWindowStart (2127)
PyVarIsFloat 4770 pyvar_gt (4109)
PyVarAsFloat 4783 PyVarEq (3356)
PyVarText 5503 pyvar_gt, pyfloormod_v
PyIntOpOverflows 5588 pymul_v, pypow_v
PyPromoteIntArith pymul_v
PyFmtExp 9244 pypercent_format (8786)
PyFmtG 9302 pypercent_format

(The routines added this session are all properly forward;-declared — checked.)

Fix

Add a forward; declaration for each, in the unit's TOP declaration block, not beside the implementation — a declaration dropped mid-file is what disturbed resolution order in the first place. Mechanical, no behaviour change intended, and the whole point is that it stops being possible to link one of these to the wrong address.

Worth pairing with a CHECK

The audit that found these is a ~30-line script: parse ^function|procedure implementations, collect the top-block and forward; declarations, and report any call to a routine defined later that is in neither set. It has to handle MULTI-LINE signatures — the first ; is often inside the parameter list, which is what made a naive version report false positives. Worth handing to Track T as a lint, since the failure mode is silent by construction.

Gate

gate.sh quick plus make test-nilpy: the change is declarations only, so any behaviour difference at all is a bug in the change. Re-run the audit afterwards and expect zero.

DONE (2026-08-09, claude-AN)

All eight now carry a forward; declaration in the unit's TOP block. The audit re-run reports zero of them remaining.

Declarations only — no behaviour change intended, and none observed: gate.sh quick GREEN, and the str/format/set/dunder test families plus four realistic programs re-diffed against CPython unchanged.

Notes for the Track T lint, learned by writing it twice

The audit is ~30 lines but has two traps that make a naive version useless:

  1. Type CASTS look like calls. TPyList(o) matches \bname\s*\(, so the class names appear as "forward uses" of themselves. Filter them out, or match only against known routine names.
  2. Multi-line signatures. A declaration's first ; is usually INSIDE the parameter list (function f(a: TObject; …), so "does this line end in forward;" is wrong — the blob has to be accumulated to the real end of the signature. A version that got this wrong reported every correctly-declared helper as a violation.

Both false-positive classes are still in the checker's output as of this fix; the eight genuine findings were separated by hand. Worth fixing before it is wired into a tier, since a lint that cries wolf gets ignored.

Log