about summary refs log tree commit diff
path: root/tvix/eval/builtin-macros/src
diff options
context:
space:
mode:
authorAspen Smith <root@gws.fyi>2024-02-01T21·48-0500
committeraspen <root@gws.fyi>2024-02-08T19·59+0000
commit780b47193a19ec34d467776b142d115bd0029dff (patch)
treea7dbbb2e43a113a7078dd8df7209037f87ad460c /tvix/eval/builtin-macros/src
parent4e040e8bc491bcee930ee7e59d6e68ef87c35bb6 (diff)
refactor(tvix/eval): Generalize propagation of catchable values r/7482
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 <tvl@lahfa.xyz>
Diffstat (limited to 'tvix/eval/builtin-macros/src')
-rw-r--r--tvix/eval/builtin-macros/src/lib.rs37
1 files changed, 31 insertions, 6 deletions
diff --git a/tvix/eval/builtin-macros/src/lib.rs b/tvix/eval/builtin-macros/src/lib.rs
index de73b4576a0c..5cc9807f54fe 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()