From 780b47193a19ec34d467776b142d115bd0029dff Mon Sep 17 00:00:00 2001 From: Aspen Smith Date: Thu, 1 Feb 2024 16:48:36 -0500 Subject: refactor(tvix/eval): Generalize propagation of catchable values Rather than explicitly checking for Value::Catchable in all builtins, make the #[builtin] proc macro insert this for all strict arguments by default, with support for a #[catch] attribute on the argument to disable this behavior. That attribute hasn't actually been *used* anywhere here, primarily because the tests pass without it, even for those builtins which weren't previously checking for Value::Catchable - if some time passes without this being used I might get rid of support for it entirely. There's also a `try_value` macro in builtins directly for the places where builtins were eg forcing something, then explicitly propagating a catchable value. Change-Id: Ie22037b9d3e305e3bdb682d105fe467bd90d53e9 Reviewed-on: https://cl.tvl.fyi/c/depot/+/10732 Tested-by: BuildkiteCI Reviewed-by: raitobezarius --- tvix/eval/builtin-macros/src/lib.rs | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) (limited to 'tvix/eval/builtin-macros') diff --git a/tvix/eval/builtin-macros/src/lib.rs b/tvix/eval/builtin-macros/src/lib.rs index de73b4576a..5cc9807f54 100644 --- a/tvix/eval/builtin-macros/src/lib.rs +++ b/tvix/eval/builtin-macros/src/lib.rs @@ -22,6 +22,10 @@ struct BuiltinArgument { /// function is called. strict: bool, + /// Propagate catchable values as values to the function, rather than short-circuit returning + /// them if encountered + catch: bool, + /// Span at which the argument was defined. span: Span, } @@ -205,6 +209,7 @@ pub fn builtins(args: TokenStream, item: TokenStream) -> TokenStream { .map(|arg| { let span = arg.span(); let mut strict = true; + let mut catch = false; let (name, ty) = match arg { FnArg::Receiver(_) => { return Err(quote_spanned!(span => { @@ -219,6 +224,9 @@ pub fn builtins(args: TokenStream, item: TokenStream) -> TokenStream { if id == "lazy" { strict = false; false + } else if id == "catch" { + catch = true; + false } else { true } @@ -233,8 +241,15 @@ pub fn builtins(args: TokenStream, item: TokenStream) -> TokenStream { } }; + if catch && !strict { + return Err(quote_spanned!(span => { + compile_error!("Cannot mix both lazy and catch on the same argument") + })); + } + Ok(BuiltinArgument { strict, + catch, span, name, ty, @@ -267,12 +282,22 @@ pub fn builtins(args: TokenStream, item: TokenStream) -> TokenStream { let ident = &arg.name; if arg.strict { - f.block = Box::new(parse_quote_spanned! {arg.span=> { - let #ident: #ty = tvix_eval::generators::request_force(&co, values.pop() - .expect("Tvix bug: builtin called with incorrect number of arguments")).await; - - #block - }}); + if arg.catch { + f.block = Box::new(parse_quote_spanned! {arg.span=> { + let #ident: #ty = tvix_eval::generators::request_force(&co, values.pop() + .expect("Tvix bug: builtin called with incorrect number of arguments")).await; + #block + }}); + } else { + f.block = Box::new(parse_quote_spanned! {arg.span=> { + let #ident: #ty = tvix_eval::generators::request_force(&co, values.pop() + .expect("Tvix bug: builtin called with incorrect number of arguments")).await; + if #ident.is_catchable() { + return Ok(#ident); + } + #block + }}); + } } else { f.block = Box::new(parse_quote_spanned! {arg.span=> { let #ident: #ty = values.pop() -- cgit 1.4.1