← board

Str() builtin breaks for float formatting when a unit shadows Copy

Problem

The compiler's Str builtin for float formatting — Str(d:width:decimals, s) — emits a semantic error when any uses unit declares its own Copy function. The error message is:

Str: expected integer decimals after : ()

The Str builtin's codegen for the float-width-decimals form internally relies on intercepting a Copy call. When a user-declared Copy (e.g. in sysutils) shadows that intercept, the compiler can no longer resolve the decimal-count argument and fails.

Reproduction

program repro;
uses sysutils;  // declares function Copy(...)
var
  d: Double;
  s: AnsiString;
begin
  d := 3.14;
  Str(d:0:2, s);  // ERROR: Str: expected integer decimals after : ()
end.

Without uses sysutils the same Str(d:0:2, s) compiles and works correctly. The integer form Str(i:width, s) is unaffected — only the float width:decimals variant breaks.

Root cause

The Str float-decimal codegen path internally emits or expects a Copy symbol that the compiler intercepts. When Copy is redeclared in a uses unit, the intercept binds to the wrong symbol (the user's Copy instead of the compiler's internal one), and the decimal-count operand is lost.

Workaround

Avoid Str for float formatting when sysutils (or any unit declaring Copy) is in scope. Implement float-to-string conversion manually using Trunc/Frac/Round, as done in lib/rtl/sysutils.pas FloatToStr.

Fix direction

The compiler's Str-builtin codegen should resolve Copy against the compiler's internal symbol table, not the user-visible namespace, so that a user-declared Copy cannot break the intercept. Alternatively, emit the float-decimal conversion inline without relying on a Copy intercept at all.

Log

CLOSED (non-reproducible) 2026-06-20 — pinned v26

Re-verified on v26: the exact repro (uses sysutils declaring Copy + Str(d:0:2, s)) compiles and prints 3.14. The Copy resolution is procIdx-gated so a user Copy and the dynarray intrinsic no longer collide ([[design-overloadable-intrinsics]]). No failing source was supplied since the v20-era filing. Closing as non-repro; reopen with exact failing source (arg shapes / nested uses order) if it recurs.