Zum Inhalt

JadeResult — Bootstrap-Compiler Ergebnistyp

Status: Normativ
Version: 0.1.0
Scope: Bootstrap-Compiler (D), alle Compiler-Subsysteme
Abhängigkeiten: jade-compiler-db-spec.md, jade-design-decisions.md


1. Motivation

Der Bootstrap-Compiler braucht einen einheitlichen Ergebnistyp der drei fundamentale Zustände ausdrückt:

Zustand Bedeutung
Ok(T) Query/Operation erfolgreich, Wert vorhanden
Err(Diagnostic) Fehler mit strukturierter Beschreibung
Empty Korrekt ausgeführt, aber kein Wert vorhanden — kein Fehler

Empty ist kein Fehler. Es bedeutet "nicht gefunden" oder "nicht vorhanden" — zum Beispiel eine GetDocComment-Query auf eine Funktion ohne Dokumentation.

Warum kein generisches Result!(T, E): Im Bootstrap-Compiler ist E immer Diagnostic. Ein generischer Fehlertyp erzeugt nur Template-Komplexität ohne Nutzen.

Warum kein separates Option(T): Empty macht Option überflüssig. Alle drei Zustände laufen durch JadeResult(T).

Warum kein std.sumtype: alias-basierte SumType-Wrapper verlieren ihre Typidentität — UFCS-Methoden werden nicht gefunden. Ein eigenes struct gibt volle Kontrolle über Ergonomie.


2. Implementierung

// jade/types.d

enum JadeResultTag { Ok, Err, Empty }

struct JadeResult(T) {
    private JadeResultTag _tag;
    private union {
        T          _value;
        Diagnostic _diag;
    }

    // --- Konstruktion ---

    static JadeResult ok(T value) @nogc nothrow {
        JadeResult r;
        r._tag   = JadeResultTag.Ok;
        r._value = value;
        return r;
    }

    static JadeResult err(Diagnostic d) @nogc nothrow {
        JadeResult r;
        r._tag  = JadeResultTag.Err;
        r._diag = d;
        return r;
    }

    static JadeResult empty() @nogc nothrow {
        JadeResult r;
        r._tag = JadeResultTag.Empty;
        return r;
    }

    // --- Abfrage ---

    bool isOk()    @nogc nothrow { return _tag == JadeResultTag.Ok;    }
    bool isErr()   @nogc nothrow { return _tag == JadeResultTag.Err;   }
    bool isEmpty() @nogc nothrow { return _tag == JadeResultTag.Empty; }

    T          value() @nogc nothrow { assert(isOk());  return _value; }
    Diagnostic diag()  @nogc nothrow { assert(isErr()); return _diag;  }

    T valueOr(T defaultValue) @nogc nothrow {
        return isOk() ? _value : defaultValue;
    }

    // --- Exhaustive Match ---

    auto match(alias onOk, alias onErr, alias onEmpty)() @nogc nothrow {
        final switch (_tag) {
            case JadeResultTag.Ok:    return onOk(_value);
            case JadeResultTag.Err:   return onErr(_diag);
            case JadeResultTag.Empty: return onEmpty();
        }
    }
}

3. Verwendung

Konstruktion

// Erfolg
JadeResult!TypeNode r = JadeResult!TypeNode.ok(node);

// Fehler
JadeResult!TypeNode s = JadeResult!TypeNode.err(Diagnostic {
    phase:    Phase.TypeEngine,
    severity: Severity.Error,
    message:  "Type not found: Foo",
    span:     sourceSpan
});

// Leer — korrekt aber kein Wert
JadeResult!DocNode t = JadeResult!DocNode.empty();

Exhaustive Match — final switch erzwingt alle Fälle

r.match!(
    (TypeNode node) => writeln("Ok: ", node.name),
    (Diagnostic d)  => writeln("Err: ", d.message),
    ()               => writeln("Kein Wert")
);

Einfache Abfragen

if (r.isOk())    auto node = r.value();
if (r.isErr())   auto d    = r.diag();
if (r.isEmpty()) // nicht gefunden, kein Fehler

auto node = r.valueOr(TypeNode.unknown);

In Handler-Funktionen

@handles!ResolveType
JadeResult!TypeNode resolveType(ResolveType q, CompilerDb* db) @nogc nothrow pure {
    if (!found) return JadeResult!TypeNode.err(Diagnostic {
        phase:    Phase.TypeEngine,
        severity: Severity.Error,
        message:  "Type not found: " ~ symbolName,
        span:     sourceSpan
    });
    return JadeResult!TypeNode.ok(node);
}

@handles!GetDocComment
JadeResult!DocNode getDocComment(GetDocComment q, CompilerDb* db) @nogc nothrow pure {
    if (!hasComment) return JadeResult!DocNode.empty();  // kein Fehler — einfach nicht vorhanden
    return JadeResult!DocNode.ok(docNode);
}

4. Abgrenzung zu JDL-Typen

JadeResult(T) ist ein D-Typ für den Bootstrap-Compiler — kein JDL-Typ.

In JDL selbst existieren Result[T, E] und Option[T] als Stdlib-Typen unverändert weiter. JadeResult hat keinen Einfluss auf die JDL-Sprachspezifikation.


5. Invarianten

  1. value() darf nur aufgerufen werden wenn isOk() == true — sonst assert-Fehler.
  2. diag() darf nur aufgerufen werden wenn isErr() == true — sonst assert-Fehler.
  3. match! mit final switch erzwingt exhaustive Behandlung aller drei Zustände — Compiler-Fehler wenn ein Fall fehlt.
  4. Empty ist kein Fehler und erzeugt keine Diagnostic.
  5. JadeResult ist @nogc und nothrow — kein Exception-Mechanismus.

6. Änderungsprotokoll

Version 0.1.0 — Initiale Definition: - Drei Zustände: Ok(T), Err(Diagnostic), Empty - match! mit final switch für exhaustive Behandlung - valueOr als sicherer Default-Zugriff - Option(T) durch Empty ersetzt - Abgrenzung zu JDL-Typen dokumentiert