about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-add-paths.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-add-paths.nix9
-rw-r--r--tvix/eval/src/value/mod.rs6
-rw-r--r--tvix/eval/src/vm.rs19
-rw-r--r--tvix/eval/tests/nix_oracle.rs11
5 files changed, 40 insertions, 6 deletions
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-add-paths.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-add-paths.exp
new file mode 100644
index 000000000000..94ba9a881ae6
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-add-paths.exp
@@ -0,0 +1 @@
+[ /bin /binbar /binbar /binbar /binbar /bin/bar /bin/bin ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-add-paths.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-add-paths.nix
new file mode 100644
index 000000000000..462f670882a0
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-add-paths.nix
@@ -0,0 +1,9 @@
+[
+  (/bin + "/")
+  (/bin + "bar")
+  (let name = "bar"; in /bin + name)
+  (let name = "bar"; in /bin + "${name}")
+  (let name = "bar"; in /bin + "/" + "${name}")
+  (let name = "bar"; in /bin + "/${name}")
+  (/bin + /bin)
+]
diff --git a/tvix/eval/src/value/mod.rs b/tvix/eval/src/value/mod.rs
index 0e9d013fdf61..31ac0c07d4c2 100644
--- a/tvix/eval/src/value/mod.rs
+++ b/tvix/eval/src/value/mod.rs
@@ -383,6 +383,12 @@ impl From<String> for Value {
     }
 }
 
+impl From<PathBuf> for Value {
+    fn from(path: PathBuf) -> Self {
+        Self::Path(path)
+    }
+}
+
 fn type_error(expected: &'static str, actual: &Value) -> ErrorKind {
     ErrorKind::TypeError {
         expected,
diff --git a/tvix/eval/src/vm.rs b/tvix/eval/src/vm.rs
index 01299ccc17da..cf766fb33523 100644
--- a/tvix/eval/src/vm.rs
+++ b/tvix/eval/src/vm.rs
@@ -1,7 +1,9 @@
 //! This module implements the virtual (or abstract) machine that runs
 //! Tvix bytecode.
 
-use std::{cell::RefMut, rc::Rc};
+use std::{cell::RefMut, path::PathBuf, rc::Rc};
+
+use path_clean::PathClean;
 
 use crate::{
     chunk::Chunk,
@@ -347,10 +349,17 @@ impl<'o> VM<'o> {
                     let b = self.pop();
                     let a = self.pop();
 
-                    let result = if let (Value::String(s1), Value::String(s2)) = (&a, &b) {
-                        Value::String(s1.concat(s2))
-                    } else {
-                        fallible!(self, arithmetic_op!(&a, &b, +))
+                    let result = match (&a, &b) {
+                        (Value::String(s1), Value::String(s2)) => Value::String(s1.concat(s2)),
+                        (Value::Path(p), v) => {
+                            let mut path = p.to_string_lossy().into_owned();
+                            path.push_str(
+                                &v.coerce_to_string(CoercionKind::Weak, self)
+                                    .map_err(|ek| self.error(ek))?,
+                            );
+                            PathBuf::from(path).clean().into()
+                        }
+                        _ => fallible!(self, arithmetic_op!(&a, &b, +)),
                     };
 
                     self.push(result)
diff --git a/tvix/eval/tests/nix_oracle.rs b/tvix/eval/tests/nix_oracle.rs
index 61f2be674e27..34ed50351053 100644
--- a/tvix/eval/tests/nix_oracle.rs
+++ b/tvix/eval/tests/nix_oracle.rs
@@ -15,7 +15,7 @@ fn nix_eval(expr: &str) -> String {
     let store_dir = TempDir::new("store-dir").unwrap();
 
     let output = Command::new(nix_binary_path())
-        .args(["--eval", "-E"])
+        .args(["--eval", "--strict", "-E"])
         .arg(format!("({expr})"))
         .env(
             "NIX_REMOTE",
@@ -66,4 +66,13 @@ compare_eval_tests! {
     literal_int("1");
     add_ints("1 + 1");
     add_lists("[1 2] ++ [3 4]");
+    add_paths(r#"[
+        (./. + "/")
+        (./foo + "bar")
+        (let name = "bar"; in ./foo + name)
+        (let name = "bar"; in ./foo + "${name}")
+        (let name = "bar"; in ./foo + "/" + "${name}")
+        (let name = "bar"; in ./foo + "/${name}")
+        (./. + ./.)
+    ]"#);
 }