# Projektplan: Hybride Monomorphisierung * **Datum:** 30.10.2025 * **Version:** 1.0 ## 1. Motivation Das aktuelle Typsystem (`Myc.Ast.Types`) ist mächtig, aber die Inferenz für Lambda-Parameter endet bei `stUnknown`. Dies erzwingt Laufzeit-Typüberprüfungen im `TEvaluatorVisitor` und verkompliziert AOT/JIT-Kompilierung. ## 2. Ziel Implementierung einer **hybriden Monomorphisierungsstrategie** zur Compile-Zeit (innerhalb des `TAstBinder`). 1. **Statische Spezialisierung:** Alle Funktionsaufrufe, deren Callee *und* Argumenttypen zur Compile-Zeit statisch bekannt sind, werden spezialisiert. `stUnknown` wird für diese Pfade eliminiert. 2. **Dynamischer Fallback:** Alle dynamischen Aufrufe (z.B. Higher-Order-Funktionen, deren Callee aus einer Variable stammt) nutzen weiterhin den bestehenden dynamischen Pfad (`vkMethod` mit `TDataValue`-Wrapper). 3. **Optimierungs-Vorbereitung:** Der statisch spezialisierte AST (der "Entrypoint") dient als saubere Basis für optionale Optimierungen (z.B. LLVM-Codegen). ## 3. Ergebnis: Die Strategie Die Implementierung erfordert eine Erweiterung des `TAstBinder`, um einen **Spezialisierungs-Cache** zu verwalten und bei Bedarf **rekursive Binding-Pässe** durchzuführen. ### Phase 1: Modifikation der Kern-Typen 1. **`TBoundLambdaExpressionNode` (Generic):** * Muss seinen *originalen, ungebundenen* Body (`IAstNode`) behalten, um als Vorlage für das Re-Binding zu dienen. * Führt einen **Spezialisierungs-Cache** (z.B. `TDictionary`). 2. **`ISpecializedBody` (Interface):** * Repräsentiert einen erfolgreich monomorphisierten Entrypoint. * `function GetBodyAst: IAstNode;` * `function GetScopeDescriptor: IScopeDescriptor;` * `function GetReturnType: IStaticType;` 3. **`TBoundFunctionCallNode`:** * Benötigt ein neues Feld, um *entweder* den dynamischen Callee (wie bisher) *oder* den statischen `ISpecializedBody` (den Entrypoint) zu halten. ### Phase 2: Anpassung des `TAstBinder` (Trigger) `TAstBinder.VisitFunctionCall` wird zur zentralen Weichenstellung: 1. Binde alle Argumente und ermittle ihre statischen Typen (die `Aufrufsignatur`). 2. Binde den `Callee`. 3. **Fallunterscheidung:** * **Fall A (Dynamischer Aufruf):** Der `Callee` ist *nicht* als `TBoundLambdaExpressionNode` statisch bekannt (z.B. `(map (if flag f1 f2) ...)`). * **Aktion:** Der Aufruf wird als dynamisch belassen (Fallback). Es wird lediglich geprüft, ob der `Callee` den Typ `stMethod` hat. Es findet keine Spezialisierung statt. * **Fall B (Statischer Aufruf):** Der `Callee` ist ein `TBoundLambdaExpressionNode`. * **Aktion:** Trigger die Monomorphisierung. * Rufe `Callee.GetSpecialization(Aufrufsignatur)` auf. * Der `TBoundFunctionCallNode` wird modifiziert und verweist direkt auf das zurückgegebene `ISpecializedBody`. * Der `StaticType` des `TBoundFunctionCallNode` wird auf `ISpecializedBody.GetReturnType` gesetzt. ### Phase 3: Die Monomorphisierung (Der Re-Bind Pass) Dies ist die Kernlogik (z.B. in `TBoundLambdaExpressionNode.GetSpecialization`): 1. **Cache-Lookup:** Prüfe, ob für die `Aufrufsignatur` bereits ein `ISpecializedBody` im Cache existiert. 2. **Cache-Miss (Re-Binding):** * Erzeuge einen neuen, temporären `IScopeDescriptor` für den Sub-Bind-Prozess. * **Populiere den Scope:** Definiere die Parameter-Namen (z.B. `[a b]`) in diesem Scope, aber verwende die *konkreten Typen* der `Aufrufsignatur` (z.B. `stFloat`, `stOrdinal`) anstelle von `stUnknown`. * **Rekursiver Binder:** Erzeuge einen neuen `TAstBinder` (oder rufe den aktuellen re-entrant auf), der den *originalen, ungebundenen Body* des Lambdas mit diesem spezialisierten Scope bindet. * **Fehlerbehandlung:** Schlägt dieser Sub-Bind-Prozess fehl (z.B. `ETypeException`, weil `a` (jetzt `stFloat`) mit einem `Text` addiert wird), ist der Aufruf ungültig -> **Compile-Fehler**. * **Erfolg:** Das Ergebnis ist der spezialisierte `IAstNode` (Body) und der `IScopeDescriptor`. Erzeuge ein `ISpecializedBody`-Objekt, speichere es im Cache und gib es zurück. ### Phase 4: Anpassung des `TEvaluatorVisitor` (Ausführung) `TEvaluatorVisitor.VisitFunctionCall` muss nun zwei Arten von Calls behandeln: 1. **Dynamischer Call:** `Callee` ist `vkMethod`. (Wie bisher: `(calleeValue.AsMethod)(argValues)`). 2. **Statischer/Monomorphisierter Call:** `Callee` ist ein `ISpecializedBody`. * Erzeuge den `IExecutionScope` (via `ISpecializedBody.GetScopeDescriptor`). * Populiere den Scope mit den (bereits evaluierten) `argValues`. * Führe den `ISpecializedBody.GetBodyAst` direkt mit `Accept(Self)` aus. (TCO muss hier ebenfalls beachtet werden). ### Phase 5: LLVM-Optimierung (Optional) Der in Phase 3 erzeugte `ISpecializedBody` ist der "corner case" für die Optimierung: * Wenn ein `ISpecializedBody` erfolgreich erstellt wurde *und* dieser AST-Body die "harten" Kriterien erfüllt (keine dynamischen Internals), wird er als **Kandidat für LLVM-AOT/JIT** markiert. * Ein LLVM-Backend kann diese Kandidaten aufnehmen, LLVM-IR generieren und den `ISpecializedBody` im Cache durch einen nativen Funktionspointer (den FFI-Entrypoint) ersetzen. --- ## 4. TODOs * [ ] `TBoundLambdaExpressionNode` erweitern, um den originalen Body und den Spezialisierungs-Cache zu halten. * [ ] `ISpecializedBody` (oder äquivalente Struktur) definieren. * [ ] `TAstBinder.VisitFunctionCall` um die Weichenstellung (Fall A/B) erweitern. * [ ] Den re-entranten Monomorphisierungs-Pass (Phase 3) implementieren. * [ ] `TEvaluatorVisitor.VisitFunctionCall` für die Ausführung von `ISpecializedBody` anpassen. * [ ] Typsystem (`TTypeRules`) auf Robustheit für die neuen, strikten Prüfungen im Sub-Bind-Pass testen.