fix(native): interface-to-field store from field-access and implicit-Self sources

EmitInterfaceToFieldSlotsAt now handles two additional source expressions
when storing an interface value into a class/record field:

- TFieldAccessExpr: uses EmitInterfaceFieldAddr to resolve the contiguous
  fat pointer address from a nested field access (e.g. Obj.IntfField),
  then reads obj+itab from that address.

- Implicit-Self TIdentExpr: when the source identifier is an implicit
  Self field (e.g. bare FIntf inside a method body), reads the fat pointer
  directly from Self+offset instead of looking up split _obj/_itab slots.

Both paths read the contiguous (obj, itab) layout at the resolved address
and push them for the subsequent store logic.
This commit is contained in:
Graeme Geldenhuys 2026-06-10 08:14:42 +01:00
parent d19f2afbab
commit c12177e090

View file

@ -2138,18 +2138,37 @@ begin
a record/class field uses a contiguous fat pointer (obj / obj+8). }
if AExpr is TIdentExpr then
begin
Self.Emit(Format(#9'movq %s, %%rax',
[Self.IntfItabOperand(TIdentExpr(AExpr).Name, TIdentExpr(AExpr).IsGlobal)]));
Self.Emit(#9'pushq %rax'); { itab }
Self.Emit(Format(#9'movq %s, %%rax',
[Self.IntfObjOperand(TIdentExpr(AExpr).Name, TIdentExpr(AExpr).IsGlobal)]));
Self.Emit(#9'pushq %rax'); { obj; stack now (obj, itab) }
if TIdentExpr(AExpr).IsImplicitSelf and
(TIdentExpr(AExpr).ImplicitFieldInfo <> nil) then
begin
Self.Emit(Format(#9'movq %s, %%rax', [Self.VarOperand('Self')]));
if TFieldInfo(TIdentExpr(AExpr).ImplicitFieldInfo).Offset > 0 then
Self.Emit(Format(#9'addq $%d, %%rax',
[TFieldInfo(TIdentExpr(AExpr).ImplicitFieldInfo).Offset]));
Self.Emit(#9'movq 8(%rax), %rcx');
Self.Emit(#9'pushq %rcx');
Self.Emit(#9'movq (%rax), %rcx');
Self.Emit(#9'pushq %rcx');
end
else
begin
Self.Emit(Format(#9'movq %s, %%rax',
[Self.IntfItabOperand(TIdentExpr(AExpr).Name, TIdentExpr(AExpr).IsGlobal)]));
Self.Emit(#9'pushq %rax');
Self.Emit(Format(#9'movq %s, %%rax',
[Self.IntfObjOperand(TIdentExpr(AExpr).Name, TIdentExpr(AExpr).IsGlobal)]));
Self.Emit(#9'pushq %rax');
end;
end
else if AExpr is TFieldAccessExpr then
begin
Self.EmitInterfaceFieldAddr(TFieldAccessExpr(AExpr), '%rax');
Self.Emit(#9'movq 8(%rax), %rcx');
Self.Emit(#9'pushq %rcx');
Self.Emit(#9'movq (%rax), %rcx');
Self.Emit(#9'pushq %rcx');
end
else
{ An interface stored in another record/class field (a TFieldAccessExpr
source) would need its contiguous fat pointer read from memory. That
receiver-shape resolution is only implemented in the QBE backend; the
native backend handles the common ident and class sources. }
raise ENativeCodeGenError.Create(
'native backend: unsupported interface source for interface-field store');
Self.Emit(#9'movq (%rsp), %rdi'); { new obj }