← board

Method call before body: by-value <=8-byte record arg mislowers (i386 error; x64 program-level unresolved-forward)

Repro

program reclaunch3;
type
  TM = record Code: Pointer; Data: Pointer; end;   { 8 bytes on i386 }
  TC = class
    FM: TM;
    procedure S(const m: TM);
  end;
procedure Launch(arg: Pointer);
var t: TC; mt: TM;
begin
  t := TC(arg);
  mt.Code := t.FM.Code;
  mt.Data := t.FM.Data;
  if mt.Code <> nil then
    t.S(mt);                 { call BEFORE TC.S's body is parsed }
end;
procedure TC.S(const m: TM);
begin
  writeln(Int64(m.Code));
end;
var c: TC;
begin
  c := TC.Create; c.FM.Code := Pointer(5); Launch(Pointer(c));
end.

Analysis pointers

Workaround (landed 2026-07-03)

lib/rtl/palthreadobj.pas forward-declares ThreadObjLauncher and defines its body AFTER TThread.Synchronize, so the call lowers with the callee's body already compiled. Revert the reorder when this is fixed (it is commented at the site).

Acceptance

The repro compiles and prints 5 on x86-64 and i386 (qemu) with the callee body in either position; make test + self-host byte-identical.

Root cause + fix (2026-07-03, Track A — same day)

The CLASS-METHOD declaration header parser (and the interface-method one) skipped ParseSubroutine's const record/variant -> by-ref promotion (parser.inc ~13457), so the declared signature said by-VALUE while the implementation header (re-parsed by ParseSubroutine) said by-REF:

Fix: apply the same promotion in both declaration parsers (class methods + interface methods; the proc-type path already had it from bug-proc-typed-call-const-record-arg — classic sibling-branch sweep). palthreadobj's launcher-reorder workaround REVERTED (original order works again). Regression: test/test_const_record_method_prebody.pas (x86-64 + i386 qemu) in make test. Self-host byte-identical.