← board

Strict flags should scope by dialect ownership, not by program-vs-unit

"Our own RTL is ours. strict-fpc only applies to external code — like Synapse, or others. Our own libraries should always be excluded from such a compiler flag."

The axis is wrong today

if StrictOverload and (FindProc(name) >= 0) and (CurrentUnitIdx < 0) then

CurrentUnitIdx < 0 means "we are in the main program". So the rule exempts every unit — ours and Synapse's — and polices only the file being compiled.

That is backwards for a parity flag. --strict-fpc exists to check that code written for FPC behaves as FPC would; Synapse is that code, and it is exactly what the current scoping skips. The flag is weaker than it looks.

The rule that makes sense

code judged by strict flags?
our RTL / builtin / lib/pcl no — written in the pxx dialect by design
external units (Synapse, fgl, fpjson) yes — they are FPC code, so hold them to FPC's rules
the user's own program yes — unchanged from today

The framing that makes this principled rather than a hack: our RTL's dialect is a property of the RTL, not a user-selectable mode. A command-line flag has no business changing how already-written library code is judged. Once that is said out loud, per-unit flag behaviour stops being an exception and becomes the rule.

Mechanism: a source directive, NOT a path predicate

The user's own framing — "our own libraries have a sort of implicit $mode pxx" — is the better mechanism, and it beats the path rule I first proposed:

Precedent already in the tree, and it is the exact inverse: {$MIMIC FPC} (lexer.inc:1747) pins FPC-compatibility in the source"so a project carries its own compatibility need". {$MODE PXX} is the same idea pointed the other way: this unit declares its dialect, so don't re-judge it by another's.

{$MODE} already exists as a hook (lexer.inc:1735) and currently only toggles DelphiMode, with other modes "accepted but inert" — so there is a place to put this without inventing syntax.

The payoff

--strict-overload can then join the --strict-fpc umbrella. Its exclusion is documented in EnableStrictFpc and the reason is exactly this:

"our own RTL uses undirectived overloads by design … requiring the overload directive would fail to compile every RTL-using program (fpjson/Synapse)"

With ownership scoping, that objection disappears: our RTL is exempt because it declares its dialect, and the user's code and external units get the rule.

Two things to state up front rather than discover

  1. The main program stays policed. "Exempt our own code" must not be misread as exempting the thing being compiled. It is our RTL that is exempt.
  2. A unit with no dialect marking defaults to policed. Anything else makes the flag silently weaker as the tree grows, which is the failure this ticket is fixing.

Gate

--strict-overload folded into EnableStrictFpc, and fpjson / Synapse / fgl still compile — which is the exact corpus its exclusion note names. Plus a unit marked {$MODE PXX} demonstrably exempt while an unmarked one beside it is not.

Triage 2026-08-19 (Track D re-triage pass, pin v364)

Genuine feature, still wanted, unchangedcompiler/parser.inc:32241 is still literally

if StrictOverload and (FindProc(name) >= 0) and (CurrentUnitIdx < 0) then

with the comment beneath it still explaining the program-vs-unit axis, so the scoping this ticket calls backwards is intact.

Prior art worth knowing before starting, found while measuring. The compiler already has an "ours vs theirs"-shaped predicate for a different purpose: NilPyUserCode (symtab.inc), introduced because CurrentUnitIdx < 0 was also the wrong axis there — it meant "main program only" where the intent was "NilPy user code, main program or an imported .py module", and the mismatch segfaulted a bound-method field (compiler/parser.inc:6928-6936). Same wrong axis, same shape of fix, already landed once. Read that predicate before writing a second one; whether dialect ownership can share it or needs its own is exactly the question to answer first.

Resolution 2026-08-21 (Track A)

Implemented as the ticket specifies: a source directive, one predicate, unmarked code defaults to policed.

Mechanism.

Both "state up front" clauses honoured. The main program stays policed — an unmarked program is judged exactly as before. A unit with no marking is policed too, which is the half the old axis got backwards. {$MODE PXX} in a program is accepted as an explicit opt-out by whoever is compiling, which is a real answer rather than a hole: they wrote the directive.

Measured, not reasoned. pinned compiles AND RUNS a unit with undirectived overloads under --strict-overload — direct proof the old axis never reached units at all. pinned also accepts {$MODE PXX} silently (inert), so the marked library files stay buildable by the stable binary; that is what makes marking 136 Track B files safe to land from Track A.

Three Makefile assertions, because any two of them pass with a broken flag: a {$MODE PXX} unit with undirectived overloads is exempt, an unmarked but directive-clean unit still compiles (the flag must not reject conformant FPC code), and an unmarked undirectived unit is rejected. Plus a fourth: with no flag, that same rejected program builds and runs — the dialect stays lax by default.

How to finish the fold (deliberately NOT done here)

The ticket's Gate has two halves. The scoping half is done and asserted. The other half — folding --strict-overload into EnableStrictFpc — is not made, and the reason is evidence rather than design:

--strict-overload folded into EnableStrictFpc, and fpjson / Synapse / fgl still compile — which is the exact corpus its exclusion note names.

fgl, Synapse and fpjson live in external/, which is gitignored and fetched on demand, and is not present in this checkout. Enrolling a flag in --strict-fpc is a promise that those corpora still build; making that promise without running them would weaken the one claim the umbrella makes — precisely what the existing comment warns about for StrictOverloadWidth. The original objection is gone (the RTL is exempt now, and an RTL-using program does compile under the flag — asserted above); only the verification is missing.

Recipe for whoever has the corpora:

  1. fetch external/ (fgl, Synapse, fpjson),
  2. build each with --strict-overload against the current pin,
  3. if any rejects, the failures are the real work — each is either an FPC-code overload we mis-diagnose (a bug) or genuinely undirectived FPC source (which would mean FPC itself does not require what the flag requires, i.e. the flag is wrong, not the corpus),
  4. only if all three pass, move StrictOverload := True into EnableStrictFpc and rewrite the comment at lexer.inc:601 (it now points here).

The comment in EnableStrictFpc was updated to say exactly this, so the next reader is not told a reason that is no longer true.

Follow-on

Only StrictOverload is rescoped. The other strict flags (StrictCase, StrictOperator, StrictVisibility, RequireForward, StrictShiftWidth, StrictVariantChar) do not test CurrentUnitIdx at all today — they apply everywhere — so there was nothing to rescope, but the moment one of them wants an ownership carve-out, DialectIsPxx is the predicate to call. Filed: [[feature-a-audit-strict-flags-against-dialectispxx]].

Log