about summary refs log tree commit diff
path: root/tvix/eval/src/compiler/scope.rs
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2022-09-06T14·05+0300
committertazjin <tazjin@tvl.su>2022-09-11T12·04+0000
commit9da99af86045b59867e6082ca99d602308553006 (patch)
tree0f6c05cc114e2ca6f168bbf7770fbc4cc474824f /tvix/eval/src/compiler/scope.rs
parent27e69503a7374d7758a7c6145427265712d45f9c (diff)
refactor(tvix/eval): encapsulate scope cleanup logic in module r/4794
Moves the logic for removing tracked locals from a given scope from
the compiler's locals list, and leaves only the actual
compiler-related stuff (emitting warnings, cleaning up locals at
runtime) in the compiler itself.

Change-Id: I9da6eb54967f0a7775f624d602fe11be4c7ed5c4
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6466
Tested-by: BuildkiteCI
Reviewed-by: sterni <sternenseemann@systemli.org>
Diffstat (limited to 'tvix/eval/src/compiler/scope.rs')
-rw-r--r--tvix/eval/src/compiler/scope.rs49
1 files changed, 45 insertions, 4 deletions
diff --git a/tvix/eval/src/compiler/scope.rs b/tvix/eval/src/compiler/scope.rs
index b6fa61e7759b..7c1e65265e1d 100644
--- a/tvix/eval/src/compiler/scope.rs
+++ b/tvix/eval/src/compiler/scope.rs
@@ -188,11 +188,11 @@ impl Scope {
         self.poisoned_tokens.contains_key(name)
     }
 
-    /// "Unpoison" tokens that were poisoned at a given depth. Used
-    /// when scopes are closed.
-    pub fn unpoison(&mut self, depth: usize) {
+    /// "Unpoison" tokens that were poisoned at the current depth.
+    /// Used when scopes are closed.
+    fn unpoison(&mut self) {
         self.poisoned_tokens
-            .retain(|_, poisoned_at| *poisoned_at != depth);
+            .retain(|_, poisoned_at| *poisoned_at != self.scope_depth);
     }
 
     /// Increase the `with`-stack size of this scope.
@@ -284,4 +284,45 @@ impl Scope {
 
         StackIdx(idx.0 - uninitialised_count)
     }
+
+    /// Decrease the scope depth and remove all locals still tracked
+    /// for the current scope.
+    ///
+    /// Returns the count of locals that were dropped while marked as
+    /// initialised (used by the compiler to determine whether to emit
+    /// scope cleanup operations), as well as the spans of the
+    /// definitions of unused locals (used by the compiler to emit
+    /// unused binding warnings).
+    pub fn end_scope(&mut self) -> (usize, Vec<codemap::Span>) {
+        debug_assert!(self.scope_depth != 0, "can not end top scope");
+
+        // If this scope poisoned any builtins or special identifiers,
+        // they need to be reset.
+        self.unpoison();
+
+        let mut pops = 0;
+        let mut unused_spans = vec![];
+
+        // TL;DR - iterate from the back while things belonging to the
+        // ended scope still exist.
+        while self.locals.last().unwrap().depth == self.scope_depth {
+            if let Some(local) = self.locals.pop() {
+                // pop the local from the stack if it was actually
+                // initialised
+                if local.initialised {
+                    pops += 1;
+                }
+
+                // analyse whether the local was accessed during its
+                // lifetime, and emit a warning otherwise (unless the
+                // user explicitly chose to ignore it by prefixing the
+                // identifier with `_`)
+                if !local.used && !local.is_ignored() {
+                    unused_spans.push(local.span);
+                }
+            }
+        }
+
+        (pops, unused_spans)
+    }
 }