fix(codegen): allow @ on dynamic-array elements

EmitAddrOfExpr's TStringSubscriptExpr block enumerated only static
and open arrays.  Taking the address of a dyn-array element via
`@A[i]` fell through to the generic L-value path, which didn't know
about TStringSubscriptExpr and raised

  Code generation error: Unsupported L-value form for var argument

Read paths and SetLength already worked; only @-of-subscript broke.
Add a tyDynArray branch that mirrors the open-array shape (no
LowBound, element size from TDynArrayTypeDesc.ElementType.RawSize) —
EmitExpr on a dyn-array var already returns the heap data pointer.

E2E regression test in cp.test.e2e.dynarray.pas writes through the
captured PRec to confirm the address points at the right element.
This commit is contained in:
Andrew Haines 2026-05-31 17:56:01 -04:00 committed by Graeme Geldenhuys
parent f6bb0ee029
commit d0128ed645
2 changed files with 44 additions and 0 deletions

View file

@ -10497,6 +10497,23 @@ begin
Result := ElemPtr;
Exit;
end;
{ Dynamic-array element address: @A[I] — EmitExpr on a dyn-array var
already returns the heap data pointer, so the address computation
mirrors the open-array case (no LowBound). }
if Sub.StrExpr.ResolvedType.Kind = tyDynArray then
begin
ElemSize := TDynArrayTypeDesc(Sub.StrExpr.ResolvedType).ElementType.RawSize;
StrPtr := EmitExpr(Sub.StrExpr);
IdxW := EmitExpr(Sub.IndexExpr);
IdxL := AllocTemp;
Offset := AllocTemp;
ElemPtr := AllocTemp;
EmitLine(Format(' %s =l extsw %s', [IdxL, IdxW]));
EmitLine(Format(' %s =l mul %s, %d', [Offset, IdxL, ElemSize]));
EmitLine(Format(' %s =l add %s, %s', [ElemPtr, StrPtr, Offset]));
Result := ElemPtr;
Exit;
end;
end;
if AExpr.Expr is TIdentExpr then
begin

View file

@ -57,6 +57,7 @@ type
procedure TestCodegen_DynArray_Write_ComputesOffset;
procedure TestCodegen_DynArray_High_CallsRTL;
procedure TestCodegen_DynArray_Low_ReturnsZero;
procedure TestCodegen_DynArray_AddrOfElement_ComputesOffset;
procedure TestSemantic_DynArray_High_Accepted;
procedure TestSemantic_DynArray_Low_Accepted;
end;
@ -426,6 +427,32 @@ begin
Pos('copy 0', IR) > 0);
end;
procedure TDynArrayTests.TestCodegen_DynArray_AddrOfElement_ComputesOffset;
var IR: string;
begin
{ Regression: @A[i] on a dyn-array used to raise "Unsupported L-value
form for var argument" because EmitAddrOfExpr only handled static
and open arrays. The fix mirrors the open-array shape (element
size from TDynArrayTypeDesc.ElementType.RawSize, no LowBound).
Verify the offset arithmetic + base load are emitted without
going through a temporary aggregate copy. }
IR := GenIR('''
program P;
type TRec = record X: Integer; end;
PRec = ^TRec;
var A: array of TRec;
P: PRec;
begin
SetLength(A, 3);
P := @A[1]
end.
''');
AssertTrue('mul for element offset in @A[i]',
Self.CountOccurrences(IR, 'mul') > 0);
AssertTrue('add to combine base + offset',
Self.CountOccurrences(IR, 'add') > 0);
end;
initialization
RegisterTest(TDynArrayTests);