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 <tvl@lahfa.xyz>
This commit is contained in:
Aspen Smith 2024-02-01 16:48:36 -05:00 committed by aspen
parent 4e040e8bc4
commit 780b47193a
2 changed files with 44 additions and 183 deletions

View file

@ -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()