← board

The fact

var q, r: Int64;
q := 300; r := 300;
WriteLn(q in [300]);   { TRUE  — constant arm, compare chain }
WriteLn(q in [r]);     { FALSE — runtime arm, 256-bit mask   }

Measured at 831919a7d. Both lines are the same operator on the same values; the only difference is whether an element is spelled as a literal. Nothing is reported.

Full runtime-arm row set (q in [4294967297, r], r = 9):

q pxx fpc 3.2.2
1 TRUE TRUE (+warning)
4294967297 FALSE TRUE (+warning)
9 TRUE TRUE
q in [r], q = r = 300 FALSE FALSE (+warning)

Why this is filed as the DOMAIN question and not as the 2^32 rows

bug-p-set-membership-item-constant-truncated-to-32-bits closed the constant arm and said in its own body that the runtime arm is broken "by what looks like a different mechanism", and that neither arm going green is evidence about the other. It is a different mechanism, and reading FPC changed what the finding is:

rt.pas(7,26) Warning: range check error while evaluating constants
             (4294967297 must be between 0 and 255)

So the 2^32 rows on this arm are an out-of-domain element, which FPC itself diagnoses. CLAUDE.md is explicit that where an input is only produced by a mistake, FPC's post-warning value is not a specification and matching it is not a goal — so q in [4294967297, r] answering FALSE is not the defect, and this ticket does not ask for it to be changed.

The q = r = 300 row is the defect, because 300 is not exotic and is in-domain on the arm next door. pxx's constant arm is a compare chain with no set at all behind it, so it deliberately has a much wider domain than a Pascal set; the mask arm is a real 0..255 set. Both choices are defensible. Having the source silently select between them is not.

Recommendation: diagnose, do not widen

Widening the mask to cover 0..2^63 is not on — a real Pascal set is 0..255 and the mask is the right representation for it. The cheap, CLAUDE.md-shaped fix is to make the mistake visible: when the runtime arm sees a constant element outside 0..255, say so, the way FPC does. That converts a silent wrong answer into a named stop and leaves both arms' representations alone.

Open question for whoever takes it, and the reason this is not a one-liner: whether the diagnostic should also fire for a variable element that happens to be out of range at runtime (FPC does not — its warning is "while evaluating constants"), or whether that stays a silent FALSE.

Prio: 25 -> 35, and deliberately not higher

Raised because the reachable row is r = 300, not 2^32: a set with a variable element is ordinary code (the parser's own comment cites fcl-json scanning with FTokenStr^ in [#0, C]), and the arm is chosen by whether an element is spelled as a literal, so this bites on a REFACTOR — replacing a literal with a constant-valued variable silently changes the answer.

Not higher, and the reason is worth keeping so nobody re-inflates it: FPC answers q in [r] with r = 300 FALSE as well, so a program written against FPC sees no divergence and no real-world Pascal is relying on the TRUE. What pxx adds is an internal inconsistency plus a missing diagnostic, and CLAUDE.md ranks a differing diagnostic as deferred. So the harm is real but it is refactor-time surprise, not a wrong answer that ships. 35 is a wrong answer from code that compiles; it is not a 55.

The coordinator independently suggested ranking this up and agreed with the reachability argument. Recording that it was raised on the measurement rather than on the endorsement — the endorsement is not the evidence.

Repro

printf 'program t;\nvar q,r:Int64;\nbegin q:=300; r:=300;\n WriteLn(q in [300]);\n WriteLn(q in [r]);\nend.\n' > /tmp/t.pas
./compiler/pascal26 /tmp/t.pas /tmp/t && /tmp/t     # TRUE then FALSE

THE ADJACENT TICKET, AND IT IS INDEPENDENT — frankB, 2026-09-06

backlog-core/bug-a-set-membership-32-bit-backends-truncate-the-set-constant (Track A, p20, unowned) names the same function, ParseSetMembershipAST, and is the sibling half of the same frankO measurement: 831919a7d widened loVal/hiVal and the Integer(IRIVal[...]) casts, which was enough for x86-64 and aarch64 and is not enough for i386, arm32 or riscv32.

It is not a blocker for this one and this one is not a blocker for it. The two questions are adjacent in one function and different:

The diagnostic asked for here is a fact about Pascal — a set is 0..255, so 300 is not a set element on any target, at any width. A 32-bit truncation cannot hide an out-of-domain constant from a check that tests < 0 or > 255, so the diagnostic does not inherit the backend defect and does not wait on it.

What DOES inherit it is any VALUE row in a test for this ticket, and that is the part to keep separate: the domain check is target-independent, the numbers printed beside it are not. Raised by frank-coordinator, and it is the CLAUDE.md class verbatim — a claim measured only on the 64-bit host.

AUDIT APERTURE — this ticket is invisible to a set-family slug grep

Its slug names in, not set. So an audit of set coverage by slug finds the set group closed in done/ with this member still open, and done/ is the reading that stops an audit. Noted by frank-coordinator while checking this seat's own count of the family.

The general form, which is worth more than the instance: a group defined by CONSTRUCT cannot be audited by SLUG. A slug records what the reporter noticed; the construct is what the fix turned out to be about. Those coincide only when the reporter already knew the cause — which is exactly the case where the group would not have needed finding.

Log