← board

The include pre-pass answers {$IFOPT} at the default state, not the source's

ExpandIncludes (compiler/elfwriter.inc) splices {$I} includes in before the lexer runs. Since 4038b32d0 it asks PasIfOptState, the same function the lexer uses, so the eight modelled letters answer honestly — at whatever state the command line and the defaults left them. It tracks {$define} (it saves and restores savedDefineActive) and does not track switch directives.

Repro

program t;
{$R+}
begin
{$ifopt R+}
  {$I guarded.inc}
{$else}
  WriteLn('else arm');
{$endif}
  WriteLn('done');
end.

pxx prints done alone. fpc prints the included line and done. Neither arm runs: the pre-pass reads R at its default (off), drops the include, and the real lexer — which did see {$R+} — then takes the true arm and finds it empty. No diagnostic.

Why it is low-prio and not rejected

It needs all three in one file: a switch set in source, an {$IFOPT} on that letter, and an {$I} inside the arm. {$IFOPT} guards in real code overwhelmingly sit on the default state, and the six letters that default ON here (C I Z G J X) are already right. R and Q default off, so the guard and the old hardwired answer agree unless the source moves them.

Recorded rather than fixed because the fix is a different size: the pre-pass would have to track switch state textually — a second, partial implementation of the lexer's switch handling, which is the third copy of that logic and the thing devdocs/dev/normalise-dont-special-case.md warns about. Doing it right probably means the pre-pass consulting one shared switch-state walker rather than growing its own.

Parent: bug-p-ifopt-is-hardwired-false-so-the-wrong-arm-compiles, whose stated residual (G, J, X) is closed.