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
+7 -26
View File
@@ -4,7 +4,6 @@ use crate::ast::compiler::bound_nodes::{
use crate::ast::diagnostics::Diagnostics;
use crate::ast::nodes::{Node, Symbol, UntypedKind};
use crate::ast::types::{Identity, StaticType, Purity};
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
@@ -123,7 +122,6 @@ pub type BindingResult = (
pub struct Binder {
functions: Vec<FunctionCompiler>,
globals: Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>,
capture_map: HashMap<Identity, std::collections::HashSet<Identity>>,
fixed_scope_idx: i32,
}
@@ -133,11 +131,9 @@ impl Binder {
initial_scopes: Vec<CompilerScope>,
initial_slot_count: u32,
fixed_scope_idx: i32,
globals: Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>,
) -> Self {
let mut binder = Self {
functions: Vec::new(),
globals,
capture_map: HashMap::new(),
fixed_scope_idx,
};
@@ -156,11 +152,10 @@ impl Binder {
initial_scopes: Vec<CompilerScope>,
initial_slot_count: u32,
fixed_scope_idx: i32,
globals: Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>,
node: &Node<UntypedKind>,
diagnostics: &mut Diagnostics,
) -> Result<BindingResult, String> {
let mut binder = Self::new(initial_scopes, initial_slot_count, fixed_scope_idx, globals);
let mut binder = Self::new(initial_scopes, initial_slot_count, fixed_scope_idx);
let bound = binder.bind(node, ExprContext::Expression, diagnostics);
let final_captures = binder
@@ -580,13 +575,7 @@ impl Binder {
}
}
// 3. Global Fallback
let globals = self.globals.borrow();
if let Some((idx, _)) = globals.get(sym) {
return Some(Address::Global(*idx));
}
// 4. Global Fallback (search in root level with context removed)
// 3. Global Fallback (search in root level with context removed)
if sym.context.is_some() {
let fallback_sym = Symbol {
name: sym.name.clone(),
@@ -600,9 +589,6 @@ impl Binder {
return Some(info.addr);
}
}
if let Some((idx, _)) = globals.get(&fallback_sym) {
return Some(Address::Global(*idx));
}
}
diag.push_error(
@@ -719,9 +705,8 @@ mod tests {
let mut parser = Parser::new(source);
let untyped = parser.parse_expression();
let globals = Rc::new(RefCell::new(HashMap::new()));
let mut diagnostics = Diagnostics::new();
let (bound, captures, _scopes, _slots) = Binder::bind_root(vec![], 0, 0, globals, &untyped, &mut diagnostics).unwrap();
let (bound, captures, _scopes, _slots) = Binder::bind_root(vec![], 0, 0, &untyped, &mut diagnostics).unwrap();
if let BoundKind::Lambda { body, .. } = &bound.kind {
if let BoundKind::Block { exprs } = &body.kind {
@@ -749,9 +734,8 @@ mod tests {
let mut parser = Parser::new(source);
let untyped = parser.parse_expression();
let globals = Rc::new(RefCell::new(HashMap::new()));
let mut diagnostics = Diagnostics::new();
let (bound, captures, _scopes, _slots) = Binder::bind_root(vec![], 0, 0, globals, &untyped, &mut diagnostics).unwrap();
let (bound, captures, _scopes, _slots) = Binder::bind_root(vec![], 0, 0, &untyped, &mut diagnostics).unwrap();
if let BoundKind::Lambda { body, .. } = &bound.kind {
if let BoundKind::Block { exprs } = &body.kind {
@@ -779,9 +763,8 @@ mod tests {
let mut parser = Parser::new(source);
let untyped = parser.parse_expression();
let globals = Rc::new(RefCell::new(HashMap::new()));
let mut diagnostics = Diagnostics::new();
let _ = Binder::bind_root(vec![], 0, 0, globals, &untyped, &mut diagnostics);
let _ = Binder::bind_root(vec![], 0, 0, &untyped, &mut diagnostics);
assert!(diagnostics.has_errors());
assert!(diagnostics.items.iter().any(|i| i.message.contains("already defined")));
@@ -789,18 +772,16 @@ mod tests {
#[test]
fn test_repro_global_redefinition() {
let globals = Rc::new(RefCell::new(HashMap::new()));
let source1 = "(def x 1) 1";
let untyped1 = Parser::new(source1).parse_expression();
let mut diagnostics = Diagnostics::new();
let (_, _, scopes1, slots1) = Binder::bind_root(vec![], 0, -1, globals.clone(), &untyped1, &mut diagnostics).unwrap();
let (_, _, scopes1, slots1) = Binder::bind_root(vec![], 0, -1, &untyped1, &mut diagnostics).unwrap();
let source2 = "(def x 2) 2";
let untyped2 = Parser::new(source2).parse_expression();
let mut diagnostics2 = Diagnostics::new();
// Here we simulate frozen scope by passing fixed_scope_idx = 0
let _ = Binder::bind_root(scopes1, slots1, 0, globals.clone(), &untyped2, &mut diagnostics2);
let _ = Binder::bind_root(scopes1, slots1, 0, &untyped2, &mut diagnostics2);
assert!(diagnostics2.has_errors());
assert!(diagnostics2.items.iter().any(|i| i.message.contains("frozen/immutable")));