← board

bug: cross gates red on two pre-existing tests (were masked behind ArgStr)

Background

The cross gate has been red at test_arm32_arg_runtime since that test landed (7b20bef, 2026-06-23). With ArgStr now fixed (bug-argstr-managed-dest-cross, 2026-06-24) the gate runs further and exposes two failures that were accumulating unnoticed behind the early stop. Both reproduce on HEAD with the ArgStr change stashed, so neither is a regression from that work.

Failure 1 — test_cross_frozen_strlen_deref (i386 + arm32)

Run under -dPXX_MANAGED_STRING. Cross output diverges from the x86-64 oracle on the 2nd/3rd lines:

i386 / arm32 : 5 / 1869566548 / 1869566548 / 26984 / 26984
x86-64 oracle: 5 / 500085772884 / 500085772884 / 26984 / 26984

A done ticket exists — bug-frozen-string-length-pointer-deref-cross (resolved 2026-06-19, Length(p^) of a frozen string returning 0 on cross). The current divergence is a different number (not 0), so either that fix regressed for this test shape or the test was added/extended afterward in a state that never passed cross. Note x86-64 itself prints 500085772884 (a 64-bit read of string bytes), so the test asserts cross==x86-64 on what is already an odd value — re-examine whether the test or the codegen is wrong. aarch64 was not reached (it dies earlier on Failure 2).

Failure 2 — test_classref (aarch64)

./compiler/pascal26 --target=aarch64 test/test_classref.pas /tmp/x
pascal26:186: error: target aarch64: load through pointer of this type not yet supported ()

The 186 is a line in typinfo (uses typinfo; the test is 36 lines), not the test — specifically GetClass's loop if entries[i].NamePtr^ = name then (typinfo.pas ~180), where NamePtr: PString = ^TRttiStr and TRttiStr = string[255].

Root cause (investigated 2026-06-24, not yet fixed — bigger than a quick patch)

This is NOT really classref/metaclass and NOT aarch64-only — it is a frozen/inline string dereferenced through a pointer field, broken on every target:

Minimal repro (segfaults on x86-64, errors on aarch64):

type TRttiStr = string[255]; PString = ^TRttiStr;
     TEntry = record NamePtr: PString; X: Integer; end; PEntry = ^TEntry;
var arr: array[0..1] of TEntry; s0: TRttiStr; entries: PEntry; name: string; i: Integer;
begin
  s0 := 'TFoo'; arr[0].NamePtr := @s0; entries := @arr[0]; name := 'TFoo';
  for i := 0 to 1 do if entries[i].NamePtr^ = name then writeln('match ', i);
end.

Proper fix (sketch)

Don't mutate the tag of the left address node in place when it is a load that actually fetches the pointer (IR_LOAD_MEM/IR_FIELD/IR_INDEX): the pointer must still be loaded pointer-width, only presented as a frozen-string address. Either wrap it in a tag-only pass-through node, or keep left as tyPointer and carry the frozen-string-ness on the consuming op. Also note string[255] here resolves to legacy tyString (ord 4), not tyFixedString — the migration alias is inconsistent and worth pinning down at the same time. (An attempt that merely widened the AN_DEREF type set and/or the aarch64 guard was reverted: it fixes the simple ps^ case but not the pointer-field case, and the underlying in-place re-tag of a pointer-load is the real defect.) i386/arm32 were not separately checked but share the same ir.inc lowering, so expect the same break.

Acceptance

Repro

git stash    # (if the ArgStr change is uncommitted; not needed once committed)
./compiler/pascal26 -dPXX_MANAGED_STRING --target=i386 test/test_cross_frozen_strlen_deref.pas /tmp/fs
tools/run_target.sh i386 /tmp/fs                 # vs the x86-64 build's output
./compiler/pascal26 --target=aarch64 test/test_classref.pas /tmp/cr   # errors at :186

Resolution (2026-06-24)

Both documented sub-failures fixed (commit 76c0cd4):

make test-aarch64 is now fully green. x86-64 gate green, self-host byte-identical.

Newly exposed (filed separately — not regressions, were masked behind the above)