refactor(tvix/eval): add compiler accessor for current scope

Change-Id: I7488087d95c1b3fb7f70fc29af0d5b0d0a25a428
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6245
Tested-by: BuildkiteCI
Reviewed-by: grfn <grfn@gws.fyi>
This commit is contained in:
Vincent Ambo 2022-08-23 23:57:36 +03:00 committed by tazjin
parent a060c8f467
commit 9d1451773b

View file

@ -33,7 +33,7 @@ pub struct CompilationResult {
pub errors: Vec<Error>, pub errors: Vec<Error>,
} }
// Represents a single local already known to the compiler. /// Represents a single local already known to the compiler.
struct Local { struct Local {
// Definition name, which can be different kinds of tokens (plain // Definition name, which can be different kinds of tokens (plain
// string or identifier). Nix does not allow dynamic names inside // string or identifier). Nix does not allow dynamic names inside
@ -106,6 +106,14 @@ impl Compiler {
.expect("compiler flaw: long-lived chunk reference") .expect("compiler flaw: long-lived chunk reference")
} }
fn scope(&self) -> &Scope {
&self.scope
}
fn scope_mut(&mut self) -> &mut Scope {
&mut self.scope
}
fn emit_constant(&mut self, value: Value) { fn emit_constant(&mut self, value: Value) {
let idx = self.chunk().push_constant(value); let idx = self.chunk().push_constant(value);
self.chunk().push_op(OpCode::OpConstant(idx)); self.chunk().push_op(OpCode::OpConstant(idx));
@ -658,9 +666,9 @@ impl Compiler {
// optimised information about any "weird" stuff that's // optimised information about any "weird" stuff that's
// happened to the scope (such as overrides of these // happened to the scope (such as overrides of these
// literals, or builtins). // literals, or builtins).
"true" if self.scope.poisoned_true == 0 => self.chunk().push_op(OpCode::OpTrue), "true" if self.scope().poisoned_true == 0 => self.chunk().push_op(OpCode::OpTrue),
"false" if self.scope.poisoned_false == 0 => self.chunk().push_op(OpCode::OpFalse), "false" if self.scope().poisoned_false == 0 => self.chunk().push_op(OpCode::OpFalse),
"null" if self.scope.poisoned_null == 0 => self.chunk().push_op(OpCode::OpNull), "null" if self.scope().poisoned_null == 0 => self.chunk().push_op(OpCode::OpNull),
name => { name => {
// Note: `with` and some other special scoping // Note: `with` and some other special scoping
@ -668,7 +676,7 @@ impl Compiler {
match self.resolve_local(name) { match self.resolve_local(name) {
Some(idx) => self.chunk().push_op(OpCode::OpGetLocal(idx)), Some(idx) => self.chunk().push_op(OpCode::OpGetLocal(idx)),
None => { None => {
if self.scope.with_stack.is_empty() { if self.scope().with_stack.is_empty() {
self.emit_error( self.emit_error(
node.syntax().clone(), node.syntax().clone(),
ErrorKind::UnknownStaticVariable, ErrorKind::UnknownStaticVariable,
@ -696,11 +704,10 @@ impl Compiler {
self.compile(node.namespace().unwrap()); self.compile(node.namespace().unwrap());
self.declare_phantom(); self.declare_phantom();
self.scope.with_stack.push(With { let depth = self.scope().scope_depth;
depth: self.scope.scope_depth, self.scope_mut().with_stack.push(With { depth });
});
let with_idx = self.scope.locals.len() - 1; let with_idx = self.scope().locals.len() - 1;
self.chunk().push_op(OpCode::OpPushWith(with_idx)); self.chunk().push_op(OpCode::OpPushWith(with_idx));
self.compile(node.body().unwrap()); self.compile(node.body().unwrap());
@ -729,25 +736,25 @@ impl Compiler {
} }
fn begin_scope(&mut self) { fn begin_scope(&mut self) {
self.scope.scope_depth += 1; self.scope_mut().scope_depth += 1;
} }
fn end_scope(&mut self) { fn end_scope(&mut self) {
debug_assert!(self.scope.scope_depth != 0, "can not end top scope"); debug_assert!(self.scope().scope_depth != 0, "can not end top scope");
// If this scope poisoned any builtins or special identifiers, // If this scope poisoned any builtins or special identifiers,
// they need to be reset. // they need to be reset.
if self.scope.poisoned_true == self.scope.scope_depth { if self.scope().poisoned_true == self.scope().scope_depth {
self.scope.poisoned_true = 0; self.scope_mut().poisoned_true = 0;
} }
if self.scope.poisoned_false == self.scope.scope_depth { if self.scope().poisoned_false == self.scope().scope_depth {
self.scope.poisoned_false = 0; self.scope_mut().poisoned_false = 0;
} }
if self.scope.poisoned_null == self.scope.scope_depth { if self.scope().poisoned_null == self.scope().scope_depth {
self.scope.poisoned_null = 0; self.scope_mut().poisoned_null = 0;
} }
self.scope.scope_depth -= 1; self.scope_mut().scope_depth -= 1;
// When ending a scope, all corresponding locals need to be // When ending a scope, all corresponding locals need to be
// removed, but the value of the body needs to remain on the // removed, but the value of the body needs to remain on the
@ -756,8 +763,8 @@ impl Compiler {
// TL;DR - iterate from the back while things belonging to the // TL;DR - iterate from the back while things belonging to the
// ended scope still exist. // ended scope still exist.
while !self.scope.locals.is_empty() while !self.scope().locals.is_empty()
&& self.scope.locals[self.scope.locals.len() - 1].depth > self.scope.scope_depth && self.scope().locals[self.scope().locals.len() - 1].depth > self.scope().scope_depth
{ {
pops += 1; pops += 1;
@ -768,7 +775,7 @@ impl Compiler {
node: Some(node), node: Some(node),
used, used,
.. ..
}) = self.scope.locals.pop() }) = self.scope_mut().locals.pop()
{ {
if !used { if !used {
self.emit_warning(node, WarningKind::UnusedBinding); self.emit_warning(node, WarningKind::UnusedBinding);
@ -780,11 +787,12 @@ impl Compiler {
self.chunk().push_op(OpCode::OpCloseScope(pops)); self.chunk().push_op(OpCode::OpCloseScope(pops));
} }
while !self.scope.with_stack.is_empty() while !self.scope().with_stack.is_empty()
&& self.scope.with_stack[self.scope.with_stack.len() - 1].depth > self.scope.scope_depth && self.scope().with_stack[self.scope().with_stack.len() - 1].depth
> self.scope().scope_depth
{ {
self.chunk().push_op(OpCode::OpPopWith); self.chunk().push_op(OpCode::OpPopWith);
self.scope.with_stack.pop(); self.scope_mut().with_stack.pop();
} }
} }
@ -794,43 +802,39 @@ impl Compiler {
fn declare_local<S: Into<String>>(&mut self, node: rnix::SyntaxNode, name: S) { fn declare_local<S: Into<String>>(&mut self, node: rnix::SyntaxNode, name: S) {
// Set up scope poisoning if required. // Set up scope poisoning if required.
let name = name.into(); let name = name.into();
let mut scope = self.scope_mut();
match name.as_str() { match name.as_str() {
"true" if self.scope.poisoned_true == 0 => { "true" if scope.poisoned_true == 0 => scope.poisoned_true = scope.scope_depth,
self.scope.poisoned_true = self.scope.scope_depth
}
"false" if self.scope.poisoned_false == 0 => { "false" if scope.poisoned_false == 0 => scope.poisoned_false = scope.scope_depth,
self.scope.poisoned_false = self.scope.scope_depth
}
"null" if self.scope.poisoned_null == 0 => { "null" if scope.poisoned_null == 0 => scope.poisoned_null = scope.scope_depth,
self.scope.poisoned_null = self.scope.scope_depth
}
_ => {} _ => {}
}; };
self.scope.locals.push(Local { scope.locals.push(Local {
name: name.into(), name: name.into(),
node: Some(node), node: Some(node),
depth: self.scope.scope_depth, depth: scope.scope_depth,
phantom: false, phantom: false,
used: false, used: false,
}); });
} }
fn declare_phantom(&mut self) { fn declare_phantom(&mut self) {
self.scope.locals.push(Local { let depth = self.scope().scope_depth;
self.scope_mut().locals.push(Local {
depth,
name: "".into(), name: "".into(),
node: None, node: None,
depth: self.scope.scope_depth,
phantom: true, phantom: true,
used: true, used: true,
}); });
} }
fn resolve_local(&mut self, name: &str) -> Option<usize> { fn resolve_local(&mut self, name: &str) -> Option<usize> {
let scope = &mut self.scope; let scope = self.scope_mut();
for (idx, local) in scope.locals.iter_mut().enumerate().rev() { for (idx, local) in scope.locals.iter_mut().enumerate().rev() {
if !local.phantom && local.name == name { if !local.phantom && local.name == name {