diff options
author | Vincent Ambo <mail@tazj.in> | 2022-09-02T02·40+0300 |
---|---|---|
committer | tazjin <tazjin@tvl.su> | 2022-09-08T08·45+0000 |
commit | 6b3c3c982669e805c9fc06ee74182606497b7bc3 (patch) | |
tree | dfe31812095d205a521c1606ffdd107ccdfc2b6b /tvix/eval/src/value/mod.rs | |
parent | 0d7ad5e6d1992d4f80f0ea08fee636b7e34eec59 (diff) |
refactor(tvix/eval): add macros for generating Value casters r/4745
The casting methods of `Value` are pretty verbose, and actually incorrect before this commit as they did not account for inner thunk values. To address this, we first attempt to make them correct by introducing a standard macro to generate them and traverse the inner thunk(s) if necessary. This is likely to be a performance hit as it will now involve more cloning of values. We can do multiple things to alleviate this, but should do some measurements first. Change-Id: If315d6e2afe7b69db727df535bc6cbfb89a691aa Reviewed-on: https://cl.tvl.fyi/c/depot/+/6412 Reviewed-by: sterni <sternenseemann@systemli.org> Tested-by: BuildkiteCI
Diffstat (limited to 'tvix/eval/src/value/mod.rs')
-rw-r--r-- | tvix/eval/src/value/mod.rs | 104 |
1 files changed, 42 insertions, 62 deletions
diff --git a/tvix/eval/src/value/mod.rs b/tvix/eval/src/value/mod.rs index 5cfad2e66ea8..46ad65c5025e 100644 --- a/tvix/eval/src/value/mod.rs +++ b/tvix/eval/src/value/mod.rs @@ -43,11 +43,42 @@ pub enum Value { DeferredUpvalue(StackIdx), } -impl Value { - pub fn is_number(&self) -> bool { - matches!(self, Value::Integer(_) | Value::Float(_)) - } +// Helper macros to generate the to_*/as_* macros while accounting for +// thunks. + +/// Generate an `as_*` method returning a reference to the expected +/// type, or a type error. This only works for types that implement +/// `Copy`, as returning a reference to an inner thunk value is not +/// possible. + +/// Generate an `as_*/to_*` accessor method that returns either the +/// expected type, or a type error. +macro_rules! gen_cast { + ( $name:ident, $type:ty, $expected:expr, $variant:pat, $result:expr ) => { + pub fn $name(&self) -> Result<$type, ErrorKind> { + match self { + $variant => Ok($result), + Value::Thunk(thunk) => Self::$name(&thunk.value()), + other => Err(type_error($expected, &other)), + } + } + }; +} +/// Generate an `is_*` type-checking method. +macro_rules! gen_is { + ( $name:ident, $variant:pat ) => { + pub fn $name(&self) -> bool { + match self { + $variant => true, + Value::Thunk(thunk) => Self::$name(&thunk.value()), + _ => false, + } + } + }; +} + +impl Value { pub fn type_of(&self) -> &'static str { match self { Value::Null => "null", @@ -70,65 +101,14 @@ impl Value { } } - pub fn as_bool(&self) -> Result<bool, ErrorKind> { - match self { - Value::Bool(b) => Ok(*b), - other => Err(type_error("bool", &other)), - } - } - - pub fn as_attrs(&self) -> Result<&NixAttrs, ErrorKind> { - match self { - Value::Attrs(attrs) => Ok(attrs), - other => Err(type_error("set", &other)), - } - } - - pub fn as_str(&self) -> Result<&str, ErrorKind> { - match self { - Value::String(s) => Ok(s.as_str()), - other => Err(type_error("string", &other)), - } - } - - pub fn as_list(&self) -> Result<&NixList, ErrorKind> { - match self { - Value::List(xs) => Ok(xs), - other => Err(type_error("list", &other)), - } - } + gen_cast!(as_bool, bool, "bool", Value::Bool(b), *b); + gen_cast!(to_str, NixString, "string", Value::String(s), s.clone()); + gen_cast!(to_attrs, Rc<NixAttrs>, "set", Value::Attrs(a), a.clone()); + gen_cast!(to_list, NixList, "list", Value::List(l), l.clone()); + gen_cast!(to_closure, Closure, "lambda", Value::Closure(c), c.clone()); - pub fn to_string(self) -> Result<NixString, ErrorKind> { - match self { - Value::String(s) => Ok(s), - other => Err(type_error("string", &other)), - } - } - - pub fn to_attrs(self) -> Result<Rc<NixAttrs>, ErrorKind> { - match self { - Value::Attrs(s) => Ok(s), - other => Err(type_error("set", &other)), - } - } - - pub fn to_list(self) -> Result<NixList, ErrorKind> { - match self { - Value::List(l) => Ok(l), - other => Err(type_error("list", &other)), - } - } - - pub fn to_closure(self) -> Result<Closure, ErrorKind> { - match self { - Value::Closure(c) => Ok(c), - other => Err(type_error("lambda", &other)), - } - } - - pub fn is_bool(&self) -> bool { - matches!(self, Value::Bool(_)) - } + gen_is!(is_number, Value::Integer(_) | Value::Float(_)); + gen_is!(is_bool, Value::Bool(_)); } impl Display for Value { |