← board

What was wrong

A directive runs in the LEX pass, so the only record of what was in force at a token is the per-token snapshot the lexer writes — nine parallel arrays beside Tokens[]. A generic template is buffered into TemplateTokens[] and spliced back into Tokens[] at the specialization site.

ShiftTokParallel fills the gap those spliced tokens occupy from the token immediately before the splice, and its own comment says why that is right: "Directive state is LEXICALLY CONTINUOUS, so a spliced token inherits the state in effect immediately before it." True for a SYNTHESIZED token. False for a verbatim copy of a template token, which was lexed somewhere else entirely.

The same comment already names the fix, for the other channel:

IT IS NOT THE RIGHT ANSWER FOR EVERY CALLER … A generic specialization splices tokens that are VERBATIM COPIES of template tokens, each with a real source range, so zeroing lost it … Any future caller splicing real tokens owes the same overwrite.

That was written for TokSrcOff/TokSrcLen (95c98db70) and the nine directive channels are the sibling arm of the same double case.

Measured, both directions

template says site says fpc 3.2.2 pxx before
byte := longint(1234) {$R+} {$R-} 201 210, no diagnostic
byte := longint(1234) {$R-} {$R+} 210 201 on legal code

A silent wrong answer one way and a spurious runtime error the other. The discriminator that says this is the SPLICE and not the unit boundary: the same body in a plain (non-generic) class in a used unit is correct.

The fix

Three channels through the pool, in pasparser_generic.inc:

Parallel arrays, not a record, and not by preference. A record's layout is hard-coded in symtab.inc's REC_* tables and baked into the compiler binary, so round 0 of the fixedpoint cannot compile a new field — measured before, at GenericFuncParams, and taken as read here.

Verified

Not covered

{$Q} has a channel here like the rest but no row in the fixture: fpc 3.2.2 does not raise on high(longint) + 1 under {$Q+} where pxx does, so an overflow row would assert a divergence that is not this ticket's. Its channel travels with the other eight and is asserted only structurally.