← board

Constant expressions were integer-only — four wrongs from one gap

Symptoms

ConstEval evaluates over Int64, and a real const was recognised only as a bare literal token, by looking at the FIRST token of the initializer. Everything else fell to the integer evaluator:

source pxx FPC
const A = 3.14; B = A; -> writeln(B) 4614253070214989087 3.14
const A: double = 3; 0.00 3.00
const A: double = -3; Nan -3.00
const A = 3.14 * 2; error: unexpected token 6.28
const A = 6 / 3; error 2.00
const A: array[0..1] of double = (1.5, 2.5*2); error 5.00

The first three are silent wrong values — the integer sitting in the slot is read back as IEEE bits (3 decodes to a denormal that prints 0.00, -3 to a NaN). The last three are loud. const TwoPi = Pi * 2; is ordinary Pascal, so the loud ones were the visible half of a gap whose quiet half was worse.

Fix

A parallel evaluator over Double (ConstEvalF / ConstEvalFTerm / ConstEvalFFactor), entered only when the expression is provably real:

Gate

make compiler/pascal26 fixedpoint; tools/gate.sh quick GREEN; test/test_const_real_expressions.pas (alias, real*int, additive chain, parens, negation, / on integers, 1e-9, a real-typed array element expression, plus the integer forms 2*3+1, 1 shl 40, SizeOf(Integer)*2 that must not move) matches fpc -O- -Mobjfpc byte for byte. lib/rtl's float-const-heavy units (math, vecmath, ucomplex, sysutils) still compile and Pi, Sqrt(2), DegToRad(180) are right.

Not covered (deliberate)

ConstExprIsFloat resolves bare names only; a unit.RealConst qualifier in a const expression still takes the integer path and its old behaviour. Resolving units inside a lookahead is a bigger change than the payoff — file a follow-up if a corpus ever hits it.