diff --git a/tvix/eval/src/builtins/mod.rs b/tvix/eval/src/builtins/mod.rs index 947854e6f..1fe5d4a83 100644 --- a/tvix/eval/src/builtins/mod.rs +++ b/tvix/eval/src/builtins/mod.rs @@ -15,6 +15,27 @@ use crate::{ use crate::arithmetic_op; +/// Helper macro to ensure that a value has been forced. The structure +/// of this is a little cumbersome as there are different reference +/// types depending on whether the value is inside a thunk or not. +macro_rules! force { + ( $vm:ident, $src:expr, $value:ident, $body:block ) => { + if let Value::Thunk(thunk) = $src { + thunk.force($vm)?; + let guard = thunk.value(); + let $value: &Value = &guard; + $body + } else { + let $value: &Value = $src; + $body + } + }; + + ( $vm:ident, $value:ident, $body:block ) => { + force!($vm, &$value, $value, $body) + }; +} + fn pure_builtins() -> Vec { vec![ Builtin::new("add", 2, |mut args, _| { @@ -96,9 +117,10 @@ fn pure_builtins() -> Vec { args.pop().unwrap().to_str()?.as_str().to_owned(), )); }), - Builtin::new("toString", 1, |args, _| { - // TODO: toString is actually not the same as Display - Ok(Value::String(format!("{}", args[0]).into())) + Builtin::new("toString", 1, |args, vm| { + force!(vm, &args[0], value, { + Ok(Value::String(format!("{}", value).into())) + }) }), Builtin::new("typeOf", 1, |args, _| { Ok(Value::String(args[0].type_of().into())) diff --git a/tvix/eval/src/vm.rs b/tvix/eval/src/vm.rs index 73a03263c..b65a37582 100644 --- a/tvix/eval/src/vm.rs +++ b/tvix/eval/src/vm.rs @@ -66,7 +66,7 @@ macro_rules! arithmetic_op { $self.push(result); }}; - ( $a:ident, $b:ident, $op:tt ) => {{ + ( $a:expr, $b:expr, $op:tt ) => {{ match ($a, $b) { (Value::Integer(i1), Value::Integer(i2)) => Ok(Value::Integer(i1 $op i2)), (Value::Float(f1), Value::Float(f2)) => Ok(Value::Float(f1 $op f2)),