Refactor type variable counter to be shared
This commit is contained in:
@@ -34,6 +34,11 @@ impl TypeChecker {
|
||||
match ty {
|
||||
StaticType::TypeVar(n) => {
|
||||
if let Some(resolved) = subst.get(&n) {
|
||||
// Guard against self-referential substitutions (TypeVar(n) → TypeVar(n))
|
||||
// which would cause infinite recursion.
|
||||
if *resolved == StaticType::TypeVar(n) {
|
||||
return StaticType::TypeVar(n);
|
||||
}
|
||||
// Follow the chain (handles transitive substitutions)
|
||||
Self::apply_subst(resolved.clone(), subst)
|
||||
} else {
|
||||
|
||||
@@ -18,8 +18,10 @@ use context::TypeContext;
|
||||
|
||||
pub struct TypeChecker {
|
||||
pub(crate) root_types: Rc<std::cell::RefCell<Vec<StaticType>>>,
|
||||
/// Monotonic counter for generating unique type variable IDs.
|
||||
pub(crate) var_counter: Cell<u32>,
|
||||
/// Shared monotonic counter for generating unique type variable IDs.
|
||||
/// Points to the `Environment`'s counter so that IDs never collide
|
||||
/// with TypeVars stored in `root_types` from earlier compilation passes.
|
||||
pub(crate) var_counter: Rc<Cell<u32>>,
|
||||
/// Global substitution map: TypeVar ID → resolved StaticType.
|
||||
/// Shared across all scopes within a single type-checking pass.
|
||||
pub(crate) subst: std::cell::RefCell<HashMap<u32, StaticType>>,
|
||||
@@ -39,11 +41,12 @@ pub struct TypeChecker {
|
||||
impl TypeChecker {
|
||||
pub fn new(
|
||||
root_types: Rc<std::cell::RefCell<Vec<StaticType>>>,
|
||||
var_counter: Rc<Cell<u32>>,
|
||||
compiler_hooks: Rc<HashMap<u32, Rc<dyn RtlCompilerHook>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
root_types,
|
||||
var_counter: Cell::new(0),
|
||||
var_counter,
|
||||
subst: std::cell::RefCell::new(HashMap::new()),
|
||||
index_constraints: std::cell::RefCell::new(HashMap::new()),
|
||||
compiler_hooks,
|
||||
|
||||
+14
-4
@@ -5,7 +5,7 @@ use crate::ast::compiler::{CapturePass, TypeChecker, TypedNode};
|
||||
use crate::ast::nodes::{AnalyzedPhase, BoundPhase, Symbol, SyntaxKind, SyntaxNode};
|
||||
use crate::ast::parser::Parser;
|
||||
use crate::ast::vm::{GlobalStore, TracingObserver, VM};
|
||||
use std::cell::RefCell;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::rc::Rc;
|
||||
@@ -71,6 +71,11 @@ pub struct Environment {
|
||||
pub function_registry: Rc<RefCell<GlobalFunctionRegistry>>,
|
||||
pub typed_function_registry: Rc<RefCell<GlobalAnalyzedRegistry>>,
|
||||
pub monomorph_cache: Rc<RefCell<MonoCache>>,
|
||||
/// Shared monotonic counter for generating unique HM type-variable IDs.
|
||||
/// Survives across multiple `compile()` / `run_script()` calls so that
|
||||
/// TypeVar IDs created in one pass never collide with IDs stored in
|
||||
/// `root_types` from earlier passes (prevents `Forall` self-substitution loops).
|
||||
pub(crate) typevar_counter: Rc<Cell<u32>>,
|
||||
pub debug_mode: bool,
|
||||
pub optimization: bool,
|
||||
pub macro_registry: Rc<RefCell<MacroRegistry>>,
|
||||
@@ -106,6 +111,7 @@ struct RuntimeMacroEvaluator {
|
||||
root_scopes: Rc<RefCell<Vec<CompilerScope>>>,
|
||||
root_slot_count: Rc<RefCell<u32>>,
|
||||
root_types: Rc<RefCell<Vec<StaticType>>>,
|
||||
typevar_counter: Rc<Cell<u32>>,
|
||||
globals: GlobalStore,
|
||||
root_purity: Rc<RefCell<Vec<Purity>>>,
|
||||
compiler_hooks: Rc<HashMap<u32, Rc<dyn RtlCompilerHook>>>,
|
||||
@@ -129,7 +135,7 @@ impl MacroEvaluator for RuntimeMacroEvaluator {
|
||||
|
||||
let (bound_ast, captures, _, _) = Binder::bind_root(initial_scopes, initial_slot_count, node, &mut diag)?;
|
||||
let bound_ast = CapturePass::apply(bound_ast, &captures);
|
||||
let checker = TypeChecker::new(self.root_types.clone(), Rc::clone(&self.compiler_hooks));
|
||||
let checker = TypeChecker::new(self.root_types.clone(), Rc::clone(&self.typevar_counter), Rc::clone(&self.compiler_hooks));
|
||||
let typed_ast = checker.check(&bound_ast, &[], &mut diag);
|
||||
|
||||
if diag.has_errors() {
|
||||
@@ -162,6 +168,7 @@ impl Environment {
|
||||
function_registry: Rc::new(RefCell::new(HashMap::new())),
|
||||
typed_function_registry: Rc::new(RefCell::new(HashMap::new())),
|
||||
monomorph_cache: Rc::new(RefCell::new(HashMap::new())),
|
||||
typevar_counter: Rc::new(Cell::new(0)),
|
||||
debug_mode: false,
|
||||
optimization: true,
|
||||
macro_registry: Rc::new(RefCell::new(MacroRegistry::new())),
|
||||
@@ -254,6 +261,7 @@ impl Environment {
|
||||
function_registry: Rc::new(RefCell::new(HashMap::new())),
|
||||
typed_function_registry: Rc::new(RefCell::new(HashMap::new())),
|
||||
monomorph_cache: Rc::new(RefCell::new(HashMap::new())),
|
||||
typevar_counter: Rc::new(Cell::new(0)),
|
||||
debug_mode: false,
|
||||
optimization: true,
|
||||
macro_registry: Rc::new(RefCell::new(MacroRegistry::new())),
|
||||
@@ -321,6 +329,7 @@ impl Environment {
|
||||
root_scopes: self.root_scopes.clone(),
|
||||
root_slot_count: self.root_slot_count.clone(),
|
||||
root_types: self.root_types.clone(),
|
||||
typevar_counter: Rc::clone(&self.typevar_counter),
|
||||
globals: self.global_store(),
|
||||
root_purity: self.root_purity.clone(),
|
||||
compiler_hooks: Rc::clone(&self.rtl.compiler_hooks),
|
||||
@@ -447,7 +456,7 @@ impl Environment {
|
||||
|
||||
/// Type-checks a bound AST and collects doc comments.
|
||||
fn type_check(&self, bound_ast: &Node<BoundPhase>, diagnostics: &mut Diagnostics) -> TypedNode {
|
||||
let checker = TypeChecker::new(self.root_types.clone(), Rc::clone(&self.rtl.compiler_hooks));
|
||||
let checker = TypeChecker::new(self.root_types.clone(), Rc::clone(&self.typevar_counter), Rc::clone(&self.rtl.compiler_hooks));
|
||||
let typed = checker.check(bound_ast, &[], diagnostics);
|
||||
self.collect_doc_comments(&typed);
|
||||
typed
|
||||
@@ -766,13 +775,14 @@ impl Environment {
|
||||
let root_purity = self.root_purity.clone();
|
||||
let optimization = self.optimization;
|
||||
let compiler_hooks = Rc::clone(&self.rtl.compiler_hooks);
|
||||
let typevar_counter = Rc::clone(&self.typevar_counter);
|
||||
|
||||
let compiler = Rc::new(
|
||||
move |func_template: Rc<Node<AnalyzedPhase>>,
|
||||
arg_types: &[StaticType]|
|
||||
-> Result<(Value, StaticType), String> {
|
||||
let mut diag = Diagnostics::new();
|
||||
let checker = TypeChecker::new(root_types.clone(), Rc::clone(&compiler_hooks));
|
||||
let checker = TypeChecker::new(root_types.clone(), Rc::clone(&typevar_counter), Rc::clone(&compiler_hooks));
|
||||
|
||||
// Monomorphization: re-type-check the function template with the concrete
|
||||
// call-site argument types, producing a specialized TypedNode.
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
;; Myc System Library
|
||||
;; Standard macros and core utilities.
|
||||
|
||||
(def func1 (fn [] (print "Hello World")))
|
||||
|
||||
(macro while [cond body]
|
||||
`((fn [] (if ~cond
|
||||
(do ~body (again))
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
use myc::ast::environment::Environment;
|
||||
use myc::ast::types::Value;
|
||||
|
||||
#[test]
|
||||
fn repl_preserves_variable_bindings() {
|
||||
let env = Environment::new();
|
||||
|
||||
let r1 = env.run_script("(def x 42)");
|
||||
assert!(r1.is_ok(), "def should succeed: {:?}", r1.err());
|
||||
|
||||
let r2 = env.run_script("(+ x 1)");
|
||||
assert_eq!(r2.unwrap(), Value::Int(43));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repl_preserves_function_bindings() {
|
||||
let env = Environment::new();
|
||||
|
||||
let r1 = env.run_script("(def double (fn [n] (* n 2)))");
|
||||
assert!(r1.is_ok(), "def fn should succeed: {:?}", r1.err());
|
||||
|
||||
let r2 = env.run_script("(double 21)");
|
||||
assert_eq!(r2.unwrap(), Value::Int(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repl_function_uses_prior_variable() {
|
||||
let env = Environment::new();
|
||||
|
||||
env.run_script("(def x 10)").unwrap();
|
||||
env.run_script("(def double (fn [n] (* n 2)))").unwrap();
|
||||
|
||||
let result = env.run_script("(double x)");
|
||||
assert_eq!(result.unwrap(), Value::Int(20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repl_reassign_variable() {
|
||||
let env = Environment::new();
|
||||
|
||||
env.run_script("(def x 1)").unwrap();
|
||||
env.run_script("(assign x 99)").unwrap();
|
||||
|
||||
let result = env.run_script("x");
|
||||
assert_eq!(result.unwrap(), Value::Int(99));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repl_macro_from_prior_call() {
|
||||
let env = Environment::new();
|
||||
|
||||
env.run_script("(macro unless [c body] `(if ~c ... ~body))")
|
||||
.unwrap();
|
||||
|
||||
let result = env.run_script(r#"(unless false "yes")"#);
|
||||
assert_eq!(result.unwrap(), Value::Text("yes".into()));
|
||||
}
|
||||
Reference in New Issue
Block a user