about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2022-10-22T20·55+0300
committerclbot <clbot@tvl.fyi>2022-10-23T15·58+0000
commitd4569cb5046882dd5d4c8f3b187d167119186fa5 (patch)
tree21165efc7ac2f1f7d49435bdd480a66db9c16c1f
parent1050a1d650d6304a8fe68691c2eb6042b9c3ef00 (diff)
feat(tvix/eval): initial attempt at setting lambda names r/5186
When compiling a lambda, take the name of the outer slot (if
available) and store it as the name on the lambda.

These names are then shown in the observer, and nowhere else (so far).

It is of course common for these things to thread through many
different context levels (e.g. `f = a: b: c: ...`), in this setup only
the outermost closure or thunk gains the name, but it's better than
nothing.

Change-Id: I681ba74e624f2b9e7a147144a27acf364fe6ccc7
Reviewed-on: https://cl.tvl.fyi/c/depot/+/7065
Reviewed-by: grfn <grfn@gws.fyi>
Autosubmit: tazjin <tazjin@tvl.su>
Tested-by: BuildkiteCI
-rw-r--r--tvix/eval/src/compiler/mod.rs10
-rw-r--r--tvix/eval/src/compiler/scope.rs10
-rw-r--r--tvix/eval/src/observer.rs18
-rw-r--r--tvix/eval/src/value/function.rs16
4 files changed, 39 insertions, 15 deletions
diff --git a/tvix/eval/src/compiler/mod.rs b/tvix/eval/src/compiler/mod.rs
index 2ab7662386..2985c7e90e 100644
--- a/tvix/eval/src/compiler/mod.rs
+++ b/tvix/eval/src/compiler/mod.rs
@@ -54,7 +54,7 @@ struct LambdaCtx {
 impl LambdaCtx {
     fn new() -> Self {
         LambdaCtx {
-            lambda: Lambda::new_anonymous(),
+            lambda: Lambda::default(),
             scope: Default::default(),
             captures_with_stack: false,
         }
@@ -62,7 +62,7 @@ impl LambdaCtx {
 
     fn inherit(&self) -> Self {
         LambdaCtx {
-            lambda: Lambda::new_anonymous(),
+            lambda: Lambda::default(),
             scope: self.scope.inherit(),
             captures_with_stack: false,
         }
@@ -897,7 +897,13 @@ impl Compiler<'_> {
         N: ToSpan,
         F: FnOnce(&mut Compiler, LocalIdx),
     {
+        let name = self.scope()[outer_slot].name();
         self.new_context();
+
+        // Set the (optional) name of the current slot on the lambda that is
+        // being compiled.
+        self.context_mut().lambda.name = name;
+
         let span = self.span_for(node);
         let slot = self.scope_mut().declare_phantom(span, false);
         self.scope_mut().begin_scope();
diff --git a/tvix/eval/src/compiler/scope.rs b/tvix/eval/src/compiler/scope.rs
index 9269a3c44c..83fba6eed7 100644
--- a/tvix/eval/src/compiler/scope.rs
+++ b/tvix/eval/src/compiler/scope.rs
@@ -15,6 +15,8 @@ use std::{
     ops::Index,
 };
 
+use smol_str::SmolStr;
+
 use crate::opcode::{StackIdx, UpvalueIdx};
 
 #[derive(Debug)]
@@ -71,6 +73,14 @@ impl Local {
         }
     }
 
+    /// Retrieve the name of the given local (if available).
+    pub fn name(&self) -> Option<SmolStr> {
+        match &self.name {
+            LocalName::Phantom => None,
+            LocalName::Ident(name) => Some(SmolStr::new(name)),
+        }
+    }
+
     /// Is this local intentionally ignored? (i.e. name starts with `_`)
     pub fn is_ignored(&self) -> bool {
         match &self.name {
diff --git a/tvix/eval/src/observer.rs b/tvix/eval/src/observer.rs
index 74ed316a6a..1617d61c87 100644
--- a/tvix/eval/src/observer.rs
+++ b/tvix/eval/src/observer.rs
@@ -137,12 +137,22 @@ impl<W: Write> TracingObserver<W> {
 
 impl<W: Write> RuntimeObserver for TracingObserver<W> {
     fn observe_enter_frame(&mut self, arg_count: usize, lambda: &Rc<Lambda>, call_depth: usize) {
+        let _ = write!(&mut self.writer, "=== entering ");
+
+        let _ = if arg_count == 0 {
+            write!(&mut self.writer, "thunk ")
+        } else {
+            write!(&mut self.writer, "closure ")
+        };
+
+        if let Some(name) = &lambda.name {
+            let _ = write!(&mut self.writer, "'{}' ", name);
+        }
+
         let _ = writeln!(
             &mut self.writer,
-            "=== entering {} frame[{}] @ {:p} ===",
-            if arg_count == 0 { "thunk" } else { "closure" },
-            call_depth,
-            lambda,
+            "in frame[{}] @ {:p} ===",
+            call_depth, lambda
         );
     }
 
diff --git a/tvix/eval/src/value/function.rs b/tvix/eval/src/value/function.rs
index bb33e2962a..f282928df9 100644
--- a/tvix/eval/src/value/function.rs
+++ b/tvix/eval/src/value/function.rs
@@ -2,6 +2,7 @@
 use std::{collections::HashMap, hash::Hash, rc::Rc};
 
 use codemap::Span;
+use smol_str::SmolStr;
 
 use crate::{chunk::Chunk, upvalues::Upvalues};
 
@@ -38,10 +39,15 @@ impl Formals {
 /// OpThunkSuspended referencing it.  At runtime `Lambda` is usually wrapped
 /// in `Rc` to avoid copying the `Chunk` it holds (which can be
 /// quite large).
-#[derive(Debug, PartialEq)]
+#[derive(Debug, Default, PartialEq)]
 pub struct Lambda {
     pub(crate) chunk: Chunk,
 
+    /// Name of the function (equivalent to the name of the
+    /// identifier (e.g. a value in a let-expression or an attribute
+    /// set entry) it is located in).
+    pub(crate) name: Option<SmolStr>,
+
     /// Number of upvalues which the code in this Lambda closes
     /// over, and which need to be initialised at
     /// runtime.  Information about the variables is emitted using
@@ -51,14 +57,6 @@ pub struct Lambda {
 }
 
 impl Lambda {
-    pub fn new_anonymous() -> Self {
-        Lambda {
-            chunk: Default::default(),
-            upvalue_count: 0,
-            formals: None,
-        }
-    }
-
     pub fn chunk(&mut self) -> &mut Chunk {
         &mut self.chunk
     }