blaise/compiler
Graeme Geldenhuys 0977dc16cb feat(lang): static class/record members (within-unit)
Introduce `static` (class-level) members to the Blaise language using the
`static` keyword — never an overloaded `class` keyword. A `static` member is
type-associated, not instance-associated: static methods take no implicit
Self, and static vars/consts are a single shared storage slot.

Surface, on classes and records:

* `static var` / `static const` — section form (`private static var`) or as a
  bare `static` continuing the current visibility. Static vars lower to one
  shared global slot (mangled `<Unit><Type>_<Name>`), zero-initialised, NOT an
  instance field. Class- and interface-typed static vars are supported (the
  canonical singleton storage) with store-time ARC and a program-exit release;
  string and dynamic-array static vars remain deferred.
* `static function` / `static procedure` — per-member prefix or section form;
  no implicit Self. Out-of-line bodies are `static function T.M`.
* `static property` — sugar over a static getter (no Self at the call site).
* record `static function` — the factory / namespaced-function form
  (`TPoint.Make(x, y): TPoint`), required to be marked `static` explicitly.

There is no `static constructor` / `static destructor` (rejected at parse):
the zero-initialisation guarantee covers nil singletons, and eager setup
belongs in a unit's `initialization`/`finalization` (the Swift/Rust/Go model,
not Java/C#/Delphi). `class` is never a member qualifier.

Implementation spans the full pipeline:

* parser — `static` is a soft keyword; section qualifier (followed by
  var/const) and per-member prefix forms; `static constructor/destructor`
  rejected.
* semantic — static vars register a shared global (bare + qualified) under a
  mangled emit label; static methods skip the Self binding; qualified
  `Type.StaticVar` / `Type.StaticProp` / `Type.StaticMethod()` resolution.
* QBE + native x86-64 codegen — no-Self method signatures and call sites
  (including the record-return sret and >6-arg paths), shared global data
  slots, qualified static var/property reads, and class/interface static-var
  release at program exit.
* `.bif` interface format — IsClassVar/ClassVarEmitName, property IsStatic, and
  record/class const decls are encoded (BLAISE-IFACE version 2 -> 3).
* OPDF debug info — static vars are emitted as `recGlobalVar`s under their
  mangled label so a debugger can print `TFoo.FInstance`.

Static members currently work within a single program/unit; carrying them
across separately-compiled units (export clone, import, TRoutineSig.IsStatic)
is a tracked follow-up — the .bif wire format is already in place for it.

Tests: cp.test.staticmembers (parser + semantic + IR) and
cp.test.e2e.staticmembers (compile+run on both backends). Full suite green on
QBE- and native-built runners (3908 tests); all fixpoints pass; bif-coverage
clean. docs/grammar.ebnf and docs/language-rationale.adoc updated.
2026-06-28 16:11:40 +01:00
..
src feat(lang): static class/record members (within-unit) 2026-06-28 16:11:40 +01:00
project.xml refactor(rtl): relocate RTL units into the compiler tree (dotted-flat names) 2026-06-25 16:05:44 +01:00