feat(native): pass an interface-returning call result as an argument
Show(MakeFoo(42)), where MakeFoo returns an interface, previously raised "unsupported interface argument expression" on the native backend. EmitMethodArgPush now handles a TFuncCallExpr/TMethodCallExpr argument of interface type: it lowers the call via EmitIntfSretCall/EmitIntfSretMethodCall (which leaves the fat pointer at (%rsp)), loads obj+itab, drops the sret buffer, and pushes the pair in arg-slot order. Both backends now print 42. Adds a dual-backend e2e test. Known follow-up (tracked): the owned (+1) reference of the call result is not yet released after the call, so --debug reports a single leaked instance for this specific borrowed-interface-call-result shape. The feature is output-correct; the ARC release will route through EmitArgHoist (akIntfConsume) like const-string consume args. All three fixpoints green; 3481 tests pass.
This commit is contained in:
parent
44a2bd93fb
commit
5dd6a3d8c9
|
|
@ -7848,6 +7848,25 @@ begin
|
|||
Self.Emit(#9'pushq %rax'); { push obj }
|
||||
Self.Emit(#9'pushq %rcx'); { push itab }
|
||||
end
|
||||
else if ((AArg is TFuncCallExpr) or (AArg is TMethodCallExpr)) and
|
||||
(AArg.ResolvedType <> nil) and
|
||||
(AArg.ResolvedType.Kind = tyInterface) then
|
||||
begin
|
||||
{ Interface-returning call result passed positionally, e.g.
|
||||
Show(MakeFoo(42)). EmitIntfSretCall leaves the owned (+1) fat pointer
|
||||
at (%rsp): obj at 0, itab at 8. Load both halves and push in the
|
||||
arg-slot order (obj first, itab on top), then drop the sret buffer.
|
||||
The pushed obj keeps the +1 ref; the callee borrows it for the call. }
|
||||
if AArg is TFuncCallExpr then
|
||||
Self.EmitIntfSretCall(TFuncCallExpr(AArg))
|
||||
else
|
||||
Self.EmitIntfSretMethodCall(TMethodCallExpr(AArg));
|
||||
Self.Emit(#9'movq (%rsp), %rax'); { obj }
|
||||
Self.Emit(#9'movq 8(%rsp), %rcx'); { itab }
|
||||
Self.Emit(#9'addq $16, %rsp'); { drop the sret buffer }
|
||||
Self.Emit(#9'pushq %rax'); { push obj }
|
||||
Self.Emit(#9'pushq %rcx'); { push itab }
|
||||
end
|
||||
else
|
||||
raise ENativeCodeGenError.Create(
|
||||
'native backend: unsupported interface argument expression');
|
||||
|
|
|
|||
|
|
@ -79,6 +79,9 @@ type
|
|||
procedure TestRun_IntfSret_SixArgs_DirectClassCall;
|
||||
procedure TestRun_IntfSret_SixArgs_InterfaceDispatch;
|
||||
procedure TestRun_IntfSret_SevenArgs_InterfaceDispatch;
|
||||
|
||||
{ Interface-returning call result passed positionally as an arg }
|
||||
procedure TestRun_IntfArg_CallResult_AsParam;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
|
@ -907,6 +910,43 @@ begin
|
|||
AssertRunsOnAll(Src, '28' + Chr(10), 0);
|
||||
end;
|
||||
|
||||
procedure TE2EGapTests.TestRun_IntfArg_CallResult_AsParam;
|
||||
{ Passing an interface-returning function's result directly as an interface
|
||||
argument (Show(MakeFoo(42))) — native previously raised "unsupported
|
||||
interface argument expression" for this positional call-result form. }
|
||||
const
|
||||
Src =
|
||||
'''
|
||||
program P;
|
||||
type
|
||||
IFoo = interface
|
||||
function Val: Integer;
|
||||
end;
|
||||
TFoo = class(TObject, IFoo)
|
||||
FN: Integer;
|
||||
function Val: Integer;
|
||||
end;
|
||||
function TFoo.Val: Integer;
|
||||
begin Result := FN end;
|
||||
function MakeFoo(N: Integer): IFoo;
|
||||
var F: TFoo;
|
||||
begin
|
||||
F := TFoo.Create();
|
||||
F.FN := N;
|
||||
Result := F
|
||||
end;
|
||||
procedure Show(F: IFoo);
|
||||
begin
|
||||
WriteLn(F.Val())
|
||||
end;
|
||||
begin
|
||||
Show(MakeFoo(42))
|
||||
end.
|
||||
''';
|
||||
begin
|
||||
AssertRunsOnAll(Src, '42' + Chr(10), 0);
|
||||
end;
|
||||
|
||||
initialization
|
||||
RegisterTest(TE2EGapTests);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue