fix(tvix/eval): inherit scope poisoning data in nested contexts
Scope poisoning must be inherited across lambda context boundaries, e.g. if an outer scope has a poisoned `null`, any lambdas defined on the same level must reference that poisoned identifier correctly. Change-Id: I1aac64e1c048a6f3bacadb6d78ed295fa439e8b4 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6410 Reviewed-by: sterni <sternenseemann@systemli.org> Tested-by: BuildkiteCI
This commit is contained in:
parent
0a13d267f0
commit
5ee89bcf5c
4 changed files with 36 additions and 4 deletions
|
@ -53,6 +53,13 @@ impl LambdaCtx {
|
|||
scope: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn inherit(&self) -> Self {
|
||||
LambdaCtx {
|
||||
lambda: Lambda::new_anonymous(),
|
||||
scope: self.scope.inherit(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Alias for the map of globally available functions that should
|
||||
|
@ -836,9 +843,7 @@ impl Compiler<'_> {
|
|||
}
|
||||
|
||||
fn compile_lambda(&mut self, slot: Option<LocalIdx>, node: ast::Lambda) {
|
||||
// Open new lambda context in compiler, which has its own
|
||||
// scope etc.
|
||||
self.contexts.push(LambdaCtx::new());
|
||||
self.new_context();
|
||||
self.begin_scope();
|
||||
|
||||
// Compile the function itself
|
||||
|
@ -912,7 +917,7 @@ impl Compiler<'_> {
|
|||
N: AstNode + Clone,
|
||||
F: FnOnce(&mut Compiler, &N, Option<LocalIdx>),
|
||||
{
|
||||
self.contexts.push(LambdaCtx::new());
|
||||
self.new_context();
|
||||
self.begin_scope();
|
||||
content(self, node, slot);
|
||||
self.end_scope(node);
|
||||
|
@ -1011,10 +1016,14 @@ impl Compiler<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Increase the scope depth of the current function (e.g. within
|
||||
/// a new bindings block, or `with`-scope).
|
||||
fn begin_scope(&mut self) {
|
||||
self.scope_mut().scope_depth += 1;
|
||||
}
|
||||
|
||||
/// Decrease scope depth of the current function and emit
|
||||
/// instructions to clean up the stack at runtime.
|
||||
fn end_scope<N: AstNode>(&mut self, node: &N) {
|
||||
debug_assert!(self.scope().scope_depth != 0, "can not end top scope");
|
||||
|
||||
|
@ -1055,6 +1064,15 @@ impl Compiler<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Open a new lambda context within which to compile a function,
|
||||
/// closure or thunk.
|
||||
fn new_context(&mut self) {
|
||||
// This must inherit the scope-poisoning status of the parent
|
||||
// in order for upvalue resolution to work correctly with
|
||||
// poisoned identifiers.
|
||||
self.contexts.push(self.context().inherit());
|
||||
}
|
||||
|
||||
/// Declare a local variable known in the scope that is being
|
||||
/// compiled by pushing it to the locals. This is used to
|
||||
/// determine the stack offset of variables.
|
||||
|
|
|
@ -152,6 +152,15 @@ impl Scope {
|
|||
}
|
||||
}
|
||||
|
||||
/// Inherit scope details from a parent scope (required for
|
||||
/// correctly nesting scopes in lambdas and thunks when special
|
||||
/// scope features like poisoning are present).
|
||||
pub fn inherit(&self) -> Self {
|
||||
let mut scope = Self::default();
|
||||
scope.poisoned_tokens = self.poisoned_tokens.clone();
|
||||
scope
|
||||
}
|
||||
|
||||
/// Check whether a given token is poisoned.
|
||||
pub fn is_poisoned(&self, name: &str) -> bool {
|
||||
self.poisoned_tokens.contains_key(name)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
42
|
|
@ -0,0 +1,4 @@
|
|||
let
|
||||
null = 1;
|
||||
f = n: n + null;
|
||||
in f 41
|
Loading…
Reference in a new issue