fix(tvix/eval): don't mark locals as used in resolve_local
resolve_local isn't exclusively used to compile variable access, so we shouldn't automatically mark a local as used when it's called. Specifically, the optimiser would call `is_user_defined`, causing locals to be marked as used even when they weren't, erroneously silencing the unused local variable warning. For resolve_upvalue I've opted not to do the same, instead renaming the function to make its behavior clearer. The reason for this is that resolve_upvalue is only used in the code for the purpose of compiling variable access and mark_used for upvalues would be needlessly expensive, as it requires recursing through enclosing contexts. Supersedes / alternative to cl/12708. Change-Id: Ib4a4a9dfcecf710ab2766b03d0a0cc09245c2d0a Reviewed-on: https://cl.tvl.fyi/c/depot/+/12869 Tested-by: BuildkiteCI Reviewed-by: tazjin <tazjin@tvl.su>
This commit is contained in:
parent
7ea442b21a
commit
bd4da5f444
2 changed files with 23 additions and 17 deletions
|
@ -716,7 +716,7 @@ impl Compiler<'_, '_> {
|
|||
match self.scope_mut().resolve_local(ident) {
|
||||
LocalPosition::Unknown => {
|
||||
// Are we possibly dealing with an upvalue?
|
||||
if let Some(idx) = self.resolve_upvalue(self.contexts.len() - 1, ident) {
|
||||
if let Some(idx) = self.resolve_upvalue_for_use(self.contexts.len() - 1, ident) {
|
||||
self.push_op(Op::GetUpvalue, node);
|
||||
self.push_uvarint(idx.0 as u64);
|
||||
return;
|
||||
|
@ -751,6 +751,8 @@ impl Compiler<'_, '_> {
|
|||
}
|
||||
|
||||
LocalPosition::Known(idx) => {
|
||||
self.scope_mut().mark_used(idx);
|
||||
|
||||
let stack_idx = self.scope().stack_index(idx);
|
||||
self.push_op(Op::GetLocal, node);
|
||||
self.push_uvarint(stack_idx.0 as u64);
|
||||
|
@ -758,12 +760,15 @@ impl Compiler<'_, '_> {
|
|||
|
||||
// This identifier is referring to a value from the same scope which
|
||||
// is not yet defined. This identifier access must be thunked.
|
||||
LocalPosition::Recursive(idx) => self.thunk(slot, node, move |compiler, _| {
|
||||
let upvalue_idx =
|
||||
compiler.add_upvalue(compiler.contexts.len() - 1, UpvalueKind::Local(idx));
|
||||
compiler.push_op(Op::GetUpvalue, node);
|
||||
compiler.push_uvarint(upvalue_idx.0 as u64);
|
||||
}),
|
||||
LocalPosition::Recursive(idx) => {
|
||||
self.scope_mut().mark_used(idx);
|
||||
self.thunk(slot, node, move |compiler, _| {
|
||||
let upvalue_idx =
|
||||
compiler.add_upvalue(compiler.contexts.len() - 1, UpvalueKind::Local(idx));
|
||||
compiler.push_op(Op::GetUpvalue, node);
|
||||
compiler.push_uvarint(upvalue_idx.0 as u64);
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -775,7 +780,8 @@ impl Compiler<'_, '_> {
|
|||
|
||||
/// Private compiler helpers related to bindings.
|
||||
impl Compiler<'_, '_> {
|
||||
fn resolve_upvalue(&mut self, ctx_idx: usize, name: &str) -> Option<UpvalueIdx> {
|
||||
// ATTN: Also marks local backing the upvalue as used if any
|
||||
fn resolve_upvalue_for_use(&mut self, ctx_idx: usize, name: &str) -> Option<UpvalueIdx> {
|
||||
if ctx_idx == 0 {
|
||||
// There can not be any upvalue at the outermost context.
|
||||
return None;
|
||||
|
@ -788,7 +794,8 @@ impl Compiler<'_, '_> {
|
|||
// stack (i.e. in the right position) *during* their runtime
|
||||
// construction
|
||||
LocalPosition::Known(idx) | LocalPosition::Recursive(idx) => {
|
||||
return Some(self.add_upvalue(ctx_idx, UpvalueKind::Local(idx)))
|
||||
self.contexts[ctx_idx - 1].scope.mark_used(idx);
|
||||
return Some(self.add_upvalue(ctx_idx, UpvalueKind::Local(idx)));
|
||||
}
|
||||
|
||||
LocalPosition::Unknown => { /* continue below */ }
|
||||
|
@ -796,7 +803,7 @@ impl Compiler<'_, '_> {
|
|||
|
||||
// If the upvalue comes from even further up, we need to recurse to make
|
||||
// sure that the upvalues are created at each level.
|
||||
if let Some(idx) = self.resolve_upvalue(ctx_idx - 1, name) {
|
||||
if let Some(idx) = self.resolve_upvalue_for_use(ctx_idx - 1, name) {
|
||||
return Some(self.add_upvalue(ctx_idx, UpvalueKind::Upvalue(idx)));
|
||||
}
|
||||
|
||||
|
|
|
@ -211,15 +211,10 @@ impl Scope {
|
|||
}
|
||||
|
||||
/// Resolve the stack index of a statically known local.
|
||||
pub fn resolve_local(&mut self, name: &str) -> LocalPosition {
|
||||
pub fn resolve_local(&self, name: &str) -> LocalPosition {
|
||||
if let Some(by_name) = self.by_name.get(name) {
|
||||
let idx = by_name.index();
|
||||
let local = self
|
||||
.locals
|
||||
.get_mut(idx.0)
|
||||
.expect("invalid compiler state: indexed local missing");
|
||||
|
||||
local.used = true;
|
||||
let local = &self.locals[idx.0];
|
||||
|
||||
// This local is still being initialised, meaning that
|
||||
// we know its final runtime stack position, but it is
|
||||
|
@ -309,6 +304,10 @@ impl Scope {
|
|||
self.locals[idx.0].initialised = true;
|
||||
}
|
||||
|
||||
pub fn mark_used(&mut self, idx: LocalIdx) {
|
||||
self.locals[idx.0].used = true;
|
||||
}
|
||||
|
||||
/// Mark local as needing a finaliser.
|
||||
pub fn mark_needs_finaliser(&mut self, idx: LocalIdx) {
|
||||
self.locals[idx.0].needs_finaliser = true;
|
||||
|
|
Loading…
Reference in a new issue