← board

PChar → string implicit conversion missing in call args (and assignment helper)

Symptom

FPC implicitly converts PChar to string/AnsiString when passing an argument or assigning. PXX does not:

function f(const s: string): Integer; begin Result := Length(s); end;
var pc: PChar;
begin pc := 'abc'; writeln(f(pc)); end.

Mismatch in MatchProcCall: name = f, nArgs = 1 (overload resolution does not consider PChar assignable to a string parameter).

Plain assignment is half-wired — it tries to convert but the helper is not found in a minimal program:

var s: string; pc: PChar;
begin pc := 'abc'; s := pc; end.

compiler error: PCharToString helper not found

So there are two faces of the same gap:

  1. Argument passing / overload match (MatchProcCall): PChar is not treated as convertible to a string param, so the call does not match at all.
  2. Assignment: the conversion path exists (emits a PCharToString helper call) but the helper is not always resolvable.

Why platonic (not a lib workaround)

PXX currently compiles Synapse only because lib/rtl/dynlibs.pas adds an extra PChar overload of LoadLibrary/GetProcedureAddress to dodge gap #1. That is a workaround, not idiomatic — FPC's dynlibs does not need a separate PChar overload because the language converts the argument. Once this lands, remove those PChar overloads from lib/rtl/dynlibs.pas (string overload only) and re-verify the synafpc probe still compiles.

Fix sketch

Done when

Resolution (2026-06-25, Track A)

Done. (1) Overload match: MatchCallDelphiProcAddr retries a failed match treating each PChar arg as AnsiString; on success wraps every PChar arg whose matched param is a string in PCharToString. Only fires after a normal match fails (never overrides a stricter overload). (2) Helper availability: a PChar/PAnsiChar token in scope pulls the builtin unit (gated by not NoDefaultRtl so the compiler's own self-build is untouched), fixing the standalone s := pc "helper not found". (3) lib/rtl/dynlibs.pas PChar overloads removed — LoadLibrary/GetProcedureAddress take const string, PChar callers (Synapse SynaFpc) bind via the conversion; verified LoadLibrary(pc) compiles + loads.

Scope: the reverse pc := 'literal' (string->PChar, stores the inline length- prefix addr) is a SEPARATE gap, left unchanged per "keep string->PChar unchanged". Tests: test/test_pchar_to_string.pas. Self-host byte-identical; make test + cross green.