Skip to content

Blaise v0.9.0 (alpha) — Runtime port & bug squashing

Choose a tag to compare

@graemeg graemeg released this 02 Jun 09:29
· 360 commits to master since this release

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.c to pure Pascal (blaise_arc.pas)
  • Port blaise_exc.c to Pascal + x86_64 assembly (blaise_setjmp_x86_64.s
    remains as the only non-Pascal file)
  • Port blaise_weak.c to pure Pascal
  • Port blaise_float.c to pure Pascal Grisu1 float-to-string implementation
  • Port blaise_str_fmt.c to pure Pascal _StringFormatN
  • Consolidate blaise_io / blaise_process / blaise_sys / blaise_time
    into rtl.platform.posix Pascal unit
  • Enable pipefail in 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 bare l <addr> was passed and the
    callee read the pointer bits as struct contents.
  • Single field alignment corrected to 4 bytes (was falling through to
    the 8-byte catch-all, doubling the size of Single-heavy records and
    breaking C struct interop)
  • Double literal narrowed to Single on record field store — previously
    emitted stores d_<lit>, ... which QBE rejected as a type mismatch
  • Double expression narrowed to Single before FFI call — a Double-
    typed actual passed to a Single formal 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, Word with 65535, SmallInt sign-extended
    via shl 16 / sar 16. C ABI leaves upper bits undefined for sub-int
    returns; callers observing the full word saw garbage.
  • @A[i] on dynamic arraysEmitAddrOfExpr now has a tyDynArray
    branch mirroring the open-array path. Previously raised "Unsupported
    L-value form for var argument".
  • Overloaded Destroy in ARC field-cleanupEmitFieldCleanupFn now
    calls the overload-mangled symbol (stored as DestroyResolvedQbeName on
    TRecordTypeDesc) rather than the bare <Class>_Destroy label, which was
    never emitted for overloaded forms, causing a linker undefined-symbol error.
  • Fix storel for proc-pointer static-array slots; add Expr()() postfix call
  • Use narrow load/store for implicit-Self Byte/Word/SmallInt fields
  • Fix QBE types for Single float 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 .Free to prevent double-free
  • Load class pointer before offsetting in var-param L-value
  • Preserve ARC variables across try/finally re-raise

ARC — correctness

  • finally blocks run on non-local exit (Exit, Break, Continue)
    via FFinallyStack — previously skipped entirely on non-local unwinds
  • [Unretained] attribute for non-owning class references (back-pointers,
    AST Resolved*Type fields, 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-_ClassAddRef in constructor-with-args codegen
  • Eliminate double-_ClassAddRef on function-return class assignments
  • Wire owned-arg-temp release into all remaining call paths
  • Remove 50+ redundant Free calls from AST destructors (field cleanup
    already handled by ARC)
  • Remove redundant Free calls 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 attribute
  • SmallInt and Word — 16-bit signed/unsigned integer types with correct
    narrow load/store and Byte/Word field packing at 1-byte / 2-byte stride
  • UInt64 / QWord — 64-bit unsigned integer type
  • packed record — suppresses per-field alignment padding
  • sar arithmetic right-shift operator
  • High / Low extended to all ordinal types; targeted float error message
  • SizeOf(expression) — previously only SizeOf(TypeName) was accepted
  • / is real division — distinct from div (integer division)
  • for..in over dynamic arrays
  • Octal / binary literals (0o777, 0b1010) and underscore separators
    in numeric literals (1_000_000, 0xFF_FF)
  • Integer-type typecasts in const initialisersCardinal(-11),
    Byte(255), SmallInt(-1), etc., with correct bit-width truncation and
    sign extension
  • Bit-op chains in const initialisersFG_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, Tanh math builtins with Single
    dispatch overloads
  • Extend High / Low to 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 const and var sections
  • Report missing units even when no --unit-path is 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

  • GetText and GetCommaText rewritten to avoid O(N²) string concatenation
  • TList<T>.IndexOf added
  • TListEnumerator<T> + TList<T>.GetEnumeratorfor..in over generic lists
  • _ClassRelease elision for provably-nil slots (codegen perf)
  • Remove all dead {$IFDEF FPC} compiler directives

Developer experience

  • --debug leak reporter — shows refcount per live object at program exit
  • blaise.cfg — automatic unit-path discovery from project directory
  • Multiple --suite filters accepted by the test runner
  • --emit-ir path now exits through normal cleanup instead of Halt(0)
  • UTF-8 bytes in comments: regression tests added to lexer suite

Compiler internals / Build

  • Mark Resolved*Type AST 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-dir flag and migrate_full.py removed (dead code)