feat: static array array[L..H] of T — stack-allocated fixed-size arrays
Adds TStaticSubscriptAssign AST node, ParseTypeName extension for
array[L..H] of T syntax (tkDotDot token), TStaticArrayTypeDesc with
RawSize for correct element-footprint sizing (1 for Byte, 4 for Integer),
and full codegen: alloc4/alloc8 + memset zero-init, storeb/storew for
writes, loadub/loadw for reads, LowBound subtraction for non-zero ranges.
Also includes array literal ['a','b','c'] call-site support (step 4):
TArrayLiteralExpr, open-array formal matching, stack buffer allocation,
and codegen emitting two QBE args (data ptr + compile-time high index).
13 new static-array tests + 9 new array-literal tests; all 888 tests pass.
2026-04-27 19:22:29 +03:00
|
|
|
|
{
|
|
|
|
|
|
Blaise - An Object Pascal Compiler
|
|
|
|
|
|
Copyright (c) 2026 Graeme Geldenhuys
|
2026-05-03 21:45:29 +03:00
|
|
|
|
SPDX-License-Identifier: Apache-2.0 WITH Swift-exception
|
|
|
|
|
|
Licensed under the Apache License v2.0 with Runtime Library Exception.
|
feat: static array array[L..H] of T — stack-allocated fixed-size arrays
Adds TStaticSubscriptAssign AST node, ParseTypeName extension for
array[L..H] of T syntax (tkDotDot token), TStaticArrayTypeDesc with
RawSize for correct element-footprint sizing (1 for Byte, 4 for Integer),
and full codegen: alloc4/alloc8 + memset zero-init, storeb/storew for
writes, loadub/loadw for reads, LowBound subtraction for non-zero ranges.
Also includes array literal ['a','b','c'] call-site support (step 4):
TArrayLiteralExpr, open-array formal matching, stack buffer allocation,
and codegen emitting two QBE args (data ptr + compile-time high index).
13 new static-array tests + 9 new array-literal tests; all 888 tests pass.
2026-04-27 19:22:29 +03:00
|
|
|
|
See LICENSE file in the project root for full license terms.
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
unit cp.test.staticarray;
|
|
|
|
|
|
|
|
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
|
|
|
|
|
|
|
|
{ Tests for static array declarations and element access:
|
|
|
|
|
|
parsing, semantic analysis, and QBE IR code generation. }
|
|
|
|
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
|
|
|
|
uses
|
|
|
|
|
|
Classes, SysUtils, fpcunit, testregistry,
|
|
|
|
|
|
uLexer, uParser, uAST, uSymbolTable, uSemantic, uCodeGenQBE;
|
|
|
|
|
|
|
|
|
|
|
|
type
|
|
|
|
|
|
TStaticArrayTests = class(TTestCase)
|
|
|
|
|
|
private
|
|
|
|
|
|
function ParseSrc(const ASrc: string): TProgram;
|
|
|
|
|
|
function AnalyseSrc(const ASrc: string): TProgram;
|
|
|
|
|
|
function GenIR(const ASrc: string): string;
|
|
|
|
|
|
published
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
{ Parser }
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
procedure TestParse_StaticArray_TypeName;
|
|
|
|
|
|
procedure TestParse_StaticArray_SubscriptAssign_Name;
|
|
|
|
|
|
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
{ Semantic }
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
procedure TestSemantic_StaticArray_Kind;
|
|
|
|
|
|
procedure TestSemantic_StaticArray_ElementType;
|
|
|
|
|
|
procedure TestSemantic_StaticArray_Bounds;
|
|
|
|
|
|
procedure TestSemantic_StaticArray_ByteSize;
|
|
|
|
|
|
procedure TestSemantic_StaticArray_IntArray;
|
|
|
|
|
|
procedure TestSemantic_StaticArray_NonZero_LowBound;
|
|
|
|
|
|
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
{ Codegen }
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
procedure TestCodegen_StaticArray_AllocEmitted;
|
|
|
|
|
|
procedure TestCodegen_StaticArray_MemsetEmitted;
|
|
|
|
|
|
procedure TestCodegen_StaticArray_WriteEmitted;
|
|
|
|
|
|
procedure TestCodegen_StaticArray_ReadEmitted;
|
|
|
|
|
|
procedure TestCodegen_StaticArray_NonZero_OffsetSubtracted;
|
2026-04-28 14:35:05 +03:00
|
|
|
|
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
{ Low / High }
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
procedure TestSemantic_StaticArray_Low_ReturnsInteger;
|
|
|
|
|
|
procedure TestSemantic_StaticArray_High_ReturnsInteger;
|
|
|
|
|
|
procedure TestCodegen_StaticArray_Low_EmitsLowBound;
|
|
|
|
|
|
procedure TestCodegen_StaticArray_High_EmitsHighBound;
|
2026-04-28 15:53:12 +03:00
|
|
|
|
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
{ Address-of }
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
procedure TestParse_AddrOf_NodeType;
|
|
|
|
|
|
procedure TestSemantic_AddrOf_ReturnsPointerType;
|
|
|
|
|
|
procedure TestSemantic_AddrOf_BaseTypeIsByte;
|
|
|
|
|
|
procedure TestCodegen_AddrOf_NoLoad;
|
|
|
|
|
|
procedure TestCodegen_AddrOf_AddressArithmetic;
|
feat: static array array[L..H] of T — stack-allocated fixed-size arrays
Adds TStaticSubscriptAssign AST node, ParseTypeName extension for
array[L..H] of T syntax (tkDotDot token), TStaticArrayTypeDesc with
RawSize for correct element-footprint sizing (1 for Byte, 4 for Integer),
and full codegen: alloc4/alloc8 + memset zero-init, storeb/storew for
writes, loadub/loadw for reads, LowBound subtraction for non-zero ranges.
Also includes array literal ['a','b','c'] call-site support (step 4):
TArrayLiteralExpr, open-array formal matching, stack buffer allocation,
and codegen emitting two QBE args (data ptr + compile-time high index).
13 new static-array tests + 9 new array-literal tests; all 888 tests pass.
2026-04-27 19:22:29 +03:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
|
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
{ Helpers }
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
|
|
|
|
|
|
function TStaticArrayTests.ParseSrc(const ASrc: string): TProgram;
|
|
|
|
|
|
var L: TLexer; P: TParser;
|
|
|
|
|
|
begin
|
|
|
|
|
|
L := TLexer.Create(ASrc);
|
|
|
|
|
|
P := TParser.Create(L);
|
|
|
|
|
|
try
|
|
|
|
|
|
Result := P.Parse;
|
|
|
|
|
|
finally
|
|
|
|
|
|
P.Free; L.Free;
|
|
|
|
|
|
end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TStaticArrayTests.AnalyseSrc(const ASrc: string): TProgram;
|
|
|
|
|
|
var A: TSemanticAnalyser;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := ParseSrc(ASrc);
|
|
|
|
|
|
A := TSemanticAnalyser.Create;
|
|
|
|
|
|
try
|
|
|
|
|
|
A.Analyse(Result);
|
|
|
|
|
|
finally
|
|
|
|
|
|
A.Free;
|
|
|
|
|
|
end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TStaticArrayTests.GenIR(const ASrc: string): string;
|
|
|
|
|
|
var P: TProgram; CG: TCodeGenQBE;
|
|
|
|
|
|
begin
|
|
|
|
|
|
P := AnalyseSrc(ASrc);
|
|
|
|
|
|
try
|
|
|
|
|
|
CG := TCodeGenQBE.Create;
|
|
|
|
|
|
try
|
|
|
|
|
|
CG.Generate(P);
|
|
|
|
|
|
Result := CG.GetOutput;
|
|
|
|
|
|
finally
|
|
|
|
|
|
CG.Free;
|
|
|
|
|
|
end;
|
|
|
|
|
|
finally
|
|
|
|
|
|
P.Free;
|
|
|
|
|
|
end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
{ Shared source snippets }
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
|
|
|
|
|
|
const
|
|
|
|
|
|
SrcByteBuf =
|
|
|
|
|
|
'program SA;' + LineEnding +
|
|
|
|
|
|
'procedure Foo;' + LineEnding +
|
|
|
|
|
|
'var Buf: array[0..7] of Byte;' + LineEnding +
|
|
|
|
|
|
'begin' + LineEnding +
|
|
|
|
|
|
' Buf[0] := 42' + LineEnding +
|
|
|
|
|
|
'end;' + LineEnding +
|
|
|
|
|
|
'begin end.';
|
|
|
|
|
|
|
|
|
|
|
|
SrcIntArray =
|
|
|
|
|
|
'program SA;' + LineEnding +
|
|
|
|
|
|
'procedure Foo;' + LineEnding +
|
|
|
|
|
|
'var A: array[0..3] of Integer;' + LineEnding +
|
|
|
|
|
|
'begin' + LineEnding +
|
|
|
|
|
|
' A[2] := 99' + LineEnding +
|
|
|
|
|
|
'end;' + LineEnding +
|
|
|
|
|
|
'begin end.';
|
|
|
|
|
|
|
|
|
|
|
|
SrcReadBack =
|
|
|
|
|
|
'program SA;' + LineEnding +
|
|
|
|
|
|
'function GetFirst: Integer;' + LineEnding +
|
|
|
|
|
|
'var A: array[0..3] of Integer;' + LineEnding +
|
|
|
|
|
|
'begin' + LineEnding +
|
|
|
|
|
|
' A[0] := 7;' + LineEnding +
|
|
|
|
|
|
' Result := A[0]' + LineEnding +
|
|
|
|
|
|
'end;' + LineEnding +
|
|
|
|
|
|
'begin end.';
|
|
|
|
|
|
|
|
|
|
|
|
SrcNonZero =
|
|
|
|
|
|
'program SA;' + LineEnding +
|
|
|
|
|
|
'procedure Foo;' + LineEnding +
|
|
|
|
|
|
'var R: array[5..9] of Integer;' + LineEnding +
|
|
|
|
|
|
'begin' + LineEnding +
|
|
|
|
|
|
' R[5] := 1' + LineEnding +
|
|
|
|
|
|
'end;' + LineEnding +
|
|
|
|
|
|
'begin end.';
|
|
|
|
|
|
|
2026-04-28 15:53:12 +03:00
|
|
|
|
SrcAddrOf =
|
|
|
|
|
|
'program SA;' + LineEnding +
|
|
|
|
|
|
'procedure Foo;' + LineEnding +
|
|
|
|
|
|
'var Buf: array[0..7] of Byte;' + LineEnding +
|
|
|
|
|
|
' P: ^Byte;' + LineEnding +
|
|
|
|
|
|
'begin' + LineEnding +
|
|
|
|
|
|
' P := @Buf[0]' + LineEnding +
|
|
|
|
|
|
'end;' + LineEnding +
|
|
|
|
|
|
'begin end.';
|
|
|
|
|
|
|
2026-04-28 14:35:05 +03:00
|
|
|
|
SrcLowHigh =
|
|
|
|
|
|
'program SA;' + LineEnding +
|
|
|
|
|
|
'function Len: Integer;' + LineEnding +
|
|
|
|
|
|
'var A: array[3..7] of Integer;' + LineEnding +
|
|
|
|
|
|
'begin' + LineEnding +
|
|
|
|
|
|
' Result := High(A) - Low(A) + 1' + LineEnding +
|
|
|
|
|
|
'end;' + LineEnding +
|
|
|
|
|
|
'begin end.';
|
|
|
|
|
|
|
feat: static array array[L..H] of T — stack-allocated fixed-size arrays
Adds TStaticSubscriptAssign AST node, ParseTypeName extension for
array[L..H] of T syntax (tkDotDot token), TStaticArrayTypeDesc with
RawSize for correct element-footprint sizing (1 for Byte, 4 for Integer),
and full codegen: alloc4/alloc8 + memset zero-init, storeb/storew for
writes, loadub/loadw for reads, LowBound subtraction for non-zero ranges.
Also includes array literal ['a','b','c'] call-site support (step 4):
TArrayLiteralExpr, open-array formal matching, stack buffer allocation,
and codegen emitting two QBE args (data ptr + compile-time high index).
13 new static-array tests + 9 new array-literal tests; all 888 tests pass.
2026-04-27 19:22:29 +03:00
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
{ Parser tests }
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
|
|
|
|
|
|
procedure TStaticArrayTests.TestParse_StaticArray_TypeName;
|
|
|
|
|
|
var P: TProgram; MD: TMethodDecl; Decl: TVarDecl;
|
|
|
|
|
|
begin
|
|
|
|
|
|
P := ParseSrc(SrcByteBuf);
|
|
|
|
|
|
try
|
|
|
|
|
|
MD := TMethodDecl(P.Block.ProcDecls[0]);
|
|
|
|
|
|
Decl := TVarDecl(MD.Body.Decls[0]);
|
|
|
|
|
|
AssertEquals('type name encoded', 'array[0..7] of Byte', Decl.TypeName);
|
|
|
|
|
|
finally P.Free; end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TStaticArrayTests.TestParse_StaticArray_SubscriptAssign_Name;
|
|
|
|
|
|
var P: TProgram; MD: TMethodDecl; Stmt: TStaticSubscriptAssign;
|
|
|
|
|
|
begin
|
|
|
|
|
|
P := ParseSrc(SrcByteBuf);
|
|
|
|
|
|
try
|
|
|
|
|
|
MD := TMethodDecl(P.Block.ProcDecls[0]);
|
|
|
|
|
|
AssertTrue('stmt is TStaticSubscriptAssign',
|
|
|
|
|
|
MD.Body.Stmts[0] is TStaticSubscriptAssign);
|
|
|
|
|
|
Stmt := TStaticSubscriptAssign(MD.Body.Stmts[0]);
|
|
|
|
|
|
AssertEquals('array name', 'Buf', Stmt.ArrayName);
|
|
|
|
|
|
finally P.Free; end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
{ Semantic tests }
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
|
|
|
|
|
|
procedure TStaticArrayTests.TestSemantic_StaticArray_Kind;
|
|
|
|
|
|
var P: TProgram; MD: TMethodDecl; Decl: TVarDecl;
|
|
|
|
|
|
begin
|
|
|
|
|
|
P := AnalyseSrc(SrcByteBuf);
|
|
|
|
|
|
try
|
|
|
|
|
|
MD := TMethodDecl(P.Block.ProcDecls[0]);
|
|
|
|
|
|
Decl := TVarDecl(MD.Body.Decls[0]);
|
|
|
|
|
|
AssertNotNull('ResolvedType set', Decl.ResolvedType);
|
|
|
|
|
|
AssertEquals('kind is tyStaticArray',
|
|
|
|
|
|
Ord(tyStaticArray), Ord(Decl.ResolvedType.Kind));
|
|
|
|
|
|
finally P.Free; end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TStaticArrayTests.TestSemantic_StaticArray_ElementType;
|
|
|
|
|
|
var P: TProgram; MD: TMethodDecl; Decl: TVarDecl; SAT: TStaticArrayTypeDesc;
|
|
|
|
|
|
begin
|
|
|
|
|
|
P := AnalyseSrc(SrcByteBuf);
|
|
|
|
|
|
try
|
|
|
|
|
|
MD := TMethodDecl(P.Block.ProcDecls[0]);
|
|
|
|
|
|
Decl := TVarDecl(MD.Body.Decls[0]);
|
|
|
|
|
|
AssertTrue('is TStaticArrayTypeDesc', Decl.ResolvedType is TStaticArrayTypeDesc);
|
|
|
|
|
|
SAT := TStaticArrayTypeDesc(Decl.ResolvedType);
|
|
|
|
|
|
AssertNotNull('ElementType set', SAT.ElementType);
|
|
|
|
|
|
AssertEquals('element is tyByte', Ord(tyByte), Ord(SAT.ElementType.Kind));
|
|
|
|
|
|
finally P.Free; end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TStaticArrayTests.TestSemantic_StaticArray_Bounds;
|
|
|
|
|
|
var P: TProgram; MD: TMethodDecl; Decl: TVarDecl; SAT: TStaticArrayTypeDesc;
|
|
|
|
|
|
begin
|
|
|
|
|
|
P := AnalyseSrc(SrcByteBuf);
|
|
|
|
|
|
try
|
|
|
|
|
|
MD := TMethodDecl(P.Block.ProcDecls[0]);
|
|
|
|
|
|
Decl := TVarDecl(MD.Body.Decls[0]);
|
|
|
|
|
|
SAT := TStaticArrayTypeDesc(Decl.ResolvedType);
|
|
|
|
|
|
AssertEquals('LowBound = 0', 0, SAT.LowBound);
|
|
|
|
|
|
AssertEquals('HighBound = 7', 7, SAT.HighBound);
|
|
|
|
|
|
finally P.Free; end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TStaticArrayTests.TestSemantic_StaticArray_ByteSize;
|
|
|
|
|
|
var P: TProgram; MD: TMethodDecl; Decl: TVarDecl;
|
|
|
|
|
|
begin
|
|
|
|
|
|
P := AnalyseSrc(SrcByteBuf);
|
|
|
|
|
|
try
|
|
|
|
|
|
MD := TMethodDecl(P.Block.ProcDecls[0]);
|
|
|
|
|
|
Decl := TVarDecl(MD.Body.Decls[0]);
|
|
|
|
|
|
{ 8 elements × 1 byte each = 8 bytes }
|
|
|
|
|
|
AssertEquals('ByteSize = 8', 8, Decl.ResolvedType.ByteSize);
|
|
|
|
|
|
finally P.Free; end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TStaticArrayTests.TestSemantic_StaticArray_IntArray;
|
|
|
|
|
|
var P: TProgram; MD: TMethodDecl; Decl: TVarDecl;
|
|
|
|
|
|
begin
|
|
|
|
|
|
P := AnalyseSrc(SrcIntArray);
|
|
|
|
|
|
try
|
|
|
|
|
|
MD := TMethodDecl(P.Block.ProcDecls[0]);
|
|
|
|
|
|
Decl := TVarDecl(MD.Body.Decls[0]);
|
|
|
|
|
|
{ 4 elements × 4 bytes each = 16 bytes }
|
|
|
|
|
|
AssertEquals('ByteSize = 16', 16, Decl.ResolvedType.ByteSize);
|
|
|
|
|
|
finally P.Free; end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TStaticArrayTests.TestSemantic_StaticArray_NonZero_LowBound;
|
|
|
|
|
|
var P: TProgram; MD: TMethodDecl; Decl: TVarDecl; SAT: TStaticArrayTypeDesc;
|
|
|
|
|
|
begin
|
|
|
|
|
|
P := AnalyseSrc(SrcNonZero);
|
|
|
|
|
|
try
|
|
|
|
|
|
MD := TMethodDecl(P.Block.ProcDecls[0]);
|
|
|
|
|
|
Decl := TVarDecl(MD.Body.Decls[0]);
|
|
|
|
|
|
SAT := TStaticArrayTypeDesc(Decl.ResolvedType);
|
|
|
|
|
|
AssertEquals('LowBound = 5', 5, SAT.LowBound);
|
|
|
|
|
|
AssertEquals('HighBound = 9', 9, SAT.HighBound);
|
|
|
|
|
|
finally P.Free; end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
{ Codegen tests }
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
|
|
|
|
|
|
procedure TStaticArrayTests.TestCodegen_StaticArray_AllocEmitted;
|
|
|
|
|
|
var IR: string;
|
|
|
|
|
|
begin
|
|
|
|
|
|
IR := GenIR(SrcByteBuf);
|
|
|
|
|
|
{ 8-byte Byte array: alloc4 alignment, 8 bytes total }
|
|
|
|
|
|
AssertTrue('alloc4 8 emitted', Pos('alloc4 8', IR) > 0);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TStaticArrayTests.TestCodegen_StaticArray_MemsetEmitted;
|
|
|
|
|
|
var IR: string;
|
|
|
|
|
|
begin
|
|
|
|
|
|
IR := GenIR(SrcByteBuf);
|
|
|
|
|
|
AssertTrue('memset call emitted', Pos('call $memset', IR) > 0);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TStaticArrayTests.TestCodegen_StaticArray_WriteEmitted;
|
|
|
|
|
|
var IR: string;
|
|
|
|
|
|
begin
|
|
|
|
|
|
IR := GenIR(SrcByteBuf);
|
|
|
|
|
|
{ Byte element write uses storeb }
|
|
|
|
|
|
AssertTrue('storeb emitted', Pos('storeb', IR) > 0);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TStaticArrayTests.TestCodegen_StaticArray_ReadEmitted;
|
|
|
|
|
|
var IR: string;
|
|
|
|
|
|
begin
|
|
|
|
|
|
IR := GenIR(SrcReadBack);
|
|
|
|
|
|
{ Integer element read uses loadw }
|
|
|
|
|
|
AssertTrue('loadw emitted', Pos('loadw', IR) > 0);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TStaticArrayTests.TestCodegen_StaticArray_NonZero_OffsetSubtracted;
|
|
|
|
|
|
var IR: string;
|
|
|
|
|
|
begin
|
|
|
|
|
|
IR := GenIR(SrcNonZero);
|
|
|
|
|
|
{ R[5] with LowBound=5: offset = (5-5)*4 = 0; sub instruction emitted }
|
|
|
|
|
|
AssertTrue('sub for low-bound adjustment', Pos('=l sub', IR) > 0);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
2026-04-28 14:35:05 +03:00
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
{ Low / High tests }
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
|
|
|
|
|
|
procedure TStaticArrayTests.TestSemantic_StaticArray_Low_ReturnsInteger;
|
|
|
|
|
|
var P: TProgram;
|
|
|
|
|
|
begin
|
|
|
|
|
|
{ If Low(A) on a static array fails semantic analysis an exception is raised here }
|
|
|
|
|
|
P := AnalyseSrc(SrcLowHigh);
|
|
|
|
|
|
try
|
|
|
|
|
|
AssertNotNull('program analysed', P);
|
|
|
|
|
|
finally P.Free; end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TStaticArrayTests.TestSemantic_StaticArray_High_ReturnsInteger;
|
|
|
|
|
|
var P: TProgram;
|
|
|
|
|
|
begin
|
|
|
|
|
|
{ If High(A) on a static array fails semantic analysis an exception is raised here }
|
|
|
|
|
|
P := AnalyseSrc(SrcLowHigh);
|
|
|
|
|
|
try
|
|
|
|
|
|
AssertNotNull('program analysed', P);
|
|
|
|
|
|
finally P.Free; end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TStaticArrayTests.TestCodegen_StaticArray_Low_EmitsLowBound;
|
|
|
|
|
|
var IR: string;
|
|
|
|
|
|
begin
|
|
|
|
|
|
IR := GenIR(SrcLowHigh);
|
|
|
|
|
|
{ Low(A) on array[3..7] emits: copy 3 }
|
|
|
|
|
|
AssertTrue('copy 3 for Low(A)', Pos('copy 3', IR) > 0);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TStaticArrayTests.TestCodegen_StaticArray_High_EmitsHighBound;
|
|
|
|
|
|
var IR: string;
|
|
|
|
|
|
begin
|
|
|
|
|
|
IR := GenIR(SrcLowHigh);
|
|
|
|
|
|
{ High(A) on array[3..7] emits: copy 7 }
|
|
|
|
|
|
AssertTrue('copy 7 for High(A)', Pos('copy 7', IR) > 0);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
2026-04-28 15:53:12 +03:00
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
{ Address-of tests }
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
|
|
|
|
|
|
procedure TStaticArrayTests.TestParse_AddrOf_NodeType;
|
|
|
|
|
|
var P: TProgram; MD: TMethodDecl; Assign: TAssignment;
|
|
|
|
|
|
begin
|
|
|
|
|
|
P := ParseSrc(SrcAddrOf);
|
|
|
|
|
|
try
|
|
|
|
|
|
MD := TMethodDecl(P.Block.ProcDecls[0]);
|
|
|
|
|
|
Assign := TAssignment(MD.Body.Stmts[0]);
|
|
|
|
|
|
AssertTrue('expr is TAddrOfExpr', Assign.Expr is TAddrOfExpr);
|
|
|
|
|
|
finally P.Free; end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TStaticArrayTests.TestSemantic_AddrOf_ReturnsPointerType;
|
|
|
|
|
|
var P: TProgram; MD: TMethodDecl; Assign: TAssignment;
|
|
|
|
|
|
begin
|
|
|
|
|
|
P := AnalyseSrc(SrcAddrOf);
|
|
|
|
|
|
try
|
|
|
|
|
|
MD := TMethodDecl(P.Block.ProcDecls[0]);
|
|
|
|
|
|
Assign := TAssignment(MD.Body.Stmts[0]);
|
|
|
|
|
|
AssertNotNull('expr resolved', Assign.Expr.ResolvedType);
|
|
|
|
|
|
AssertEquals('@Buf[0] resolves to tyPointer',
|
|
|
|
|
|
Ord(tyPointer), Ord(Assign.Expr.ResolvedType.Kind));
|
|
|
|
|
|
finally P.Free; end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TStaticArrayTests.TestSemantic_AddrOf_BaseTypeIsByte;
|
|
|
|
|
|
var P: TProgram; MD: TMethodDecl; Assign: TAssignment; PT: TPointerTypeDesc;
|
|
|
|
|
|
begin
|
|
|
|
|
|
P := AnalyseSrc(SrcAddrOf);
|
|
|
|
|
|
try
|
|
|
|
|
|
MD := TMethodDecl(P.Block.ProcDecls[0]);
|
|
|
|
|
|
Assign := TAssignment(MD.Body.Stmts[0]);
|
|
|
|
|
|
AssertTrue('is TPointerTypeDesc', Assign.Expr.ResolvedType is TPointerTypeDesc);
|
|
|
|
|
|
PT := TPointerTypeDesc(Assign.Expr.ResolvedType);
|
|
|
|
|
|
AssertNotNull('BaseType set', PT.BaseType);
|
|
|
|
|
|
AssertEquals('BaseType is tyByte', Ord(tyByte), Ord(PT.BaseType.Kind));
|
|
|
|
|
|
finally P.Free; end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TStaticArrayTests.TestCodegen_AddrOf_NoLoad;
|
|
|
|
|
|
var IR: string;
|
|
|
|
|
|
begin
|
|
|
|
|
|
IR := GenIR(SrcAddrOf);
|
|
|
|
|
|
{ @Buf[0] takes address only — no loadub should appear }
|
|
|
|
|
|
AssertTrue('no loadub emitted', Pos('loadub', IR) = 0);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TStaticArrayTests.TestCodegen_AddrOf_AddressArithmetic;
|
|
|
|
|
|
var IR: string;
|
|
|
|
|
|
begin
|
|
|
|
|
|
IR := GenIR(SrcAddrOf);
|
|
|
|
|
|
{ address is computed: base + offset using add }
|
|
|
|
|
|
AssertTrue('=l add emitted', Pos('=l add', IR) > 0);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
feat: static array array[L..H] of T — stack-allocated fixed-size arrays
Adds TStaticSubscriptAssign AST node, ParseTypeName extension for
array[L..H] of T syntax (tkDotDot token), TStaticArrayTypeDesc with
RawSize for correct element-footprint sizing (1 for Byte, 4 for Integer),
and full codegen: alloc4/alloc8 + memset zero-init, storeb/storew for
writes, loadub/loadw for reads, LowBound subtraction for non-zero ranges.
Also includes array literal ['a','b','c'] call-site support (step 4):
TArrayLiteralExpr, open-array formal matching, stack buffer allocation,
and codegen emitting two QBE args (data ptr + compile-time high index).
13 new static-array tests + 9 new array-literal tests; all 888 tests pass.
2026-04-27 19:22:29 +03:00
|
|
|
|
initialization
|
|
|
|
|
|
RegisterTest(TStaticArrayTests);
|
|
|
|
|
|
|
|
|
|
|
|
end.
|