Synapse library — proper compile check (Track B)
- Type: feature / investigation (library compat target)
- Status: done
- Owner: — (Track B — libraries/RTL; uses
$(PXX_STABLE), never rebuilds the compiler) - Opened: 2026-06-22
- Note: was blocked on
feature-mimic-fpc; that landed 2026-06-22. Actionable. - Relation: the correctness/compat half of [[feature-networking]] (which names Synapse as compiler-compat target + test suite). Likely consumer of [[feature-mode-delphi-remaining]] (per-unit mode reset bites first in a multi-unit Synapse build) and RTL breadth. Companion driver to [[goal-compile-fpc-compiler]].
Why this is Track B
The dialect/compiler side of consuming Synapse is largely done (Track A):
{$mode delphi} core, dotted unit names, {$IF DECLARED}, directive-if-numeric.
What remains to actually compile Synapse is RTL availability and source
compat — Posix.* shim units, Classes/SysUtils surface, syncobjs, the
blocking socket face — i.e. library work built with the pinned stable compiler.
So the activity belongs to Track B; genuine compiler/language gaps it surfaces
get filed back as Track A tickets.
The task
Once --mimic-fpc lands (Track A), run a proper Synapse compile pass and
catalogue the blocker classes — this replaces the old ad-hoc directive-wall
probing with a real attempt:
- Drive with
test/manual/try_synapse_compile.shand theexternal/synapsesmoke units (start with the leaf units:synautil,synaip,synacode, thenblcksock). - Build with
$(PXX_STABLE)+--mimic-fpc. Do not rebuild the compiler. - For each failure, classify:
- RTL gap (missing/short unit or routine) → fix in
lib/rtl(our own from-scratch RTL, FPC naming — never port real FPC RTL; see the own-RTL strategy memory) or aPosix.*shim, and add amake lib-testsmoke. - Compiler/language gap → file a focused Track A ticket (e.g. pull a slice from [[feature-mode-delphi-remaining]]); do not work around it in the lib.
- RTL gap (missing/short unit or routine) → fix in
- Record the running blocker list + the furthest unit reached in [[feature-networking]] (or here), so progress is visible across sessions.
Expectations / known edges
- Per-unit
{$mode}reset is the prime first trap:DelphiModeis a whole-compile flag today, so a non-delphi unit pulled after a delphi Synapse unit inherits delphi semantics. If hit, pull that slice from [[feature-mode-delphi-remaining]] (Track A). - DNS is not libc-blocked — reuse
synadnsover UDP (see networking strategy). - Two-layer plan: our own transport (
net.pas/asyncnet.pas, already landed for IPv4 loopback) under Synapse's reused protocol units (HTTP/FTP/SMTP). - Mimic profile must stay opt-in;
lib/rtlstays{$ifdef FPC}-clean.
Done when
A defined Synapse subset (target: the blocking HTTP client path) compiles with
$(PXX_STABLE) --mimic-fpc and a smoke unit exercises it under make lib-test,
with every remaining gap either fixed in lib/rtl or filed as a Track A ticket.
Recon 2026-06-22 (first --mimic-fpc pass, leaf units)
Ran program p; uses <unit>; per leaf with --mimic-fpc (fresh compiler).
mimic acceptance MET: every failure is now missing-RTL or a concrete
compiler gap, NOT a directive/branch error — units get past jedi.inc into the
FPC path. First blocker per unit:
synautil,synaip,asn1util,synachar→usesunit not found:unixutil(RTL — Track B).synsock,blcksock→usesunit not found:dynlibs(RTL — Track B).synacode→ two Track A gaps, both since handled/filed:- capital
Arraykeyword (synacode.pas:344) → FIXED (b5c0252). - then
undefined variable (Move)(:359) —Move/FillCharSystem primitives absent (RTL — Track B; provide inlib/rtl, or as compiler builtins).
- capital
Track A spinoff filed + FIXED: [[bug-var-open-array-fixed-arg-length]] (a var array of T param got a wrong length from a static-array argument — var and
field, on synacode's ArrByteToLong/MD5 path); also
[[bug-static-array-length-direct]] (1-D).
Re-probe 2026-06-22 (after the Track A fixes) — Track A blockers CLEARED
Same leaf sweep with --mimic-fpc after the capital-array keyword fix
(b5c0252) and the var-open-array fixes. Every remaining first-blocker is now
RTL (no parse/codegen/dialect gap surfaces):
synautil/synaip/asn1util/synachar→usesunit not found:unixutil.synsock/blcksock/mimepart/mimemess/smtpsend/httpsend/ftpsend→usesunit not found:dynlibs.synacode→undefined variable (Move)(itsusesis satisfiable; stops on theMoveSystem primitive).FillCharis the sibling.
So the Track A (compiler) side of the Synapse path is, for the leaf set, done for now. The compiler no longer trips on Synapse dialect/parse/codegen.
Track B next (RTL breadth, the gating units):
unixutil— small POSIX util shim unit.dynlibs—LoadLibrary/GetProcAddress/UnloadLibraryover the PAL dynlib surface (PXX_HAS_DYNLIB).Move/FillChar— System memory primitives, auto-available withoutuses. Now unblocked for Track B as plain RTL functions: untypedvar/constparameters landed (Track A, [[feature-untyped-parameters]], aafd222), so these are writable inlib/rtldirectly:Move(const Source; var Dest; Count)+FillChar(var X; Count; Value: Byte)over@Source/@Dest. Caveats:Movemust be overlap-safe (memmove — copy backward whendst>srcand ranges overlap; the internalPXXMemMoveis forward-only/memcpy);FillCharneeds a byte fill (onlyPXXMemZeroexists). Auto-load so they resolve withoutuses.
Once these land, re-run and record the next class (expected: Classes/SysUtils
surface depth).
Scoping 2026-06-22 (Synapse source installed)
Ran tools/install_externals.sh (geby/synapse @ 9c590c1, shallow clone into the
gitignored external/synapse). Scoped the gating unixutil blocker to size the
RTL shims — it is a multi-unit chain, not one small unit:
synautil.pas:81FPC/Unix branch isuses UnixUtil, Unix, BaseUnix;— so "unixutil not found" is the first of three. Symbolssynautilactually references from that chain:TZSeconds(fromUnixUtil),gettimeofday/fpgettimeofday+TTimeVal(fromUnix/BaseUnix).synaip,asn1util,synacharpullunixutiltransitively viauses synautil.- So the minimal unblock for the four leaf units is a small
unixutil(justTZSeconds) plusUnix/BaseUnixshims providinggettimeofday/fpgettimeofday/TTimeVal(these can sit over PAL clock + raw syscalls). Thedynlibsset (synsock/blcksock/…) additionally needs a PAL dynlib surface — notePalHasDynlibreturns True on posix but noPalDlOpen/Sym/Closeprimitives exist yet, and a libc-freedlopenis a design decision (loader vs. link libc) — resolve that beforedynlibs.
This is a deep RTL-breadth cascade (BaseUnix/Classes/SysUtils depth follows), so it wants a dedicated push rather than opportunistic slices. The analysis above is the starting point.
Keystone 2026-06-22 — dynlibs gates the ENTIRE leaf path
Re-probing uses synautil on v38 (untyped params landed): first wall is still
unixutil, but synautil unconditionally uses ... SynaFpc, and SynaFpc
itself uses dynlibs (under {$IFDEF FPC}). So dynlibs is required even for
the "simple" leaf units (synautil/synaip/asn1util/synachar), not just
synsock/blcksock. SynaFpc needs only TLibHandle + LoadLibrary /
FreeLibrary / GetProcAddress / GetProcedureAddress / UnloadLibrary /
NilHandle — it wraps them; Synapse tolerates LoadLibrary returning the nil
handle (that path just means "optional lib, e.g. SSL, unavailable").
So an honest minimal dynlibs (FPC signatures; LoadLibrary -> NilHandle,
GetProcAddress -> nil until a real PAL loader exists) unblocks compilation and
the no-dynamic-lib (plain-HTTP, no-SSL) path. It is NOT a workaround for a
compiler bug — libc-free POSIX genuinely has no runtime loader; PalHasDynlib
returning True on posix is the actual inconsistency to fix (set it False, or add
real PalDlOpen over libc when a "link-libc" profile is chosen — a deliberate
design decision, file separately). Recommended order for the dedicated push:
dynlibs (stub) -> unixutil/Unix/BaseUnix -> verify SysUtils/Classes
depth. (Coordination: Move/FillChar are owned elsewhere on Track B; keep
dynlibs/the unix shims separate from them.)
Progress 2026-06-24 — dynlibs + unix shims LANDED; blocked on a directive bug
Track B RTL landed (all with make lib-test smokes):
lib/rtl/dynlibs.pas— honest stub (LoadLibrary->NilHandle,GetProcedureAddress->nil). UnblocksSynaFpcand thus every leaf unit'suses. Real loader split into [[feature-real-dynlib-loader]] (opt-in, syscall-first / libc-cheat policy). Carries a PChar-overload workaround for [[bug-pchar-to-string-implicit-conv]] (Track A) — remove when that lands.lib/rtl/baseunix.pas—timevalfamily +fpgettimeofdayover a real CLOCK_REALTIMEclock_gettimesyscall (native-width timespec per arch).lib/rtl/unix.pas—Tzseconds(0/UTC; TZif parse deferred).lib/rtl/unixutil.pas— presence-only.
With these the uses chain of synautil/synaip/asn1util/synachar fully
resolves. Furthest reached: synautil now passes the uses clause and stops
at a Track A compiler bug — spurious unterminated conditional directive on
synautil + jedi.inc (jedi alone OK, synautil sans jedi OK, together fail).
Filed urgent: [[bug-conditional-directive-miscount-synautil]]. No lib workaround
(Platonic).
Blocker stack now (leaf set):
- [[bug-conditional-directive-miscount-synautil]] (Track A, urgent) — gates synautil/synaip/asn1util/synachar at the directive phase.
- After that:
synafpc/synautilneedStrLCopy(and likely siblingstrings-unit routines) — RTL gap, Track B. Move/FillChar— owned separately on Track B.
Also open from earlier: [[bug-hex-char-code-literal]] (Track A, urgent) for
synacode's #$NN set constants.
Progress 2026-06-24 (cont.) — synafpc COMPILES; StrLCopy/Sleep landed
- [[bug-hex-char-code-literal]] FIXED by Track A (v50).
synacodenow parses past its#$NNset constants and stops only atMove/FillChar. - Added to
lib/rtl/sysutils.pas(FPCstrings/SysUtils surface):StrLCopy,StrLComp(synafpc'sSysUtils.StrLCopy/StrLCompwrappers — FPC path, not kylix/posix), andSleep(nanosleep syscall). Smoke:test/lib_strpchar. synafpcnow compiles fully (ok) — the keystone unit is unblocked.
Leaf-set re-probe (v50, --mimic-fpc):
| unit | state |
|---|---|
synafpc |
OK |
synautil/synaip/asn1util/synachar |
blocked on [[bug-conditional-directive-miscount-synautil]] (Track A) |
synacode |
needs Move/FillChar (RTL; intrinsic future = [[feature-move-fillchar-intrinsics]]) |
synsock/blcksock |
next gap: uses unit not found sockets (FPC Sockets unit — RTL, Track B) |
Spinoff bug filed while verifying the hex fix: [[bug-set-of-char-const-corrupts-char-codegen]]
(Track A, urgent) — a set of char typed constant corrupts Ord(char-var)
codegen; Synapse uses such constants, so it threatens correctness later.
Track B next (not blocked on Track A): Move/FillChar RTL, then the
sockets unit for synsock/blcksock.
Progress 2026-06-24 (cont. 2) — sysutils breadth; synacode hits {$R-} at emit
Added to lib/rtl/sysutils.pas (all smoked in test/lib_strpchar):
Move (overlap-safe/memmove) + FillChar (interim home — see
[[feature-move-fillchar-intrinsics]]; FPC's is System/no-uses), IntToHex,
StringOfChar. synacode resolves all of these now.
synacode new wall: {$R-} at emit — [[bug-r-directive-toggle-treated-as-resource]]
(Track A, urgent). The {$R} lexer reads the range-check toggle -/+ as a
resource filename; the error only fires at emit, so it was masked until synacode
started passing semantics. Latent since 2026-05-30, not a v50 regression.
Spinoff: const-string-index quirks folded into
[[bug-set-of-char-const-corrupts-char-codegen]] (untyped string const index →
garbage char, worked around in IntToHex; typed const s: string = ... → parse
error).
Track A urgent queue now (all block Synapse):
- [[bug-conditional-directive-miscount-synautil]] — synautil/synaip/asn1util/synachar.
- [[bug-r-directive-toggle-treated-as-resource]] — synacode (and the above, at emit).
- [[bug-set-of-char-const-corrupts-char-codegen]] — correctness.
Track B (not blocked): sockets unit → synsock/blcksock (+ our own net lib /
HTTP, async-aware — see the design note to come).
Re-probe 2026-06-24 (cont. 3) — sockets unit advanced synsock; new gaps
Leaf/socket/protocol sweep (v53, --mimic-fpc, all current RTL):
| unit | state |
|---|---|
synafpc |
OK |
synautil/synaip/asn1util/synachar |
[[bug-conditional-directive-miscount-synautil]] (Track A) |
synacode |
[[bug-r-directive-toggle-treated-as-resource]] at emit (Track A) |
synsock/blcksock/httpsend/ftpsend/smtpsend |
advanced past sockets (unit now exists) → next: termio (added, trivial), then [[bug-unit-qualified-constant-not-resolved]] (Track A) on ssfpc's FIONREAD = termio.FIONREAD; |
Track B added lib/rtl/termio.pas (FIONREAD/FIONBIO/FIOASYNC). After the
qualified-const bug clears, the next RTL gaps for the socket path are the
sockets address-string helpers (StrToNetAddr/NetAddrToStr/HostToNet/IPv6
variants) and a netdb shim (THostEntry/GetHostByName/ResolveName/+6/
TProtocolEntry/TServiceEntry, over lib/rtl/dns), then Classes depth
(TStream — itself Track-A-blocked). syncobjs already exists.
Synapse is now heavily Track-A-gated: directive-miscount, R-toggle, qualified-const (+ string-cmp / Read-Write-names / untyped-method-params for the Classes it needs). Track B shim work past synsock waits on the qualified-const fix.
Re-probe 2026-06-25 (v55) — {$R-} fixed; synacode COMPILES + partially runs
{$R-} fix (v55) + all the RTL/compiler fixes mean synacode now compiles
fully, joining synafpc. Leaf set:
| unit | state |
|---|---|
synafpc, synacode |
compile OK |
synautil/synaip/asn1util/synachar |
[[bug-conditional-directive-miscount-synautil]] (Track A, open) |
synsock/blcksock |
[[bug-unit-qualified-constant-not-resolved]] (termio.FIONREAD, Track A, open) |
synacode functional dogfood (--mimic-fpc): EncodeBase64('hello world') =
aGVsbG8gd29ybGQ= — correct. But DecodeBase64 returns garbage and MD5
segfaults at runtime — both index/process const lookup tables
(ReTableBase64, the MD5 state), so this is the const-table-index / managed-Move
runtime codegen family ([[bug-set-of-char-const-corrupts-char-codegen]] and
relatives). Compiling is necessary but not sufficient — synacode needs those
runtime codegen bugs fixed (Track A) for correct Base64-decode/MD5. A minimal
repro from synacode's Decode4to3Ex / MD5 path would sharpen the Track A ticket.
Two leaf units down (compile); the rest gated on directive-miscount / qualified-const, and full synacode correctness on the const-codegen runtime bugs.
Re-probe 2026-06-28 (v83) — prior Track A gates cleared; two new bugs found
All previously open Track A blockers are now in done/:
[[bug-conditional-directive-miscount-synautil]], [[bug-unit-qualified-constant-not-resolved]],
[[bug-set-of-char-const-corrupts-char-codegen]], [[bug-proc-typed-call-const-record-arg]].
Fresh probe at v83 with --mimic-fpc:
| unit | state |
|---|---|
synafpc, synacode |
compile OK (unchanged) |
synautil/synaip/asn1util/synachar |
[[bug-chr-builtin-shadows-param-name]] (Track A, new) — CountOfChar(…; Chr: char): Chr treated as built-in, rejected as param name |
synsock/blcksock/httpsend |
[[bug-consteval-named-type-cast]] (Track A, new) — ssfpc.inc: INVALID_SOCKET = TSocket(NOT(0)) fails ConstEval |
Both bugs filed 2026-06-28. When Track A fixes them, re-probe to find the next wall.
TRIAGE (2026-06-30, multi-agent verify)
UPDATE (verify): both listed Track A blockers (bug-chr-builtin-shadows-param-name, bug-consteval-named-type-cast) are now in done/. Re-probe 'uses synautil --mimic-fpc' advances past them to a NEW wall: 'too many array constant elements' (candidate focused Track A ticket / capacity bump). Track B compile target still open.
Re-probe 2026-07-11 (v201, opus-night)
uses synautil --mimic-fpc now dies at jedi.inc: PXX evaluates {$...}
directives inside (* ... *) comments, and jedi.inc's big doc comment
(lines 48-699) contains example directives with 14.2 float literals →
"unexpected character" (misreported at synautil.pas:458 via the include
splice). Same lexer bug the New-ZenGL ladder hit the same night —
[[bug-pascal-directive-inside-paren-star-comment]], prio raised to 65 since
it walls BOTH corpora. Without --mimic-fpc the probe instead takes the
Kylix path and stops at uses libc (expected). Next wall after the lexer
fix is presumably the previously-noted "too many array constant elements".
2026-07-12: lexer walls DOWN — next wall is RTL surface
With bug-pascal-directive-inside-paren-star-comment resolved (both the
expansion-pass comment bug AND a second find: jedi.inc's {$MODE DELPHI}
leaked NestedComments into every unit lexed after it — fixed per-unit),
uses synautil --mimic-fpc now parses the entire unit and fails on
SEMANTICS: undefined variable (DayOfWeek) at synautil's date code — a
lib/rtl sysutils surface gap (Track B, this ticket's own lane). The
directive/lexer era of this ticket is over.
2026-07-12 (later): RTL surface batch landed, next wall = Move(Pointer(v)^, ...)
Iterated synautil's undefined-symbol walls at v202+HEAD:
- sysutils: DayOfWeek, IsLeapYear, MonthDays (array[False..True,1..12]), UnixDateDelta/DateDelta, FormatDateTime (y/m/d/h/n/s/z tokens + quoted literals), StrToTime, Now (PalRealtime), TTimeStamp + DateTimeToTimeStamp, AnsiUpper/LowerCase, TimeSeparator/DateSeparator/ DecimalSeparator vars (initialization), FileExists/DirectoryExists/ DeleteFile (PAL stat/delete).
- baseunix: fpsettimeofday (clock_settime x86-64; -1 elsewhere).
- parser:
on <Class> dobinderless except-handler form (test_on_binderless in test-core). NEXT WALL:Move(Pointer(Value)^, Pointer(@Result[l+1])^, x-1)(synautil.pas:1477) — the Move builtin's argument parser rejects a deref of a Pointer(...) cast ("expected comma or close parenthesis"). Shared-parser / builtin arg lowering — needs its own focused ticket when picked up.
2026-07-12 (later 2): untyped Pointer(p)^ expression form FIXED; wall = method by-ref args
Pointer(expr)^ now parses (builtin-cast branch grew a deref postfix,
modeled as a Byte lvalue — address-identical for untyped params;
test_ptr_untyped_deref in test-core). synautil advances to
Stream.read(PAnsiChar(Result)^, Len) — the METHOD-call by-ref argument
path still resolves bare idents only: filed
[[bug-cast-deref-as-varparam-arg]] (Track P, prio 50). That is now the
single wall in front of the whole synautil compile.
2026-07-12 (morning): full-unit probe matrix at HEAD + ESys errno batch
| unit | state |
|---|---|
| synafpc, synacode | compile OK |
| synautil, synaip, asn1util, synachar | ONE wall: [[bug-cast-deref-as-varparam-arg]] (Stream.read(PAnsiChar(x)^, …) method by-ref arg) |
| synsock, blcksock, httpsend, smtpsend, pop3send, ftpsend | were ESysEINTR ConstEval — fixed (42 ESys* errno consts added to lib/rtl/baseunix); now stop at [[bug-pascal-nested-variant-record-tagged]] (ssfpc.inc TVarSin) |
So the ENTIRE Synapse stack is behind exactly TWO Track P parser tickets: cast-deref-as-varparam-arg (prio 50) and nested-variant-record-tagged (prio 55). Runtime caveat once compiling: synacode's DecodeBase64/MD5 had const-table codegen corruption historically (Track A fixes since landed in done/) — re-verify values, don't assume.
Session log 2026-07-12 (opus-p, Track P+B)
HTTP chain GREEN end-to-end. synsock, synautil, synaip, asn1util,
synacode, blcksock, httpsend, smtpsend, pop3send all compile with
--mimic-fpc -Fuexternal/synapse -Fulib/rtl -Fulib/rtl/platform/posix.
Runtime smoke: THTTPSend.HTTPMethod('GET', 'http://example.com/') → 200,
559 bytes, body matched — over our own sockets/DNS RTL, no libc.
Parser fixes this session (commits 541f8fda, 2b2a0c29, +HTTP-chain commit): nested variant records w/ tagged discriminant (TVarSin, SizeOf=28 = FPC), cast-deref by-ref args (PChar(s)^), qualified type refs (sockets.Tin6_addr), const concat of named char consts (CRLF = CR + LF), metaclass const/var init (TSSLClass = TSSLNone), class aliases (TOptionList = TList), FField.Free / property.Free, @obj.field, f(...)^ untyped deref, SizeOf(obj.field), System.X-beats-param, proc-property invocation, MAX_METHFIX 8192, and the CRITICAL AllocParam SymBlockId recycling bug (on-handler poisoned the next routine's params — pre-existing in v202).
RTL additions: sockets (fpGetPeerName/fp{Set,Get}SockOpt/fpIoctl, in6 union arms, NetAddrToStr(6)/StrToNetAddr(6), HostToNet/NetToHost), netdb shim over lib/rtl/dns, unix.GetHostName, sysutils (GetTempFileName, SetString, StrToInt64Def, LastDelimiter, AdjustLineBreaks), classes (TStrings LoadFromStream/SaveToStream/Assign/AddStrings, writable TStream.Size).
Remaining:
ftpsend— needs adateutilsRTL unit (MonthDays/EncodeDate helpers).synachar— needs iconv/charset tables (IconvArr); big, separate slice.- Runtime breadth: only the GET path smoke-tested; SMTP/POP3 compile-only.
- synacode runtime values (Base64/MD5) STILL unverified (carried over).
Update 2026-07-12 (later, opus-p)
-
ftpsend compiles (dateutils/strutils shim units, TFileStream, with-statement upgrades — see commits 73a2f234^..632af8cb).
-
synacode runtime VERIFIED byte-exact vs FPC: EncodeBase64/DecodeBase64, MD5('abc'), SHA1('abc'), Crc32('123456789'), EncodeURL. Chasing this found 3 silent codegen bugs (const-open-array field args, indirect-call open-array writeback, High(rec.fieldarray)) — fixed in 632af8cb.
-
Remaining: synachar (iconv tables), HMAC/QuotedPrintable etc. unverified, SMTP/POP3/FTP runtime untested (need live/loopback servers).
-
2026-07-19 (backlog sweep) RESOLVED. Done-when met: full HTTP chain compiles under --mimic-fpc; test/lib_synapse.pas smoke gated in make lib-test (b64/md5/sha1/crc32 byte-exact vs FPC + blcksock loopback ping/pong; e222393b…6facf2e1). Follow-up candidates if wanted: synachar iconv tables, SMTP/POP3/FTP runtime breadth.
Log
- 2026-07-19 — resolved, commit 6facf2e1.