Refactor call hooks to use trait objects

This commit refactors the call hook mechanism in the compiler. Instead
of using function pointer types (`PostCallHook`, `FinalizeHook`) and a
struct to hold them (`CallHooks`), it introduces a trait
`RtlCompilerHook` with default implementations for `post_call` and
`finalize`.

This change aligns with Rust's idiomatic way of handling extension
points and allows for more flexible and extensible compiler behavior.
The `TypeChecker` now holds a map of `Rc<dyn RtlCompilerHook>`, enabling
different RTL functions to register their specific compiler hooks. The
`SeriesHook` and `PushHook` structs are introduced to demonstrate this
new pattern.
This commit is contained in:
2026-03-29 14:52:58 +02:00
parent 916460ab03
commit a665e2c1a5
5 changed files with 216 additions and 204 deletions
+35 -26
View File
@@ -5,8 +5,8 @@ use crate::ast::diagnostics::Diagnostics;
use crate::ast::nodes::{Address, NodeKind, TypedNode, TypedPhase, VirtualId};
use crate::ast::types::StaticType;
/// Gives a call-hook access to the type-checker's core inference operations and
/// scope slot types without depending on the concrete `TypeChecker` type
/// 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`.
@@ -35,29 +35,38 @@ pub trait InferenceAccess {
fn get_slot_type(&self, addr: Address<VirtualId>) -> StaticType;
}
/// Called after the call's return type is resolved.
/// May replace the return type and/or perform unification side-effects (e.g. push).
pub type PostCallHook = fn(
args: &TypedNode,
ret_ty: StaticType,
ctx: &dyn InferenceAccess,
diag: &mut Diagnostics,
) -> StaticType;
/// Called during AST finalization.
/// Returns `Some(new_kind)` to rewrite the node, or `None` to keep the original.
pub type FinalizeHook = fn(
callee: Rc<TypedNode>,
args: Rc<TypedNode>,
node_ty: &StaticType,
subst: &HashMap<u32, StaticType>,
) -> Option<NodeKind<TypedPhase>>;
/// Hooks attached to a specific RTL function slot.
/// Enables type-inference extensions and AST rewrites without hardcoding symbol
/// 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.
#[derive(Default)]
pub struct CallHooks {
pub post_call: Option<PostCallHook>,
pub finalize: Option<FinalizeHook>,
///
/// 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<TypedNode>,
_args: Rc<TypedNode>,
_node_ty: &StaticType,
_subst: &HashMap<u32, StaticType>,
) -> Option<NodeKind<TypedPhase>> {
None
}
}