fix: SCC interscope reference
This commit is contained in:
@@ -2,10 +2,10 @@
|
|||||||
* String operation builtin functions
|
* String operation builtin functions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { NixValue } from "../types";
|
import type { NixInt, NixValue } from "../types";
|
||||||
import { forceString, forceList, forceInt } from "../type-assert";
|
import { forceString, forceList, forceInt } from "../type-assert";
|
||||||
|
|
||||||
export const stringLength = (e: NixValue): number => forceString(e).length;
|
export const stringLength = (e: NixValue): NixInt => BigInt(forceString(e).length);
|
||||||
|
|
||||||
export const substring =
|
export const substring =
|
||||||
(start: NixValue) =>
|
(start: NixValue) =>
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ struct DependencyTracker {
|
|||||||
graph: Graph<ExprId, (), Directed>,
|
graph: Graph<ExprId, (), Directed>,
|
||||||
current_binding: Option<ExprId>,
|
current_binding: Option<ExprId>,
|
||||||
let_scope_exprs: HashSet<ExprId>,
|
let_scope_exprs: HashSet<ExprId>,
|
||||||
|
// The outer binding that owns this tracker (for nested let scopes in function params)
|
||||||
|
owner_binding: Option<ExprId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Scope<'ctx> {
|
enum Scope<'ctx> {
|
||||||
@@ -90,14 +92,44 @@ impl DowngradeContext for DowngradeCtx<'_> {
|
|||||||
}
|
}
|
||||||
Scope::Let(let_scope) => {
|
Scope::Let(let_scope) => {
|
||||||
if let Some(&expr) = let_scope.get(&sym) {
|
if let Some(&expr) = let_scope.get(&sym) {
|
||||||
if let Some(tracker) = self.dep_tracker_stack.last_mut()
|
// Find which tracker contains this expression
|
||||||
&& let Some(current) = tracker.current_binding
|
let expr_tracker_idx = self
|
||||||
&& tracker.let_scope_exprs.contains(¤t)
|
.dep_tracker_stack
|
||||||
&& tracker.let_scope_exprs.contains(&expr)
|
.iter()
|
||||||
{
|
.position(|t| t.let_scope_exprs.contains(&expr));
|
||||||
let from = tracker.expr_to_node[¤t];
|
|
||||||
let to = tracker.expr_to_node[&expr];
|
// Find the innermost tracker with a current_binding
|
||||||
tracker.graph.add_edge(from, to, ());
|
let current_tracker_idx = self
|
||||||
|
.dep_tracker_stack
|
||||||
|
.iter()
|
||||||
|
.rposition(|t| t.current_binding.is_some());
|
||||||
|
|
||||||
|
// Record dependency if both exist
|
||||||
|
if let (Some(expr_idx), Some(curr_idx)) = (expr_tracker_idx, current_tracker_idx) {
|
||||||
|
let current_binding = self.dep_tracker_stack[curr_idx].current_binding.unwrap();
|
||||||
|
let owner_binding = self.dep_tracker_stack[curr_idx].owner_binding;
|
||||||
|
|
||||||
|
// If referencing from inner scope to outer scope
|
||||||
|
if curr_idx >= expr_idx {
|
||||||
|
let tracker = &mut self.dep_tracker_stack[expr_idx];
|
||||||
|
if let (Some(&from_node), Some(&to_node)) = (
|
||||||
|
tracker.expr_to_node.get(¤t_binding),
|
||||||
|
tracker.expr_to_node.get(&expr),
|
||||||
|
) {
|
||||||
|
// Same-level reference: record directly
|
||||||
|
tracker.graph.add_edge(from_node, to_node, ());
|
||||||
|
} else if curr_idx > expr_idx {
|
||||||
|
// Cross-scope reference: use owner_binding if available
|
||||||
|
if let Some(owner) = owner_binding {
|
||||||
|
if let (Some(&from_node), Some(&to_node)) = (
|
||||||
|
tracker.expr_to_node.get(&owner),
|
||||||
|
tracker.expr_to_node.get(&expr),
|
||||||
|
) {
|
||||||
|
tracker.graph.add_edge(from_node, to_node, ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(self.new_expr(Ir::ExprRef(expr)));
|
return Ok(self.new_expr(Ir::ExprRef(expr)));
|
||||||
@@ -216,9 +248,34 @@ impl DowngradeContext for DowngradeCtx<'_> {
|
|||||||
graph,
|
graph,
|
||||||
current_binding: None,
|
current_binding: None,
|
||||||
let_scope_exprs,
|
let_scope_exprs,
|
||||||
|
owner_binding: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn push_dep_tracker_with_owner(&mut self, slots: &[ExprId], owner: ExprId) {
|
||||||
|
let mut graph = Graph::new();
|
||||||
|
let mut expr_to_node = HashMap::new();
|
||||||
|
let mut let_scope_exprs = HashSet::new();
|
||||||
|
|
||||||
|
for &expr in slots.iter() {
|
||||||
|
let node = graph.add_node(expr);
|
||||||
|
expr_to_node.insert(expr, node);
|
||||||
|
let_scope_exprs.insert(expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.dep_tracker_stack.push(DependencyTracker {
|
||||||
|
expr_to_node,
|
||||||
|
graph,
|
||||||
|
current_binding: None,
|
||||||
|
let_scope_exprs,
|
||||||
|
owner_binding: Some(owner),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_current_binding(&self) -> Option<ExprId> {
|
||||||
|
self.dep_tracker_stack.last().and_then(|t| t.current_binding)
|
||||||
|
}
|
||||||
|
|
||||||
fn set_current_binding(&mut self, expr: Option<ExprId>) {
|
fn set_current_binding(&mut self, expr: Option<ExprId>) {
|
||||||
if let Some(tracker) = self.dep_tracker_stack.last_mut() {
|
if let Some(tracker) = self.dep_tracker_stack.last_mut() {
|
||||||
tracker.current_binding = expr;
|
tracker.current_binding = expr;
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ pub trait DowngradeContext {
|
|||||||
fn get_current_dir(&self) -> std::path::PathBuf;
|
fn get_current_dir(&self) -> std::path::PathBuf;
|
||||||
|
|
||||||
fn push_dep_tracker(&mut self, slots: &[ExprId]);
|
fn push_dep_tracker(&mut self, slots: &[ExprId]);
|
||||||
|
fn push_dep_tracker_with_owner(&mut self, slots: &[ExprId], owner: ExprId);
|
||||||
|
fn get_current_binding(&self) -> Option<ExprId>;
|
||||||
fn set_current_binding(&mut self, expr: Option<ExprId>);
|
fn set_current_binding(&mut self, expr: Option<ExprId>);
|
||||||
fn pop_dep_tracker(&mut self) -> Result<SccInfo>;
|
fn pop_dep_tracker(&mut self) -> Result<SccInfo>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,7 +283,10 @@ where
|
|||||||
Some(param_syms.iter().copied().collect())
|
Some(param_syms.iter().copied().collect())
|
||||||
};
|
};
|
||||||
|
|
||||||
let (scc_info, body) = downgrade_bindings_generic(
|
// Get the owner from outer tracker's current_binding
|
||||||
|
let owner = ctx.get_current_binding();
|
||||||
|
|
||||||
|
let (scc_info, body) = downgrade_bindings_generic_with_owner(
|
||||||
ctx,
|
ctx,
|
||||||
binding_keys,
|
binding_keys,
|
||||||
|ctx, sym_to_slot| {
|
|ctx, sym_to_slot| {
|
||||||
@@ -318,6 +321,7 @@ where
|
|||||||
Ok(bindings)
|
Ok(bindings)
|
||||||
},
|
},
|
||||||
body_fn,
|
body_fn,
|
||||||
|
owner, // Pass the owner to track cross-scope dependencies
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(PatternBindings {
|
Ok(PatternBindings {
|
||||||
@@ -343,6 +347,21 @@ pub fn downgrade_bindings_generic<Ctx, B, F>(
|
|||||||
compute_bindings_fn: B,
|
compute_bindings_fn: B,
|
||||||
body_fn: F,
|
body_fn: F,
|
||||||
) -> Result<(SccInfo, ExprId)>
|
) -> Result<(SccInfo, ExprId)>
|
||||||
|
where
|
||||||
|
Ctx: DowngradeContext,
|
||||||
|
B: FnOnce(&mut Ctx, &HashMap<SymId, ExprId>) -> Result<HashMap<SymId, ExprId>>,
|
||||||
|
F: FnOnce(&mut Ctx, &[SymId]) -> Result<ExprId>,
|
||||||
|
{
|
||||||
|
downgrade_bindings_generic_with_owner(ctx, binding_keys, compute_bindings_fn, body_fn, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn downgrade_bindings_generic_with_owner<Ctx, B, F>(
|
||||||
|
ctx: &mut Ctx,
|
||||||
|
binding_keys: Vec<SymId>,
|
||||||
|
compute_bindings_fn: B,
|
||||||
|
body_fn: F,
|
||||||
|
owner: Option<ExprId>,
|
||||||
|
) -> Result<(SccInfo, ExprId)>
|
||||||
where
|
where
|
||||||
Ctx: DowngradeContext,
|
Ctx: DowngradeContext,
|
||||||
B: FnOnce(&mut Ctx, &HashMap<SymId, ExprId>) -> Result<HashMap<SymId, ExprId>>,
|
B: FnOnce(&mut Ctx, &HashMap<SymId, ExprId>) -> Result<HashMap<SymId, ExprId>>,
|
||||||
@@ -355,7 +374,11 @@ where
|
|||||||
.zip(slots.iter().copied())
|
.zip(slots.iter().copied())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
ctx.push_dep_tracker(&slots);
|
if let Some(owner_binding) = owner {
|
||||||
|
ctx.push_dep_tracker_with_owner(&slots, owner_binding);
|
||||||
|
} else {
|
||||||
|
ctx.push_dep_tracker(&slots);
|
||||||
|
}
|
||||||
|
|
||||||
ctx.with_let_scope(let_bindings.clone(), |ctx| {
|
ctx.with_let_scope(let_bindings.clone(), |ctx| {
|
||||||
let bindings = compute_bindings_fn(ctx, &let_bindings)?;
|
let bindings = compute_bindings_fn(ctx, &let_bindings)?;
|
||||||
|
|||||||
Reference in New Issue
Block a user