about summary refs log tree commit diff
diff options
context:
space:
mode:
authorsterni <sternenseemann@systemli.org>2022-10-15T14·42+0200
committerclbot <clbot@tvl.fyi>2022-10-16T19·11+0000
commit4dcb8f38c2e442b6433b21b8a7de303736a04568 (patch)
treeb47d780f78d6522926e7842013e436aee0b44222
parent0624d78af0839cd8b290eb9cb6e5737f01162b96 (diff)
fix(tvix/eval): resolve home relative paths at runtime r/5147
Home relative paths depend on the environment to be resolved. We have
elected to do everything that depends on the environment, e.g. resolving
SPATH expressions using NIX_PATH, at runtime, so tvix evaluation would
continue to behave correctly even if we separated the compilation and
execution phases more, e.g. via serializing bytecode. Then the value of
HOME, NIX_PATH etc. could reasonably change in the time until execution,
yielding wrong results if the resolution results were cached in the
bytecode.

We also take the opportunity to fix the broken path concatenation
previously found in the compiler, fixing b/205.

Another thing we could consider is emitting a warning for home relative
path literals, as they are by nature relatively fragile.

One sideeffect of this change is that home path resolution errors
become catchable which is not the case in C++ Nix. This will need to be
fixed up in a subsequent change.

Change-Id: I30bd69b575667c49170a9fdea23a020565d0f9ec
Reviewed-on: https://cl.tvl.fyi/c/depot/+/7024
Autosubmit: sterni <sternenseemann@systemli.org>
Reviewed-by: Adam Joseph <adam@westernsemico.com>
Tested-by: BuildkiteCI
-rw-r--r--tvix/eval/src/compiler/mod.rs20
-rw-r--r--tvix/eval/src/opcode.rs3
-rw-r--r--tvix/eval/src/vm.rs20
3 files changed, 31 insertions, 12 deletions
diff --git a/tvix/eval/src/compiler/mod.rs b/tvix/eval/src/compiler/mod.rs
index 11537e76b5c9..95de69487071 100644
--- a/tvix/eval/src/compiler/mod.rs
+++ b/tvix/eval/src/compiler/mod.rs
@@ -268,19 +268,15 @@ impl Compiler<'_> {
         let path = if raw_path.starts_with('/') {
             Path::new(&raw_path).to_owned()
         } else if raw_path.starts_with('~') {
-            let mut buf = match dirs::home_dir() {
-                Some(buf) => buf,
-                None => {
-                    self.emit_error(
-                        node,
-                        ErrorKind::PathResolution("failed to determine home directory".into()),
-                    );
-                    return;
-                }
-            };
+            return self.thunk(slot, node, move |c, _| {
+                // We assume that paths that home paths start with ~/ or fail to parse
+                // TODO: this should be checked using a parse-fail test.
+                debug_assert!(raw_path.len() > 2 && raw_path.starts_with("~/"));
 
-            buf.push(&raw_path);
-            buf
+                let home_relative_path = &raw_path[2..(raw_path.len())];
+                c.emit_constant(Value::UnresolvedPath(home_relative_path.into()), node);
+                c.push_op(OpCode::OpResolveHomePath, node);
+            });
         } else if raw_path.starts_with('.') {
             let mut buf = self.root_dir.clone();
             buf.push(&raw_path);
diff --git a/tvix/eval/src/opcode.rs b/tvix/eval/src/opcode.rs
index fdc916ca7f79..70ef1a3b5324 100644
--- a/tvix/eval/src/opcode.rs
+++ b/tvix/eval/src/opcode.rs
@@ -117,6 +117,9 @@ pub enum OpCode {
     /// [`NixSearchPath`]: crate::nix_search_path::NixSearchPath
     OpFindFile,
 
+    /// Attempt to resolve a path literal relative to the home dir
+    OpResolveHomePath,
+
     // Type assertion operators
     OpAssertBool,
 
diff --git a/tvix/eval/src/vm.rs b/tvix/eval/src/vm.rs
index 05adec5f6ea6..7fcdb9ea739b 100644
--- a/tvix/eval/src/vm.rs
+++ b/tvix/eval/src/vm.rs
@@ -536,6 +536,26 @@ impl<'o> VM<'o> {
                 _ => panic!("tvix compiler bug: OpFindFile called on non-UnresolvedPath"),
             },
 
+            OpCode::OpResolveHomePath => match self.pop() {
+                Value::UnresolvedPath(path) => {
+                    match dirs::home_dir() {
+                        None => {
+                            return Err(self.error(ErrorKind::PathResolution(
+                                "failed to determine home directory".into(),
+                            )));
+                        }
+                        Some(mut buf) => {
+                            buf.push(path);
+                            self.push(buf.into());
+                        }
+                    };
+                }
+
+                _ => {
+                    panic!("tvix compiler bug: OpResolveHomePath called on non-UnresolvedPath")
+                }
+            },
+
             OpCode::OpJump(JumpOffset(offset)) => {
                 debug_assert!(offset != 0);
                 self.frame_mut().ip += offset;