← board

A local var-section array initializer

The measurement, and it is the whole ranking argument

function ByConst(c: TCC): string;
const Names: array[TCC] of string = ('', 'register', 'cdecl');   { WORKS }
begin ByConst := Names[c]; end;

function ByVar(c: TCC): string;
var Names: array[TCC] of string = ('', 'register', 'cdecl');     { REFUSED }
begin ByVar := Names[c]; end;

fpc 3.2.2 -Mobjfpc prints register/cdecl for both. pxx prints it for the first and refuses the second at parse time.

So nothing about the shape is unsupported. The element loop, the enum-indexed bound, the string elements, the routine-local flush into the prologue — all of it runs correctly today for the const spelling. This is one more enumerated door: the var site already forks to ParseRecordInitializerInto for var L: TRec = (n: 9), and the array arm beside it is Error(...).

Why it is not a small fix anyway

The const array initializer is not a routine. It is ~315 lines inline in ParseConstSection (roughly pasparser_decl.inc:3556:3910) and it reads and writes that routine's locals throughout: cIdx, cLo/cHi, cElemTk, cnNDims, cnDimLo[]/cnDimSpan[], cDepth, cElem, cAi, isLocalConst, plus LastTypeRecId and the pointee state. It also parses the type itself, from array[ onward, which the var path has already done.

So the extraction has to split "parse the type" from "parse the initializer" and give the second half its dimensions from the already-declared symbol (SymArrNDims, the element kind, the bounds) rather than from the parse it currently shares. That is the right design — one initializer parser, two declaration spellings — and it is the step that stops being mechanical, because each derived value has to be verified equal to what the parse produced rather than assumed.

Gate

Both spellings in one file asserting the same VALUES, plus a multi-dimensional row and a record-element row (the two shapes most likely to differ between the parse-derived and symbol-derived dimension lists). The const spelling is the control and must not move: every const array in lib/rtl goes through this code, so gate.sh quick, the fgl corpus and the conformance suite all bear on the extraction.

CORRECTION 2026-09-06, same day, by the author — the premise above is wrong

"Why it is not a small fix anyway" is false, and so is the sentence in the original summary that the work was an EXTRACTION from ParseConstSection. Everything the section says about the const initializer is accurate — it is ~315 lines inline, it does read that routine's locals — and none of it was relevant, because the var path never needed to call it.

ParseVarSection has its own array-initializer loop, and that loop already does the thing the ticket proposed building: it derives its dimensions from the already-declared SYMBOL (SymArrNDims, vRowSpan, the element kind) rather than from the parse. It was complete. It just wrote PendingInit* at every one of its element sites — the global table — and a routine has no pending-init table, so the whole loop was fenced off behind one Error for locals.

How the wrong premise was formed: I reasoned from the const spelling working, concluded the machinery lived in ParseConstSection, and never looked for a second copy in the routine I was about to edit. The global var spelling already worked too, and that is the observation that would have settled it in one line — var G: array[...] of T = (...) at file scope has always compiled. A capability that works in two places is evidence about where the code is, and I sampled only one of them. [[a-caller-census-cannot-find-an-absent-copy]]

The fix as landed

RegisterVarInitElem(symIdx, elemIdx, gKind, val, valAux, tkOrd, isLocal) — inserted immediately before ParseVarSection — writes LocalInit* when isLocal and PendingInit* otherwise, maps global kind 8 (metaclass) to local kind 5, and Errors on kind 10 for locals. The local block stops refusing arrays wholesale and refuses only isDyn; vIsLocal := CurProc >= 0 is computed once beside vNDims; the four paren-loop element sites and both SpreadCharLiteral calls route through the helper.

What was measured, not assumed

Residual

A routine-local dynamic array initializer (var v: array of Integer = (1,2,3) inside a routine) is still refused. fpc accepts it. See [[feature-p-a-routine-local-dynamic-array-initializer]].

Log