← board

An unsigned dividend made the whole division unsigned

Repro

var w: Word; si: SmallInt;
begin
  w := 40000; si := -25536;
  writeln(w div si, ' ', w mod si);   { FPC: -1 14464   pxx: 0 40000 }
end.

Every width was affected and every one of them silently:

operands expression FPC pxx
Word / SmallInt 40000 div -25536 -1 0
Byte / ShortInt 200 div -3 -66 0
LongWord / Integer 3000000000 div -7 -428571428 0
QWord / Int64 high(QWord) div -5 0 1

The boundary is sharp and is what named the cause: every case with a SIGNED dividend agreed (si div w, sh div b, i div c), and the explicitly-cast Integer(w) div Integer(si) was right. So it was not the division codegen — it was which division the codegen was asked for.

Cause

TypeDivideUnsigned read one operand:

function TypeDivideUnsigned(dividend: TTypeKind): Boolean;
begin
  Result := TypeIsOrdinal(dividend) and not TypeSigned(dividend);
end;

Its comparison sibling four screens up, TypeCompareUnsigned(lhs, rhs), has read BOTH operands for two csmith fixes now. Two mechanisms for one concept — "which signedness domain do these two operands live in" — and, exactly as normalise-dont-special-case.md predicts, the one that was never given the second operand is the one that stayed broken.

Fix

One two-operand rule, one place, six consumers:

The C half

Not a Pascal-only bug, though it was found from Pascal. Against gcc, the pinned binary got three of fourteen lines wrong — uchar/schar and ushort/short (C promotes both to int, so the division is signed) and long/ulong (C converts the signed dividend to unsigned, so it is unsigned — the case where the dividend-only rule erred in the other direction). All fourteen agree now. C's existing CTrunc32 normalisation in cparser.inc is untouched and still carries the equal-rank 32-bit case.

Gate

test/test_div_mod_mixed_signedness.pas — 35 assertions, every expectation fpc -O- -Mobjfpc 3.2.2's: the four broken widths, the signed-dividend cases that had to stay put, all-unsigned pairs, unsigned-over-positive-literal at three widths, mixed widths, and two sign checks that pin the RESULT type rather than just the bits. Wired into make test. tools/gate.sh quick GREEN, self-host fixedpoint byte-identical, C probe identical to gcc.