Refactor: Remove explicit upvalue analysis pass
The `UpvalueAnalyzer` pass is no longer necessary as the binder now directly tracks captures. This commit removes the `UpvalueAnalyzer` struct and associated logic from `src/ast/compiler/upvalues.rs`. The `Binder::bind_root` function now returns the `captures` map, which is then processed by a new `CapturePass` in `src/ast/environment.rs` before type checking. This consolidates capture logic within the binder and its subsequent processing steps.
This commit is contained in:
+58
-54
@@ -8,6 +8,7 @@ use std::rc::Rc;
|
||||
#[derive(Debug, Clone)]
|
||||
struct LocalInfo {
|
||||
slot: u32,
|
||||
identity: Identity,
|
||||
// Note: Binder doesn't strictly need the type anymore,
|
||||
// but it might be useful for built-ins during resolution.
|
||||
// For now we keep it as Any or Unknown.
|
||||
@@ -28,7 +29,7 @@ impl CompilerScope {
|
||||
}
|
||||
}
|
||||
|
||||
fn define(&mut self, sym: &Symbol) -> Result<u32, String> {
|
||||
fn define(&mut self, sym: &Symbol, identity: Identity) -> Result<u32, String> {
|
||||
if self.locals.contains_key(sym) {
|
||||
return Err(format!(
|
||||
"Variable '{}' is already defined in this scope level.",
|
||||
@@ -40,6 +41,7 @@ impl CompilerScope {
|
||||
sym.clone(),
|
||||
LocalInfo {
|
||||
slot,
|
||||
identity,
|
||||
_ty: StaticType::Any,
|
||||
},
|
||||
);
|
||||
@@ -59,14 +61,16 @@ enum ScopeKind {
|
||||
}
|
||||
|
||||
struct FunctionCompiler {
|
||||
identity: Identity,
|
||||
scope: CompilerScope,
|
||||
upvalues: Vec<Address>,
|
||||
kind: ScopeKind,
|
||||
}
|
||||
|
||||
impl FunctionCompiler {
|
||||
fn new(kind: ScopeKind) -> Self {
|
||||
fn new(kind: ScopeKind, identity: Identity) -> Self {
|
||||
Self {
|
||||
identity,
|
||||
scope: CompilerScope::new(),
|
||||
upvalues: Vec::new(),
|
||||
kind,
|
||||
@@ -76,7 +80,8 @@ impl FunctionCompiler {
|
||||
fn define_variable(
|
||||
&mut self,
|
||||
name: &Symbol,
|
||||
globals: &Rc<RefCell<HashMap<Symbol, u32>>>,
|
||||
identity: Identity,
|
||||
globals: &Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>,
|
||||
) -> Result<Address, String> {
|
||||
match self.kind {
|
||||
ScopeKind::Root => {
|
||||
@@ -88,11 +93,11 @@ impl FunctionCompiler {
|
||||
));
|
||||
}
|
||||
let idx = globals_map.len() as u32;
|
||||
globals_map.insert(name.clone(), idx);
|
||||
globals_map.insert(name.clone(), (idx, identity));
|
||||
Ok(Address::Global(idx))
|
||||
}
|
||||
ScopeKind::Local => {
|
||||
let slot = self.scope.define(name)?;
|
||||
let slot = self.scope.define(name, identity)?;
|
||||
Ok(Address::Local(slot))
|
||||
}
|
||||
}
|
||||
@@ -110,48 +115,53 @@ impl FunctionCompiler {
|
||||
|
||||
pub struct Binder {
|
||||
functions: Vec<FunctionCompiler>,
|
||||
// Globals mapping: Symbol -> Index
|
||||
globals: Rc<RefCell<HashMap<Symbol, u32>>>,
|
||||
// Globals mapping: Symbol -> (Index, DefinitionIdentity)
|
||||
globals: Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>,
|
||||
// Map of Declaration Identity -> List of Lambda Identities that capture it
|
||||
capture_map: HashMap<Identity, Vec<Identity>>,
|
||||
capture_map: HashMap<Identity, std::collections::HashSet<Identity>>,
|
||||
}
|
||||
|
||||
impl Binder {
|
||||
pub fn new(globals: Rc<RefCell<HashMap<Symbol, u32>>>) -> Self {
|
||||
Self::with_boxed(globals, HashMap::new())
|
||||
}
|
||||
|
||||
fn with_boxed(
|
||||
globals: Rc<RefCell<HashMap<Symbol, u32>>>,
|
||||
captures: HashMap<Identity, Vec<Identity>>,
|
||||
) -> Self {
|
||||
pub fn new(globals: Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>) -> Self {
|
||||
let mut binder = Self {
|
||||
functions: Vec::new(),
|
||||
globals,
|
||||
capture_map: captures,
|
||||
capture_map: HashMap::new(),
|
||||
};
|
||||
binder
|
||||
.functions
|
||||
.push(FunctionCompiler::new(ScopeKind::Root));
|
||||
binder.functions.push(FunctionCompiler::new(
|
||||
ScopeKind::Root,
|
||||
Rc::new(crate::ast::types::NodeIdentity {
|
||||
location: crate::ast::types::SourceLocation { line: 0, col: 0 },
|
||||
}),
|
||||
));
|
||||
binder
|
||||
}
|
||||
|
||||
pub fn bind_root(
|
||||
globals: Rc<RefCell<HashMap<Symbol, u32>>>,
|
||||
globals: Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>,
|
||||
node: &Node<UntypedKind>,
|
||||
) -> Result<BoundNode, String> {
|
||||
let captures = crate::ast::compiler::upvalues::UpvalueAnalyzer::analyze(node);
|
||||
let mut binder = Self::with_boxed(globals, captures);
|
||||
binder.bind(node)
|
||||
) -> Result<(BoundNode, HashMap<Identity, Vec<Identity>>), String> {
|
||||
let mut binder = Self::new(globals);
|
||||
let bound = binder.bind(node)?;
|
||||
|
||||
// Convert HashSet to sorted Vec
|
||||
let final_captures = binder
|
||||
.capture_map
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, v.into_iter().collect()))
|
||||
.collect();
|
||||
|
||||
Ok((bound, final_captures))
|
||||
}
|
||||
|
||||
fn declare_variable(
|
||||
&mut self,
|
||||
name: &Symbol,
|
||||
identity: Identity,
|
||||
_kind: crate::ast::compiler::bound_nodes::DeclarationKind,
|
||||
) -> Result<Address, String> {
|
||||
let current_fn = self.functions.last_mut().unwrap();
|
||||
current_fn.define_variable(name, &self.globals)
|
||||
current_fn.define_variable(name, identity, &self.globals)
|
||||
}
|
||||
|
||||
pub fn bind(&mut self, node: &Node<UntypedKind>) -> Result<BoundNode, String> {
|
||||
@@ -203,14 +213,10 @@ impl Binder {
|
||||
if let UntypedKind::Parameter(ref name) = target.kind {
|
||||
let addr = self.declare_variable(
|
||||
name,
|
||||
node.identity.clone(), // Identity of the Def node
|
||||
crate::ast::compiler::bound_nodes::DeclarationKind::Variable,
|
||||
)?;
|
||||
let val_node = self.bind(value)?;
|
||||
let captured_by = self
|
||||
.capture_map
|
||||
.get(&target.identity)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(self.make_node(
|
||||
node.identity.clone(),
|
||||
@@ -219,7 +225,7 @@ impl Binder {
|
||||
addr,
|
||||
kind: crate::ast::compiler::bound_nodes::DeclarationKind::Variable,
|
||||
value: Box::new(val_node),
|
||||
captured_by,
|
||||
captured_by: Vec::new(), // Will be filled by post-pass
|
||||
},
|
||||
))
|
||||
} else {
|
||||
@@ -265,7 +271,10 @@ impl Binder {
|
||||
|
||||
UntypedKind::Lambda { params, body } => {
|
||||
let identity = node.identity.clone();
|
||||
self.functions.push(FunctionCompiler::new(ScopeKind::Local));
|
||||
self.functions.push(FunctionCompiler::new(
|
||||
ScopeKind::Local,
|
||||
identity.clone(),
|
||||
));
|
||||
|
||||
// 1. Bind the parameter pattern/tuple
|
||||
let params_bound = self.bind_pattern(params, DeclarationKind::Parameter)?;
|
||||
@@ -409,7 +418,13 @@ impl Binder {
|
||||
if let Some(info) = self.functions[i].scope.resolve(sym) {
|
||||
let mut addr = Address::Local(info.slot);
|
||||
|
||||
// Record the capture for each lambda level in between
|
||||
for k in (i + 1)..=current_fn_idx {
|
||||
let lambda_id = self.functions[k].identity.clone();
|
||||
self.capture_map
|
||||
.entry(info.identity.clone())
|
||||
.or_default()
|
||||
.insert(lambda_id);
|
||||
addr = Address::Upvalue(self.functions[k].add_upvalue(addr));
|
||||
}
|
||||
return Ok(addr);
|
||||
@@ -418,7 +433,7 @@ impl Binder {
|
||||
|
||||
// 3. Try Global
|
||||
let globals = self.globals.borrow();
|
||||
if let Some(idx) = globals.get(sym) {
|
||||
if let Some((idx, _)) = globals.get(sym) {
|
||||
return Ok(Address::Global(*idx));
|
||||
}
|
||||
|
||||
@@ -428,7 +443,7 @@ impl Binder {
|
||||
name: sym.name.clone(),
|
||||
context: None,
|
||||
};
|
||||
if let Some(idx) = globals.get(&fallback_sym) {
|
||||
if let Some((idx, _)) = globals.get(&fallback_sym) {
|
||||
return Ok(Address::Global(*idx));
|
||||
}
|
||||
}
|
||||
@@ -443,12 +458,7 @@ impl Binder {
|
||||
) -> Result<BoundNode, String> {
|
||||
match &node.kind {
|
||||
UntypedKind::Parameter(sym) => {
|
||||
let addr = self.declare_variable(sym, kind)?;
|
||||
let captured_by = self
|
||||
.capture_map
|
||||
.get(&node.identity)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let addr = self.declare_variable(sym, node.identity.clone(), kind)?;
|
||||
|
||||
Ok(self.make_node(
|
||||
node.identity.clone(),
|
||||
@@ -457,7 +467,7 @@ impl Binder {
|
||||
addr,
|
||||
kind,
|
||||
value: Box::new(self.make_node(node.identity.clone(), BoundKind::Nop)),
|
||||
captured_by,
|
||||
captured_by: Vec::new(), // Filled by post-pass
|
||||
},
|
||||
))
|
||||
}
|
||||
@@ -530,19 +540,16 @@ mod tests {
|
||||
let untyped = parser.parse_expression().unwrap();
|
||||
|
||||
let globals = Rc::new(RefCell::new(HashMap::new()));
|
||||
let bound = Binder::bind_root(globals, &untyped).unwrap();
|
||||
let (bound, captures) = Binder::bind_root(globals, &untyped).unwrap();
|
||||
|
||||
// Structure: Lambda -> Block -> [ Define(x), Define(f), Get(x) ]
|
||||
if let BoundKind::Lambda { body, .. } = &bound.kind {
|
||||
if let BoundKind::Block { exprs } = &body.kind {
|
||||
let x_decl = &exprs[0];
|
||||
if let BoundKind::Define {
|
||||
captured_by, addr, ..
|
||||
} = &x_decl.kind
|
||||
{
|
||||
if let BoundKind::Define { addr, .. } = &x_decl.kind {
|
||||
assert!(matches!(addr, Address::Local(_)));
|
||||
assert!(
|
||||
!captured_by.is_empty(),
|
||||
captures.contains_key(&x_decl.identity),
|
||||
"Variable 'x' should have capturers because it is used in lambda 'f'"
|
||||
);
|
||||
} else {
|
||||
@@ -566,18 +573,15 @@ mod tests {
|
||||
let untyped = parser.parse_expression().unwrap();
|
||||
|
||||
let globals = Rc::new(RefCell::new(HashMap::new()));
|
||||
let bound = Binder::bind_root(globals, &untyped).unwrap();
|
||||
let (bound, captures) = Binder::bind_root(globals, &untyped).unwrap();
|
||||
|
||||
if let BoundKind::Lambda { body, .. } = &bound.kind {
|
||||
if let BoundKind::Block { exprs } = &body.kind {
|
||||
let x_decl = &exprs[0];
|
||||
if let BoundKind::Define {
|
||||
captured_by, addr, ..
|
||||
} = &x_decl.kind
|
||||
{
|
||||
if let BoundKind::Define { addr, .. } = &x_decl.kind {
|
||||
assert!(matches!(addr, Address::Local(_)));
|
||||
assert!(
|
||||
captured_by.is_empty(),
|
||||
!captures.contains_key(&x_decl.identity),
|
||||
"Variable 'x' should NOT have any capturers"
|
||||
);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user