Working Draft: Compiler-Annotations (@...) als deterministische Schnittstelle zwischen JDL und Jade¶
Status: Working Draft (ohne bindende Wirkung)
Zweck: Ideen-/Architektur-Referenz für Jade/JDL. Syntaxdetails sind ausdrücklich nicht final.
1. Motivation¶
Jade/JDL braucht eine schmale, präzise definierte „Kante“ zwischen:
- JDL (User-Code, Semantik, Typen, Effekte) und
- Jade (VM, Runtime-Subsysteme, Loader/Linker, Diagnostik/Tooling)
Diese Kante soll:
- deterministisch sein (keine „Makro-Magie“, kein unkontrollierbares Code-Rewriting),
- minimal bleiben (nicht alles ins Typsystem pressen, nur weil es dort theoretisch geht),
- effizient sein (Compiler/VM dürfen klare Datenstrukturen erzeugen),
- tooling-freundlich sein (Parser/LSP/Indexer verstehen das zuverlässig).
Dafür wird eine kleine Menge Compiler-Annotations eingeführt, die visuell und syntaktisch über @... erkennbar sind.
2. Grundsatzregel: Was bedeutet @?¶
Wenn du ein @ siehst, siehst du eine Compiler-Annotation.
Compiler-Annotationen sind nicht das Meta-System für Domain- oder Framework-Logik.
Sie sind ausschließlich für Dinge gedacht, die:
- der Compiler zwingend wissen muss, oder
- die deterministische Build-/Runtime-Verträge ausdrücken, oder
- nur Diagnostik/Registrierung für Tooling bereitstellen.
Alles Domain-/App-spezifische Meta gehört ins Typ-Metasystem (Type Descriptors, TypeFns, Refinements, Generatoren/Collector-Mechaniken).
3. Design-Leitplanken (nicht verhandelbar, sonst kippt es)¶
3.1 Determinismus¶
@-Annotationen dürfen keine dynamische Programmlogik einführen und keine Laufzeit-Umschreibung durchführen.
Erlaubt sind nur: - AST-Pruning (Entfernen von Code vor Name Resolution/Typecheck), - compile-time Validierung (harte Fehlermeldungen), - Diagnostik (Warnings/Lints), - deterministische Registrierung (z.B. Tests, Runtime-Hooks, Handler-Tabellen), - Backend-Hints, sofern sie Semantik nicht ändern.
3.2 Keine zweite Sprache¶
Keine User-definierten Compiler-Annotationen mit eigener Semantik.
Wenn User eigene Metadaten brauchen: Typ-Meta.
3.3 Keine Expression-level @cfg im MVP¶
@cfg wirkt auf Items/Blöcke aus Items.
Keine „überall im Ausdruck“ Bedingungen, sonst wird @ zur Präprozessor-Hölle.
4. Kategorien von Compiler-Annotationen¶
A) Prune-Annotationen (Frontend, AST-Entfernung)¶
Ziel: Codezweige eliminieren, bevor Scope/Typen/Codegen überhaupt betroffen sind.
Kernfeature: @cfg(predicate)
Formen (konzeptionell):
- Item-Form: @cfg(pred) <item>
- Block-Form: @cfg(pred) { <item>* }
Wichtig: Block ist kein neuer Semantik-Scope.
Wenn aktiv, ist der Inhalt so, als stünde er normal im Modul.
Wenn inaktiv, ist er komplett entfernt.
Pruning-Pass-Reihenfolge:
1. Parse (inkl. Annotation-Knoten)
2. cfg_prune gegen festen Build-Kontext
3. erst danach: Name Resolution, Typecheck, Codegen
Damit gilt: inaktive Zweige müssen nicht typkorrekt sein.
B) Validate-Annotationen (Compile-time Checks)¶
Ziel: harte Garantien (z.B. Plattform, ABI, Layout-Annahmen), ohne allgemeines CTFE.
Kernfeature: @assert(predicate, msg?)
Zwei sinnvolle „Stufen“:
- Early assert (nur Build/Target/Feature):
- Predicate-DSL wie bei
@cfg - läuft direkt nach
cfg_prune -
Beispiele: „Linux only“, „Feature X required“
-
Late / type-aware assert (Layout/Size/Align):
- läuft nach Typauflösung/Layoutberechnung
- erlaubt zusätzlich Compiler-Queries wie
sizeof(T),alignof(T),layoutof(T) - geeignet für FFI ABI Checks
Das kann entweder über zwei Namen erfolgen:
- @assert(...) (early)
- @assert_type(...) (late)
oder über explizite Stages. Zwei Namen sind in der Praxis klarer.
C) Diagnose-Annotationen (Warnings/Lints)¶
Ziel: Entwicklerführung, ohne Semantik zu verändern.
Kernfeature: @deprecated(...)
Semantik: - Warnung bei Verwendung, nicht beim bloßen Import. - Payload optional: message, since, replacement (alles rein diagnostisch).
Optional ergänzend:
- @lint.allow(...), @lint.deny(...) (wenn Lints/Rules existieren)
- @inline, @noinline als reine Codegen-Hints (falls nötig)
D) Tool-/Build-Registrierung (deterministisch, statisch)¶
Ziel: Compiler sammelt strukturierte Informationen und stellt sie dem Tooling zur Verfügung.
Kernfeature: @test
- Markiert Testfunktionen
- Compiler validiert ggf. Signatur (z.B. () -> void oder () -> Result)
- Compiler/Tool erzeugt Test-Manifest/Registry
- Ausführung erfolgt in einem separaten Test-Modus, nicht im normalen Build
Optionen:
- @test.ignore("reason")
- @test.only (nur selektiv)
E) Runtime-Hooks / „Lang Items“ (Jade-verdratet)¶
Ziel: explizit deklarieren, welches Symbol eine spezielle Rolle im Runtime-Vertrag hat.
Beispiele:
- globaler Error-/Panic-Handler
- Allocator/Arena-Provider (Default Storage)
- Entry Point (main)
- Modul-Init/Deinit Hooks (wenn nötig)
- GC root enumerator (falls relevant)
- Scheduler Hook (falls relevant)
Konzeptionell:
- @lang_item("global_error_handler") def handle(err: Error) -> Never { ... }
Regeln: - pro Rolle genau eine Definition (pro Link-Unit/Programm, definieren!) - Signatur ist strikt vorgegeben - Fehlen: Default aus stdlib oder Compile-Error (klar definieren) - Doppelt: Compile-Error
5. Predicate-DSL (für @cfg und frühe @assert)¶
Ziel: Minimal, deterministisch, keine User-Funktionen, kein CTFE.
Erlaubt:
- Literale: bool, int, string
- Operatoren: &&, ||, !, ==, != (optional <, >)
- Zugriff auf Build-Kontext:
- target.* (os, arch, ptr_width, endian, …)
- build.* (profile, sanitize, …)
- Funktion: feature("name")
Wichtig: Unbekannte Keys/Features sind Compile-Errors (Tippfehler sind zu teuer).
6. @intrinsic vs. @compiler¶
6.1 @intrinsic als Lowering-Markierung¶
@intrinsic ist eine Compiler-Annotation auf einer Funktionsdeklaration.
Sie signalisiert dem Compiler, dass diese Funktion nicht als normaler JDL-Aufruf emittiert wird,
sondern auf eine rohe VM- oder Compiler-Spezialbehandlung gesenkt wird (Lowering).
Normativ:
- @intrinsic ist eine Annotation, kein Namespace.
- Eine mit @intrinsic markierte Deklaration beschreibt eine stabile Compiler-/VM-Grenze.
- Aufrufe auf solche Deklarationen werden beim Lowering auf eine spezielle IR-/VM-Repräsentation
abgebildet und nicht wie gewöhnliche JDL-Funktionsaufrufe emittiert.
- @intrinsic hat nichts mit @compiler::... als compiler-provided Namespace zu tun.
Beispielhaft (Ziel-Namespace: jade::vm, Syntax konzeptionell):
// spekulativ — Deklaration einer intrinsischen Spawn-Funktion im Modul jade::vm
@intrinsic
pub def spawn(fn: Handle, args: Handle, mailbox: MailboxConfig) -> Handle
6.2 Rolle von @compiler in diesem Bild¶
@compiler ist ein compiler-provided Namespace.
Er ist nicht „das gleiche“ wie Compiler-Annotationen, kann aber ihre Predicate-/Query-Schicht liefern.
Saubere Trennung:
- Compiler-Annotationen (Syntax/AST-Knoten, wirken in Compiler-Phasen):
-
@cfg,@assert,@deprecated,@test,@lang_item, … -
Intrinsics (Queries/Primitives, die die Sprache nicht anders ausdrücken kann):
- Build/Target/Feature Fakten:
@compiler::target::*,@compiler::build::*,@compiler::feature("x") - Layout/Type Queries:
@compiler::sizeof(T),@compiler::alignof(T),@compiler::layoutof(T)(später) - Low-level Ops:
trap, atomics, bitcast, etc. (separat und effect-gebunden)
Empfehlung: Predicates dürfen intern auf @compiler basieren, aber @cfg selbst bleibt eine Annotation, damit echtes AST-Pruning möglich ist.
7. MVP-Whitelist für @... (empfohlen)¶
Minimal, aber brauchbar:
@cfg(predicate)@assert(predicate, msg?)(early)@assert_type(predicate, msg?)(late, optional wenn Layout-Queries verfügbar)@deprecated(msg?, since?, replacement?)@test,@test.ignore(reason),@test.only@lang_item(name)(Runtime-Hooks / special symbols)@intrinsic(Lowering-Markierung für rohe VM-/Compiler-Grenzen)- optional:
@lint.allow/deny,@inline/@noinline(nur wenn wirklich nötig)
Alles andere: Typ-Meta (TypeFns, Refinements, Generatoren, Collector).
8. Offene Punkte (gezielt, damit es nicht ausufert)¶
- Init/Load Policy für Registries
- Link-time fix? Load-time init? lazy?
-
Was ist garantiert wann verfügbar?
-
Scope/Link-Unit Definition für
@lang_item - pro Modul? pro Package? pro Programm?
-
Override-Regeln (stdlib default vs user override)
-
Welche
target.*undbuild.*Keys sind garantiert? -
minimaler Satz definieren, rest optional
-
Wie streng sind Unknown-Annotations?
- Empfehlung: unbekannte
@...sind Compile-Error, um Wildwuchs zu verhindern -
Ausnahme: wenn du dokumentarische
@docwillst, dann explizit als Typ-Meta statt@ -
@mockist nicht Teil der MVP-Whitelist. - Vor einer Aufnahme muss entschieden werden, ob
@mockrein compile-time selection bleibt oder ob es dynamisches Verhalten impliziert. - Nur die compile-time Variante wäre mit Determinismus vereinbar (
@cfg(build.test), Test-Build-gebundene Resolver-Auswahl, kein Monkey-Patching). - Solange diese Entscheidung nicht normativ gefallen ist, bleibt
@mockein offener Punkt und keine freigegebene Compiler-Annotation.
9. Zusammenfassung¶
@steht in JDL für Compiler-Annotations: deterministisch, minimal, Jade-verdrahtet.@cfgliefert echtes AST-Pruning vor Typcheck.@assert/@assert_typeliefern compile-time Garantien ohne CTFE.@deprecatedund@testsind sichere, deterministische Diagnostik/Registrierung.- Runtime-Hooks (Lang Items) sind deklarative Bindungen von Symbolen an Jade-Verträge.
Ende (Working Draft)