Add IndexElementFn for series type probing

Introduce `IndexElementFn` as a type alias for a function that maps a
`StaticType` to an optional `StaticType` representing its element type.
This allows the `TypeChecker` to determine the element type of indexable
types like `Series` without needing to be aware of their specific
implementations.

The `series_element_type` function is created and registered with the
`TypeChecker` to handle `Series` types. This design centralizes
type-specific logic for indexable types, making the `TypeChecker` more
generic and extensible for future indexable types such as `Stream`.
This commit is contained in:
2026-03-29 13:20:49 +02:00
parent 9c434fb49d
commit 93a85cd15c
4 changed files with 44 additions and 22 deletions
+10
View File
@@ -36,6 +36,16 @@ pub trait TypeInferenceAccess {
fn try_record_promote(&self, a: &StaticType, b: &StaticType) -> Option<StaticType>;
}
/// Maps a concrete type to its element type for index-constraint propagation.
/// Called by `bind_var` when a TypeVar with an index constraint is bound.
///
/// Returns `Some(elem_ty)` if `ty` supports integer-index access (e.g. `Series`),
/// or `None` for all non-indexable types.
///
/// Register the appropriate implementation via `TypeChecker::new`. Each indexable
/// type (Series, and in future Stream) contributes a match arm here.
pub type IndexElementFn = fn(&StaticType) -> Option<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(
+15 -19
View File
@@ -1,4 +1,4 @@
use crate::ast::compiler::call_hooks::{CallHooks, TypeInferenceAccess, TypeSlotAccess};
use crate::ast::compiler::call_hooks::{CallHooks, IndexElementFn, TypeInferenceAccess, TypeSlotAccess};
use crate::ast::nodes::{
Address, AssignBinding, BoundLike, BoundPhase, DefBinding, IdentifierBinding, LambdaBinding,
Node, NodeKind, TypedNode, TypedPhase, VirtualId,
@@ -179,12 +179,13 @@ pub struct TypeChecker {
/// When a `TypeVar` is used as a callable with a single `Int` argument —
/// the series lookback pattern `(s 0)` — we record the pairing here instead
/// of eagerly unifying `TypeVar = Series(elem)`. The constraint is evaluated
/// lazily in `bind_var`: only when the callee TypeVar is bound to `Series(inner)`
/// is the result TypeVar also bound to `inner`.
///
/// **Extension point**: other indexable types (e.g. `Stream`, future `Map`)
/// can participate by being matched in `bind_var`'s propagation arm.
/// lazily in `bind_var`: when the callee TypeVar is bound to a concrete type,
/// `index_element_fn` determines the element type (if any) and propagates it.
index_constraints: std::cell::RefCell<HashMap<u32, u32>>,
/// Resolves the element type of an indexable type for index-constraint propagation.
/// Registered at construction; keeps the type-checker core free of type-specific logic.
/// See [`IndexElementFn`] for the contract.
index_element_fn: IndexElementFn,
/// Type-inference and finalization hooks keyed by global slot index.
/// Populated from the frozen RTL snapshot; empty for non-RTL call sites.
call_hooks: Rc<HashMap<u32, CallHooks>>,
@@ -194,12 +195,14 @@ impl TypeChecker {
pub fn new(
root_types: Rc<std::cell::RefCell<Vec<StaticType>>>,
call_hooks: Rc<HashMap<u32, CallHooks>>,
index_element_fn: IndexElementFn,
) -> Self {
Self {
root_types,
var_counter: Cell::new(0),
subst: std::cell::RefCell::new(HashMap::new()),
index_constraints: std::cell::RefCell::new(HashMap::new()),
index_element_fn,
call_hooks,
}
}
@@ -214,22 +217,15 @@ impl TypeChecker {
/// Binds a TypeVar to a concrete type in the substitution, then propagates
/// any pending index-call constraints registered by Step 9.
///
/// When `ty` is `Series(inner)` and `index_constraints[id]` is set, the
/// result TypeVar is also bound to `inner` — connecting the callee TypeVar
/// to its element type without the eager over-constraint of unification.
///
/// To add support for another indexable type (e.g. `Stream`), extend the
/// `match &ty` arm in the propagation block below.
/// If `index_constraints[id]` is set and `index_element_fn` returns an element
/// type for `ty`, the result TypeVar is bound to that element type — connecting
/// the callee TypeVar to its element type without the eager over-constraint of
/// unification. For non-indexable types the result TypeVar stays unresolved.
fn bind_var(&self, id: u32, ty: StaticType) {
self.subst.borrow_mut().insert(id, ty.clone());
let ret_id = self.index_constraints.borrow().get(&id).copied();
if let Some(ret_id) = ret_id {
match &ty {
StaticType::Series(inner) => {
self.subst.borrow_mut().insert(ret_id, *inner.clone());
}
_ => {}
}
if let (Some(ret_id), Some(elem_ty)) = (ret_id, (self.index_element_fn)(&ty)) {
self.subst.borrow_mut().insert(ret_id, elem_ty);
}
}