← board

pxx accepts invalid programs the FPC suite's %FAIL tests reject

Symptom

13 curated { %FAIL } tests — programs the reference compiler must REJECT — compile cleanly under pxx. Each is a missing semantic check. Skip-list reason: accepts-invalid. Current list (audit 2026-07-10):

tarrconstr8 tdefault2 tdefault4 tdefault6 tdefault12 tenum4 terecs9 terecs12c terecs13c tforin11 tgenconstraint38 tgenconstraint39 tgeneric56

Read each test's header comment for what diagnostic is expected (e.g. Default() on forbidden types, extended-record visibility violations, generic-constraint violations, invalid array constructors, for-in over non-iterable).

Method

One test at a time: reproduce → add the check (with a matching test/*.pas negative test in our own suite) → unskip. Split out sub-tickets if any check turns out deep.

Progress 2026-07-11 (opus-a)

Batches 1–3 landed (f6908a23, 034ce7ea, 3a601a9a) — 7 of 22 burned: tdefault2/4/6 (file-type + Default() checks; TextFile now a real record), tforin11 (string for-in requires Char var), terecs9 (self-containing record), terecs12c/13c (class var in record rejected). Full sweep 263-ish pass / 0 fail.

Batches 4-5 (3f606750, 625f6114): SymEnumId enum identity plumbing — toperatorerror (enum vs pointer compare) + tforin20 (for-in over holed enum)

Remaining 12 cluster deep:

Gate

make test + self-host byte-identical; burn the skip-list entries.

2026-07-11 — 24 more exposed by the headerless-program fix

The mandatory program header was accidentally rejecting 24 headerless {%FAIL} negative tests before their actual invalid construct was ever reached. With bug-pascal-headerless-program fixed, pxx now COMPILES them — each is a real missing-diagnostic gap. From the conformance run (skiplist reason "missing diagnostic: accepts invalid code"):

tcase3/6/9/10/11/19/20/23/26/27/35/36/39/42/43 (case-of-string validation: overlapping/inverted ranges, duplicate labels), tclass13c, tenum2, tforin20, tgeneric13/14/20/21, toperatorerror, tover3.

The tcase cluster is the bulk: case-statement label validation (duplicate labels, inverted ranges) is simply not checked today.

Triage 2026-07-11 (user review) — bug vs by-design

User call: PXX is more lax by design; a {%FAIL} test passing is only a bug when pxx's semantics are undefined/silently wrong. Split of the 15 that remained:

Not bugs — retagged dialect-pass in pxx.skip (do not burn):

Real gaps — keep accepts-invalid, useful reminder tests, rainy-day:

Parked (user call): tclass13c — TRootClass.Integer nested-type member, needs a per-class nested-type registry; near-zero value. Also noted in feature-pascal-corpus-fpc-testsuite.

Case-label validation moved behind --strict-case (2026-07-11)

86cf34ea's duplicate/overlap + inverted-range errors were stricter than the dialect intends: overlapping labels previously worked with first-match semantics. Reverted the default to lax (first-match; inverted range = never matches) and gated the FPC-parity errors behind the new --strict-case / {$STRICT_CASE ON} (pattern of --strict-overload). Selector-TYPE checks stay unconditional (the string/ordinal lowering depends on them). The conformance sweep now passes --strict-case, so the burned tcase {%FAIL} tests stay green; test_cross_case_range got its deliberate 'y'/'x'..'z' overlap back.

2026-07-14 — the "real gaps" from the triage are BURNED (b342, b343)

User call: take it even though it was flagged rainy-day. Both of the clusters the 2026-07-11 triage kept as real gaps are done, and neither was what the ticket thought.

tenum4 — the directive was fine; the missing TYPE CHECK was the bug (b342, 4e55e07d)

{$SCOPEDENUMS} is honoured (landed since the triage): a scoped member is not reachable unqualified. That is precisely what made tenum4 dangerous — En1 := first resolves first to the OTHER, unscoped enum, and pxx then silently took its ordinal.

The real hole was general and had nothing to do with the directive: two enum types were not distinct. c := banana (TFruit into a TColor) stored the RHS's ordinal, so the TColor read back as green. c = apple compared across enums and answered. Fixed both.

Two sub-findings worth keeping:

The check sits at the IR lowering of AN_ASSIGN (every syntactic assignment funnels through it), which needed a new ErrorAt(line, msg): at that point the parser is long past EOF, so Error's line and near: window both pointed at the end of the program.

The template cluster — 5 entries, ONE of them needed code (b343, 9cd57ddf)

Default(<bare template>) was the only live gap (tdefault11/12): a template has no size and no zero value, so the expression is undefined. Rejected via the template registry, with Default(TBox<Integer>) / Default(specialize TBox<Integer>) still legal.

tgeneric55, tgeneric56 and tgeneric13 were already rejected — their skip notes were stale. Burned them from the skip-list rather than leaving them claiming a gap that no longer exists.

Conformance now

265 -> 270 pass, 20 fail, 226 skip. make test green, self-host byte-identical.

What is actually left

2026-07-14 (later) — the RECORD cluster is burned (b347); and a correction

Landed b347: what a RECORD may legally CONTAIN. Ten of the sweep's failures, five rules — no published, no protected/strict protected (records do not inherit), a record's class method must be static, a record constructor needs a MANDATORY parameter (Create(I: Integer = 0) is the same hole spelled differently), and a LOCAL or ANONYMOUS record type gets fields only (a method there could never be given an implementation).

Correction — do not repeat this: a triage pass claimed "13 of the 17 failures are ONE bug: member visibility is not enforced". That was wrong, and it would have sent someone to write an access-control checker to fix tests that have nothing to do with access control. The terecs* cluster is declaration legality, not visibility. Exactly ONE of the seventeen (tclass12bstrict private reached from a descendant) is really visibility.

Visibility genuinely IS unenforced (private fields are readable and writable from outside the type, and the record parser said so in a comment) — it is simply not what those tests were failing on. Worth its own ticket when someone takes it; note FPC's rule is UNIT-scoped (private = visible to the whole unit), not type-scoped, so the naive "same type only" check would be wrong.

Sweep now 283 pass / 7 fail (from 273/17). Remaining: tclass12b, tclass14b, toperator71/92/95, and the two real compile gaps tdefault8 (nested type reference) and tset4 (TSysCharSet missing from the RTL).

2026-07-14 (later still) — SWEEP GREEN: 289 pass / 0 fail (b369). Ticket CLOSED.

User unparked the rainy-day flag. The final five:

Conformance: 285 -> 289 pass, 0 fail. tgeneric21 (nested generic-in-generic, semantics unverified) remains noted in feature-pascal-corpus-fpc-testsuite.