← board

FPC {$mode objfpc} assigned-enum ordinals use :=, not =

Found 2026-08-21 immediately behind [[feature-mimic-fpc-compiler-define-profile]]. This is the wall on the cclasses / globtype path — the sibling of [[feature-p-fpc-global-operator-overload-declarations]], which is the wall on the cutils path.

Repro

{ FPC 3.2.2 compiler/globtype.pas, line 800 }
tmsgstate = (
  ms_on := 1,
  ms_off := 2,
  ms_error := 3,
  ...
);
$ pascal26 --mimic-fpc-compiler p_cclasses.pas
Expected: ), but got:  (Kind: 63, Line: 1103)
pascal26:1103: error: unexpected token
  near:  type tmsgstate   ms_on >>>

(Line 1103 does not exist in globtype.pas, which is 843 lines — see [[bug-a-a-parse-error-in-a-used-unit-reports-a-line-in-no-file]].)

What it is

Assigning explicit ordinals to enum members. The spelling differs by mode, which is the whole of this ticket:

mode spelling
Delphi / {$mode delphi} (ms_on = 1, ms_off = 2)
objfpc / {$mode objfpc} (ms_on := 1, ms_off := 2)

pxx accepts the = form (the error is "Expected: )", i.e. it parsed the identifier and then wanted the list to end). So this is one token in one place, not a feature — the enum machinery behind it already exists.

Worth checking while there whether the two spellings should BOTH be accepted unconditionally or whether the := form belongs behind -Mobjfpc. pxx's dialect is deliberately lax by default and FPC-parity strictness lives behind per-feature flags, which argues for accepting both always — but that is the call to state in the ticket rather than assume. See devdocs/dev/normalise-dont-special-case.md: two spellings of one concept want one path, not a second one.

Gate

globtype.pas parses; cclasses.pas gets past it under --mimic-fpc-compiler. Pascal suite green + self-host byte-identical.

Outcome — 2026-08-26

One token, one place, as the ticket predicted. ParseEnumMembers (compiler/pasparser_decl.inc:50) tested CurTok.Kind = tkEq; it now tests tkEq or tkAssign and everything downstream — AddEnumVal, the continue-from-value+1 rule, EnumTypeHasHoles, the unscoped-member SymEnumId tagging — is unchanged and shared.

The open question in the ticket, answered

"Whether the two spellings should BOTH be accepted unconditionally or whether the := form belongs behind -Mobjfpc."

Both, unconditionally. Two reasons, and they agree:

Measured while there: FPC itself accepts the two spellings INTERLEAVED in one list ((m_a := 2, m_b = 4, m_c) compiles under -Mobjfpc), so a mode-gated implementation would have had to allow the mix anyway. That is in the test.

Gate — the ticket's own, met

$ pascal26 --mimic-fpc-compiler -Fu<fpc-source>/compiler p_globtype.pas p_globtype
ok: p_globtype  [code=118047B data=19040B bss=46440B procs=234]

globtype.pas does not merely parse, it compiles clean. cclasses.pas now gets past it and stops at the NEXT wall, which is the already-filed sibling:

pascal26:298: error: binary operator must take exactly two parameters
  in: <fpc-source>/compiler/constexp.pas

— i.e. [[feature-p-fpc-global-operator-overload-declarations]], unchanged and still open. (FPC compiler sources are not in library_candidates; they live at /data/borg-rescue/home-rene/src/fpc-source/compiler on this box, which is where both runs above point.)

Measured

test/test_enum_assigned_ordinal_colon_equals.pas (+ .expected, wired into test-core), byte-identical to fpc -O- -Mobjfpc 3.2.2 on every row — globtype's own tmsgstate shape, the Delphi = form, the mixed list, hex values, and the identity checks that prove := goes through the same AddEnumVal path (m = ms_warn TRUE, m = ms_on FALSE):

1 2 3 4 10
5 7 8
2 4 5
17 34 51
4 8 5 34
TRUE FALSE
TRUE TRUE

Gate

make compiler/pascal26 byte-identical (9bb20fbd0c0f) · tools/gate.sh quick GREEN · pascal-conformance 346/0/170/34 · c-conformance 220/0.

Log