Commit graph

11 commits

Author SHA1 Message Date
mzhoot 94ae8c354c Рабочие синонимы 2026-07-04 21:44:54 +03:00
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
Graeme Geldenhuys d7460cb069 feat(parser): require parentheses on inherited calls; fix metaclass diagnostic
The mandatory-parentheses rule ("every function, procedure, method, and
constructor call requires parentheses, even with no arguments") made no
exception for 'inherited'.  A bare 'inherited Create' is a method call and
should be rejected like any other parenless call, but the parser silently
accepted it.

Enforce the rule in both 'inherited' parse paths (statement and expression
position): a bare 'inherited Method' now raises the same "requires () for a
call" diagnostic as any other parenless call.

Also fix a misleading semantic error for the metaclass-variable case.  A bare
'C.Create' on a 'class of T' variable parses as a field access, which reached
the "'C' is not a record or class" guard — confusing, since C is a valid
metaclass variable.  AnalyseFieldAccess now detects a constructor/method name
on a metaclass receiver and emits the "requires () for a call" diagnostic
instead.  (The parenthesised 'C.Create()' already worked: it parses as a
TMethodCallExpr, which has the metaclass-dispatch branch.)

Migrate every bare 'inherited Method;' call site in stdlib, the bif-coverage
tool, and embedded e2e test programs to the parenthesised form.  The
parenthesised form compiles on the prior compiler too, so the migration and the
enforcement land together without breaking the rolling-bootstrap chain.

Docs: update language-rationale.adoc (inherited section + mandatory-parens
section) and grammar.ebnf (expression-position inherited rule).

Tests: add TestSemantic_MetaclassVar_BareCreate_RequiresParens and
TestParse_Inherited_Bare{Stmt,Expr}_RequiresParens.
2026-06-25 13:36:27 +01:00
Graeme Geldenhuys c45fcbca35 bif-coverage: replace TStringList-with-Objects[] casts with parallel generics
TASTClass.Fields was a TStringList storing field names in the string slot and
source line numbers in Objects[] behind Pointer(PtrUInt(line)) /
Integer(PtrUInt(...)) casts. Split it into Fields: TList<String> +
FieldLines: TList<Integer> (the parallel-list idiom already used elsewhere in
this tool), removing both unsafe casts. Behaviour unchanged: coverage run
reports OK, 71 AST classes scanned.
2026-06-23 19:50:37 +01:00
Graeme Geldenhuys 676cee0cae Modernise stdlib testing + tools: TList<String> over TStringList, drop .Free
Showcase generics + ARC across the test framework and tools. Replace
TStringList with TList<String> wherever only the basic list API was used
(Create/Add/Get/Count/Clear/Delete/IndexOf), rewriting .Strings[i] to .Get(i),
and remove manual .Free calls since Blaise reference-counts objects.

Lists genuinely needing TStringList-only API stay as TStringList: file I/O
(LoadFromFile/SaveToFile/Text), object association (AddObject/Objects[] in the
test registry and bif-coverage AST), and dup control (Duplicates). Destructors
that only freed fields are deleted.

stdlib 47 tests, compiler 3743 tests (20 pre-existing toolchain failures
unchanged), varcheck 18 regression tests all pass.
2026-06-23 19:21:09 +01:00
Graeme Geldenhuys 74da3eadcc test(bif-coverage): extend drift guard to .bif interface types
bif-coverage verified that every AST node field round-trips through the .bif
encoder/decoder, but the .bif interface-container types (TRoutineSig,
TUnitInterface, TMethodParam, TConstEntry, TVarEntry) were hand-serialised in
WriteMeta/EncodeMethodSig/etc. with no drift guard. Every cached-rebuild bug
just fixed was a serialised field on one of those types dropped from one side
of the round-trip — invisible to the tool, surfacing only as a runtime
miscompile.

Generalise the class scanner to ScanClassFile(path, names, objs, allowList);
add ScanInterfaceTypes() over an allow-list of the container types in
uUnitInterface.pas. Split uUnitInterfaceIO.pas into encoder-side and
decoder-side text and assert each serialised interface-type field's identifier
appears in both — the same looseness as the AST mechanism. Status file gains
the interface-type entries (serialise/safe); mutator-repopulated owning
collections are { no-bif }-exempt (their element data round-trips through the
per-entry encoders).

Negative test confirmed: dropping ImplUsedUnits/HasInitialization/VTableSlot
from either side is now reported as a gap with exit 1. Clean tree exits 0.
2026-06-22 12:38:28 +01:00
Graeme Geldenhuys 3404ac13ea chore: adopt -SNAPSHOT version suffix, update README
Align the compiler's internal version strings with PasBuild's
convention: 0.11.0-dev → 0.11.0-SNAPSHOT (Blaise.pas, uCompilerId.pas).
Update rolling-bootstrap.sh to strip -SNAPSHOT instead of -dev.

README updates:
- Document the native x86-64 backend alongside QBE
- Update Phase 7 from "LLVM" to native backend parity
- Fix runtime layout (main/asm/, not main/c/)
- Add --backend native usage example
- Update test count to 2768
2026-06-10 10:39:14 +01:00
Graeme Geldenhuys 328493baf2 fix(bif): resolve bif-coverage gaps for current master
- Add TIndirectFuncCallExpr encoder/decoder to uUnitInterfaceIO.pas
  (was missing entirely, causing --incremental to silently drop
  indirect call nodes)
- Bump COMPILER_ID to blaise-0.11.0-dev+bif1
- Fix version check to compare base version only (strip -SNAPSHOT
  and -dev suffixes before comparing)
- Change test to Ignore() when binary missing (module is not
  activeByDefault)
- Remove redundant <version> from tool project.xml (inherits root)
- Regenerate bif-coverage.status for current AST (68 classes,
  38 encoder/decoder cases)
2026-06-08 00:34:12 +01:00
Andrew Haines 00ca54afc4 fix(tools): add () to bare zero-arg calls in bif-coverage
Master's mandatory-parens enforcement (51ebf23) now flags every bare
zero-arg function reference. Sweep BifCoverage.pas and its TestRunner
wrapper, and swap sLineBreak for LineEnding.
2026-06-08 00:27:24 +01:00
Andrew Haines 565fbea9bc fix(tools): bif-coverage resolves paths by walking up from CWD
Previously the binary used `../../compiler/src/main/pascal/...` and
`bif-coverage.status` directly, locking it to invocation from
tools/bif-coverage/.  Now it walks up from CWD looking for a directory
that contains both `compiler/src/main/pascal/uAST.pas` and
`project.xml`, then resolves every other path under that root.  Lets
the verifier be invoked from the project root, from any subdir, or
from a test runner's CWD (compiler/) without setup.
2026-06-08 00:27:24 +01:00
Andrew Haines 56c3fe1d0b feat(tools): add bif-coverage verifier for AST/encoder/decoder drift
Static-analysis tool that cross-checks uAST.pas against
uUnitInterfaceIO.pas and the root project.xml. For every TASTStmt /
TASTExpr subclass it confirms the class has a dispatch case in
EncodeStmt/EncodeExpr and ReadStmt/ReadExpr, then walks the public
fields and ensures each is either referenced from both encoder and
decoder (`serialise`) or explicitly excluded (`safe`).

Truth is checked-in: bif-coverage.status is a flat file with one
`<TClass>.<Field>  <serialise|safe>` line per field. The default
invocation diffs the live sources against the status file and reports:

  [version]   COMPILER_ID does not match root project <version>
  [encoder]   missing (Class.Field, uAST.pas line)
  [decoder]   missing (Class.Field, uAST.pas line)
  [new]       field exists in AST but is not in the status file
  [stale]     status names a field or class the AST no longer has
  [broken]    serialise field missing from encoder or decoder
  [drift]     safe field has crept into encoder/decoder (with the
              offending uUnitInterfaceIO.pas line)

`bif-coverage --reset` regenerates the status file from current state,
inferring `serialise` when the encoder references the field and `safe`
otherwise. Use after deliberate AST or .bif format changes to
re-baseline.
2026-06-08 00:27:24 +01:00