operator enumerator on an array type is refused at the declaration
Measured 2026-09-08, compiler b2361e541c4b, fpc 3.2.2 -Mobjfpc.
type
TDyn = array of Integer;
TStat = array[0..1] of Integer;
operator enumerator(a: TDyn): TEnum; begin ... end; { pxx: refused }
operator enumerator(a: TStat): TEnum; begin ... end; { pxx: refused }
pascal26:15: error: operator overloading: TDyn is not a supported operand type
pascal26:18: error: operator overloading: TStat is not a supported operand type
fpc accepts both, and for i in <that array> then runs the OPERATOR rather
than the built-in element iteration — 55 and 77, the operators' own
constants, not 5 6.
Why it is worth more than its volume
It is loud, so nobody gets a wrong answer from it. It ranks because of what it
does to the INSTRUMENT: enumerator is the one operator whose whole purpose is
to give a type an iteration meaning, and the two array families are exactly
where a container library would want one. While the declaration is refused,
for i in <array> can only ever take the built-in path in pxx, so the
precedence question its sibling ticket is about is not merely unanswered for
arrays — it is unaskable, and a rule written to cover all families would land
with two of them untested.
Where
compiler/pasparser_call.inc:573 gates which operand types an operator may be
declared for; OPK_ENUMERATOR is listed there beside OPK_INC/OPK_DEC as
one of the unary-ish keys. The refusal text is the general
operator overloading: <T> is not a supported operand type, so the array kinds
are simply absent from the accepted set rather than refused by an arm that
mentions enumerators.
Check whether the same gate refuses a record and a set operand — the set
form IS accepted today (measured, operator enumerator(a: TSet) compiles), so
the accepted set is not simply "scalars".
Re-scoped 2026-09-08 (frankS) — twice, both corrections downward
It is not a missing entry in an accepted set. defs.inc gives the operator
table three columns — OvrlOpKind, OvrlTypeKind, OvrlRecId — and an array
type has no rec id. So an array operand could only be keyed on its ELEMENT kind,
and operator enumerator(a: TDyn) with TDyn = array of Integer would register
under (tyInteger, REC_NONE): the same row as operator enumerator(a: Integer), first declared winning.
That is not a hypothesis about a new column; it is the collapse the tree already
has and already documents. tforin15.pp is skipped for exactly it — Twice and
Integer under one key, pxx printing 1 where fpc prints 2 — and its reason ends
"Burning this row needs alias IDENTITY in the operator table AND at the use
site… Do not half-plumb it; that ticket settled that a channel which guesses is
worse than one that abstains." OperandTypeKindRec's own header says the same
in the paragraph immediately above the refusal this ticket quotes, and names
tarray18 as the array case staying a gap.
The use site cannot supply the identity either, which is what makes this
larger than the declaration. A symbol container can recover its array row
(FindArrayType over SymDeclTypeNOff, as pasparser_expr.inc:4752 does), but
no AST node carries one — so the EXPRESSION spelling would still key on the
element kind and the two spellings would disagree. Wiring only the declaration
is the half-plumbing both prior tickets refuse.
So this is blocked-by
[[bug-p-a-distinct-type-declaration-is-parsed-but-is-not-distinct]] — the same
channel, extended to operators — and re-ranked 35 -> 30, because it is one more
consumer of a channel that does not exist rather than a standalone fix.
And it does not block the precedence work — this ticket said it did
The summary originally argued that two of the five container families are unmeasurable and therefore "any precedence rule written for them today would land with two arms untested". That is the wrong reading of what those two families are. With the operator declaration refused, an array container in pxx has exactly ONE enumerator candidate — the built-in. A rule that RANKS candidates has nothing to rank there. It is not untested; it is vacuous, and it stays vacuous until this ticket is burned.
What is genuinely unmeasurable is this ticket's own arm, not the rule. Recorded because the original phrasing would have held up [[bug-p-for-in-over-a-string-prefers-a-user-operator-enumerator-and-fpc-prefers-the-builtin]] behind a blocker that is not one, and I wrote it.