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:
@@ -6,7 +6,7 @@ use std::collections::{HashMap, HashSet};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct Analyzer<'a> {
|
||||
global_purity: &'a HashMap<GlobalIdx, Purity>,
|
||||
root_purity: &'a [Purity],
|
||||
/// Stack of currently visiting lambdas to detect direct recursion.
|
||||
lambda_stack: Vec<crate::ast::types::Identity>,
|
||||
/// Map of global index to its Lambda identity if known.
|
||||
@@ -18,10 +18,10 @@ pub struct Analyzer<'a> {
|
||||
impl<'a> Analyzer<'a> {
|
||||
pub fn analyze(
|
||||
node: &TypedNode,
|
||||
global_purity: &'a HashMap<GlobalIdx, Purity>,
|
||||
root_purity: &'a [Purity],
|
||||
) -> AnalyzedNode {
|
||||
let mut analyzer = Self {
|
||||
global_purity,
|
||||
root_purity,
|
||||
lambda_stack: Vec::new(),
|
||||
globals_to_lambdas: HashMap::new(),
|
||||
recursive_identities: HashSet::new(),
|
||||
@@ -70,7 +70,7 @@ impl<'a> Analyzer<'a> {
|
||||
BoundKind::Get { addr, name } => {
|
||||
let p = match addr {
|
||||
Address::Global(idx) => {
|
||||
self.global_purity.get(idx).cloned().unwrap_or(Purity::Pure)
|
||||
self.root_purity.get(idx.0 as usize).cloned().unwrap_or(Purity::Pure)
|
||||
}
|
||||
_ => Purity::Pure,
|
||||
};
|
||||
@@ -206,8 +206,8 @@ impl<'a> Analyzer<'a> {
|
||||
..
|
||||
} = &callee.kind
|
||||
{
|
||||
self.global_purity
|
||||
.get(idx)
|
||||
self.root_purity
|
||||
.get(idx.0 as usize)
|
||||
.cloned()
|
||||
.unwrap_or(Purity::Impure)
|
||||
} else {
|
||||
|
||||
@@ -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")));
|
||||
|
||||
@@ -627,7 +627,6 @@ mod tests {
|
||||
use crate::ast::compiler::Binder;
|
||||
use crate::ast::parser::Parser;
|
||||
use crate::ast::types::{Object, Value};
|
||||
use std::cell::RefCell;
|
||||
|
||||
struct SimpleEvaluator;
|
||||
impl MacroEvaluator for SimpleEvaluator {
|
||||
@@ -783,7 +782,7 @@ mod tests {
|
||||
|
||||
let mut diag = crate::ast::diagnostics::Diagnostics::new();
|
||||
// fixed_scope_idx = 0 means scope 0 is frozen (Global)
|
||||
let result = Binder::bind_root(initial_scopes, 1, 0, Rc::new(RefCell::new(HashMap::new())), &expanded, &mut diag);
|
||||
let result = Binder::bind_root(initial_scopes, 1, 0, &expanded, &mut diag);
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"Should find global '*' Error: {:?}",
|
||||
@@ -807,7 +806,7 @@ mod tests {
|
||||
let expanded = expander.expand(untyped).unwrap();
|
||||
|
||||
let mut diag = crate::ast::diagnostics::Diagnostics::new();
|
||||
let result = Binder::bind_root(vec![], 0, 0, Rc::new(RefCell::new(HashMap::new())), &expanded, &mut diag);
|
||||
let result = Binder::bind_root(vec![], 0, 0, &expanded, &mut diag);
|
||||
assert!(result.is_ok(), "Hygiene failed: {:?}", result.err());
|
||||
}
|
||||
|
||||
@@ -827,7 +826,7 @@ mod tests {
|
||||
let expanded = expander.expand(untyped).unwrap();
|
||||
|
||||
let mut diag = crate::ast::diagnostics::Diagnostics::new();
|
||||
let result = Binder::bind_root(vec![], 0, 0, Rc::new(RefCell::new(HashMap::new())), &expanded, &mut diag);
|
||||
let result = Binder::bind_root(vec![], 0, 0, &expanded, &mut diag);
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"Explicit Hygiene with Backticks failed: {:?}",
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
Address, AnalyzedNode, BoundKind, GlobalAnalyzedRegistry, GlobalIdx, UpvalueIdx,
|
||||
Address, AnalyzedNode, BoundKind, GlobalAnalyzedRegistry, UpvalueIdx,
|
||||
};
|
||||
use crate::ast::nodes::Node;
|
||||
use crate::ast::types::{Purity, Value};
|
||||
use crate::ast::vm::Closure;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::folder::Folder;
|
||||
@@ -17,7 +16,7 @@ pub struct Optimizer {
|
||||
pub enabled: bool,
|
||||
max_passes: usize,
|
||||
pub globals: Option<Rc<RefCell<Vec<Value>>>>,
|
||||
pub global_purity: Option<Rc<RefCell<HashMap<GlobalIdx, Purity>>>>,
|
||||
pub root_purity: Option<Rc<RefCell<Vec<Purity>>>>,
|
||||
pub lambda_registry: Option<Rc<RefCell<GlobalAnalyzedRegistry>>>,
|
||||
}
|
||||
|
||||
@@ -27,7 +26,7 @@ impl Optimizer {
|
||||
enabled,
|
||||
max_passes: 5,
|
||||
globals: None,
|
||||
global_purity: None,
|
||||
root_purity: None,
|
||||
lambda_registry: None,
|
||||
}
|
||||
}
|
||||
@@ -37,8 +36,8 @@ impl Optimizer {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_purity(mut self, purity: Rc<RefCell<HashMap<GlobalIdx, Purity>>>) -> Self {
|
||||
self.global_purity = Some(purity);
|
||||
pub fn with_purity(mut self, purity: Rc<RefCell<Vec<Purity>>>) -> Self {
|
||||
self.root_purity = Some(purity);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -79,7 +78,7 @@ impl Optimizer {
|
||||
path: &mut PathTracker,
|
||||
base_sub: Option<SubstitutionMap>,
|
||||
) -> Option<Rc<AnalyzedNode>> {
|
||||
let inliner = Inliner::new(&self.globals, &self.global_purity);
|
||||
let inliner = Inliner::new(&self.globals, &self.root_purity);
|
||||
let mut inner_sub = base_sub.unwrap_or_else(|| sub.new_for_inlining());
|
||||
|
||||
if inliner.prepare_beta_reduction(params, arg_nodes, body, &mut inner_sub).is_some() {
|
||||
@@ -105,7 +104,7 @@ impl Optimizer {
|
||||
) -> Rc<AnalyzedNode> {
|
||||
let node = &*node_rc;
|
||||
let folder = Folder::new(&self.globals);
|
||||
let inliner = Inliner::new(&self.globals, &self.global_purity);
|
||||
let inliner = Inliner::new(&self.globals, &self.root_purity);
|
||||
|
||||
let (new_kind, metrics) = match &node.kind {
|
||||
BoundKind::Get { addr, name } => {
|
||||
@@ -215,11 +214,13 @@ impl Optimizer {
|
||||
|
||||
if let Address::Global(global_index) = addr
|
||||
&& value_opt.ty.purity > Purity::Impure
|
||||
&& let Some(purity_rc) = &self.global_purity
|
||||
&& let Some(purity_rc) = &self.root_purity
|
||||
{
|
||||
purity_rc
|
||||
.borrow_mut()
|
||||
.insert(*global_index, value_opt.ty.purity);
|
||||
let mut pr = purity_rc.borrow_mut();
|
||||
let idx = global_index.0 as usize;
|
||||
if idx < pr.len() {
|
||||
pr[idx] = value_opt.ty.purity;
|
||||
}
|
||||
}
|
||||
|
||||
if Rc::ptr_eq(&value_opt, value) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, GlobalIdx, LocalSlot};
|
||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, LocalSlot};
|
||||
use crate::ast::types::{Purity, Value};
|
||||
use crate::ast::vm::Closure;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::HashSet;
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::substitution_map::SubstitutionMap;
|
||||
@@ -10,17 +10,17 @@ use super::utils::UsageInfo;
|
||||
|
||||
pub struct Inliner<'a> {
|
||||
pub globals: &'a Option<Rc<RefCell<Vec<Value>>>>,
|
||||
pub global_purity: &'a Option<Rc<RefCell<HashMap<GlobalIdx, Purity>>>>,
|
||||
pub root_purity: &'a Option<Rc<RefCell<Vec<Purity>>>>,
|
||||
}
|
||||
|
||||
impl<'a> Inliner<'a> {
|
||||
pub fn new(
|
||||
globals: &'a Option<Rc<RefCell<Vec<Value>>>>,
|
||||
global_purity: &'a Option<Rc<RefCell<HashMap<GlobalIdx, Purity>>>>,
|
||||
root_purity: &'a Option<Rc<RefCell<Vec<Purity>>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
globals,
|
||||
global_purity,
|
||||
root_purity,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,10 +48,10 @@ impl<'a> Inliner<'a> {
|
||||
}
|
||||
|
||||
if let Address::Global(idx) = addr {
|
||||
if let Some(purity_rc) = &self.global_purity {
|
||||
if let Some(purity_rc) = &self.root_purity {
|
||||
let purity = purity_rc
|
||||
.borrow()
|
||||
.get(&idx)
|
||||
.get(idx.0 as usize)
|
||||
.cloned()
|
||||
.unwrap_or(Purity::Impure);
|
||||
return purity >= Purity::Pure;
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use crate::ast::compiler::bound_nodes::{Address, BoundKind, BoundNode, GlobalIdx, TypedNode};
|
||||
use crate::ast::compiler::bound_nodes::{Address, BoundKind, BoundNode, TypedNode};
|
||||
use crate::ast::diagnostics::Diagnostics;
|
||||
use crate::ast::nodes::Node;
|
||||
use crate::ast::types::StaticType;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// Manages the types of locals and upvalues during a single type-checking pass.
|
||||
@@ -12,8 +11,8 @@ struct TypeContext<'a> {
|
||||
slots: Vec<StaticType>,
|
||||
/// Types of captured variables (passed from outer scope)
|
||||
upvalue_types: Vec<StaticType>,
|
||||
/// Access to global types for unified resolution
|
||||
global_types: &'a std::cell::RefCell<HashMap<GlobalIdx, StaticType>>,
|
||||
/// Access to root types for unified resolution
|
||||
root_types: &'a std::cell::RefCell<Vec<StaticType>>,
|
||||
/// The expected parameters of the current function (for 'again' validation)
|
||||
current_params_ty: Option<StaticType>,
|
||||
}
|
||||
@@ -22,14 +21,14 @@ impl<'a> TypeContext<'a> {
|
||||
fn new(
|
||||
slot_count: u32,
|
||||
upvalue_types: Vec<StaticType>,
|
||||
global_types: &'a std::cell::RefCell<HashMap<GlobalIdx, StaticType>>,
|
||||
root_types: &'a std::cell::RefCell<Vec<StaticType>>,
|
||||
parent: Option<&'a TypeContext<'a>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
_parent: parent,
|
||||
slots: vec![StaticType::Any; slot_count as usize],
|
||||
upvalue_types,
|
||||
global_types,
|
||||
root_types,
|
||||
current_params_ty: None,
|
||||
}
|
||||
}
|
||||
@@ -42,9 +41,9 @@ impl<'a> TypeContext<'a> {
|
||||
.cloned()
|
||||
.unwrap_or(StaticType::Any),
|
||||
Address::Global(idx) => self
|
||||
.global_types
|
||||
.root_types
|
||||
.borrow()
|
||||
.get(&idx)
|
||||
.get(idx.0 as usize)
|
||||
.cloned()
|
||||
.unwrap_or(StaticType::Any),
|
||||
Address::Upvalue(idx) => self
|
||||
@@ -63,7 +62,10 @@ impl<'a> TypeContext<'a> {
|
||||
}
|
||||
}
|
||||
Address::Global(idx) => {
|
||||
self.global_types.borrow_mut().insert(idx, ty);
|
||||
let mut rt = self.root_types.borrow_mut();
|
||||
if (idx.0 as usize) < rt.len() {
|
||||
rt[idx.0 as usize] = ty;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -71,12 +73,12 @@ impl<'a> TypeContext<'a> {
|
||||
}
|
||||
|
||||
pub struct TypeChecker {
|
||||
global_types: Rc<std::cell::RefCell<HashMap<GlobalIdx, StaticType>>>,
|
||||
root_types: Rc<std::cell::RefCell<Vec<StaticType>>>,
|
||||
}
|
||||
|
||||
impl TypeChecker {
|
||||
pub fn new(global_types: Rc<std::cell::RefCell<HashMap<GlobalIdx, StaticType>>>) -> Self {
|
||||
Self { global_types }
|
||||
pub fn new(root_types: Rc<std::cell::RefCell<Vec<StaticType>>>) -> Self {
|
||||
Self { root_types }
|
||||
}
|
||||
|
||||
pub fn check(
|
||||
@@ -99,9 +101,9 @@ impl TypeChecker {
|
||||
}
|
||||
|
||||
// 2. Create the specialized context
|
||||
let root_ctx = TypeContext::new(0, vec![], &self.global_types, None);
|
||||
let root_ctx = TypeContext::new(0, vec![], &self.root_types, None);
|
||||
let mut lambda_ctx =
|
||||
TypeContext::new(64, upvalue_types, &self.global_types, Some(&root_ctx));
|
||||
TypeContext::new(64, upvalue_types, &self.root_types, Some(&root_ctx));
|
||||
|
||||
// 3. INJECT specialized argument types into slots
|
||||
let arg_tuple_ty = if arg_types.is_empty() {
|
||||
@@ -141,7 +143,7 @@ impl TypeChecker {
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let mut root_ctx = TypeContext::new(0, vec![], &self.global_types, None);
|
||||
let mut root_ctx = TypeContext::new(0, vec![], &self.root_types, None);
|
||||
self.check_node(node, &mut root_ctx, diag)
|
||||
}
|
||||
}
|
||||
@@ -484,7 +486,7 @@ impl TypeChecker {
|
||||
|
||||
// 2. Create nested context for lambda body
|
||||
let mut lambda_ctx =
|
||||
TypeContext::new(64, upvalue_types, ctx.global_types, Some(ctx));
|
||||
TypeContext::new(64, upvalue_types, ctx.root_types, Some(ctx));
|
||||
|
||||
// 3. Check parameters and body
|
||||
let params_typed = self.check_params(
|
||||
|
||||
Reference in New Issue
Block a user