← board

Integer vs LongInt: the same type lost the exact overload match

Symptom

var i: integer; li: longint;
begin i := -1; li := -1;
  writeln(IntToHex(i, 8));    { FPC FFFFFFFF   pxx FFFFFFFFFFFFFFFF }
  writeln(IntToHex(li, 8));   { FPC FFFFFFFF   pxx FFFFFFFF         }

sysutils declares IntToHex three times — Int64, LongInt, LongWord — precisely so a 32-bit value renders eight digits and not sixteen, and the LongInt body already masks correctly. So the library was right and the binding was wrong: longint hit the exact-match phase, integer fell past it into the compatible phases and bound Int64.

Root cause: integer types as tyInteger and longint as tyInt32 — the same 4-byte signed type wearing two kinds (FPC declares one as the other's alias) — and the exact phase compared kinds directly, so it discriminated on the spelling. integer is the far more common spelling, which is what made the wrong half the visible one.

Fix

MatchParamExact(paramTk, argTk) — the exact-match test used by Phase 1 and Phase 1b — treats tyInteger and tyInt32 as one. A unit cannot legitimately declare both overloads (FPC rejects the pair as duplicate), so unifying them cannot make a previously-unambiguous call ambiguous. The ranking phases below are untouched.

Residuals (both deliberate, both measured)

The default column of test_strict_overload_width moved — deliberately

Track T filed a NEW-RED on this commit within the hour (test-core#src:test/test_strict_overload_width.pas@1), and it was a real expectation change, not a flake. That test asserts BOTH columns from one source; four rows of the default column moved (Integer, literal, MyInt -> longint, and hex -> FFFFFFFF) and the --strict-overload-width column did not move at all.

That is not the flag leaking into the default. Nothing is being ranked: Integer and LongInt are one type, so the LongInt overload is an EXACT match. What the user settled on 2026-08-14 was ranking between DIFFERENT widths, and the default still declines to do it — SmallInt, Byte and Cardinal still widen to Int64 in the unflagged column. The four rows that moved all moved TOWARD FPC, which is the standing default (reference behaviour by default, deviations behind --strict-*). Makefile expectation updated with that reasoning inline.

Gate

make compiler/pascal26 fixedpoint; tools/gate.sh quick GREEN; test/test_integer_longint_overload.pas (IntToHex across integer/longint/ cardinal/int64 plus a hand-written three-way overload set proving which one each spelling binds) matches fpc -O- -Mobjfpc byte for byte. Six earlier sweep probes (sysutils strings, strings/Copy, records, exceptions, OOP dispatch) re-run unchanged.