fix: SCC interscope reference

This commit is contained in:
2026-01-11 16:43:02 +08:00
parent 7d04d8262f
commit 75cb3bfaf1
4 changed files with 94 additions and 12 deletions

View File

@@ -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) =>

View File

@@ -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(&current) .dep_tracker_stack
&& tracker.let_scope_exprs.contains(&expr) .iter()
{ .position(|t| t.let_scope_exprs.contains(&expr));
let from = tracker.expr_to_node[&current];
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(&current_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;

View File

@@ -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>;
} }

View File

@@ -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)?;