{$IF DECLARED(...)} conditional directive support
- Type: feature (Track A compiler / conditional directives)
- Status: done
- Owner: Track A
- Opened: 2026-06-21 (Synapse POSIX profile smoke)
- Relation: unblocks
feature-networking/ Synapse Delphi-Posix.*path; follows the existing directive-expression evaluator work
Problem
Synapse's Delphi-POSIX socket include probes platform constants with
{$IF DECLARED(Qualified.Symbol)}:
{$IF DECLARED(Posix.StrOpts.FIONREAD)}
FIONREAD = Posix.StrOpts.FIONREAD;
{$ELSE}
FIONREAD = {$IFDEF ANDROID}$541B{$ELSE}$4004667F{$ENDIF};
{$ENDIF}
The pinned stable compiler currently fails the POSIX-profile Synapse smoke with:
pascal26:177: error: conditional directive: expected operator ()
This is a compiler feature gap. The library side should not edit Synapse or replace these probes with hand-written constants just to get past parsing.
Required behavior
- Accept
DECLARED(...)as a boolean operator inside{$IF ...}expressions. - Support qualified symbols, at least
UnitName.SymbolandNamespace.Unit.Symbol, because Synapse probes importedPosix.*constants. - Evaluate true when the symbol is visible at that source location and false when it is not.
- Compose with the existing conditional expression operators (
not,and,or, parentheses) without changingDEFINED(...)behavior.
Non-goals
- Do not implement a Synapse-only special case.
- Do not silently treat unknown directive functions as false; unsupported conditional functions should still fail loudly.
- Do not implement the
Posix.*constants in this ticket. This ticket only makes the conditional probe expressible.
Acceptance
- A focused test proves
{$IF DECLARED(LocalConst)}selects the true branch. - A focused test proves
{$IF DECLARED(MissingConst)}selects the false branch. - A focused test proves
{$IF DECLARED(Posix.StrOpts.FIONREAD)}works when a stub dotted unit exports that constant. - Re-running
SYNAPSE_PROFILE=posix test/manual/try_synapse_compile.shno longer fails atssposix.incwithconditional directive: expected operator.
Log
- 2026-06-21 — filed from Track B Synapse smoke. This is the first parser failure after forcing Synapse toward the desired Delphi-POSIX socket branch.
- 2026-06-21 — DONE.
DECLARED(name)added to the{$IF}expression evaluator (lexer.inc, alongsidedefined). Resolution insight: conditionals are evaluated during LexAll/LexAppend, so the only knowable symbols are those whose declaration has already been emitted intoTokens[0..TokCount-1]— exactly "visible at this source location" in PXX's lex-ordered model.PasCondNameDeclaredscans the emitted token stream with a small const/type/var/routine section state machine;ReadPasCondQualifiedNamereads dotted names and the final component is matched. Composes withnot/and/or/parens; unknown directive functions still fail loudly (non-goal honored). Regression testtest/test_declared_directive.pas(inmake test-core) covers all four acceptance probes; gate green (self-host + threadsafe byte-identical).- Acceptance 1 (
DECLARED(LocalConst)→ true): PASS. - Acceptance 2 (
DECLARED(MissingConst)→ false): PASS. - Acceptance 3 (qualified positive): PASS via final-component match when the
symbol is in-stream. LIMITATION: namespace-precise qualified resolution
(checking the unit, not just the leaf name) is deferred to
feature-dotted-unit-names; for nowA.B.leafresolves onleafalone. - Acceptance 4 (Synapse smoke): PASS —
ssposix.incno longer errors withconditional directive: expected operator. The POSIX-profile probes (Posix.StrOpts.*, no such unit) correctly evaluate false → Synapse takes the{$ELSE}literal. Synapse now advances to the next gap,uses: unit source not found: posix(thefeature-dotted-unit-names/ unit-resolution work — out of scope here per non-goals).
- Acceptance 1 (