feat(stdlib): TList<T> default array property — List[i] subscript
TList<T> now exposes a `default` array property, so elements can be read
and written with the familiar subscript syntax instead of .Get(i):
L[0] := 100; // L.SetItem(0, 100)
WriteLn(L[0] + L[1]); // L.Get(i)
Adds TList<T>.SetItem (a pointer-target store, so the compiler's ARC
discipline releases the old element and retains the new for managed T)
and the property Items[AIndex]: T read Get write SetItem; default.
Also fixes the generic-instance clone path in the semantic analyser: it
copied every property-decl field except IsDefault, so a default property
declared on a generic template (TList<T>) lost its default flag on
instantiation and List[i] did not resolve. The clone now carries
IsDefault.
Verified List[i] read, write, and polymorphic element dispatch
(List[i].Area through a base-typed element) on both backends. Adds
TE2ETListTests.TestRun_TList_DefaultProperty_{ReadWrite,Polymorphic}.
Note: this makes the stdlib depend on the `default` directive (added in
the previous commit), so the pre-release bootstrap binary is refreshed to
a default-aware stage-2 in a follow-up.
This commit is contained in:
parent
798df8eb15
commit
42ca9d7535
|
|
@ -2386,6 +2386,7 @@ begin
|
|||
NewPDecl.WriteName := PDecl.WriteName;
|
||||
NewPDecl.IndexParamName := PDecl.IndexParamName;
|
||||
NewPDecl.IndexTypeName := SubstTypeParam(PDecl.IndexTypeName, Templ.ParamNames, Args);
|
||||
NewPDecl.IsDefault := PDecl.IsDefault;
|
||||
ClonedCD.Properties.Add(NewPDecl);
|
||||
end;
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ type
|
|||
reference was a local in an exited routine must survive in the list
|
||||
(decision recorded in language-rationale: collection ownership). }
|
||||
procedure TestRun_TList_ClassElements_RetainedAcrossScope;
|
||||
|
||||
{ Default array property: List[i] subscript for read and write. }
|
||||
procedure TestRun_TList_DefaultProperty_ReadWrite;
|
||||
procedure TestRun_TList_DefaultProperty_Polymorphic;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
|
@ -207,6 +211,69 @@ begin
|
|||
AssertEquals('retained elements readable (native)', '77' + #10 + '88' + #10, Output);
|
||||
end;
|
||||
|
||||
const
|
||||
SrcTListDefaultRW = '''
|
||||
program P;
|
||||
uses Generics.Collections;
|
||||
var lst: TList<Integer>;
|
||||
begin
|
||||
lst := TList<Integer>.Create;
|
||||
lst.Add(1); lst.Add(2);
|
||||
lst[0] := 100; lst[1] := 200;
|
||||
WriteLn(lst[0] + lst[1]);
|
||||
lst.Free()
|
||||
end.
|
||||
''';
|
||||
|
||||
SrcTListDefaultPoly = '''
|
||||
program P;
|
||||
uses Generics.Collections;
|
||||
type
|
||||
TShape = class function Area: Integer; virtual; begin Result := 0 end; end;
|
||||
TBox = class(TShape)
|
||||
FS: Integer;
|
||||
constructor Create(s: Integer); begin FS := s end;
|
||||
function Area: Integer; override; begin Result := FS * FS end;
|
||||
end;
|
||||
var lst: TList<TShape>; i, total: Integer;
|
||||
begin
|
||||
lst := TList<TShape>.Create;
|
||||
lst.Add(TBox.Create(3)); lst.Add(TBox.Create(4));
|
||||
total := 0;
|
||||
for i := 0 to lst.Count - 1 do total := total + lst[i].Area();
|
||||
WriteLn(total);
|
||||
lst.Free()
|
||||
end.
|
||||
''';
|
||||
|
||||
procedure TE2ETListTests.TestRun_TList_DefaultProperty_ReadWrite;
|
||||
var Output: string; RCode: Integer;
|
||||
begin
|
||||
if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end;
|
||||
AssertTrue('compile+run (qbe)',
|
||||
CompileAndRunWithRTLDebugOn(beQBE, SrcTListDefaultRW, Output, RCode, False));
|
||||
AssertEquals('exit code (qbe)', 0, RCode);
|
||||
AssertEquals('List[i] read/write (qbe)', '300' + #10, Output);
|
||||
AssertTrue('compile+run (native)',
|
||||
CompileAndRunWithRTLDebugOn(beNative, SrcTListDefaultRW, Output, RCode, False));
|
||||
AssertEquals('exit code (native)', 0, RCode);
|
||||
AssertEquals('List[i] read/write (native)', '300' + #10, Output);
|
||||
end;
|
||||
|
||||
procedure TE2ETListTests.TestRun_TList_DefaultProperty_Polymorphic;
|
||||
var Output: string; RCode: Integer;
|
||||
begin
|
||||
if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end;
|
||||
AssertTrue('compile+run (qbe)',
|
||||
CompileAndRunWithRTLDebugOn(beQBE, SrcTListDefaultPoly, Output, RCode, False));
|
||||
AssertEquals('exit code (qbe)', 0, RCode);
|
||||
AssertEquals('List[i].Area (qbe)', '25' + #10, Output);
|
||||
AssertTrue('compile+run (native)',
|
||||
CompileAndRunWithRTLDebugOn(beNative, SrcTListDefaultPoly, Output, RCode, False));
|
||||
AssertEquals('exit code (native)', 0, RCode);
|
||||
AssertEquals('List[i].Area (native)', '25' + #10, Output);
|
||||
end;
|
||||
|
||||
initialization
|
||||
RegisterTest(TE2ETListTests);
|
||||
|
||||
|
|
|
|||
|
|
@ -36,12 +36,15 @@ type
|
|||
procedure Grow;
|
||||
procedure Add(Value: T);
|
||||
function Get(AIndex: Integer): T;
|
||||
procedure SetItem(AIndex: Integer; Value: T);
|
||||
function IndexOf(Value: T): Integer;
|
||||
procedure Delete(AIndex: Integer);
|
||||
procedure Clear;
|
||||
procedure Destroy;
|
||||
function GetEnumerator: TListEnumerator<T>;
|
||||
property Count: Integer read FCount;
|
||||
{ Default array property — enables List[i] for read and write. }
|
||||
property Items[AIndex: Integer]: T read Get write SetItem; default;
|
||||
end;
|
||||
|
||||
{ Generic LIFO stack backed by a dynamic array. Push/Pop/Peek operate on
|
||||
|
|
@ -262,6 +265,16 @@ begin
|
|||
Result := Src^
|
||||
end;
|
||||
|
||||
procedure TList<T>.SetItem(AIndex: Integer; Value: T);
|
||||
var
|
||||
Dest: ^T;
|
||||
begin
|
||||
{ The ^T := Value store carries the compiler's ARC discipline for a managed
|
||||
T — the previous element is released and the new one retained. }
|
||||
Dest := Self.FData + AIndex * SizeOf(T);
|
||||
Dest^ := Value
|
||||
end;
|
||||
|
||||
function TList<T>.IndexOf(Value: T): Integer;
|
||||
var
|
||||
I: Integer;
|
||||
|
|
|
|||
Loading…
Reference in a new issue