Refactor: Use root_purity instead of global_purity

This commit refactors the purity analysis from using a
`HashMap<GlobalIdx, Purity>` to a `Vec<Purity>` indexed by
`GlobalIdx.0`.

This change simplifies the data structure and makes purity lookups more
efficient. The `Binder`'s `globals` field has also been removed as it
was not being used.
This commit is contained in:
Michael Schimmel
2026-03-12 16:57:06 +01:00
parent dcb7685d29
commit bf86c76bb6
8 changed files with 123 additions and 131 deletions
+68 -59
View File
@@ -21,7 +21,7 @@ use crate::ast::compiler::optimizer::Optimizer;
use crate::ast::compiler::specializer::{FunctionRegistry, MonoCache, Specializer};
use crate::ast::rtl::intrinsics;
use crate::ast::types::{
Identity, NodeIdentity, Object, Purity, SourceLocation, StaticType, Value,
NodeIdentity, Object, Purity, SourceLocation, StaticType, Value,
};
use crate::ast::diagnostics::{DiagnosticLevel, Diagnostics};
@@ -71,10 +71,9 @@ impl CompilationResult {
}
pub struct Environment {
pub global_names: Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>,
pub global_types: Rc<RefCell<HashMap<GlobalIdx, StaticType>>>,
pub global_purity: Rc<RefCell<HashMap<GlobalIdx, Purity>>>,
pub global_values: Rc<RefCell<Vec<Value>>>,
pub root_types: Rc<RefCell<Vec<StaticType>>>,
pub root_purity: Rc<RefCell<Vec<Purity>>>,
pub root_values: Rc<RefCell<Vec<Value>>>,
pub fixed_scope_idx: i32,
pub root_scopes: Rc<RefCell<Vec<crate::ast::compiler::binder::CompilerScope>>>,
pub root_slot_count: Rc<RefCell<u32>>,
@@ -112,11 +111,11 @@ impl FunctionRegistry for EnvFunctionRegistry {
}
struct RuntimeMacroEvaluator {
global_names: Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>,
root_scopes: Rc<RefCell<Vec<crate::ast::compiler::binder::CompilerScope>>>,
root_slot_count: Rc<RefCell<u32>>,
global_types: Rc<RefCell<HashMap<GlobalIdx, StaticType>>>,
global_values: Rc<RefCell<Vec<Value>>>,
root_types: Rc<RefCell<Vec<StaticType>>>,
root_values: Rc<RefCell<Vec<Value>>>,
root_purity: Rc<RefCell<Vec<Purity>>>,
fixed_scope_idx: i32,
}
@@ -136,9 +135,9 @@ impl MacroEvaluator for RuntimeMacroEvaluator {
let initial_scopes = self.root_scopes.borrow().clone();
let initial_slot_count = *self.root_slot_count.borrow();
let (bound_ast, captures, _, _) = Binder::bind_root(initial_scopes, initial_slot_count, self.fixed_scope_idx, self.global_names.clone(), node, &mut diag)?;
let (bound_ast, captures, _, _) = Binder::bind_root(initial_scopes, initial_slot_count, self.fixed_scope_idx, node, &mut diag)?;
let bound_ast = crate::ast::compiler::captures::CapturePass::apply(bound_ast, &captures);
let checker = TypeChecker::new(self.global_types.clone());
let checker = TypeChecker::new(self.root_types.clone());
let typed_ast = checker.check(&bound_ast, &[], &mut diag);
if diag.has_errors() {
@@ -150,9 +149,9 @@ impl MacroEvaluator for RuntimeMacroEvaluator {
.join("\n"));
}
let exec_ast = Lowering::lower(Analyzer::analyze(&typed_ast, &HashMap::new())); // Minimal analysis for macro eval
let exec_ast = Lowering::lower(Analyzer::analyze(&typed_ast, &self.root_purity.borrow())); // Minimal analysis for macro eval
let mut vm = VM::new(self.global_values.clone());
let mut vm = VM::new(self.root_values.clone());
vm.run(&exec_ast)
}
}
@@ -166,10 +165,9 @@ impl Default for Environment {
impl Environment {
pub fn new() -> Self {
let env = Self {
global_names: Rc::new(RefCell::new(HashMap::new())),
global_types: Rc::new(RefCell::new(HashMap::new())),
global_purity: Rc::new(RefCell::new(HashMap::new())),
global_values: Rc::new(RefCell::new(Vec::new())),
root_types: Rc::new(RefCell::new(Vec::new())),
root_purity: Rc::new(RefCell::new(Vec::new())),
root_values: Rc::new(RefCell::new(Vec::new())),
fixed_scope_idx: -1,
root_scopes: Rc::new(RefCell::new(vec![crate::ast::compiler::binder::CompilerScope::new()])),
root_slot_count: Rc::new(RefCell::new(0)),
@@ -226,11 +224,11 @@ impl Environment {
fn get_expander(&self) -> MacroExpander<RuntimeMacroEvaluator> {
let evaluator = RuntimeMacroEvaluator {
global_names: self.global_names.clone(),
root_scopes: self.root_scopes.clone(),
root_slot_count: self.root_slot_count.clone(),
global_types: self.global_types.clone(),
global_values: self.global_values.clone(),
root_types: self.root_types.clone(),
root_values: self.root_values.clone(),
root_purity: self.root_purity.clone(),
fixed_scope_idx: self.fixed_scope_idx,
};
MacroExpander::new(self.macro_registry.borrow().clone(), evaluator)
@@ -459,7 +457,7 @@ impl Environment {
let initial_slot_count = *self.root_slot_count.borrow();
let (bound_ast, captures, final_scopes, final_slot_count) =
match Binder::bind_root(initial_scopes, initial_slot_count, self.fixed_scope_idx, self.global_names.clone(), &expanded_ast, diagnostics) {
match Binder::bind_root(initial_scopes, initial_slot_count, self.fixed_scope_idx, &expanded_ast, diagnostics) {
Ok(res) => res,
Err(e) => {
diagnostics.push_error(e, None);
@@ -475,7 +473,7 @@ impl Environment {
// Pre-allocate global slots to prevent out-of-bounds during specialization/optimization
{
let mut values = self.global_values.borrow_mut();
let mut values = self.root_values.borrow_mut();
let count = *self.root_slot_count.borrow();
if (count as usize) > values.len() {
values.resize(count as usize, Value::Void);
@@ -484,7 +482,7 @@ impl Environment {
LambdaCollector::collect(&bound_ast, &mut self.function_registry.borrow_mut());
let checker = TypeChecker::new(self.global_types.clone());
let checker = TypeChecker::new(self.root_types.clone());
let wrapped_ast = if let crate::ast::compiler::bound_nodes::BoundKind::Lambda { .. } = bound_ast.kind {
bound_ast
} else {
@@ -529,19 +527,13 @@ impl Environment {
ty: StaticType,
func: Rc<crate::ast::types::NativeFunction>,
) {
let mut names = self.global_names.borrow_mut();
let mut types = self.global_types.borrow_mut();
let mut values = self.global_values.borrow_mut();
let mut purity = self.global_purity.borrow_mut();
let mut types = self.root_types.borrow_mut();
let mut values = self.root_values.borrow_mut();
let mut purity = self.root_purity.borrow_mut();
let idx = GlobalIdx(values.len() as u32);
let identity = NodeIdentity::new(SourceLocation { line: 0, col: 0 });
names.insert(Symbol::from(name), (idx, identity.clone()));
types.insert(idx, ty.clone());
purity.insert(idx, func.purity);
values.push(Value::Function(func.clone()));
// Parallel mode: populate root_scopes[0]
// populate root_scopes[0]
let mut root_scopes = self.root_scopes.borrow_mut();
let mut slot_count = self.root_slot_count.borrow_mut();
let slot = crate::ast::compiler::bound_nodes::LocalSlot(*slot_count);
@@ -550,11 +542,23 @@ impl Environment {
crate::ast::compiler::binder::LocalInfo {
addr: Address::Global(GlobalIdx(slot.0)),
identity,
_ty: ty,
_ty: ty.clone(),
purity: func.purity,
},
);
*slot_count += 1;
// Ensure arrays are large enough
let idx = slot.0 as usize;
if idx >= values.len() {
values.resize(idx + 1, Value::Void);
types.resize(idx + 1, StaticType::Any);
purity.resize(idx + 1, Purity::Impure);
}
types[idx] = ty;
purity[idx] = func.purity;
values[idx] = Value::Function(func);
}
pub fn register_native_fn(
@@ -575,19 +579,13 @@ impl Environment {
}
pub fn register_constant(&self, name: &str, ty: StaticType, val: Value) {
let mut names = self.global_names.borrow_mut();
let mut types = self.global_types.borrow_mut();
let mut values = self.global_values.borrow_mut();
let mut purity = self.global_purity.borrow_mut();
let mut types = self.root_types.borrow_mut();
let mut values = self.root_values.borrow_mut();
let mut purity = self.root_purity.borrow_mut();
let idx = GlobalIdx(values.len() as u32);
let identity = NodeIdentity::new(SourceLocation { line: 0, col: 0 });
names.insert(Symbol::from(name), (idx, identity.clone()));
types.insert(idx, ty.clone());
purity.insert(idx, Purity::Pure);
values.push(val);
// Parallel mode: populate root_scopes[0]
// populate root_scopes[0]
let mut root_scopes = self.root_scopes.borrow_mut();
let mut slot_count = self.root_slot_count.borrow_mut();
let slot = crate::ast::compiler::bound_nodes::LocalSlot(*slot_count);
@@ -596,11 +594,22 @@ impl Environment {
crate::ast::compiler::binder::LocalInfo {
addr: Address::Global(GlobalIdx(slot.0)),
identity,
_ty: ty,
_ty: ty.clone(),
purity: Purity::Pure,
},
);
*slot_count += 1;
let idx = slot.0 as usize;
if idx >= values.len() {
values.resize(idx + 1, Value::Void);
types.resize(idx + 1, StaticType::Any);
purity.resize(idx + 1, Purity::Impure);
}
types[idx] = ty;
purity[idx] = Purity::Pure;
values[idx] = val;
}
@@ -640,7 +649,7 @@ impl Environment {
pub fn link(&self, node: TypedNode) -> ExecNode {
// 1. Analyze
let analyzed = Analyzer::analyze(&node, &self.global_purity.borrow());
let analyzed = Analyzer::analyze(&node, &self.root_purity.borrow());
// 2. Collect Analyzed Lambdas
LambdaCollector::collect(&analyzed, &mut self.typed_function_registry.borrow_mut());
@@ -650,8 +659,8 @@ impl Environment {
// 4. Optimize
let optimizer = Optimizer::new(self.optimization)
.with_globals(self.global_values.clone())
.with_purity(self.global_purity.clone())
.with_globals(self.root_values.clone())
.with_purity(self.root_purity.clone())
.with_registry(self.typed_function_registry.clone());
let optimized = optimizer.optimize(specialized);
@@ -660,7 +669,7 @@ impl Environment {
}
pub fn instantiate(&self, node: ExecNode) -> Rc<crate::ast::types::NativeFunction> {
let global_values = self.global_values.clone();
let root_values = self.root_values.clone();
if let BoundKind::Lambda {
params,
upvalues,
@@ -683,7 +692,7 @@ impl Environment {
return Rc::new(crate::ast::types::NativeFunction {
purity: Purity::Impure,
func: Rc::new(move |args| {
let mut vm = VM::new(global_values.clone());
let mut vm = VM::new(root_values.clone());
match vm.run_with_args(closure_obj.clone(), args) {
Ok(v) => v,
Err(e) => panic!("Myc Runtime Error: {}", e),
@@ -696,7 +705,7 @@ impl Environment {
Rc::new(crate::ast::types::NativeFunction {
purity: Purity::Impure,
func: Rc::new(move |args| {
let mut vm = VM::new(global_values.clone());
let mut vm = VM::new(root_values.clone());
let res = match vm.run(&exec_node) {
Ok(v) => v,
Err(e) => panic!("Myc Runtime Error: {}", e),
@@ -729,9 +738,9 @@ impl Environment {
let typed_reg = self.typed_function_registry.clone();
let untyped_reg = self.function_registry.clone();
let mono_cache = self.monomorph_cache.clone();
let global_values = self.global_values.clone();
let global_types = self.global_types.clone();
let global_purity = self.global_purity.clone();
let root_values = self.root_values.clone();
let root_types = self.root_types.clone();
let root_purity = self.root_purity.clone();
let optimization = self.optimization;
let compiler = Rc::new(
@@ -739,7 +748,7 @@ impl Environment {
arg_types: &[StaticType]|
-> Result<(Value, StaticType), String> {
let mut diag = Diagnostics::new();
let checker = TypeChecker::new(global_types.clone());
let checker = TypeChecker::new(root_types.clone());
let retyped_ast = checker.check(func_template.as_ref(), arg_types, &mut diag);
if diag.has_errors() {
@@ -751,7 +760,7 @@ impl Environment {
.join("\n"));
}
let analyzed = Analyzer::analyze(&retyped_ast, &global_purity.borrow());
let analyzed = Analyzer::analyze(&retyped_ast, &root_purity.borrow());
let sub_registry = Rc::new(EnvFunctionRegistry {
registry: untyped_reg.clone(),
@@ -770,12 +779,12 @@ impl Environment {
let specialized_ast = sub_specializer.specialize(analyzed);
let optimizer = Optimizer::new(optimization)
.with_globals(global_values.clone())
.with_purity(global_purity.clone());
.with_globals(root_values.clone())
.with_purity(root_purity.clone());
let optimized_ast = optimizer.optimize(specialized_ast);
let exec_ast = Lowering::lower(optimized_ast);
let mut vm = VM::new(global_values.clone());
let mut vm = VM::new(root_values.clone());
let compiled_val = match vm.run(&exec_ast) {
Ok(v) => v,
Err(e) => return Err(format!("VM Error during specialization: {}", e)),
@@ -824,7 +833,7 @@ impl Environment {
let compiled = self.compile(source).into_result()?;
let linked = self.link(compiled);
let mut vm = VM::new(self.global_values.clone());
let mut vm = VM::new(self.root_values.clone());
let mut observer = TracingObserver::new();
// 1. Run the script wrapper (returns a closure representing the script)