diff options
-rw-r--r-- | tvix/eval/docs/builtins.md | 2 | ||||
-rw-r--r-- | tvix/eval/src/builtins/mod.rs | 23 | ||||
-rw-r--r-- | tvix/glue/src/builtins/mod.rs | 45 |
3 files changed, 65 insertions, 5 deletions
diff --git a/tvix/eval/docs/builtins.md b/tvix/eval/docs/builtins.md index f3a4a29c26f2..6e0c7b559516 100644 --- a/tvix/eval/docs/builtins.md +++ b/tvix/eval/docs/builtins.md @@ -121,7 +121,7 @@ The `impl` column indicates implementation status in tvix: | tryEval | false | | | | | typeOf | false | | | | | unsafeDiscardOutputDependency | false | | | context | -| unsafeDiscardStringContext | false | | | context | +| unsafeDiscardStringContext | false | | | | | unsafeGetAttrPos | false | | | todo | | valueSize | false | | | todo | diff --git a/tvix/eval/src/builtins/mod.rs b/tvix/eval/src/builtins/mod.rs index 4cf9074fb8bf..b5adc3775fc0 100644 --- a/tvix/eval/src/builtins/mod.rs +++ b/tvix/eval/src/builtins/mod.rs @@ -1348,11 +1348,26 @@ mod placeholder_builtins { #[builtin("unsafeDiscardStringContext")] async fn builtin_unsafe_discard_string_context( - _: GenCo, - #[lazy] s: Value, + co: GenCo, + s: Value, ) -> Result<Value, ErrorKind> { - // Tvix does not manually track contexts, and this is a no-op for us. - Ok(s) + let span = generators::request_span(&co).await; + let mut v = s + .coerce_to_string( + co, + // It's weak because + // lists, integers, floats and null are not + // accepted as parameters. + CoercionKind { + strong: false, + import_paths: true, + }, + span, + ) + .await? + .to_contextful_str()?; + v.clear_context(); + Ok(Value::String(v)) } #[builtin("addErrorContext")] diff --git a/tvix/glue/src/builtins/mod.rs b/tvix/glue/src/builtins/mod.rs index c70edceb3ebd..6ae99ba14759 100644 --- a/tvix/glue/src/builtins/mod.rs +++ b/tvix/glue/src/builtins/mod.rs @@ -143,6 +143,51 @@ mod tests { ); } + /// Construct two derivations with the same parameters except + /// one of them lost a context string for a dependency, causing + /// the loss of an element in the `inputDrvs` derivation. + /// Therefore, making `outPath` different. + #[test] + fn test_unsafe_discard_string_context() { + let code = r#" + let + dep = builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; }; + in + (builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; env = "${dep}"; }).outPath != + (builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; env = "${builtins.unsafeDiscardStringContext dep}"; }).outPath + "#; + + let value = eval(code).value.expect("must succeed"); + match value { + tvix_eval::Value::Bool(v) => { + assert!(v); + } + _ => panic!("unexpected value type: {:?}", value), + } + } + + /// Construct an attribute set + /// that coerces to a derivation + /// and verify that the return type is a string. + #[test] + fn test_unsafe_discard_string_context_of_coercible() { + let code = r#" + let + dep = builtins.derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; }; + attr = { __toString = _: dep; }; + in + builtins.typeOf (builtins.unsafeDiscardStringContext attr) == "string" + "#; + + let value = eval(code).value.expect("must succeed"); + match value { + tvix_eval::Value::Bool(v) => { + assert!(v); + } + _ => panic!("unexpected value type: {:?}", value), + } + } + #[test] fn builtins_placeholder_hashes() { assert_eq!( |