2026-06-04 22:31:10 +03:00
|
|
|
# bif-coverage status - one line per public AST field.
|
|
|
|
|
# Format: <TClass>.<Field> <serialise|safe>
|
|
|
|
|
# serialise must appear in EncodeStmt/EncodeExpr AND ReadStmt/ReadExpr
|
|
|
|
|
# safe intentionally not serialised (set by semantic etc.)
|
|
|
|
|
# Regenerate from scratch with: bif-coverage --reset
|
|
|
|
|
|
|
|
|
|
# TIntLiteral (uAST.pas:36)
|
|
|
|
|
TIntLiteral.Value serialise
|
|
|
|
|
TIntLiteral.IsUInt64 safe
|
|
|
|
|
|
|
|
|
|
# TFloatLiteral (uAST.pas:44)
|
|
|
|
|
TFloatLiteral.Value serialise
|
|
|
|
|
|
|
|
|
|
# TStringLiteral (uAST.pas:49)
|
|
|
|
|
TStringLiteral.Value serialise
|
|
|
|
|
TStringLiteral.IsCharCoerce safe
|
|
|
|
|
TStringLiteral.CharOrdValue safe
|
|
|
|
|
|
|
|
|
|
# TStringSubscriptExpr (uAST.pas:56)
|
|
|
|
|
TStringSubscriptExpr.StrExpr serialise
|
|
|
|
|
TStringSubscriptExpr.IndexExpr serialise
|
|
|
|
|
|
|
|
|
|
# TArrayLiteralExpr (uAST.pas:63)
|
|
|
|
|
TArrayLiteralExpr.Elements serialise
|
feat(params): array of const (heterogeneous variadic parameters)
A parameter declared 'array of const' accepts a single call-site bracket list
of mixed-type values, boxed into an array of the intrinsic record TVarRec and
passed via the existing open-array ABI:
procedure Log(args: array of const);
...
Log([42, 'hi', 3.5, True]);
This is the one loosely-typed-passing mechanism Blaise adopts (see the
rationale section); untyped params, varargs, and Variant remain omitted.
- RTL/builtins: TVarRec is registered as a compiler-intrinsic record
{ VType: Byte; VValue: Pointer } (16-byte layout, mirroring TMethod), with
vt* discriminant constants - all available with no uses clause, matching
Delphi's auto-available System.TVarRec. Blaise has no record variant parts,
so the callee reads each element by reinterpret-casting the single VValue
slot (Integer(v.VValue), string(PChar(v.VValue)), PDouble(v.VValue)^, ...).
- Parser: 'array of const' parses as an open array whose element is TVarRec.
- Semantic: a heterogeneous bracket literal is typed 'array of TVarRec' rather
than rejected; overload resolution binds it (and homogeneous / empty
literals) to an array-of-const formal; retyping runs for proc, func, and
method calls.
- Codegen (both backends): EmitConstArrayLiteral builds one 16-byte TVarRec
per element, tagging by inferred type. Borrow semantics (FPC) - strings and
objects are stored without AddRef. Doubles are heap-boxed via _BlaiseGetMem
(vtExtended holds a PDouble) since a double does not fit the pointer slot.
- Native: also fixes a pre-existing gap - reading a float through a pointer
deref (PDouble^) in EmitExprToXmm0 - needed for vtExtended read-back.
Tests: cp.test.arrayofconst (parser/semantic/IR) and cp.test.e2e.arrayofconst
(compile + run on both backends, including value read-back, empty/homogeneous
lists, and string-variable borrow). Grammar and language rationale documented.
2026-06-13 19:37:59 +03:00
|
|
|
TArrayLiteralExpr.IsConstArray safe
|
2026-06-04 22:31:10 +03:00
|
|
|
|
feat(sets): range syntax in set literals — [lo..hi] (#105)
A set literal element may now be a constant range:
e := [Red..Blue]; { {Red, Green, Blue} }
e := [m0, m2..m4, m7]; { ranges mix with single members }
The parser builds a transient TSetRangeExpr for each lo..hi element; the
semantic pass (AnalyseSetLiteralExpr) expands it into the individual member
idents before any other consumer runs, so overload resolution, the bitmask
folder, the jumbo-set path, and both code generators see an ordinary member
list and need no range-specific logic.
Both bounds must be compile-time constants of the set's base type. A
reversed constant range [Blue..Red] is a compile-time error rather than a
silently empty set — matching Blaise's preference for rejecting
confusing-but-legal constructs over FPC's empty-set-with-warning behaviour.
Variable bounds [lo..hi] are rejected ("set range bound must be a
constant"); runtime-variable ranges are deferred.
TSetRangeExpr is wired into the .bif serialiser and the AST cloner so a
generic template body containing an unexpanded range round-trips through
separate compilation (verified by hand: a generic returning [m1..m3]
compiled to .o, then instantiated from the .bif with source hidden).
Set base types remain enumerations; the issue's set-of-byte example needs
ordinal-base set types, tracked separately in docs/future-improvements.adoc.
Range expansion is base-type-agnostic, so ranges will work for those
automatically once they land.
Docs: grammar.ebnf SetElement rule; language-rationale.adoc decision +
alternatives. bif-coverage.status regenerated for the new node.
FIXPOINT_OK + NATIVE_FIXPOINT_OK; full suite OK (3201 tests, 11 new).
2026-06-15 19:14:05 +03:00
|
|
|
# TSetRangeExpr (uAST.pas:80)
|
|
|
|
|
TSetRangeExpr.LowExpr serialise
|
|
|
|
|
TSetRangeExpr.HighExpr serialise
|
2026-06-04 22:31:10 +03:00
|
|
|
|
feat(sets): range syntax in set literals — [lo..hi] (#105)
A set literal element may now be a constant range:
e := [Red..Blue]; { {Red, Green, Blue} }
e := [m0, m2..m4, m7]; { ranges mix with single members }
The parser builds a transient TSetRangeExpr for each lo..hi element; the
semantic pass (AnalyseSetLiteralExpr) expands it into the individual member
idents before any other consumer runs, so overload resolution, the bitmask
folder, the jumbo-set path, and both code generators see an ordinary member
list and need no range-specific logic.
Both bounds must be compile-time constants of the set's base type. A
reversed constant range [Blue..Red] is a compile-time error rather than a
silently empty set — matching Blaise's preference for rejecting
confusing-but-legal constructs over FPC's empty-set-with-warning behaviour.
Variable bounds [lo..hi] are rejected ("set range bound must be a
constant"); runtime-variable ranges are deferred.
TSetRangeExpr is wired into the .bif serialiser and the AST cloner so a
generic template body containing an unexpanded range round-trips through
separate compilation (verified by hand: a generic returning [m1..m3]
compiled to .o, then instantiated from the .bif with source hidden).
Set base types remain enumerations; the issue's set-of-byte example needs
ordinal-base set types, tracked separately in docs/future-improvements.adoc.
Range expansion is base-type-agnostic, so ranges will work for those
automatically once they land.
Docs: grammar.ebnf SetElement rule; language-rationale.adoc decision +
alternatives. bif-coverage.status regenerated for the new node.
FIXPOINT_OK + NATIVE_FIXPOINT_OK; full suite OK (3201 tests, 11 new).
2026-06-15 19:14:05 +03:00
|
|
|
# TNilLiteral (uAST.pas:87)
|
|
|
|
|
|
|
|
|
|
# TIdentExpr (uAST.pas:97)
|
2026-06-04 22:31:10 +03:00
|
|
|
TIdentExpr.Name serialise
|
2026-06-10 04:06:43 +03:00
|
|
|
TIdentExpr.ParamMode safe
|
2026-06-04 22:31:10 +03:00
|
|
|
TIdentExpr.IsConstant safe
|
|
|
|
|
TIdentExpr.ConstValue safe
|
|
|
|
|
TIdentExpr.ConstString safe
|
|
|
|
|
TIdentExpr.IsGlobal safe
|
2026-06-08 02:34:12 +03:00
|
|
|
TIdentExpr.IsThreadVar safe
|
2026-06-04 22:31:10 +03:00
|
|
|
TIdentExpr.IsImplicitSelf safe
|
|
|
|
|
TIdentExpr.IsImplicitSelfMethod safe
|
|
|
|
|
TIdentExpr.IsMetaclassRef safe
|
|
|
|
|
TIdentExpr.ConstArraySymbol safe
|
|
|
|
|
|
feat(sets): range syntax in set literals — [lo..hi] (#105)
A set literal element may now be a constant range:
e := [Red..Blue]; { {Red, Green, Blue} }
e := [m0, m2..m4, m7]; { ranges mix with single members }
The parser builds a transient TSetRangeExpr for each lo..hi element; the
semantic pass (AnalyseSetLiteralExpr) expands it into the individual member
idents before any other consumer runs, so overload resolution, the bitmask
folder, the jumbo-set path, and both code generators see an ordinary member
list and need no range-specific logic.
Both bounds must be compile-time constants of the set's base type. A
reversed constant range [Blue..Red] is a compile-time error rather than a
silently empty set — matching Blaise's preference for rejecting
confusing-but-legal constructs over FPC's empty-set-with-warning behaviour.
Variable bounds [lo..hi] are rejected ("set range bound must be a
constant"); runtime-variable ranges are deferred.
TSetRangeExpr is wired into the .bif serialiser and the AST cloner so a
generic template body containing an unexpanded range round-trips through
separate compilation (verified by hand: a generic returning [m1..m3]
compiled to .o, then instantiated from the .bif with source hidden).
Set base types remain enumerations; the issue's set-of-byte example needs
ordinal-base set types, tracked separately in docs/future-improvements.adoc.
Range expansion is base-type-agnostic, so ranges will work for those
automatically once they land.
Docs: grammar.ebnf SetElement rule; language-rationale.adoc decision +
alternatives. bif-coverage.status regenerated for the new node.
FIXPOINT_OK + NATIVE_FIXPOINT_OK; full suite OK (3201 tests, 11 new).
2026-06-15 19:14:05 +03:00
|
|
|
# TFieldAccessExpr (uAST.pas:118)
|
2026-06-04 22:31:10 +03:00
|
|
|
TFieldAccessExpr.RecordName serialise
|
|
|
|
|
TFieldAccessExpr.FieldName serialise
|
|
|
|
|
TFieldAccessExpr.Base serialise
|
|
|
|
|
TFieldAccessExpr.FieldInfo safe
|
|
|
|
|
TFieldAccessExpr.IsConstant safe
|
|
|
|
|
TFieldAccessExpr.ConstValue safe
|
|
|
|
|
TFieldAccessExpr.ConstString safe
|
|
|
|
|
TFieldAccessExpr.ConstArraySymbol safe
|
|
|
|
|
TFieldAccessExpr.IsConstructorCall safe
|
|
|
|
|
TFieldAccessExpr.IsClassAccess safe
|
|
|
|
|
TFieldAccessExpr.PropRead safe
|
|
|
|
|
TFieldAccessExpr.PropOwnerType safe
|
fix(codegen): virtual-dispatch property accessors through the vtable
A property whose getter or setter is declared `virtual` did not dispatch
through the vtable: reading or writing the property through a base-typed
variable holding a derived instance called the BASE accessor, not the
override. A direct b.GetVal() call dispatched correctly, but b.Val (the
property over the same virtual getter) did not.
TBase = class
function GetVal: Integer; virtual; begin Result := 1; end;
property Val: Integer read GetVal;
end;
TDerived = class(TBase)
function GetVal: Integer; override; begin Result := 99; end;
end;
b: TBase := TDerived.Create;
WriteLn(b.Val); // was 1, now 99
The property read/write lowering always emitted a static call to the
accessor's declaring class. Now the semantic pass records the accessor's
vtable slot on the AST node (PropAccessorVSlot, -1 when the accessor is
not virtual), and codegen dispatches through the vtable when the slot is
>= 0, exactly as a direct method call does.
QBE: PropAccessorTarget computes the call target — emitting the vptr+slot
loads and returning the function-pointer temp for a virtual accessor, or
the static mangled symbol otherwise — and each call site emits its own
`call <target>(...)`. (A single emit-and-return helper was tried first
but tripped a latent native-backend miscompile on the self-compile; see
bugs.txt. The target-string shape avoids it.) Native:
EmitPropAccessorCallNative dispatches through the vtable or statically.
Covers getter and setter, on both backends. Adds
TE2EInheritTests.TestRun_VirtualProperty{Getter,Setter}_Dispatches
(dual-backend). The two new AST fields are marked `safe` in
bif-coverage.status (semantic-set, not serialised).
2026-06-16 14:59:46 +03:00
|
|
|
TFieldAccessExpr.PropAccessorVSlot safe
|
2026-06-04 22:31:10 +03:00
|
|
|
TFieldAccessExpr.IsImplicitSelf safe
|
|
|
|
|
TFieldAccessExpr.IsMethodCall safe
|
|
|
|
|
TFieldAccessExpr.IsInterfaceCall safe
|
|
|
|
|
TFieldAccessExpr.IsGlobal safe
|
|
|
|
|
TFieldAccessExpr.IsClassNameAccess safe
|
|
|
|
|
TFieldAccessExpr.IsClassTypeAccess safe
|
|
|
|
|
TFieldAccessExpr.IsBuiltinToString safe
|
|
|
|
|
TFieldAccessExpr.IsVarParam safe
|
|
|
|
|
TFieldAccessExpr.PropIndexExpr serialise
|
|
|
|
|
TFieldAccessExpr.IsCharAccess safe
|
2026-06-08 02:34:12 +03:00
|
|
|
TFieldAccessExpr.IsArrayAccess safe
|
2026-06-04 22:31:10 +03:00
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TIsExpr (uAST.pas:151)
|
2026-06-04 22:31:10 +03:00
|
|
|
TIsExpr.Obj serialise
|
|
|
|
|
TIsExpr.TypeName serialise
|
|
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TAsExpr (uAST.pas:159)
|
2026-06-04 22:31:10 +03:00
|
|
|
TAsExpr.Obj serialise
|
|
|
|
|
TAsExpr.TypeName serialise
|
|
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TSupportsExpr (uAST.pas:168)
|
2026-06-04 22:31:10 +03:00
|
|
|
TSupportsExpr.Obj serialise
|
|
|
|
|
TSupportsExpr.IntfTypeName serialise
|
|
|
|
|
TSupportsExpr.OutVarName serialise
|
|
|
|
|
TSupportsExpr.OutVarIsGlobal safe
|
|
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TInheritedCallExpr (uAST.pas:182)
|
|
|
|
|
TInheritedCallExpr.Name serialise
|
|
|
|
|
TInheritedCallExpr.Args serialise
|
|
|
|
|
|
|
|
|
|
# TBinaryExpr (uAST.pas:199)
|
2026-06-04 22:31:10 +03:00
|
|
|
TBinaryExpr.Op serialise
|
|
|
|
|
TBinaryExpr.Left serialise
|
|
|
|
|
TBinaryExpr.Right serialise
|
|
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TNotExpr (uAST.pas:207)
|
2026-06-04 22:31:10 +03:00
|
|
|
TNotExpr.Expr serialise
|
|
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TAssignment (uAST.pas:219)
|
2026-06-04 22:31:10 +03:00
|
|
|
TAssignment.Name serialise
|
|
|
|
|
TAssignment.Expr serialise
|
|
|
|
|
TAssignment.IsVarParam safe
|
|
|
|
|
TAssignment.IsGlobal safe
|
2026-06-08 02:34:12 +03:00
|
|
|
TAssignment.IsThreadVar safe
|
2026-06-04 22:31:10 +03:00
|
|
|
TAssignment.IsWeakLhs safe
|
|
|
|
|
TAssignment.ImplicitSelfField safe
|
|
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TIfStmt (uAST.pas:235)
|
2026-06-04 22:31:10 +03:00
|
|
|
TIfStmt.Condition serialise
|
|
|
|
|
TIfStmt.ThenStmt serialise
|
|
|
|
|
TIfStmt.ElseStmt serialise
|
|
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TCompoundStmt (uAST.pas:243)
|
2026-06-04 22:31:10 +03:00
|
|
|
TCompoundStmt.Stmts serialise
|
|
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TWhileStmt (uAST.pas:250)
|
2026-06-04 22:31:10 +03:00
|
|
|
TWhileStmt.Condition serialise
|
|
|
|
|
TWhileStmt.Body serialise
|
|
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TRepeatStmt (uAST.pas:257)
|
2026-06-04 22:31:10 +03:00
|
|
|
TRepeatStmt.Body serialise
|
|
|
|
|
TRepeatStmt.Condition serialise
|
|
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TForStmt (uAST.pas:264)
|
2026-06-04 22:31:10 +03:00
|
|
|
TForStmt.VarName serialise
|
|
|
|
|
TForStmt.IsGlobal safe
|
|
|
|
|
TForStmt.StartExpr serialise
|
|
|
|
|
TForStmt.EndExpr serialise
|
|
|
|
|
TForStmt.IsDownTo serialise
|
|
|
|
|
TForStmt.Body serialise
|
|
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TForInStmt (uAST.pas:275)
|
2026-06-04 22:31:10 +03:00
|
|
|
TForInStmt.VarName serialise
|
|
|
|
|
TForInStmt.VarIsGlobal safe
|
|
|
|
|
TForInStmt.CollExpr serialise
|
|
|
|
|
TForInStmt.Body serialise
|
|
|
|
|
TForInStmt.IsArrayIter safe
|
|
|
|
|
TForInStmt.EnumVarName safe
|
|
|
|
|
TForInStmt.ResolvedEnumTypeName safe
|
|
|
|
|
TForInStmt.IdxVarName safe
|
|
|
|
|
TForInStmt.ArrayLow safe
|
|
|
|
|
TForInStmt.ArrayHigh safe
|
|
|
|
|
TForInStmt.IsDynArrayIter safe
|
|
|
|
|
TForInStmt.IsStringIter safe
|
2026-06-08 02:34:12 +03:00
|
|
|
TForInStmt.IsCodePointIter safe
|
|
|
|
|
TForInStmt.AdvVarName safe
|
2026-06-04 22:31:10 +03:00
|
|
|
TForInStmt.IsSetIter safe
|
|
|
|
|
TForInStmt.SetBitCount safe
|
|
|
|
|
TForInStmt.SetMaskVarName safe
|
feat(sets): jumbo sets — set of enum up to 256 members
Extend set of <enum> from a 64-member cap to 256, Java-EnumSet style. Sets
of 64 members or fewer keep the existing single-register bitmask (QBE w/l);
sets of 65..256 members ('jumbo') become an inline byte-array bitmap of
ceil(N/8) bytes, treated as a value aggregate (passed by reference, returned
via sret, memset/memcpy), with operations performed by new RTL helpers.
Representation (uSymbolTable): TSetTypeDesc.IsJumbo (BitCount > 64) and
RawByteSize; RawSize/ByteSize/AllocAlign sized accordingly. TSymbol gains
ConstSetBytes/ConstSetQbe for jumbo constants (can't fit an Int64 mask).
RTL: new runtime/src/main/pascal/blaise_set.pas — _SetIn/_SetInclude/
_SetExclude/_SetUnion/_SetInter/_SetDiff/_SetEqual/_SetCopy over byte-array
bitmaps (overlap-safe). Wired into runtime/Makefile.
Semantic: the four >64 caps become >256. AnalyseSetConstDecl folds a jumbo
const to a byte array. The anonymous set for 'X in [a,b,c]' is sized to the
largest listed ordinal (when constant), not the full enum — keeping the
common low-ordinal membership test (incl. the compiler's own TokenKind
tests) on the fast register path. This also fixes a latent miscompile: the
old fixed-l representation silently dropped any listed ordinal >= 64.
Codegen (both backends): jumbo branches for literal, in, +/-/*, =/<>,
Include/Exclude, for-in, assignment, params (pmJumboSetValue ABI), and sret
returns. The register 'in' gained a range guard for literal-sized sets.
Native reserves two 32-byte scratch slots per frame (and .bss in main) for
set-op/literal result buffers. QBE adds IsAggregateAddrType so jumbo sets
ride the record/static-array address paths and are never promoted.
OPDF: no format change — recSet SizeInBytes (1 byte) already covers <=32.
Verified: FIXPOINT_OK and NATIVE_FIXPOINT_OK; full suite (3172 tests) green
built by the stage-2 binary, the QBE fixpoint binary, and the native
fixpoint binary; bif-coverage OK. Tests: cp.test.jumboset (12 IR) and
cp.test.e2e.jumboset (6 e2e, both backends via AssertRunsOnAll); the
>256-member rejection test in cp.test.sets updated. Docs: grammar.ebnf and
language-rationale set-type sections updated for the 256 cap and literal
sizing.
Closes the design discussion behind #81.
2026-06-15 01:23:19 +03:00
|
|
|
TForInStmt.SetIsJumbo safe
|
2026-06-04 22:31:10 +03:00
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TTryFinallyStmt (uAST.pas:312)
|
2026-06-04 22:31:10 +03:00
|
|
|
TTryFinallyStmt.TryBody serialise
|
|
|
|
|
TTryFinallyStmt.FinallyBody serialise
|
|
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TTryExceptStmt (uAST.pas:329)
|
2026-06-04 22:31:10 +03:00
|
|
|
TTryExceptStmt.TryBody serialise
|
|
|
|
|
TTryExceptStmt.Handlers serialise
|
|
|
|
|
TTryExceptStmt.ElseBody serialise
|
|
|
|
|
TTryExceptStmt.ExceptBody serialise
|
|
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TRaiseStmt (uAST.pas:342)
|
2026-06-04 22:31:10 +03:00
|
|
|
TRaiseStmt.Expr serialise
|
|
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TExitStmt (uAST.pas:353)
|
2026-06-22 14:34:07 +03:00
|
|
|
TExitStmt.Value serialise
|
2026-06-04 22:31:10 +03:00
|
|
|
TExitStmt.ResultAssign safe
|
|
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TBreakStmt (uAST.pas:361)
|
2026-06-04 22:31:10 +03:00
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TContinueStmt (uAST.pas:364)
|
2026-06-04 22:31:10 +03:00
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TCaseStmt (uAST.pas:375)
|
2026-06-04 22:31:10 +03:00
|
|
|
TCaseStmt.Selector serialise
|
|
|
|
|
TCaseStmt.Branches serialise
|
|
|
|
|
TCaseStmt.ElseStmt serialise
|
|
|
|
|
TCaseStmt.IsStringCase safe
|
|
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TFieldAssignment (uAST.pas:386)
|
2026-06-04 22:31:10 +03:00
|
|
|
TFieldAssignment.RecordName serialise
|
|
|
|
|
TFieldAssignment.FieldName serialise
|
|
|
|
|
TFieldAssignment.Expr serialise
|
|
|
|
|
TFieldAssignment.ObjExpr serialise
|
|
|
|
|
TFieldAssignment.FieldInfo safe
|
|
|
|
|
TFieldAssignment.IsClassAccess safe
|
|
|
|
|
TFieldAssignment.IsImplicitSelf safe
|
|
|
|
|
TFieldAssignment.IsGlobal safe
|
|
|
|
|
TFieldAssignment.IsVarParam safe
|
|
|
|
|
TFieldAssignment.PropIndexExpr serialise
|
|
|
|
|
TFieldAssignment.PropOwnerType safe
|
fix(codegen): virtual-dispatch property accessors through the vtable
A property whose getter or setter is declared `virtual` did not dispatch
through the vtable: reading or writing the property through a base-typed
variable holding a derived instance called the BASE accessor, not the
override. A direct b.GetVal() call dispatched correctly, but b.Val (the
property over the same virtual getter) did not.
TBase = class
function GetVal: Integer; virtual; begin Result := 1; end;
property Val: Integer read GetVal;
end;
TDerived = class(TBase)
function GetVal: Integer; override; begin Result := 99; end;
end;
b: TBase := TDerived.Create;
WriteLn(b.Val); // was 1, now 99
The property read/write lowering always emitted a static call to the
accessor's declaring class. Now the semantic pass records the accessor's
vtable slot on the AST node (PropAccessorVSlot, -1 when the accessor is
not virtual), and codegen dispatches through the vtable when the slot is
>= 0, exactly as a direct method call does.
QBE: PropAccessorTarget computes the call target — emitting the vptr+slot
loads and returning the function-pointer temp for a virtual accessor, or
the static mangled symbol otherwise — and each call site emits its own
`call <target>(...)`. (A single emit-and-return helper was tried first
but tripped a latent native-backend miscompile on the self-compile; see
bugs.txt. The target-string shape avoids it.) Native:
EmitPropAccessorCallNative dispatches through the vtable or statically.
Covers getter and setter, on both backends. Adds
TE2EInheritTests.TestRun_VirtualProperty{Getter,Setter}_Dispatches
(dual-backend). The two new AST fields are marked `safe` in
bif-coverage.status (semantic-set, not serialised).
2026-06-16 14:59:46 +03:00
|
|
|
TFieldAssignment.PropAccessorVSlot safe
|
2026-06-11 11:19:35 +03:00
|
|
|
TFieldAssignment.IsElemWrite safe
|
2026-06-04 22:31:10 +03:00
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TStaticSubscriptAssign (uAST.pas:414)
|
2026-06-04 22:31:10 +03:00
|
|
|
TStaticSubscriptAssign.ArrayName serialise
|
|
|
|
|
TStaticSubscriptAssign.IndexExpr serialise
|
|
|
|
|
TStaticSubscriptAssign.ValueExpr serialise
|
feat(arrays): support multi-dimensional static arrays
Add multi-dimensional static array syntax, both the comma form
(array[0..1, 0..2] of Integer; A[i, j]) and the equivalent nested/chained
form (array[0..1] of array[0..2] of Integer; A[i][j]). The comma forms are
syntactic sugar: the parser desugars array[a, b] of T into the nested
array[a] of array[b] of T, and A[i, j] into chained subscripts A[i][j], so
the two notations are fully interchangeable in every position.
Layers:
- uParser: comma loops in ParseTypeName and subscript reads; the statement
LHS now lowers A[i, j] := v and A[i][j] := v to a TStaticSubscriptAssign
carrying a new BaseExpr (the inner-array address expression). The previous
"chained base not yet supported" rejection is removed.
- uAST: TStaticSubscriptAssign.BaseExpr (owned); wired into CloneStmt and the
.bif encoder/decoder (uUnitInterfaceIO); bif-coverage status updated.
- uSemantic: BaseExpr branch resolves the inner static-array type and checks
the index and value element type.
- Codegen (both backends): a nested static-array element now evaluates to its
inline address (mirroring record/interface elements) so a further subscript
indexes into it; the static-subscript store reads its base from BaseExpr
when set. Nested arrays are a flat row-major contiguous block.
- OPDF: no new record needed - each dimension emits one recArray whose element
points at the next inner recArray; pdr follows the chain and renders the
value as a true multi-dimensional structure.
Tests: IR unit tests (cp.test.staticarray), e2e tests on both backends via
AssertRunsOnAll (cp.test.e2e.staticarray), and a nested-recArray OPDF test
(cp.test.opdf). Grammar and language rationale documented.
2026-06-13 13:56:38 +03:00
|
|
|
TStaticSubscriptAssign.BaseExpr serialise
|
2026-06-04 22:31:10 +03:00
|
|
|
TStaticSubscriptAssign.IsGlobal safe
|
fix: var parameters of dynamic-array, static-array, PChar and interface types
Diagnosis credit: Andrew Haines (issue #89) correctly identified that
the subscript-assign path loads through a var/out parameter's slot only
once — the slot holds the ADDRESS of the caller's variable, so element
writes landed in the wrong memory and corrupted the heap. His patch
covered the QBE dyn-array and class cases; the class half had already
landed independently (248dccf). This implements the remaining cases
across BOTH backends in the current tree.
Subscript writes through var/out params (TStaticSubscriptAssign gains
IsVarParam, set by the semantic pass; bif-coverage status updated):
- dynamic arrays: one extra dereference to reach the data pointer
(writes were lost and stray stores corrupted the heap; SetLength and
element reads already worked),
- static arrays: load the array address from the slot instead of
offsetting the slot itself (QBE wrote into the parameter slot region;
the native ident READ also produced garbage — pmVar now treated like
the other by-ref param modes),
- PChar: extra dereference before the byte store (writes were lost).
Interface var/out parameters (previously did not even compile: QBE
emitted loads from a non-existent %_var_G_obj; native mis-spilled the
single incoming pointer as a two-register fat pointer):
- interface variables now occupy ONE contiguous 16-byte fat-pointer
block (obj at +0, itab at +8). QBE locals: a single alloc8 16 with
the _itab name derived at +8; QBE globals: a single 16-byte data item
$Name_obj with the itab half addressed as $Name_obj + 8 (the separate
$Name_itab item could not be guaranteed adjacent). Native locals and
globals were already contiguous.
- new IntfObjAddr/IntfItabAddr helpers route every QBE obj/itab access
(assignment variants incl. weak, dispatch, expr-pair reads, as-out
binding); var/out params dereference the slot first.
- native: var-param-aware receiver load in EmitInterfaceCall, var-param
LHS in EmitInterfaceAssign (shares the sret-Result pointer path),
interface globals usable as var args (leaq Name_obj), and the call
slot counter treats a var interface arg as ONE pointer slot (it was
counted as two, desynchronising the argument register pops).
IR assertions updated for the new global fat-pointer layout. New e2e
tests: TestRun_VarParamDynArray_WriteAndGrow,
TestRun_VarParamStaticArray_PChar,
TestRun_VarParamInterface_DispatchAndReassign.
Closes #89.
2026-06-11 20:50:50 +03:00
|
|
|
TStaticSubscriptAssign.IsVarParam safe
|
2026-06-11 11:19:35 +03:00
|
|
|
TStaticSubscriptAssign.IsImplicitSelf safe
|
feat(properties): default array property — Obj[I] sugar
Adds the `default` directive on an indexed property, enabling subscript
sugar on the object itself:
property Items[I: Integer]: T read Get write Put; default;
...
V[0] := 10; // lowers to V.Put(0, 10)
WriteLn(V[0]); // lowers to V.Get(0)
This is the mechanism behind the familiar List[i] syntax and was a real
foundational gap (the directive did not even parse — "Expected ':'").
Implementation:
- Parser: accept the trailing `default;` directive after a property
declaration; set TPropertyDecl.IsDefault.
- AST / symbol table: IsDefault on TPropertyDecl and TPropertyInfo;
TRecordTypeDesc.FindDefaultProperty walks the inheritance chain.
- Semantic: Obj[I] read (AnalyseStringSubscriptExpr) synthesises the
default property's field access and reuses the indexed-property read
path; Obj[I] := V write (AnalyseStaticSubscriptAssign) records the
setter on the TStaticSubscriptAssign node.
- Codegen (both backends): the write path emits the setter call via the
existing PropAccessorTarget / EmitPropAccessorCallNative helpers, so it
honours virtual/override on the accessor; the read path delegates the
TStringSubscriptExpr to its folded property-read field access.
- Cross-unit: IsDefault is serialised in the .bif interface so a default
property declared in one unit keeps its subscript sugar elsewhere.
Verified read, write, string-element, inherited, and cross-unit cases on
both backends. Adds IR tests (TPropertyTests.TestCodegen_DefaultProperty_
{Read,Write}) and dual-backend e2e tests (TE2EPropertyTests.TestRun_
DefaultProperty_{ReadWrite,StringElement,Inherited}). Grammar and
language-rationale updated; the two new TStaticSubscriptAssign fields are
marked safe in bif-coverage.status.
2026-06-16 16:48:38 +03:00
|
|
|
TStaticSubscriptAssign.PropOwnerType safe
|
|
|
|
|
TStaticSubscriptAssign.PropAccessorVSlot safe
|
2026-06-04 22:31:10 +03:00
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TPointerWriteStmt (uAST.pas:444)
|
2026-06-04 22:31:10 +03:00
|
|
|
TPointerWriteStmt.PtrExpr serialise
|
|
|
|
|
TPointerWriteStmt.ValExpr serialise
|
|
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TProcCall (uAST.pas:452)
|
2026-06-04 22:31:10 +03:00
|
|
|
TProcCall.Name serialise
|
|
|
|
|
TProcCall.Args serialise
|
|
|
|
|
TProcCall.IsImplicitSelfMethod safe
|
|
|
|
|
TProcCall.IsIndirectCall safe
|
|
|
|
|
TProcCall.IndirectCallIsGlobal safe
|
|
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TFuncCallExpr (uAST.pas:465)
|
2026-06-04 22:31:10 +03:00
|
|
|
TFuncCallExpr.Name serialise
|
|
|
|
|
TFuncCallExpr.Args serialise
|
|
|
|
|
TFuncCallExpr.IsImplicitSelfMethod safe
|
|
|
|
|
TFuncCallExpr.IsIndirectCall safe
|
|
|
|
|
TFuncCallExpr.IndirectCallIsGlobal safe
|
|
|
|
|
TFuncCallExpr.IsBuiltinHasClassAttr safe
|
|
|
|
|
TFuncCallExpr.HasClassAttrClass safe
|
|
|
|
|
TFuncCallExpr.HasClassAttrAttr safe
|
|
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TIndirectFuncCallExpr (uAST.pas:491)
|
2026-06-08 02:34:12 +03:00
|
|
|
TIndirectFuncCallExpr.CalleeExpr serialise
|
|
|
|
|
TIndirectFuncCallExpr.Args serialise
|
2026-06-04 22:31:10 +03:00
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TDerefExpr (uAST.pas:501)
|
2026-06-04 22:31:10 +03:00
|
|
|
TDerefExpr.Expr serialise
|
|
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TAddrOfExpr (uAST.pas:508)
|
2026-06-04 22:31:10 +03:00
|
|
|
TAddrOfExpr.Expr serialise
|
|
|
|
|
TAddrOfExpr.ResolvedFreeRoutine safe
|
|
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TMethodCallStmt (uAST.pas:520)
|
2026-06-04 22:31:10 +03:00
|
|
|
TMethodCallStmt.ObjectName serialise
|
|
|
|
|
TMethodCallStmt.Name serialise
|
|
|
|
|
TMethodCallStmt.Args serialise
|
|
|
|
|
TMethodCallStmt.ObjExpr serialise
|
|
|
|
|
TMethodCallStmt.IsImplicitSelf safe
|
|
|
|
|
TMethodCallStmt.IsGlobal safe
|
|
|
|
|
TMethodCallStmt.IsVarParam safe
|
|
|
|
|
TMethodCallStmt.IsBuiltinToString safe
|
2026-06-17 12:07:47 +03:00
|
|
|
TMethodCallStmt.IsConstructorCall safe
|
|
|
|
|
TMethodCallStmt.IsMetaclassDispatch safe
|
2026-06-04 22:31:10 +03:00
|
|
|
TMethodCallStmt.IsProcFieldCall safe
|
|
|
|
|
|
2026-06-17 12:07:47 +03:00
|
|
|
# TInheritedCallStmt (uAST.pas:554)
|
2026-06-04 22:31:10 +03:00
|
|
|
TInheritedCallStmt.Name serialise
|
|
|
|
|
TInheritedCallStmt.Args serialise
|
|
|
|
|
|
2026-06-22 14:34:07 +03:00
|
|
|
# TMethodCallExpr (uAST.pas:835)
|
2026-06-04 22:31:10 +03:00
|
|
|
TMethodCallExpr.ObjectName serialise
|
|
|
|
|
TMethodCallExpr.Name serialise
|
|
|
|
|
TMethodCallExpr.Args serialise
|
|
|
|
|
TMethodCallExpr.ObjExpr serialise
|
|
|
|
|
TMethodCallExpr.IsConstructorCall safe
|
2026-06-17 12:07:47 +03:00
|
|
|
TMethodCallExpr.IsMetaclassDispatch safe
|
2026-06-04 22:31:10 +03:00
|
|
|
TMethodCallExpr.IsGlobal safe
|
|
|
|
|
TMethodCallExpr.IsVarParam safe
|
|
|
|
|
TMethodCallExpr.IsBuiltinToString safe
|
|
|
|
|
TMethodCallExpr.IsBuiltinInheritsFrom safe
|
2026-06-08 02:34:12 +03:00
|
|
|
TMethodCallExpr.IsProcFieldCall safe
|
2026-06-04 22:31:10 +03:00
|
|
|
|
2026-06-22 14:34:07 +03:00
|
|
|
# === Interface-container types (uUnitInterface.pas + TMethodParam) ===
|
|
|
|
|
# serialise must appear in the encoder AND decoder of uUnitInterfaceIO.pas
|
|
|
|
|
|
|
|
|
|
# TRoutineSig (uUnitInterface.pas:64)
|
|
|
|
|
TRoutineSig.Name serialise
|
|
|
|
|
TRoutineSig.IsFunction serialise
|
|
|
|
|
TRoutineSig.Params serialise
|
|
|
|
|
TRoutineSig.ReturnType serialise
|
|
|
|
|
TRoutineSig.IsInline safe
|
|
|
|
|
TRoutineSig.IsPublished safe
|
|
|
|
|
TRoutineSig.IsExternal safe
|
|
|
|
|
TRoutineSig.ExternalName safe
|
|
|
|
|
TRoutineSig.CallingConv serialise
|
|
|
|
|
TRoutineSig.VTableSlot serialise
|
|
|
|
|
TRoutineSig.ResolvedQbeName serialise
|
|
|
|
|
TRoutineSig.IsVirtual serialise
|
|
|
|
|
TRoutineSig.IsOverride serialise
|
|
|
|
|
|
|
|
|
|
# TConstEntry (uUnitInterface.pas:97)
|
|
|
|
|
TConstEntry.Decl serialise
|
|
|
|
|
TConstEntry.TypeRef serialise
|
|
|
|
|
|
|
|
|
|
# TVarEntry (uUnitInterface.pas:105)
|
|
|
|
|
TVarEntry.Name serialise
|
|
|
|
|
TVarEntry.TypeRef serialise
|
|
|
|
|
TVarEntry.IsThreadVar serialise
|
|
|
|
|
|
|
|
|
|
# TUnitInterface (uUnitInterface.pas:169)
|
|
|
|
|
TUnitInterface.Name serialise
|
|
|
|
|
TUnitInterface.SourceFile serialise
|
|
|
|
|
TUnitInterface.SourceHash serialise
|
|
|
|
|
TUnitInterface.SourceModTime serialise
|
|
|
|
|
TUnitInterface.CompilerId serialise
|
|
|
|
|
TUnitInterface.UsedUnits serialise
|
|
|
|
|
TUnitInterface.ImplUsedUnits serialise
|
|
|
|
|
TUnitInterface.HasInitialization serialise
|
|
|
|
|
|
|
|
|
|
# TMethodParam (uAST.pas:764)
|
|
|
|
|
TMethodParam.ParamName serialise
|
|
|
|
|
TMethodParam.TypeName serialise
|
|
|
|
|
TMethodParam.IsVarParam serialise
|
|
|
|
|
TMethodParam.IsConstParam serialise
|
|
|
|
|
TMethodParam.IsOutParam serialise
|
|
|
|
|
TMethodParam.IsOpenArray serialise
|
|
|
|
|
TMethodParam.DefaultValue serialise
|
|
|
|
|
TMethodParam.HasDefault serialise
|
|
|
|
|
|