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:
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ use crate::ast::compiler::macros::{MacroEvaluator, MacroExpander, MacroRegistry}
|
||||
use crate::ast::compiler::optimizer::Optimizer;
|
||||
use crate::ast::compiler::specializer::{FunctionRegistry, MonoCache, RtlLookupFunc, Specializer};
|
||||
use crate::ast::rtl::{self, intrinsics};
|
||||
use crate::ast::rtl::series::series_element_type;
|
||||
use crate::ast::rtl::docs::{PipelineGenerator, RtlDocEntry, RtlRegistration};
|
||||
use crate::ast::types::{
|
||||
CommentLine, NativeFunction, NodeIdentity, Purity, SourceLocation, StaticType, Value,
|
||||
@@ -132,7 +133,7 @@ impl MacroEvaluator for RuntimeMacroEvaluator {
|
||||
|
||||
let (bound_ast, captures, _, _) = Binder::bind_root(initial_scopes, initial_slot_count, self.fixed_scope_idx, node, &mut diag)?;
|
||||
let bound_ast = CapturePass::apply(bound_ast, &captures);
|
||||
let checker = TypeChecker::new(self.root_types.clone(), Rc::clone(&self.call_hooks));
|
||||
let checker = TypeChecker::new(self.root_types.clone(), Rc::clone(&self.call_hooks), series_element_type);
|
||||
let typed_ast = checker.check(&bound_ast, &[], &mut diag);
|
||||
|
||||
if diag.has_errors() {
|
||||
@@ -480,7 +481,7 @@ impl Environment {
|
||||
|
||||
LambdaCollector::collect(&bound_ast, &mut self.function_registry.borrow_mut());
|
||||
|
||||
let checker = TypeChecker::new(self.root_types.clone(), Rc::clone(&self.rtl.call_hooks));
|
||||
let checker = TypeChecker::new(self.root_types.clone(), Rc::clone(&self.rtl.call_hooks), series_element_type);
|
||||
let wrapped_ast = if let NodeKind::Lambda { .. } = bound_ast.kind {
|
||||
bound_ast
|
||||
} else {
|
||||
@@ -834,7 +835,7 @@ impl Environment {
|
||||
arg_types: &[StaticType]|
|
||||
-> Result<(Value, StaticType), String> {
|
||||
let mut diag = Diagnostics::new();
|
||||
let checker = TypeChecker::new(root_types.clone(), Rc::clone(&call_hooks));
|
||||
let checker = TypeChecker::new(root_types.clone(), Rc::clone(&call_hooks), series_element_type);
|
||||
|
||||
// Monomorphization: re-type-check the function template with the concrete
|
||||
// call-site argument types, producing a specialized TypedNode.
|
||||
|
||||
@@ -15,6 +15,21 @@ use crate::ast::types::{NativeFunction, NodeIdentity, Purity, Signature, SourceL
|
||||
// 4. Type-Inference Hooks (registered via RTL bootstrap, no name coupling)
|
||||
// ============================================================================
|
||||
|
||||
/// Index-element hook for the `Series` type.
|
||||
///
|
||||
/// Returns the element type of a `Series(inner)`, or `None` for all other types.
|
||||
/// Registered with `TypeChecker` so that the core type-checker remains agnostic
|
||||
/// about which concrete types support integer-index access.
|
||||
///
|
||||
/// When `Stream` (or future indexable types) is added, extend this function
|
||||
/// with additional match arms rather than touching the type-checker.
|
||||
pub(crate) fn series_element_type(ty: &StaticType) -> Option<StaticType> {
|
||||
match ty {
|
||||
StaticType::Series(inner) => Some(*inner.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Post-call hook for `(series n)`.
|
||||
/// Replaces the `Series(Any)` return type with a fresh TypeVar so that
|
||||
/// subsequent `push` calls can unify the element type (HM inference).
|
||||
|
||||
Reference in New Issue
Block a user