Language improvements + RTL/StdLib expansion + bug fixing is continuous work,
not a phase that completes — "Ongoing" reflects that better than "In-Progress".
Phase 7 was "Native backend feature parity + Windows + macOS ARM64
(In-Progress)". Native is now the default backend with a working internal
assembler and linker, so:
- Phase 7 is now "Native backend feature parity" — Complete.
- New Phase 8 "Windows + macOS ARM64 targets" — Planned.
- Former phases 8/9 (LSP + VS Code, migration analyser) renumbered to 9/10 so
the sequence is contiguous.
README.adoc:
- Native x86-64 is the default backend since v0.12.0; QBE is opt-in
(--backend qbe). Reframed the intro, project status, bootstrap, and
single-file compile examples accordingly (native one-step build leads; QBE
shown as the opt-in multi-step path).
- Test count 3474 -> 3800+ (3744 compiler + 57 stdlib).
- Added a Standard library status line (generics collections, JSON, SHA-1,
Base64, GUIDs, sockets, WebSockets, HTTP server, blaise.testing).
- Bootstrap example resolves the newest releases/v* binary instead of the
stale v0.7.0 path, and uses the native --output build.
- OPDF now debugs incrementally-compiled multi-unit programs.
grammar.ebnf (CLAUDE.md sync rule):
- TypeName accepts a unit-qualified, possibly-dotted QualIdent
(UnitName.TypeName, System.SysUtils.TFormatSettings).
- FieldAssignment l-value extended with a Selector chain
(.Field / [Index] / .Method(...)) so Self.F[i].Sub := value parses.
language-rationale.adoc:
- New entries "Qualified type names" and "Chained l-value assignment"
recording the decision, reasoning, and alternatives for the two
conservative FPC/Delphi-compatible parser extensions made this cycle.
The general 'set of' operation e2e tests (Include/Exclude, in, union/intersect,
valued constant, literal argument, equality, for-in, and the 33..64-member
Set64 boundary cases) lived in cp.test.e2e.misc and ran on QBE only via
CompileAndRun. They test named-set operator semantics, distinct from the
inline-set syntax covered by cp.test.e2e.inlineset, so they move to a new
purpose-named unit cp.test.e2e.sets (TE2ESetOpsTests) paralleling the IR-level
cp.test.sets — and are promoted to AssertRunsOnAll so each now runs on both the
QBE and native backends, closing a native-coverage gap.
Pure relocation + both-backend promotion: full suite unchanged at 3172 tests
(11 set tests moved misc -> sets). README test count updated 2768 -> 3172.
Align the QBE backend unit names with the dotted naming convention
already used by the native backend (blaise.codegen.native.*).
uCodeGen.pas → blaise.codegen.pas
uCodeGenQBE.pas → blaise.codegen.qbe.pas
Updated all uses clauses (~70 test files, main compiler units),
documentation references (README + 5 docs/*.adoc files), and
comment references in runtime/stdlib.
Assigning nil to a class-typed variable (O := nil) did not call
_ClassRelease on the old value in either backend. The nil literal
has ResolvedType.Kind = tyNil which bypassed the tyClass ARC branch
in EmitAssignment, falling through to the generic scalar store that
performs no reference counting.
QBE backend: added a class-nil branch in EmitAssignment that loads the
old value, calls _ClassRelease, and stores zero. Handles strong, weak,
and promoted-local cases.
Native backend: added the same class-nil assignment branch, plus two
further fixes:
- EmitExprToEax now handles TNilLiteral (emits xorq %rax, %rax)
- EmitFieldCleanupFn now emits the destructor call and ARC field
releases instead of being an empty stub, bringing it to parity with
the QBE backend's FieldCleanup emission.
Updated leak-check tests whose leak scenarios relied on the broken
nil-assignment behaviour — they now use _ClassAddRef to create genuine
unbalanced reference leaks.
Updated test count in README (2293 -> 2606).
Add scripts/rolling-bootstrap.sh to rebuild the self-hosting chain from
the last release binary up to the checked-out revision. A released binary
can only compile source up to its own feature set, so between releases it
can no longer cold-bootstrap master directly — the moment a feature is
taught to the parser in one commit and used in the runtime/compiler in a
later commit, the gap opens. The script replays the chain commit-by-commit
(each step built by the previous step's binary), carrying the binary
forward, and installs the result as releases/v<version>-pre/.
It runs in a throwaway git worktree, so the live tree (including
uncommitted changes) is untouched; it passes QBE= and BLAISE= into the
runtime Makefile and uses the main repo's built QBE; and it smoke-tests
each step. A failed step names the exact commit (BOOTSTRAP_BROKEN),
making it usable as a CI check that the two-step discipline holds.
Document the prerequisite (place the last release binary under
releases/vX.Y.Z/, which is gitignored) and usage in scripts/BOOTSTRAP.adoc,
with a pointer from README.adoc.
Replace the C variadic _StringFormat with a pure Pascal _StringFormatN
that takes a format string, a pointer to an array of tag/value pairs,
and a count. The codegen allocates the arg array on the stack via
alloc8, stores (tag:l, value:l) pairs, and calls the non-variadic
function — eliminating the last dependency on C va_list.
This required a multi-stage bootstrap: release binary compiled stage-1
(with new codegen), stage-1 compiled stage-2 (self-hosted with new ABI),
then the C file could be safely removed. Integer args are widened via
extsw when w-typed to satisfy QBE's storel type checking.
Only 2 C files remain in the runtime: blaise_exc.c (setjmp/longjmp)
and blaise_arc_class.c (function-pointer dispatch).
A var declaration shadowing a same-block const (or a const declared
twice in the same block) was silently accepted instead of raising a
Duplicate identifier error.
Root cause: AnalyseBlock registers consts in the outer scope then pushes
a new inner scope before calling AnalyseVarDecls, so FTable.Define
(which only checks the current scope) could not detect the clash.
Fix: AnalyseVarDecls now scans the block's own ConstDecls list before
defining each var name. AnalyseConstDecls now treats a duplicate as an
error when the collision originates from an earlier const in the same
block; cross-unit const shadowing (e.g. a unit redefining a system.pas
constant) continues to be silently accepted.
Also removes the redundant LineEnding const from blaise.testing.pas —
it is already exported by system.pas.
Fixes: https://github.com/graemeg/blaise/issues/30
Separate always-linked runtime code (system.pas, ARC, strings, platform
abstraction) from opt-in standard library units (sysutils, classes, math,
dateutils, generics, etc.). This mirrors the Go runtime/ vs stdlib, Rust
core vs std, and Swift SwiftRuntime vs SwiftCore patterns.
- runtime/ contains code linked into every binary + C shims
- stdlib/ contains units imported explicitly via uses clause
- Single platform abstraction layer (rtl.platform.*) in runtime/,
shared by both runtime/ and stdlib/ units
- bcl.testing renamed to blaise.testing across all 95+ test files
- Updated PasBuild project.xml, fixpoint.sh, README, unit search paths
- All 1931 tests pass, fixpoint OK (stage-3/stage-4)
Implements the 'abstract' method directive, following FPC/Delphi
semantics:
procedure Draw; virtual; abstract;
Abstract methods must not have an implementation body (semantic
error if one is provided). A class that has or inherits any
un-overridden abstract method is implicitly abstract and cannot
be instantiated (compile-time error on TFoo.Create).
Abstract vtable slots point to a $__abstract_method_error stub
(calls abort()) as a defence-in-depth measure.
Changes:
uAST.pas: IsAbstract field on TMethodDecl
uSymbolTable.pas: IsAbstract on TVTableEntry; HasAbstractMethods
on TRecordTypeDesc; CopyVTableFrom now propagates IsAbstract
uParser.pas: 'abstract' directive sets IsAbstract (and IsVirtual)
uSemantic.pas: abstract slot tracking in vtable pre-pass;
post-vtable check for inherited-but-unoverridden abstract slots;
AnalyseMethodDecl rejects abstract methods with bodies;
TFoo.Create raises ESemanticError for abstract classes (both
constructor call sites)
uCodeGenQBE.pas: EmitVTableDefs emits $__abstract_method_error
stub when needed; abstract slots reference it in the vtable
cp.test.vtable.pas: 9 new tests covering parse flags, semantic
validation, and codegen. 1846 tests pass. Fixpoint OK.
Re-enabled cp.test.lexer, cp.test.parser, cp.test.codegen,
cp.test.symtable, cp.test.semantic, cp.test.records in TestRunner.pas —
all six were commented out when the test runner was migrated from FPC to
Blaise, reducing the suite from ~1433 to ~1376 tests.
Changes needed to make all six units compile and pass (1541 tests total):
- Remove leftover `Classes, SysUtils` imports from all six units (FPC
fpcunit era; Blaise has neither unit).
- Replace default-property bracket subscripts (TObjectList[i],
TStringList[i]) with explicit .Items[i] / .Strings[i] calls, since
Blaise does not yet implement the `default` indexed property keyword.
- codegen: add tyRecord branch in EmitStaticSubscriptAssign — record
elements in static arrays must use EmitRecordCopy, not a raw storew.
- codegen: add tyRecord early-return in EmitStringSubscriptExpr static
array branch — return element pointer directly (records are by-value
via pointer).
- codegen: fix EmitInstancePtr to delegate to EmitExpr when the field
access is property-backed (PropRead != nil), avoiding a nil FieldInfo
assertion.
- test: fix IRContains to use Pos(...) >= 0 (Blaise Pos is 0-based,
returns -1 on no-match, not 0 like FPC).
On a clean checkout, compiler/target/blaise does not exist yet, so
plain `make` in rtl/ produces empty .ssa files and a silently broken
RTL archive. The fix is to pass BLAISE=../releases/v0.7.0/blaise to
make on the first RTL build. Document this in both README.adoc and
CLAUDE.md with the correct three-step cold-bootstrap sequence.
Replace FPC prerequisites with Blaise release binary. Update build
instructions to show bootstrap-from-release workflow. Update test
count to 1268 and note that the test suite compiles under Blaise.
- Rewrite intro with tagline and project summary
- Fix licence: BSD 3-Clause → Apache 2.0 with Runtime Library Exception
- Fix LICENSE filename reference (American spelling matches actual file)
- Update test count to 1200+
- Add status snapshot (self-hosting, test coverage, backend)
- Correct phase 4 and 5 status to Complete / In-Progress
- Add Community section linking to GitHub Discussions
- Add closing attribution line
Record the decision (option A) in docs/design.adoc with full pros/cons
analysis of the three options considered. Update the Phase 3 status
table so interface-ref ARC reads "Planned — option A chosen" rather
than "Deferred — design-blocked". Mirror the commitment in README.adoc
under Design Philosophy and list the TObject/TInterfacedObject split
as dropped.