difftreelog
feat(evaluator) tailstrict functions
in: master
5 files changed
crates/jsonnet-evaluator/src/ctx.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/ctx.rs
+++ b/crates/jsonnet-evaluator/src/ctx.rs
@@ -1,8 +1,6 @@
use crate::{
- future_wrapper, lazy_binding, lazy_val, rc_fn_helper, LazyBinding, LazyVal, ObjValue, Result,
- Val,
+ future_wrapper, rc_fn_helper, resolved_lazy_val, LazyBinding, LazyVal, ObjValue, Result, Val,
};
-use closure::closure;
use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
rc_fn_helper!(
@@ -68,12 +66,7 @@
pub fn with_var(&self, name: String, value: Val) -> Result<Context> {
let mut new_bindings: HashMap<_, LazyBinding> = HashMap::new();
- new_bindings.insert(
- name,
- lazy_binding!(
- closure!(clone value, |_t, _s|Ok(lazy_val!(closure!(clone value, ||Ok(value.clone())))))
- ),
- );
+ new_bindings.insert(name, LazyBinding::Bound(resolved_lazy_val!(value.clone())));
self.extend(new_bindings, None, None, None)
}
@@ -95,7 +88,7 @@
new.insert(k.clone(), v.clone());
}
for (k, v) in new_bindings.into_iter() {
- new.insert(k, v.0(this.clone(), super_obj.clone())?);
+ new.insert(k, v.evaluate(this.clone(), super_obj.clone())?);
}
Rc::new(new)
};
crates/jsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/evaluate.rs
+++ b/crates/jsonnet-evaluator/src/evaluate.rs
@@ -1,7 +1,7 @@
use crate::{
binding, context_creator, create_error, function_default, function_rhs, future_wrapper,
- lazy_binding, lazy_val, push, Context, ContextCreator, FuncDesc, LazyBinding, ObjMember,
- ObjValue, Result, Val,
+ lazy_val, push, Context, ContextCreator, FuncDesc, LazyBinding, LazyVal, ObjMember, ObjValue,
+ Result, Val,
};
use closure::closure;
use jsonnet_parser::{
@@ -19,18 +19,20 @@
let args = args.clone();
(
b.name.clone(),
- lazy_binding!(move |this, super_obj| Ok(lazy_val!(
- closure!(clone b, clone args, clone context_creator, || Ok(evaluate_method(
- context_creator.0(this.clone(), super_obj.clone())?,
- &b.value,
- args.clone()
- )))
- ))),
+ LazyBinding::Bindable(Rc::new(move |this, super_obj| {
+ Ok(lazy_val!(
+ closure!(clone b, clone args, clone context_creator, || Ok(evaluate_method(
+ context_creator.0(this.clone(), super_obj.clone())?,
+ &b.value,
+ args.clone()
+ )))
+ ))
+ })),
)
} else {
(
b.name.clone(),
- lazy_binding!(move |this, super_obj| {
+ LazyBinding::Bindable(Rc::new(move |this, super_obj| {
Ok(lazy_val!(closure!(clone context_creator, clone b, ||
push(b.value.clone(), "thunk".to_owned(), ||{
evaluate(
@@ -39,7 +41,7 @@
)
})
)))
- }),
+ })),
)
}
}
@@ -405,7 +407,7 @@
&evaluate(context.clone(), s)?,
&Val::Obj(evaluate_object(context, t.clone())?),
)?,
- Apply(value, ArgsDesc(args)) => {
+ Apply(value, ArgsDesc(args), tailstrict) => {
let value = evaluate(context.clone(), value)?.unwrap_if_lazy()?;
match value {
Val::Intristic(ns, name) => match (&ns as &str, &name as &str) {
@@ -502,7 +504,8 @@
evaluate(context.clone(), &args[0].1)?,
evaluate(context, &args[1].1)?,
) {
- println!("{}", a);
+ // TODO: Line numbers as in original jsonnet
+ println!("TRACE: {}", a);
b
} else {
panic!("bad trace call");
@@ -517,9 +520,15 @@
.map(move |a| {
(
a.clone().0,
- Val::Lazy(lazy_val!(
- closure!(clone context, clone a, || evaluate(context.clone(), &a.clone().1))
- )),
+ if *tailstrict {
+ Val::Lazy(LazyVal::new_resolved(
+ evaluate(context.clone(), &a.1).unwrap(),
+ ))
+ } else {
+ Val::Lazy(lazy_val!(
+ closure!(clone context, clone a, || evaluate(context.clone(), &a.clone().1))
+ ))
+ },
)
})
.collect(),
crates/jsonnet-evaluator/src/function.rsdiffbeforeafterbothno changes
crates/jsonnet-evaluator/src/lib.rsdiffbeforeafterboth1#![feature(box_syntax, box_patterns)]2#![feature(type_alias_impl_trait)]3#![feature(debug_non_exhaustive)]4#![allow(macro_expanded_macro_exports_accessed_by_absolute_paths)]5mod ctx;6mod dynamic;7mod error;8mod evaluate;9mod function;10mod obj;11mod val;1213pub use ctx::*;14pub use dynamic::*;15pub use error::*;16pub use evaluate::*;17use jsonnet_parser::*;18pub use obj::*;19use std::{cell::RefCell, collections::HashMap, fmt::Debug, path::PathBuf, rc::Rc};20pub use val::*;2122rc_fn_helper!(23 Binding,24 binding,25 dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Result<Val>26);27rc_fn_helper!(FunctionRhs, function_rhs, dyn Fn(Context) -> Result<Val>);28rc_fn_helper!(29 FunctionDefault,30 function_default,31 dyn Fn(Context, LocExpr) -> Result<Val>32);3334#[derive(Clone)]35pub enum LazyBinding {36 Bindable(Rc<dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Result<LazyVal>>),37 Bound(LazyVal),38}3940impl Debug for LazyBinding {41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {42 write!(f, "LazyBinding")43 }44}45impl LazyBinding {46 pub fn evaluate(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal> {47 match self {48 LazyBinding::Bindable(v) => v(this, super_obj),49 LazyBinding::Bound(v) => Ok(v.clone()),50 }51 }52}5354pub struct FileData(String, LocExpr, Option<Val>);55#[derive(Default)]56pub struct EvaluationStateInternals {57 /// Used for stack-overflows and stacktraces58 stack: RefCell<Vec<StackTraceElement>>,59 /// Contains file source codes and evaluated results for imports and pretty60 /// printing stacktraces61 files: RefCell<HashMap<PathBuf, FileData>>,62 globals: RefCell<HashMap<String, Val>>,63}6465thread_local! {66 pub static EVAL_STATE: RefCell<Option<EvaluationState>> = RefCell::new(None)67}68pub(crate) fn with_state<T>(f: impl FnOnce(&EvaluationState) -> T) -> T {69 EVAL_STATE.with(|s| f(s.borrow().as_ref().unwrap()))70}71pub(crate) fn create_error<T>(err: Error) -> Result<T> {72 with_state(|s| s.error(err))73}74pub(crate) fn push<T>(e: LocExpr, comment: String, f: impl FnOnce() -> Result<T>) -> Result<T> {75 with_state(|s| s.push(e, comment, f))76}7778#[derive(Default, Clone)]79pub struct EvaluationState(Rc<EvaluationStateInternals>);80impl EvaluationState {81 pub fn add_file(&self, name: PathBuf, code: String) -> std::result::Result<(), ParseError> {82 self.0.files.borrow_mut().insert(83 name.clone(),84 FileData(85 code.clone(),86 parse(87 &code,88 &ParserSettings {89 file_name: name,90 loc_data: true,91 },92 )?,93 None,94 ),95 );9697 Ok(())98 }99 pub fn add_parsed_file(100 &self,101 name: PathBuf,102 code: String,103 parsed: LocExpr,104 ) -> std::result::Result<(), ()> {105 self.0106 .files107 .borrow_mut()108 .insert(name, FileData(code, parsed, None));109110 Ok(())111 }112 pub fn get_source(&self, name: &PathBuf) -> Option<String> {113 let ro_map = self.0.files.borrow();114 ro_map.get(name).map(|value| value.0.clone())115 }116 pub fn evaluate_file(&self, name: &PathBuf) -> Result<Val> {117 self.begin_state();118 let expr: LocExpr = {119 let ro_map = self.0.files.borrow();120 let value = ro_map121 .get(name)122 .unwrap_or_else(|| panic!("file not added: {:?}", name));123 if value.2.is_some() {124 return Ok(value.2.clone().unwrap());125 }126 value.1.clone()127 };128 let value = evaluate(self.create_default_context()?, &expr)?;129 {130 self.0131 .files132 .borrow_mut()133 .get_mut(name)134 .unwrap()135 .2136 .replace(value.clone());137 }138 self.end_state();139 Ok(value)140 }141142 pub fn parse_evaluate_raw(&self, code: &str) -> Result<Val> {143 let parsed = parse(144 &code,145 &ParserSettings {146 file_name: PathBuf::from("raw.jsonnet"),147 loc_data: true,148 },149 );150 self.begin_state();151 let value = evaluate(self.create_default_context()?, &parsed.unwrap());152 self.end_state();153 value154 }155156 pub fn add_global(&self, name: String, value: Val) {157 self.0.globals.borrow_mut().insert(name, value);158 }159160 pub fn add_stdlib(&self) {161 self.begin_state();162 use jsonnet_stdlib::STDLIB_STR;163 if cfg!(feature = "serialized-stdlib") {164 self.add_parsed_file(165 PathBuf::from("std.jsonnet"),166 STDLIB_STR.to_owned(),167 bincode::deserialize(include_bytes!(concat!(env!("OUT_DIR"), "/stdlib.bincode")))168 .expect("deserialize stdlib"),169 )170 .unwrap();171 } else {172 self.add_file(PathBuf::from("std.jsonnet"), STDLIB_STR.to_owned())173 .unwrap();174 }175 let val = self.evaluate_file(&PathBuf::from("std.jsonnet")).unwrap();176 self.add_global("std".to_owned(), val);177 self.end_state();178 }179180 pub fn create_default_context(&self) -> Result<Context> {181 let globals = self.0.globals.borrow();182 let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();183 for (name, value) in globals.iter() {184 new_bindings.insert(185 name.clone(),186 LazyBinding::Bound(resolved_lazy_val!(value.clone())),187 );188 }189 Context::new().extend(new_bindings, None, None, None)190 }191192 pub fn push<T>(&self, e: LocExpr, comment: String, f: impl FnOnce() -> Result<T>) -> Result<T> {193 {194 let mut stack = self.0.stack.borrow_mut();195 if stack.len() > 500 {196 drop(stack);197 return self.error(Error::StackOverflow);198 } else {199 stack.push(StackTraceElement(e, comment));200 }201 }202 let result = f();203 self.0.stack.borrow_mut().pop();204 result205 }206 pub fn print_stack_trace(&self) {207 for e in self.stack_trace().0 {208 println!("{:?} - {:?}", e.0, e.1)209 }210 }211 pub fn stack_trace(&self) -> StackTrace {212 StackTrace(self.0.stack.borrow().iter().rev().cloned().collect())213 }214 pub fn error<T>(&self, err: Error) -> Result<T> {215 Err(LocError(err, self.stack_trace()))216 }217218 fn begin_state(&self) {219 EVAL_STATE.with(|v| v.borrow_mut().replace(self.clone()));220 }221 fn end_state(&self) {222 EVAL_STATE.with(|v| v.borrow_mut().take());223 }224}225226#[cfg(test)]227pub mod tests {228 use super::Val;229 use crate::EvaluationState;230 use jsonnet_parser::*;231 use std::path::PathBuf;232233 #[test]234 fn eval_state_stacktrace() {235 let state = EvaluationState::default();236 state237 .push(238 loc_expr!(239 Expr::Num(0.0),240 true,241 (PathBuf::from("test1.jsonnet"), 10, 20)242 ),243 "outer".to_owned(),244 || {245 state.push(246 loc_expr!(247 Expr::Num(0.0),248 true,249 (PathBuf::from("test2.jsonnet"), 30, 40)250 ),251 "inner".to_owned(),252 || {253 state.print_stack_trace();254 Ok(())255 },256 )?;257 Ok(())258 },259 )260 .unwrap();261 }262263 #[test]264 fn eval_state_standard() {265 let state = EvaluationState::default();266 state.add_stdlib();267 assert_eq!(268 state269 .parse_evaluate_raw(r#"std.assertEqual(std.base64("test"), "dGVzdA==")"#)270 .unwrap(),271 Val::Bool(true)272 );273 }274275 macro_rules! eval {276 ($str: expr) => {277 evaluate(278 Context::new(),279 EvaluationState::default(),280 &parse(281 $str,282 &ParserSettings {283 loc_data: true,284 file_name: "test.jsonnet".to_owned(),285 },286 )287 .unwrap(),288 )289 };290 }291292 macro_rules! eval_stdlib {293 ($str: expr) => {{294 let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";";295 evaluate(296 Context::new(),297 EvaluationState::default(),298 &parse(299 &(std + $str),300 &ParserSettings {301 loc_data: true,302 file_name: "test.jsonnet".to_owned(),303 },304 )305 .unwrap(),306 )307 }};308 }309310 macro_rules! assert_eval {311 ($str: expr) => {312 assert_eq!(313 evaluate(314 Context::new(),315 EvaluationState::default(),316 &parse(317 $str,318 &ParserSettings {319 loc_data: true,320 file_name: "test.jsonnet".to_owned(),321 }322 )323 .unwrap()324 ),325 Val::Bool(true)326 )327 };328 }329 macro_rules! assert_json {330 ($str: expr, $out: expr) => {331 assert_eq!(332 format!(333 "{}",334 evaluate(335 Context::new(),336 EvaluationState::default(),337 &parse(338 $str,339 &ParserSettings {340 loc_data: true,341 file_name: "test.jsonnet".to_owned(),342 }343 )344 .unwrap()345 )346 ),347 $out348 )349 };350 }351 macro_rules! assert_json_stdlib {352 ($str: expr, $out: expr) => {353 assert_eq!(format!("{}", eval_stdlib!($str)), $out)354 };355 }356 macro_rules! assert_eval_neg {357 ($str: expr) => {358 assert_eq!(359 evaluate(360 Context::new(),361 EvaluationState::default(),362 &parse(363 $str,364 &ParserSettings {365 loc_data: true,366 file_name: "test.jsonnet".to_owned(),367 }368 )369 .unwrap()370 ),371 Val::Bool(false)372 )373 };374 }375376 /*377 /// Sanity checking, before trusting to another tests378 #[test]379 fn equality_operator() {380 assert_eval!("2 == 2");381 assert_eval_neg!("2 != 2");382 assert_eval!("2 != 3");383 assert_eval_neg!("2 == 3");384 assert_eval!("'Hello' == 'Hello'");385 assert_eval_neg!("'Hello' != 'Hello'");386 assert_eval!("'Hello' != 'World'");387 assert_eval_neg!("'Hello' == 'World'");388 }389390 #[test]391 fn math_evaluation() {392 assert_eval!("2 + 2 * 2 == 6");393 assert_eval!("3 + (2 + 2 * 2) == 9");394 }395396 #[test]397 fn string_concat() {398 assert_eval!("'Hello' + 'World' == 'HelloWorld'");399 assert_eval!("'Hello' * 3 == 'HelloHelloHello'");400 assert_eval!("'Hello' + 'World' * 3 == 'HelloWorldWorldWorld'");401 }402403 #[test]404 fn local() {405 assert_eval!("local a = 2; local b = 3; a + b == 5");406 assert_eval!("local a = 1, b = a + 1; a + b == 3");407 assert_eval!("local a = 1; local a = 2; a == 2");408 }409410 #[test]411 fn object_lazyness() {412 assert_json!("local a = {a:error 'test'}; {}", r#"{}"#);413 }414415 #[test]416 fn object_inheritance() {417 assert_json!("{a: self.b} + {b:3}", r#"{"a":3,"b":3}"#);418 }419420 #[test]421 fn test_object() {422 assert_json!("{a:2}", r#"{"a":2}"#);423 assert_json!("{a:2+2}", r#"{"a":4}"#);424 assert_json!("{a:2}+{b:2}", r#"{"a":2,"b":2}"#);425 assert_json!("{b:3}+{b:2}", r#"{"b":2}"#);426 assert_json!("{b:3}+{b+:2}", r#"{"b":5}"#);427 assert_json!("local test='a'; {[test]:2}", r#"{"a":2}"#);428 assert_json!(429 r#"430 {431 name: "Alice",432 welcome: "Hello " + self.name + "!",433 }434 "#,435 r#"{"name":"Alice","welcome":"Hello Alice!"}"#436 );437 assert_json!(438 r#"439 {440 name: "Alice",441 welcome: "Hello " + self.name + "!",442 } + {443 name: "Bob"444 }445 "#,446 r#"{"name":"Bob","welcome":"Hello Bob!"}"#447 );448 }449450 #[test]451 fn functions() {452 assert_json!(r#"local a = function(b, c = 2) b + c; a(2)"#, "4");453 assert_json!(454 r#"local a = function(b, c = "Dear") b + c + d, d = "World"; a("Hello")"#,455 r#""HelloDearWorld""#456 );457 }458459 #[test]460 fn local_methods() {461 assert_json!(r#"local a(b, c = 2) = b + c; a(2)"#, "4");462 assert_json!(463 r#"local a(b, c = "Dear") = b + c + d, d = "World"; a("Hello")"#,464 r#""HelloDearWorld""#465 );466 }467468 #[test]469 fn object_locals() {470 assert_json!(r#"{local a = 3, b: a}"#, r#"{"b":3}"#);471 assert_json!(r#"{local a = 3, local c = a, b: c}"#, r#"{"b":3}"#);472 assert_json!(473 r#"{local a = function (b) {[b]:4}, test: a("test")}"#,474 r#"{"test":{"test":4}}"#475 );476 }477478 #[test]479 fn direct_self() {480 println!(481 "{:#?}",482 eval!(483 r#"484 {485 local me = self,486 a: 3,487 b(): me.a,488 }489 "#490 )491 );492 }493494 #[test]495 fn indirect_self() {496 // `self` assigned to `me` was lost when being497 // referenced from field498 eval_stdlib!(499 r#"{500 local me = self,501 a: 3,502 b: me.a,503 }.b"#504 );505 }506507 // We can't trust other tests (And official jsonnet testsuite), if assert is not working correctly508 #[test]509 fn std_assert_ok() {510 eval_stdlib!("std.assertEqual(4.5 << 2, 16)");511 }512513 #[test]514 #[should_panic]515 fn std_assert_failure() {516 eval_stdlib!("std.assertEqual(4.5 << 2, 15)");517 }518519 #[test]520 fn string_is_string() {521 assert_eq!(522 eval_stdlib!("local arr = 'hello'; (!std.isArray(arr)) && (!std.isString(arr))"),523 Val::Bool(false)524 );525 }526527 #[test]528 fn base64_works() {529 assert_json_stdlib!(r#"std.base64("test")"#, r#""dGVzdA==""#);530 }531532 #[test]533 fn utf8_chars() {534 assert_json_stdlib!(535 r#"local c="😎";{c:std.codepoint(c),l:std.length(c)}"#,536 r#"{"c":128526,"l":1}"#537 )538 }539540 #[test]541 fn json() {542 assert_json_stdlib!(543 r#"std.manifestJsonEx({a:3, b:4, c:6},"")"#,544 r#""{\n"a": 3,\n"b": 4,\n"c": 6\n}""#545 );546 }547548 #[test]549 fn test() {550 assert_json_stdlib!(551 r#"[[a, b] for a in [1,2,3] for b in [4,5,6]]"#,552 "[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]"553 );554 }555556 #[test]557 fn sjsonnet() {558 eval!(559 r#"560 local x0 = {k: 1};561 local x1 = {k: x0.k + x0.k};562 local x2 = {k: x1.k + x1.k};563 local x3 = {k: x2.k + x2.k};564 local x4 = {k: x3.k + x3.k};565 local x5 = {k: x4.k + x4.k};566 local x6 = {k: x5.k + x5.k};567 local x7 = {k: x6.k + x6.k};568 local x8 = {k: x7.k + x7.k};569 local x9 = {k: x8.k + x8.k};570 local x10 = {k: x9.k + x9.k};571 local x11 = {k: x10.k + x10.k};572 local x12 = {k: x11.k + x11.k};573 local x13 = {k: x12.k + x12.k};574 local x14 = {k: x13.k + x13.k};575 local x15 = {k: x14.k + x14.k};576 local x16 = {k: x15.k + x15.k};577 local x17 = {k: x16.k + x16.k};578 local x18 = {k: x17.k + x17.k};579 local x19 = {k: x18.k + x18.k};580 local x20 = {k: x19.k + x19.k};581 local x21 = {k: x20.k + x20.k};582 x21.k583 "#584 );585 }586 */587}crates/jsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/val.rs
+++ b/crates/jsonnet-evaluator/src/val.rs
@@ -1,6 +1,5 @@
use crate::{
- create_error, lazy_binding, Context, Error, FunctionDefault, FunctionRhs, LazyBinding,
- ObjValue, Result,
+ create_error, Context, Error, FunctionDefault, FunctionRhs, LazyBinding, ObjValue, Result,
};
use closure::closure;
use jsonnet_parser::{Param, ParamsDesc};
@@ -12,7 +11,7 @@
};
struct LazyValInternals {
- pub f: Box<dyn Fn() -> Result<Val>>,
+ pub f: Option<Box<dyn Fn() -> Result<Val>>>,
pub cached: RefCell<Option<Val>>,
}
#[derive(Clone)]
@@ -20,10 +19,16 @@
impl LazyVal {
pub fn new(f: Box<dyn Fn() -> Result<Val>>) -> Self {
LazyVal(Rc::new(LazyValInternals {
- f,
+ f: Some(f),
cached: RefCell::new(None),
}))
}
+ pub fn new_resolved(val: Val) -> Self {
+ LazyVal(Rc::new(LazyValInternals {
+ f: None,
+ cached: RefCell::new(Some(val)),
+ }))
+ }
pub fn evaluate(&self) -> Result<Val> {
{
let cached = self.0.cached.borrow();
@@ -31,17 +36,24 @@
return Ok(cached.clone().unwrap());
}
}
- let result = (self.0.f)()?;
+ let result = (self.0.f.as_ref().unwrap())()?;
self.0.cached.borrow_mut().replace(result.clone());
Ok(result)
}
}
+
#[macro_export]
macro_rules! lazy_val {
($f: expr) => {
$crate::LazyVal::new(Box::new($f))
};
}
+#[macro_export]
+macro_rules! resolved_lazy_val {
+ ($f: expr) => {
+ $crate::LazyVal::new_resolved($f)
+ };
+}
impl Debug for LazyVal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.0.cached.borrow().is_some() {
@@ -75,25 +87,23 @@
let eval_default = self.eval_default.clone();
new_bindings.insert(
name,
- lazy_binding!(closure!(clone future_ctx, clone default, clone eval_default, |_, _| Ok(lazy_val!(closure!(clone future_ctx, clone eval_default, clone default, || (eval_default.clone()).0
- (future_ctx.clone().unwrap(), default.clone())))))),
+ LazyBinding::Bound(lazy_val!(
+ closure!(clone future_ctx, clone eval_default, clone default, || (eval_default.clone()).0
+ (future_ctx.clone().unwrap(), default.clone()))
+ )),
);
}
for (name, val) in args.clone().into_iter().filter(|e| e.0.is_some()) {
new_bindings.insert(
name.as_ref().unwrap().clone(),
- lazy_binding!(
- closure!(clone val, |_, _| Ok(lazy_val!(closure!(clone val, || Ok(val.clone())))))
- ),
+ LazyBinding::Bound(resolved_lazy_val!(val.clone())),
);
}
for (i, param) in self.params.0.iter().enumerate() {
if let Some((None, val)) = args.get(i) {
new_bindings.insert(
param.0.clone(),
- lazy_binding!(
- closure!(clone val, |_, _| Ok(lazy_val!(closure!(clone val, || Ok(val.clone())))))
- ),
+ LazyBinding::Bound(resolved_lazy_val!(val.clone())),
);
}
}