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(),
|
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
|
/// 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) {
|
fn compile_lambda(&mut self, slot: Option<LocalIdx>, node: ast::Lambda) {
|
||||||
// Open new lambda context in compiler, which has its own
|
self.new_context();
|
||||||
// scope etc.
|
|
||||||
self.contexts.push(LambdaCtx::new());
|
|
||||||
self.begin_scope();
|
self.begin_scope();
|
||||||
|
|
||||||
// Compile the function itself
|
// Compile the function itself
|
||||||
|
@ -912,7 +917,7 @@ impl Compiler<'_> {
|
||||||
N: AstNode + Clone,
|
N: AstNode + Clone,
|
||||||
F: FnOnce(&mut Compiler, &N, Option<LocalIdx>),
|
F: FnOnce(&mut Compiler, &N, Option<LocalIdx>),
|
||||||
{
|
{
|
||||||
self.contexts.push(LambdaCtx::new());
|
self.new_context();
|
||||||
self.begin_scope();
|
self.begin_scope();
|
||||||
content(self, node, slot);
|
content(self, node, slot);
|
||||||
self.end_scope(node);
|
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) {
|
fn begin_scope(&mut self) {
|
||||||
self.scope_mut().scope_depth += 1;
|
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) {
|
fn end_scope<N: AstNode>(&mut self, node: &N) {
|
||||||
debug_assert!(self.scope().scope_depth != 0, "can not end top scope");
|
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
|
/// Declare a local variable known in the scope that is being
|
||||||
/// compiled by pushing it to the locals. This is used to
|
/// compiled by pushing it to the locals. This is used to
|
||||||
/// determine the stack offset of variables.
|
/// 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.
|
/// Check whether a given token is poisoned.
|
||||||
pub fn is_poisoned(&self, name: &str) -> bool {
|
pub fn is_poisoned(&self, name: &str) -> bool {
|
||||||
self.poisoned_tokens.contains_key(name)
|
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