Refactor type variable counter to be shared
This commit is contained in:
@@ -34,6 +34,11 @@ impl TypeChecker {
|
|||||||
match ty {
|
match ty {
|
||||||
StaticType::TypeVar(n) => {
|
StaticType::TypeVar(n) => {
|
||||||
if let Some(resolved) = subst.get(&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)
|
// Follow the chain (handles transitive substitutions)
|
||||||
Self::apply_subst(resolved.clone(), subst)
|
Self::apply_subst(resolved.clone(), subst)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ use context::TypeContext;
|
|||||||
|
|
||||||
pub struct TypeChecker {
|
pub struct TypeChecker {
|
||||||
pub(crate) root_types: Rc<std::cell::RefCell<Vec<StaticType>>>,
|
pub(crate) root_types: Rc<std::cell::RefCell<Vec<StaticType>>>,
|
||||||
/// Monotonic counter for generating unique type variable IDs.
|
/// Shared monotonic counter for generating unique type variable IDs.
|
||||||
pub(crate) var_counter: Cell<u32>,
|
/// 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.
|
/// Global substitution map: TypeVar ID → resolved StaticType.
|
||||||
/// Shared across all scopes within a single type-checking pass.
|
/// Shared across all scopes within a single type-checking pass.
|
||||||
pub(crate) subst: std::cell::RefCell<HashMap<u32, StaticType>>,
|
pub(crate) subst: std::cell::RefCell<HashMap<u32, StaticType>>,
|
||||||
@@ -39,11 +41,12 @@ pub struct TypeChecker {
|
|||||||
impl TypeChecker {
|
impl TypeChecker {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
root_types: Rc<std::cell::RefCell<Vec<StaticType>>>,
|
root_types: Rc<std::cell::RefCell<Vec<StaticType>>>,
|
||||||
|
var_counter: Rc<Cell<u32>>,
|
||||||
compiler_hooks: Rc<HashMap<u32, Rc<dyn RtlCompilerHook>>>,
|
compiler_hooks: Rc<HashMap<u32, Rc<dyn RtlCompilerHook>>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
root_types,
|
root_types,
|
||||||
var_counter: Cell::new(0),
|
var_counter,
|
||||||
subst: std::cell::RefCell::new(HashMap::new()),
|
subst: std::cell::RefCell::new(HashMap::new()),
|
||||||
index_constraints: std::cell::RefCell::new(HashMap::new()),
|
index_constraints: std::cell::RefCell::new(HashMap::new()),
|
||||||
compiler_hooks,
|
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::nodes::{AnalyzedPhase, BoundPhase, Symbol, SyntaxKind, SyntaxNode};
|
||||||
use crate::ast::parser::Parser;
|
use crate::ast::parser::Parser;
|
||||||
use crate::ast::vm::{GlobalStore, TracingObserver, VM};
|
use crate::ast::vm::{GlobalStore, TracingObserver, VM};
|
||||||
use std::cell::RefCell;
|
use std::cell::{Cell, RefCell};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@@ -71,6 +71,11 @@ pub struct Environment {
|
|||||||
pub function_registry: Rc<RefCell<GlobalFunctionRegistry>>,
|
pub function_registry: Rc<RefCell<GlobalFunctionRegistry>>,
|
||||||
pub typed_function_registry: Rc<RefCell<GlobalAnalyzedRegistry>>,
|
pub typed_function_registry: Rc<RefCell<GlobalAnalyzedRegistry>>,
|
||||||
pub monomorph_cache: Rc<RefCell<MonoCache>>,
|
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 debug_mode: bool,
|
||||||
pub optimization: bool,
|
pub optimization: bool,
|
||||||
pub macro_registry: Rc<RefCell<MacroRegistry>>,
|
pub macro_registry: Rc<RefCell<MacroRegistry>>,
|
||||||
@@ -106,6 +111,7 @@ struct RuntimeMacroEvaluator {
|
|||||||
root_scopes: Rc<RefCell<Vec<CompilerScope>>>,
|
root_scopes: Rc<RefCell<Vec<CompilerScope>>>,
|
||||||
root_slot_count: Rc<RefCell<u32>>,
|
root_slot_count: Rc<RefCell<u32>>,
|
||||||
root_types: Rc<RefCell<Vec<StaticType>>>,
|
root_types: Rc<RefCell<Vec<StaticType>>>,
|
||||||
|
typevar_counter: Rc<Cell<u32>>,
|
||||||
globals: GlobalStore,
|
globals: GlobalStore,
|
||||||
root_purity: Rc<RefCell<Vec<Purity>>>,
|
root_purity: Rc<RefCell<Vec<Purity>>>,
|
||||||
compiler_hooks: Rc<HashMap<u32, Rc<dyn RtlCompilerHook>>>,
|
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, captures, _, _) = Binder::bind_root(initial_scopes, initial_slot_count, node, &mut diag)?;
|
||||||
let bound_ast = CapturePass::apply(bound_ast, &captures);
|
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);
|
let typed_ast = checker.check(&bound_ast, &[], &mut diag);
|
||||||
|
|
||||||
if diag.has_errors() {
|
if diag.has_errors() {
|
||||||
@@ -162,6 +168,7 @@ impl Environment {
|
|||||||
function_registry: Rc::new(RefCell::new(HashMap::new())),
|
function_registry: Rc::new(RefCell::new(HashMap::new())),
|
||||||
typed_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())),
|
monomorph_cache: Rc::new(RefCell::new(HashMap::new())),
|
||||||
|
typevar_counter: Rc::new(Cell::new(0)),
|
||||||
debug_mode: false,
|
debug_mode: false,
|
||||||
optimization: true,
|
optimization: true,
|
||||||
macro_registry: Rc::new(RefCell::new(MacroRegistry::new())),
|
macro_registry: Rc::new(RefCell::new(MacroRegistry::new())),
|
||||||
@@ -254,6 +261,7 @@ impl Environment {
|
|||||||
function_registry: Rc::new(RefCell::new(HashMap::new())),
|
function_registry: Rc::new(RefCell::new(HashMap::new())),
|
||||||
typed_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())),
|
monomorph_cache: Rc::new(RefCell::new(HashMap::new())),
|
||||||
|
typevar_counter: Rc::new(Cell::new(0)),
|
||||||
debug_mode: false,
|
debug_mode: false,
|
||||||
optimization: true,
|
optimization: true,
|
||||||
macro_registry: Rc::new(RefCell::new(MacroRegistry::new())),
|
macro_registry: Rc::new(RefCell::new(MacroRegistry::new())),
|
||||||
@@ -321,6 +329,7 @@ impl Environment {
|
|||||||
root_scopes: self.root_scopes.clone(),
|
root_scopes: self.root_scopes.clone(),
|
||||||
root_slot_count: self.root_slot_count.clone(),
|
root_slot_count: self.root_slot_count.clone(),
|
||||||
root_types: self.root_types.clone(),
|
root_types: self.root_types.clone(),
|
||||||
|
typevar_counter: Rc::clone(&self.typevar_counter),
|
||||||
globals: self.global_store(),
|
globals: self.global_store(),
|
||||||
root_purity: self.root_purity.clone(),
|
root_purity: self.root_purity.clone(),
|
||||||
compiler_hooks: Rc::clone(&self.rtl.compiler_hooks),
|
compiler_hooks: Rc::clone(&self.rtl.compiler_hooks),
|
||||||
@@ -447,7 +456,7 @@ impl Environment {
|
|||||||
|
|
||||||
/// Type-checks a bound AST and collects doc comments.
|
/// Type-checks a bound AST and collects doc comments.
|
||||||
fn type_check(&self, bound_ast: &Node<BoundPhase>, diagnostics: &mut Diagnostics) -> TypedNode {
|
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);
|
let typed = checker.check(bound_ast, &[], diagnostics);
|
||||||
self.collect_doc_comments(&typed);
|
self.collect_doc_comments(&typed);
|
||||||
typed
|
typed
|
||||||
@@ -766,13 +775,14 @@ impl Environment {
|
|||||||
let root_purity = self.root_purity.clone();
|
let root_purity = self.root_purity.clone();
|
||||||
let optimization = self.optimization;
|
let optimization = self.optimization;
|
||||||
let compiler_hooks = Rc::clone(&self.rtl.compiler_hooks);
|
let compiler_hooks = Rc::clone(&self.rtl.compiler_hooks);
|
||||||
|
let typevar_counter = Rc::clone(&self.typevar_counter);
|
||||||
|
|
||||||
let compiler = Rc::new(
|
let compiler = Rc::new(
|
||||||
move |func_template: Rc<Node<AnalyzedPhase>>,
|
move |func_template: Rc<Node<AnalyzedPhase>>,
|
||||||
arg_types: &[StaticType]|
|
arg_types: &[StaticType]|
|
||||||
-> Result<(Value, StaticType), String> {
|
-> Result<(Value, StaticType), String> {
|
||||||
let mut diag = Diagnostics::new();
|
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
|
// Monomorphization: re-type-check the function template with the concrete
|
||||||
// call-site argument types, producing a specialized TypedNode.
|
// call-site argument types, producing a specialized TypedNode.
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
;; Myc System Library
|
;; Myc System Library
|
||||||
;; Standard macros and core utilities.
|
;; Standard macros and core utilities.
|
||||||
|
|
||||||
(def func1 (fn [] (print "Hello World")))
|
|
||||||
|
|
||||||
(macro while [cond body]
|
(macro while [cond body]
|
||||||
`((fn [] (if ~cond
|
`((fn [] (if ~cond
|
||||||
(do ~body (again))
|
(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