{$I %MACRO%} is not implemented and fails silently as 0
Repro
program m;
const d = {$I %DATE%};
begin WriteLn('[', d, '] SizeOf=', SizeOf(d)); end.
fpc: [2026/09/16] SizeOf=10. pxx: [0] SizeOf=4. No warning, no error.
fpc 3.2.2's semantics, measured
Every row produced by compiling and running, not read from documentation:
| macro | fpc answers |
|---|---|
%DATE% |
2026/09/16 |
%TIME% |
16:08:36 |
%FPCVERSION% |
3.2.2 |
%FPCTARGET% |
x86_64 |
%FPCTARGETCPU% |
x86_64 |
%FPCTARGETOS% |
Linux |
%FILE% |
the source file's basename |
%LINE% |
the line the directive sits on |
| anything else | the ENVIRONMENT VARIABLE of that name |
| an unset environment variable | '', plus Warning: Include environment "X" not found in environment |
All of them expand to a STRING literal, which is why date_string is a string
in fpc and an Integer here.
The seam
ExpandIncludes in compiler/elfwriter.inc handles {$I file} by splicing the
file's text in. For {$I %...%} it takes a deliberate different arm:
{ NOT an include: `{$I+}` / `{$I-}` IO-checking switches and
`{$I %MACRO%}` compile-time info macros -- leave them in the text
for the lexer (the hard miss-error below must not fire on them). }
The {$I±} half of that comment is true. The %MACRO% half is not: nothing in
paslexer.inc handles it. ProcessPasDirective has no arm for it, so the whole
{...} is consumed as an unrecognised directive and the const initialiser is
left empty.
Two independent defects, and the second is the general one. Even with the
macros unimplemented, const d = ; should not compile. It yields 0 — the
empty/default collision this project's own rules warn about, where "the
machinery did nothing" and "the answer is 0" are the same observation.
Why it is not a small fix
The pre-pass is the right place (it already substitutes text, and substituting a
quoted literal is the same operation, with the cursor advance already written).
But %DATE%/%TIME% need a runtime clock on both build paths:
- under real FPC the compiler already imports
SysUtils, so that arm is free; - under self-host it needs a raw
clock_gettime, and the syscall number is host-architecture-specific.compiler/elfwriter.incalready hardcodes x86-64 numbers (217 for getdents64) with only aCPU_WASM32escape, so following that pattern would widen an existing latent portability assumption rather than add a new one — worth deciding rather than copying.
Plus civil-from-days arithmetic (the compiler has none: no EncodeDate, no
1970 anywhere), a local-vs-UTC decision (fpc's %DATE% is local), and the
environment fallback — for which the compiler has no getenv at all.
Suggested shape
Substitute in ExpandIncludes, emitting a quoted literal with '' doubling.
Implement the macros that need no clock and no environment first
(%FILE%, %LINE%, and the target names from TargetArchName), and error
loudly on every macro not implemented rather than leaving any of them at 0 —
refusing a program fpc accepts is a smaller harm than compiling a different one,
and it is the half of this ticket that can land before the clock question is
settled.