diff --git a/compiler/src/main/pascal/uSemantic.pas b/compiler/src/main/pascal/uSemantic.pas index 0194305..5a4fc55 100644 --- a/compiler/src/main/pascal/uSemantic.pas +++ b/compiler/src/main/pascal/uSemantic.pas @@ -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; diff --git a/compiler/src/test/pascal/cp.test.e2e.tlist.pas b/compiler/src/test/pascal/cp.test.e2e.tlist.pas index b049b49..e380d40 100644 --- a/compiler/src/test/pascal/cp.test.e2e.tlist.pas +++ b/compiler/src/test/pascal/cp.test.e2e.tlist.pas @@ -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; + begin + lst := TList.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; i, total: Integer; + begin + lst := TList.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); diff --git a/stdlib/src/main/pascal/generics.collections.pas b/stdlib/src/main/pascal/generics.collections.pas index d25dc3f..60d4a18 100644 --- a/stdlib/src/main/pascal/generics.collections.pas +++ b/stdlib/src/main/pascal/generics.collections.pas @@ -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; 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.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.IndexOf(Value: T): Integer; var I: Integer;