Blaise v0.9.0 (alpha) — Runtime port & bug squashing
Changelog — Blaise v0.9.0
Fixpoint: stage-2 == stage-3 at 166,479 lines of QBE IR (up from 135,311 in v0.8.0).
Test suite: 2,293 tests, 0 failures.
Runtime — C elimination
- Port
blaise_arc_class.cto pure Pascal (blaise_arc.pas) - Port
blaise_exc.cto Pascal + x86_64 assembly (blaise_setjmp_x86_64.s
remains as the only non-Pascal file) - Port
blaise_weak.cto pure Pascal - Port
blaise_float.cto pure Pascal Grisu1 float-to-string implementation - Port
blaise_str_fmt.cto pure Pascal_StringFormatN - Consolidate
blaise_io/blaise_process/blaise_sys/blaise_time
intortl.platform.posixPascal unit - Enable
pipefailin the runtime Makefile so compiler errors fail the build
FFI / Codegen — interop correctness
- Records by value through SysV aggregate ABI: record-by-value parameters
now use QBE:_ffi_<Name>aggregate types so fields are scattered into
INTEGER/SSE registers per the platform ABI. Applied uniformly to all call
sites (external and intra-Blaise), so callbacks are consistent. Win64 ABI
comes from QBE for free. Previously, a barel <addr>was passed and the
callee read the pointer bits as struct contents. Singlefield alignment corrected to 4 bytes (was falling through to
the 8-byte catch-all, doubling the size ofSingle-heavy records and
breaking C struct interop)- Double literal narrowed to
Singleon record field store — previously
emittedstores d_<lit>, ...which QBE rejected as a type mismatch - Double expression narrowed to
Singlebefore FFI call — aDouble-
typed actual passed to aSingleformal put 8 bytes into the float slot;
the C callee read the low mantissa half as IEEE-754 single (noise) - Narrow FFI return values masked to declared width:
Byte/Boolean
returns are ANDed with 255,Wordwith 65535,SmallIntsign-extended
viashl 16/sar 16. C ABI leaves upper bits undefined for sub-int
returns; callers observing the full word saw garbage. @A[i]on dynamic arrays —EmitAddrOfExprnow has atyDynArray
branch mirroring the open-array path. Previously raised "Unsupported
L-value form for var argument".- Overloaded
Destroyin ARC field-cleanup —EmitFieldCleanupFnnow
calls the overload-mangled symbol (stored asDestroyResolvedQbeNameon
TRecordTypeDesc) rather than the bare<Class>_Destroylabel, which was
never emitted for overloaded forms, causing a linker undefined-symbol error. - Fix
storelfor proc-pointer static-array slots; addExpr()()postfix call - Use narrow load/store for implicit-Self
Byte/Word/SmallIntfields - Fix QBE types for
Singlefloat arithmetic and comparisons - Emit correct data items for typed array consts inside function bodies
- Fix ARC retain/release on array element writes
- Allocate 16 bytes for procedure-of-object class fields
- Retain/release class refs stored through typed pointer
- Nil class/string field slots after
.Freeto prevent double-free - Load class pointer before offsetting in var-param L-value
- Preserve ARC variables across
try/finallyre-raise
ARC — correctness
finallyblocks run on non-local exit (Exit,Break,Continue)
viaFFinallyStack— previously skipped entirely on non-local unwinds[Unretained]attribute for non-owning class references (back-pointers,
ASTResolved*Typefields, symtab links) that must not participate in ARC- Release
+1-owned temporaries produced by function/property returns when
used transiently or passed as value arguments - Release
+1-owned receiver temporaries after method calls - Honour
[Weak]on implicit-Self class field assignments - Retain caught exception when binding to handler variable (
except E: T do) - Eliminate double-
_ClassAddRefin constructor-with-args codegen - Eliminate double-
_ClassAddRefon function-return class assignments - Wire owned-arg-temp release into all remaining call paths
- Remove 50+ redundant
Freecalls from AST destructors (field cleanup
already handled by ARC) - Remove redundant
Freecalls from symbol-table destructors
Language — new features
- Nested procedures with captured-variable closure (full access to
enclosing scope locals) - Custom attributes —
[MyAttribute]/[MyAttribute(args)]on any
declaration; compiler validates attribute class and resolves constructor [Unretained]non-owning reference attributeSmallIntandWord— 16-bit signed/unsigned integer types with correct
narrow load/store and Byte/Word field packing at 1-byte / 2-byte strideUInt64/QWord— 64-bit unsigned integer typepacked record— suppresses per-field alignment paddingsararithmetic right-shift operatorHigh/Lowextended to all ordinal types; targeted float error messageSizeOf(expression)— previously onlySizeOf(TypeName)was accepted/is real division — distinct fromdiv(integer division)for..inover dynamic arrays- Octal / binary literals (
0o777,0b1010) and underscore separators
in numeric literals (1_000_000,0xFF_FF) - Integer-type typecasts in const initialisers —
Cardinal(-11),
Byte(255),SmallInt(-1), etc., with correct bit-width truncation and
sign extension - Bit-op chains in const initialisers —
FG_BLUE or FG_GREEN,1 shl 8,
$FF and 15,A xor B, including mixed literal+named chains and array
elements; folded at semantic time ArcSin,ArcCos,Sinh,Cosh,Tanhmath builtins withSingle
dispatch overloads- Extend
High/Lowto dynamic array types - Allow direct invocation of proc-typed class fields
- Allow implicit integer-to-float assignment; reject implicit float↔integer
Semantic — fixes
- Implicit Self member no longer shadows unit-level symbol
- Nested procs with the same name in different outer procs no longer ambiguous
- Clone method bodies per generic instance (was sharing mutable AST nodes)
- Allow two instances of the same generic class in the same scope
- Wire up parent class on generic instances
- Walk parent chain when resolving inherited properties
- Register var decls before analysing method bodies
- Use overload resolution for constructor calls
- Emit support data for generic classes declared in a unit
- Normalise all identifier references to declared casing
- Reject duplicate identifiers across
constandvarsections - Report missing units even when no
--unit-pathis given - Fix
SizeOf(expression)to resolve the expression type
Threading
TThread,TCriticalSection, and full POSIX thread bindings- Self-referential thread lifetime under ARC (thread holds own refcount during
Execute, preventing use-after-free on fire-and-forget patterns) [Threaded]E2E test suites run as parallel subprocesses
Stdlib / Performance
GetTextandGetCommaTextrewritten to avoid O(N²) string concatenationTList<T>.IndexOfaddedTListEnumerator<T>+TList<T>.GetEnumerator—for..inover generic lists_ClassReleaseelision for provably-nil slots (codegen perf)- Remove all dead
{$IFDEF FPC}compiler directives
Developer experience
--debugleak reporter — shows refcount per live object at program exitblaise.cfg— automatic unit-path discovery from project directory- Multiple
--suitefilters accepted by the test runner --emit-irpath now exits through normal cleanup instead ofHalt(0)- UTF-8 bytes in comments: regression tests added to lexer suite
Compiler internals / Build
- Mark
Resolved*TypeAST fields[Unretained]— prevents spurious retain
cycles on type descriptors shared across the AST - Remove all old FPC compiler directives no longer needed in Blaise
--cache-dirflag andmigrate_full.pyremoved (dead code)