← board

REJECTED 2026-08-26 — never. Operand evaluation order is unspecified in ISO Pascal and undocumented in FPC. Only a program that already relies on unspecified behaviour can observe the difference, so no correct Pascal source compiles or runs wrong here. That is the bottom row of the FPC-parity table in CLAUDE.md: an observable no compiling-correct program can reach. The measurement below is worth keeping — it is why a pasmith seed diverged — so the ticket survives here as a record rather than as work.

pxx evaluates binary-operator operands (and CALL ARGUMENTS) left-to-right; FPC evaluates right-to-left

The difference

Order of evaluation of a binary operator's two operands is unspecified in ISO Pascal and in FPC's documentation. The two implementations pick opposite orders:

Observable only when one operand has a side effect the other operand reads — i.e. badly written code that already relies on unspecified behaviour. Minimal:

program ord;
var r: word;
function sidef: word; begin r := 810; sidef := 0; end;
var res: word;
begin
  r := 778;
  res := r xor sidef;   { FPC prints 810 (r read after call), pxx prints 778 }
  writeln(res);
end.

Both compilers are internally self-consistent across O0/O2/O3. Neither is wrong.

Why it is NOT fixed

When it might matter

Only under a future strict --mimic-fpc / --strict-fpc mode where byte-for-byte FPC behavioral parity on unspecified constructs is a goal (e.g. importing real-world FPC code that leans on FPC's right-to-left order). Even then, prefer a diagnostic ("operand has a side effect a sibling reads") over silently reordering. Not worth doing outside that context.

Same divergence, second construct: CALL ARGUMENTS (2026-08-22)

Found independently by the parameter-passing differential family, before this ticket was read — which is itself the useful part of the report: the two constructs look unrelated from a test, and are one decision in the compiler.

function A(x: Integer): Integer; begin WriteLn('A', x); Result := x + 1; end;
function B(x, y: Integer): Integer; begin WriteLn('B', x, y); Result := x * y; end;
begin
  WriteLn(B(A(1), A(2)));
end.
FPC : A2 A1 B23 6        <- arguments right-to-left
pxx : A1 A2 B23 6        <- arguments left-to-right

Everything this ticket already says applies unchanged: unspecified in ISO Pascal and in FPC's documentation, observable only when one argument has a side effect another argument reads, and therefore only in code that already depends on unspecified behaviour. The result (6) and the callee's own view of its parameters are identical; only the order of the side-effect prints differs.

Scope note for whoever eventually takes this: it is one decision with two lowering sites, and fixing either alone would leave the codebase inconsistent with itself, which is worse than being consistently different from FPC. If it is ever done, do both — AN_BINOP lowering and the argument loop in IRLowerCallArg's callers — in one change, and gate it on the full suite: a right-to-left argument order interacts with the by-ref temp queue (PostCallIntfSym) and with the deferred-argument optimisation (ArgSymAddrClean), neither of which is order-neutral by construction.

Still prio 15, still rainy-day. Recorded here rather than as a second ticket so the pair cannot be fixed by halves.