Zum Inhalt

06 — JME, Handles, Tags und Phantom-Policies

Status: Normativer Konsolidierungsentwurf


1. Grundsatz

JDL kennt keine rohen Pointer in der Sprache.

JDL arbeitet mit:

- Werten
- Handles
- typisierten Runtime-Abstraktionen
- compile-time Policies

Low-Level in JDL bedeutet nicht Pointer-Arithmetik mit religiösem Beistand, sondern präzise Handle- und Policy-Semantik.


2. HandleId ist technische Identität

HandleId = { index: u32, generation: u32 }

HandleId schützt gegen stale handles / ABA-Probleme. Er sagt aber allein nicht genug über die Semantik des Handles.

Semantisch wichtige Fragen:

Was liegt hinter dem Handle?
Welche Memory-Policy gilt?
In welcher Arena oder welchem Pool liegt das Objekt?
Ist der Zugriff strong oder weak?
Darf der Wert kopiert, bewegt oder geteilt werden?
Darf er Task-Grenzen überschreiten?

Diese Bedeutungen gehören in Type Engine + Tags + Meta-Records, nicht als lose Kommentare in D-Code.


3. JME-Schichten

JDL Source
  |
  |  type RequestData : struct { ... }
  |      :> Memory(Arena[RequestArena])
  v
Type Engine
  |
  |  normalisiert Meta
  |  beweist Labels
  v
Generator
  |
  |  entscheidet Register-/Handle-Repräsentation
  v
VM
  |
  |  transportiert HandleId bei JME-Werten
  v
JME
  |
  |  prüft index + generation
  |  verwaltet Slot, Refcount, Arena, Pool
  v
Objekt

4. Tags für JME-Regionen

tag ArenaKind

tag RequestArena : ArenaKind
tag AnalysisArena : ArenaKind

tag PoolKind

tag ConnectionPool : PoolKind
tag ParserNodePool : PoolKind

Verwendung:

type RequestData : struct {
    headers: Map[str, str]
    body:    str
} :> Memory(Arena[RequestArena])

type DbConnection : struct {
    native: NativeDbConnection
} :> Memory(Pool[ConnectionPool])

RequestArena und ConnectionPool sind keine Runtime-Werte. Sie markieren Speicherklassen.


5. Memory-Policies

Empfohlene Tag-/Type-Level-Form:

tag MemoryKind : enum =
    | Value
    | Rc
    | Weak
    | Arena
    | Pool

Parametrisierte Policies wie Arena[RequestArena] und Pool[ConnectionPool] sind keine einfachen Tag-Varianten, sondern Type-Level-Policy-Ausdrücke.

typefn Memory(policy: MemorySpec) = <{ memory: policy }>

Konzeptionell:

MemorySpec
 ├─ Value
 ├─ Rc
 ├─ Weak
 ├─ Arena[ArenaKind]
 └─ Pool[PoolKind]

Syntax bleibt JDL-typisch:

:> Memory(Value)
:> Memory(Rc)
:> Memory(Weak)
:> Memory(Arena[RequestArena])
:> Memory(Pool[ConnectionPool])

6. Typisierte Handle-Abstraktionen

Nicht jeder User-Typ muss explizit einen Handle wrapper enthalten.

Normaler Fall:

type User : struct {
    name: str
    email: str
} :> Memory(Rc)

Der User schreibt Typ + Policy. Der Compiler/JME entscheidet Handle-Repräsentation.

Explizite Handle-Typen gehören in die Stdlib oder an Systemgrenzen:

type Weak[T] : struct {
    id: HandleId
} :> Memory(Value)

type Strong[T] : struct {
    id: HandleId
} :> Memory(Value)

type JoinHandle[T] : struct {
    id: HandleId
} :> Own(Unique) :> Share(Send)

7. Phantom-Policies an Handles

Für spezielle Systemtypen können Tags über Phantom-Parameter gebunden werden.

tag AccessMode : enum =
    | ReadOnly
    | ReadWrite
    | Volatile
    | Atomic

type ResourceHandle[T, phantom Access: AccessMode] : struct {
    id: HandleId
}

Beispiele:

def readConfig(handle: ResourceHandle[ConfigBlob, ReadOnly]) -> ConfigBlob

def writeBuffer(handle: ResourceHandle[Buffer, ReadWrite], data: Array[u8]) -> ()

def readDeviceRegister(handle: ResourceHandle[RegisterBlock, Volatile], offset: u32) -> u32

Runtime:

ResourceHandle[...] -> HandleId

Compile-Time:

ResourceHandle[Buffer, ReadOnly]
ResourceHandle[Buffer, ReadWrite]
ResourceHandle[RegisterBlock, Volatile]

nicht austauschbar

8. Trust-Level für Ressourcen

tag TrustLevel : enum =
    | Untrusted
    | Validated
    | Trusted

type Buffer[phantom Trust: TrustLevel] : struct {
    data: Array[u8]
}

APIs:

def parseRaw(input: Buffer[Untrusted])
    -> Result[Buffer[Validated], ParseError]

def execute(input: Buffer[Trusted])
    -> Result[(), ExecError]

Damit kann ein unvalidierter Buffer nicht versehentlich an eine gefährliche API gehen.


9. Weak und Option

Weak-Referenzen sind nicht-besitzende Handles.

type Node : struct {
    value:    i32
    parent:   Weak[Node]
    children: Array[Strong[Node]]
}

Zugriff:

match node.parent.upgrade() {
    | Some(parent) => use(parent)
    | None         => handleMissingParent()
}

Mit try-Boundary:

val parentOpt = try {
    val parent =? node.parent.upgrade()
    parent
}

10. Keine rohen Pointer

Verboten in JDL:

type Ptr[T] : struct { addr: usize }

Ausnahme:

FFI- oder Host-Implementierungsdetails in D.
Nicht als normales JDL-Sprachkonstrukt.

JDL-seitig werden Low-Level-Ressourcen über Handles und Policies modelliert.


11. Normative Invarianten

JME-1: JDL kennt keine rohen Pointer.
JME-2: JDL-Code arbeitet mit Werten, Handles und typisierten Ressourcenabstraktionen.
JME-3: HandleId ist technische Runtime-Identität, keine vollständige Semantik.
JME-4: Memory-/Access-/Trust-/Region-Semantik wird durch Tags, Meta-Records und Phantom-Parameter modelliert.
JME-5: Die Type Engine normalisiert JME-Policies vor Codegen.
JME-6: Die JME erzwingt Runtime-Sicherheit über Handle Table + Generation.
JME-7: Die Type Engine erzwingt statische Sicherheit über Labels + Policies.
JME-8: Explizite HandleId-Felder sind System-/Stdlib- oder FFI-nahe Konstrukte und müssen starke Ownership-Labels tragen.