← board

bug: importing a unit whose class calls a sibling method corrupts the importer's name resolution

Symptom

Adding one more unit to a large program's uses clause makes the program fail to compile with undefined variable errors on the program's OWN class members (methods and fields) — members that resolve fine without that import. The error points at the importer; the cause is the imported unit.

Real case: the Eliah IDE (apps/ide/eliah/main.pas, ~12-unit graph: gtk3, controls, stdctrls, extctrls, graphics, forms, sysutils, buffer, runner, docmodel, designer) builds fine. Add , lfmload (the garin .lfm box-loader) to uses and nothing else, and it fails:

pascal26:198: error: undefined variable (OnPlaceToggle)   { a THandler method }

Remove the lfm seeding and the import again → builds. lfmload itself compiles and passes its gate clean (bochan, 31/31) — the defect is purely in the combination large-importer + this-unit.

What actually triggers it (reduced)

The imported unit only needs a class with a method that calls another method of the same class — by any call syntax:

unit ubad6;
interface
type TThing = class n: Integer; procedure Reset; procedure Go; end;
implementation
procedure TThing.Reset; begin n := 0; end;
procedure TThing.Go; begin Reset; end;      { bare; Reset() and Self.Reset behave identically }
end.

Importing ubad6 into the Eliah program (good_main.pas = the committed apps/ide/eliah/main.pas) reproduces:

pascal26:185: error: undefined variable (PlaceMode)   { a THandler FIELD }

Variants tested (all against the Eliah importer):

So the trigger is the presence of an intra-class method reference in the imported unit, not its call syntax.

Capacity-dependent

A small importer does not reproduce, no matter the call form:

Only the full Eliah unit graph tips over. This points at a fixed-size table (symbol / method-ref / relocation / fixup) that the large graph nearly fills; the extra intra-class reference from the imported unit overflows it, and the overflow corrupts later symbol resolution — surfacing as "undefined variable" on whatever member the importer references next.

Expected

Importing a unit must never change how the importer resolves its own class members. Either grow/guard the table, or fix the indexing so an imported unit's method-refs don't alias the importer's symbol slots.

Repro (in-tree)

  1. git show <eliah-with-editable+palette>:apps/ide/eliah/main.pas > /tmp/good_main.pas
  2. create ubad6.pas (above) in /tmp
  3. sed -i 's/, designer;/, designer, ubad6;/' /tmp/good_main.pas
  4. stable_linux_amd64/default/pinned -Fulib/rtl -Fulib/pcl -Fuapps/ide/garin -Fuapps/ide/eliah -Fu/tmp /tmp/good_main.pas /tmp/xundefined variable (PlaceMode). Drop ubad6 from uses → builds.

Track B impact / parking

apps/ide/garin/lfmload.pas + its bochan gate (31/31) land now (garin core, no GUI graph — under the limit). Wiring it into Eliah (LoadLfmText seeding the designer docmodel) is parked: Eliah keeps its hardcoded sample form until this is fixed. No app-logic workaround applied — the integration is simply blocked.

Resolution

Fixed 5b38502 (Track A). Root cause: MAX_UMETH = 256 (method slots across all user classes) overflowed by the 12-unit Eliah graph + ubad6's intra-class method ref. AddUMeth had NO bounds check → silent corruption of adjacent globals → "undefined variable" on the importer's own members. Grew table to 8192 and added Error guards to AddUMeth + AddUClass (latter also unchecked). Front-end-only, self-host byte-identical, full make test green.

Track B verification 2026-06-23 — still reproduces against pinned v39

Marked done (fixed in compiler source), but the fix is NOT in the current pin: re-tested apps/ide/eliah/main.pas + uses lfmload against stable_linux_amd64/default/pinned (929fa70) and it still fails: pascal26:194: error: undefined variable (OnPlaceToggle). So wiring LoadLfmText into Eliah stays parked on the Track B side until a re-pin carries the fix (same shape as chore-repin: source fixed, pin lags). No code change here.