use std::collections::HashMap; use std::rc::Rc; use crate::ast::diagnostics::Diagnostics; use crate::ast::nodes::{Address, NodeKind, TypedNode, TypedPhase, VirtualId}; use crate::ast::types::StaticType; /// Gives a compiler hook access to the type-checker's core inference operations /// and scope slot types without depending on the concrete `TypeChecker` type /// (avoids a circular module dependency). pub trait InferenceAccess { /// Allocate a fresh unique `TypeVar`. fn fresh_var(&self) -> StaticType; /// Unify two types, recording the substitution. Emits a diagnostic on conflict. fn unify(&self, a: StaticType, b: StaticType, diag: &mut Diagnostics); /// Directly bind a `TypeVar` ID to a resolved type in the substitution map. fn bind_typevar(&self, id: u32, ty: StaticType); /// Apply the current substitution to a type, resolving all known `TypeVar`s. fn apply_subst_ty(&self, ty: StaticType) -> StaticType; /// Int/Float numeric-widening rule. Returns the promoted type, or `None` if /// the combination is not a widening pair. fn try_numeric_widen(&self, a: &StaticType, b: &StaticType) -> Option; /// Record-field promotion rule. Returns the promoted record type when one record /// is a strict widening of the other, or `None` otherwise. fn try_record_promote(&self, a: &StaticType, b: &StaticType) -> Option; /// Read the raw (unsubstituted) type stored in a scope slot. /// Used by hooks that need to recover the original `TypeVar` ID after /// the substitution has already resolved it to a concrete type. fn get_slot_type(&self, addr: Address) -> StaticType; } /// Extension point that lets RTL functions participate in the compiler's /// type-inference and AST-finalization passes without hardcoding symbol /// names anywhere in the compiler core. /// /// Each RTL function that needs custom type-level behaviour (e.g. `series`, /// `push`) implements this trait. Hooks are registered by global slot index /// during RTL bootstrap and dispatched automatically by the type checker. /// /// Both methods have no-op defaults so that implementors only override what /// they need. pub trait RtlCompilerHook { /// Called after the call's return type is resolved during type inference. /// May replace the return type and/or perform unification side-effects. fn post_call( &self, _args: &TypedNode, ret_ty: StaticType, _ctx: &dyn InferenceAccess, _diag: &mut Diagnostics, ) -> StaticType { ret_ty } /// Called during AST finalization. /// Returns `Some(new_kind)` to rewrite the node, or `None` to keep the original. fn finalize( &self, _callee: Rc, _args: Rc, _node_ty: &StaticType, _subst: &HashMap, ) -> Option> { None } }