difftreelog
feat simplify Thunk creation with closure syntax
in: master
9 files changed
crates/jrsonnet-evaluator/src/arr/spec.rsdiffbeforeafterboth7use super::ArrValue;7use super::ArrValue;8use crate::{8use crate::{9 error::ErrorKind::InfiniteRecursionDetected, evaluate, function::FuncVal, typed::Typed,9 error::ErrorKind::InfiniteRecursionDetected, evaluate, function::FuncVal, typed::Typed,10 val::ThunkValue, Context, Error, ObjValue, Result, Thunk, Val,10 Context, Error, ObjValue, Result, Thunk, Val,11};11};121213pub trait ArrayLike: Any + Trace + Debug {13pub trait ArrayLike: Any + Trace + Debug {182 Ok(Some(new_value))182 Ok(Some(new_value))183 }183 }184 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {184 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {185 #[derive(Trace)]186 struct ArrayElement {187 arr_thunk: ExprArray,188 index: usize,189 }190191 impl ThunkValue for ArrayElement {192 type Output = Val;193194 fn get(self: Box<Self>) -> Result<Self::Output> {195 self.arr_thunk196 .get(self.index)197 .transpose()198 .expect("index checked")199 }200 }201202 if index >= self.len() {185 if index >= self.len() {203 return None;186 return None;208 ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}191 ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}209 };192 };210193194 let arr_thunk = self.clone();211 Some(Thunk::new(ArrayElement {195 Some(Thunk!(move || {212 arr_thunk: self.clone(),196 arr_thunk.get(index).transpose().expect("index checked")213 index,214 }))197 }))215 }198 }216 fn get_cheap(&self, _index: usize) -> Option<Val> {199 fn get_cheap(&self, _index: usize) -> Option<Val> {492 Ok(Some(new_value))475 Ok(Some(new_value))493 }476 }494 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {477 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {495 #[derive(Trace)]496 struct ArrayElement<const WITH_INDEX: bool> {497 arr_thunk: MappedArray<WITH_INDEX>,498 index: usize,499 }500501 impl<const WITH_INDEX: bool> ThunkValue for ArrayElement<WITH_INDEX> {502 type Output = Val;503504 fn get(self: Box<Self>) -> Result<Self::Output> {505 self.arr_thunk506 .get(self.index)507 .transpose()508 .expect("index checked")509 }510 }511512 if index >= self.len() {478 if index >= self.len() {513 return None;479 return None;518 ArrayThunk::Waiting(()) | ArrayThunk::Pending => {}484 ArrayThunk::Waiting(()) | ArrayThunk::Pending => {}519 };485 };520486487 let arr_thunk = self.clone();521 Some(Thunk::new(ArrayElement {488 Some(Thunk!(move || {522 arr_thunk: self.clone(),489 arr_thunk.get(index).transpose().expect("index checked")523 index,524 }))490 }))525 }491 }526492crates/jrsonnet-evaluator/src/evaluate/destructure.rsdiffbeforeafterboth1use jrsonnet_gcmodule::Trace;2use jrsonnet_interner::IStr;1use jrsonnet_interner::IStr;3use jrsonnet_parser::{BindSpec, Destruct, LocExpr, ParamsDesc};2use jrsonnet_parser::{BindSpec, Destruct};435use crate::{4use crate::{6 bail,5 bail,7 error::{ErrorKind::*, Result},6 error::{ErrorKind::*, Result},8 evaluate, evaluate_method, evaluate_named,7 evaluate, evaluate_method, evaluate_named,9 gc::GcHashMap,8 gc::GcHashMap,10 val::ThunkValue,11 Context, Pending, Thunk, Val,9 Context, Pending, Thunk, Val,12};10};131132 Destruct::Array { start, rest, end } => {30 Destruct::Array { start, rest, end } => {33 use jrsonnet_parser::DestructRest;31 use jrsonnet_parser::DestructRest;343235 use crate::arr::ArrValue;33 let min_len = start.len() + end.len();3634 let has_rest = rest.is_some();37 #[derive(Trace)]38 struct DataThunk {39 parent: Thunk<Val>,40 min_len: usize,41 has_rest: bool,42 }43 impl ThunkValue for DataThunk {44 type Output = ArrValue;35 let full = Thunk!(move || {4546 fn get(self: Box<Self>) -> Result<Self::Output> {47 let v = self.parent.evaluate()?;36 let v = parent.evaluate()?;48 let Val::Arr(arr) = v else {37 let Val::Arr(arr) = v else {49 bail!("expected array");38 bail!("expected array");50 };39 };51 if !self.has_rest {40 if !has_rest {52 if arr.len() != self.min_len {41 if arr.len() != min_len {53 bail!("expected {} elements, got {}", self.min_len, arr.len())42 bail!("expected {} elements, got {}", min_len, arr.len())54 }43 }55 } else if arr.len() < self.min_len {44 } else if arr.len() < min_len {56 bail!(45 bail!(57 "expected at least {} elements, but array was only {}",46 "expected at least {} elements, but array was only {}",58 self.min_len,47 min_len,59 arr.len()48 arr.len()60 )49 )61 }50 }62 Ok(arr)51 Ok(arr)63 }52 });64 }6566 let full = Thunk::new(DataThunk {67 min_len: start.len() + end.len(),68 has_rest: rest.is_some(),69 parent,70 });715372 {54 {73 #[derive(Trace)]74 struct BaseThunk {75 full: Thunk<ArrValue>,76 index: usize,77 }78 impl ThunkValue for BaseThunk {79 type Output = Val;8081 fn get(self: Box<Self>) -> Result<Self::Output> {82 let full = self.full.evaluate()?;83 Ok(full.get(self.index)?.expect("length is checked"))84 }85 }86 for (i, d) in start.iter().enumerate() {55 for (i, d) in start.iter().enumerate() {56 let full = full.clone();87 destruct(57 destruct(88 d,58 d,89 Thunk::new(BaseThunk {59 Thunk!(move || Ok(full.evaluate()?.get(i)?.expect("length is checked"))),90 full: full.clone(),91 index: i,92 }),93 fctx.clone(),60 fctx.clone(),94 new_bindings,61 new_bindings,95 )?;62 )?;986599 match rest {66 match rest {100 Some(DestructRest::Keep(v)) => {67 Some(DestructRest::Keep(v)) => {101 #[derive(Trace)]102 struct RestThunk {103 full: Thunk<ArrValue>,104 start: usize,68 let start = start.len();105 end: usize,106 }107 impl ThunkValue for RestThunk {108 type Output = Val;109110 fn get(self: Box<Self>) -> Result<Self::Output> {111 let full = self.full.evaluate()?;69 let end = end.len();112 let to = full.len() - self.end;70 let full = full.clone();113 Ok(Val::Arr(full.slice(114 Some(self.start as i32),115 Some(to as i32),116 None,117 )))118 }119 }120121 destruct(71 destruct(122 &Destruct::Full(v.clone()),72 &Destruct::Full(v.clone()),123 Thunk::new(RestThunk {73 Thunk!(move || {124 full: full.clone(),74 let full = full.evaluate()?;125 start: start.len(),75 let to = full.len() - end;126 end: end.len(),76 Ok(Val::Arr(full.slice(77 Some(start as i32),78 Some(to as i32),79 None,80 )))127 }),81 }),128 fctx.clone(),82 fctx.clone(),129 new_bindings,83 new_bindings,133 }87 }13488135 {89 {136 #[derive(Trace)]137 struct EndThunk {138 full: Thunk<ArrValue>,139 index: usize,140 end: usize,141 }142 impl ThunkValue for EndThunk {143 type Output = Val;144145 fn get(self: Box<Self>) -> Result<Self::Output> {146 let full = self.full.evaluate()?;147 Ok(full148 .get(full.len() - self.end + self.index)?149 .expect("length is checked"))150 }151 }152 for (i, d) in end.iter().enumerate() {90 for (i, d) in end.iter().enumerate() {153 destruct(154 d,155 Thunk::new(EndThunk {156 full: full.clone(),91 let full = full.clone();157 index: i,158 end: end.len(),92 let end = end.len();93 destruct(94 d,95 Thunk!(move || {96 let full = full.evaluate()?;97 Ok(full.get(full.len() - end + i)?.expect("length is checked"))159 }),98 }),160 fctx.clone(),99 fctx.clone(),161 new_bindings,100 new_bindings,162 )?;101 )?;163 }102 }164 }103 }165 }104 }166 #[cfg(feature = "exp-destruct")]105 #[cfg(feature = "exp-destruct")]167 Destruct::Object { fields, rest } => {106 Destruct::Object { fields, rest } => {168 use crate::obj::ObjValue;169170 #[derive(Trace)]171 struct DataThunk {172 parent: Thunk<Val>,173 field_names: Vec<(IStr, bool)>,107 let field_names: Vec<_> = fields108 .iter()174 has_rest: bool,109 .map(|f| (f.0.clone(), f.2.is_some()))175 }110 .collect();176 impl ThunkValue for DataThunk {177 type Output = ObjValue;111 let has_rest = rest.is_some();178179 fn get(self: Box<Self>) -> Result<Self::Output> {112 let full = Thunk!(move || {180 let v = self.parent.evaluate()?;113 let v = parent.evaluate()?;181 let Val::Obj(obj) = v else {114 let Val::Obj(obj) = v else {182 bail!("expected object");115 bail!("expected object");183 };116 };184 for (field, has_default) in &self.field_names {117 for (field, has_default) in &field_names {185 if !has_default && !obj.has_field_ex(field.clone(), true) {118 if !has_default && !obj.has_field_ex(field.clone(), true) {186 bail!("missing field: {field}");119 bail!("missing field: {field}");187 }120 }188 }121 }189 if !self.has_rest {122 if !has_rest {190 let len = obj.len();123 let len = obj.len();191 if len > self.field_names.len() {124 if len > field_names.len() {192 bail!("too many fields, and rest not found");125 bail!("too many fields, and rest not found");193 }126 }194 }127 }195 Ok(obj)128 Ok(obj)196 }129 });197 }198 let field_names: Vec<_> = fields199 .iter()200 .map(|f| (f.0.clone(), f.2.is_some()))201 .collect();202 let full = Thunk::new(DataThunk {203 parent,204 field_names,205 has_rest: rest.is_some(),206 });207130208 for (field, d, default) in fields {131 for (field, d, default) in fields {209 #[derive(Trace)]132 let default = default.clone().map(|e| (fctx.clone(), e));210 struct FieldThunk {211 full: Thunk<ObjValue>,212 field: IStr,213 default: Option<(Pending<Context>, LocExpr)>,214 }215 impl ThunkValue for FieldThunk {133 let value = {216 type Output = Val;134 let field = field.clone();217135 let full = full.clone();218 fn get(self: Box<Self>) -> Result<Self::Output> {136 Thunk!(move || {219 let full = self.full.evaluate()?;137 let full = full.evaluate()?;220 if let Some(field) = full.get(self.field)? {138 if let Some(field) = full.get(field)? {221 Ok(field)139 Ok(field)222 } else {140 } else {223 let (fctx, expr) = self.default.as_ref().expect("shape is checked");141 let (fctx, expr) = default.as_ref().expect("shape is checked");224 Ok(evaluate(fctx.clone().unwrap(), expr)?)142 Ok(evaluate(fctx.clone().unwrap(), expr)?)225 }143 }226 }144 })227 }145 };228 let value = Thunk::new(FieldThunk {146229 full: full.clone(),230 field: field.clone(),231 default: default.clone().map(|e| (fctx.clone(), e)),232 });233 if let Some(d) = d {147 if let Some(d) = d {234 destruct(d, value, fctx.clone(), new_bindings)?;148 destruct(d, value, fctx.clone(), new_bindings)?;235 } else {149 } else {253) -> Result<()> {167) -> Result<()> {254 match d {168 match d {255 BindSpec::Field { into, value } => {169 BindSpec::Field { into, value } => {256 #[derive(Trace)]257 struct EvaluateThunkValue {258 name: Option<IStr>,170 let name = into.name();259 fctx: Pending<Context>,171 let value = value.clone();260 expr: LocExpr,261 }262 impl ThunkValue for EvaluateThunkValue {172 let data = {263 type Output = Val;173 let fctx = fctx.clone();264 fn get(self: Box<Self>) -> Result<Self::Output> {265 self.name.map_or_else(174 Thunk!(move || name.map_or_else(266 || evaluate(self.fctx.unwrap(), &self.expr),175 || evaluate(fctx.unwrap(), &value),267 |name| evaluate_named(self.fctx.unwrap(), &self.expr, name),176 |name| evaluate_named(fctx.unwrap(), &value, name),268 )177 ))269 }270 }178 };271 let data = Thunk::new(EvaluateThunkValue {272 name: into.name(),273 fctx: fctx.clone(),274 expr: value.clone(),275 });276 destruct(into, data, fctx, new_bindings)?;179 destruct(into, data, fctx, new_bindings)?;277 }180 }278 BindSpec::Function {181 BindSpec::Function {279 name,182 name,280 params,183 params,281 value,184 value,282 } => {185 } => {283 #[derive(Trace)]284 struct MethodThunk {285 fctx: Pending<Context>,286 name: IStr,287 params: ParamsDesc,186 let params = params.clone();288 value: LocExpr,289 }290 impl ThunkValue for MethodThunk {291 type Output = Val;187 let name = name.clone();292293 fn get(self: Box<Self>) -> Result<Self::Output> {294 Ok(evaluate_method(295 self.fctx.unwrap(),188 let value = value.clone();296 self.name,297 self.params,298 self.value,299 ))300 }301 }302303 let old = new_bindings.insert(189 let old = new_bindings.insert(name.clone(), {304 name.clone(),305 Thunk::new(MethodThunk {306 fctx,307 name: name.clone(),190 let name = name.clone();308 params: params.clone(),191 Thunk!(move || Ok(evaluate_method(fctx.unwrap(), name, params, value)))309 value: value.clone(),310 }),192 });311 );312 if old.is_some() {193 if old.is_some() {313 bail!(DuplicateLocalVar(name.clone()))194 bail!(DuplicateLocalVar(name))314 }195 }315 }196 }316 }197 }crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth18 function::{CallLocation, FuncDesc, FuncVal},18 function::{CallLocation, FuncDesc, FuncVal},19 in_frame,19 in_frame,20 typed::Typed,20 typed::Typed,21 val::{CachedUnbound, IndexableVal, NumValue, StrValue, Thunk, ThunkValue},21 val::{CachedUnbound, IndexableVal, NumValue, StrValue, Thunk},22 Context, Error, GcHashMap, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result,22 Context, Error, GcHashMap, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result,23 ResultExt, Unbound, Val,23 ResultExt, Unbound, Val,24};24};139 #[cfg(feature = "exp-preserve-order")]139 #[cfg(feature = "exp-preserve-order")]140 false,140 false,141 ) {141 ) {142 #[derive(Trace)]143 struct ObjectFieldThunk {144 obj: ObjValue,145 field: IStr,146 }147 impl ThunkValue for ObjectFieldThunk {148 type Output = Val;149150 fn get(self: Box<Self>) -> Result<Self::Output> {151 self.obj.get(self.field).transpose().expect(152 "field exists, as field name was obtained from object.fields()",153 )154 }155 }156157 let fctx = Pending::new();142 let fctx = Pending::new();158 let mut new_bindings = GcHashMap::with_capacity(var.capacity_hint());143 let mut new_bindings = GcHashMap::with_capacity(var.capacity_hint());144 let obj = obj.clone();159 let value = Thunk::evaluated(Val::Arr(ArrValue::lazy(vec![145 let value = Thunk::evaluated(Val::Arr(ArrValue::lazy(vec![160 Thunk::evaluated(Val::string(field.clone())),146 Thunk::evaluated(Val::string(field.clone())),161 Thunk::new(ObjectFieldThunk {147 Thunk!(move || obj.get(field).transpose().expect(162 field: field.clone(),148 "field exists, as field name was obtained from object.fields()",163 obj: obj.clone(),149 )),164 }),165 ])));150 ])));166 destruct(var, value, fctx.clone(), &mut new_bindings)?;151 destruct(var, value, fctx.clone(), &mut new_bindings)?;167 let ctx = ctx152 let ctx = ctx609 if items.is_empty() {594 if items.is_empty() {610 Val::Arr(ArrValue::empty())595 Val::Arr(ArrValue::empty())611 } else if items.len() == 1 {596 } else if items.len() == 1 {612 #[derive(Trace)]613 struct ArrayElement {614 ctx: Context,615 item: LocExpr,597 let item = items[0].clone();616 }617 impl ThunkValue for ArrayElement {618 type Output = Val;619 fn get(self: Box<Self>) -> Result<Val> {620 evaluate(self.ctx, &self.item)621 }622 }623 Val::Arr(ArrValue::lazy(vec![Thunk::new(ArrayElement {598 Val::Arr(ArrValue::lazy(vec![Thunk!(move || evaluate(ctx, &item))]))624 ctx,625 item: items[0].clone(),626 })]))627 } else {599 } else {628 Val::Arr(ArrValue::expr(ctx, items.iter().cloned()))600 Val::Arr(ArrValue::expr(ctx, items.iter().cloned()))629 }601 }630 }602 }631 ArrComp(expr, comp_specs) => {603 ArrComp(expr, comp_specs) => {632 let mut out = Vec::new();604 let mut out = Vec::new();633 evaluate_comp(ctx, comp_specs, &mut |ctx| {605 evaluate_comp(ctx, comp_specs, &mut |ctx| {634 #[derive(Trace)]635 struct EvaluateThunk {636 ctx: Context,637 expr: LocExpr,606 let expr = expr.clone();638 }639 impl ThunkValue for EvaluateThunk {640 type Output = Val;641 fn get(self: Box<Self>) -> Result<Val> {642 evaluate(self.ctx, &self.expr)643 }644 }645 out.push(Thunk::new(EvaluateThunk {607 out.push(Thunk!(move || evaluate(ctx, &expr)));646 ctx,647 expr: expr.clone(),648 }));649 Ok(())608 Ok(())650 })?;609 })?;651 Val::Arr(ArrValue::lazy(out))610 Val::Arr(ArrValue::lazy(out))crates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth232/// Creates Context, which has all argument default values applied233/// Creates Context, which has all argument default values applied233/// and with unbound values causing error to be returned234/// and with unbound values causing error to be returned234pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {235pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {235 #[derive(Trace)]236 struct DependsOnUnbound(IStr, ParamsDesc);237 impl ThunkValue for DependsOnUnbound {238 type Output = Val;239 fn get(self: Box<Self>) -> Result<Val> {240 Err(FunctionParameterNotBoundInCall(241 Some(self.0.clone()),242 self.1243 .iter()244 .map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))245 .collect(),246 )247 .into())248 }249 }250251 let fctx = Context::new_future();236 let fctx = Context::new_future();252237267 } else {252 } else {268 destruct(253 destruct(269 ¶m.0,254 ¶m.0,270 Thunk::new(DependsOnUnbound(255 {271 param.0.name().unwrap_or_else(|| "<destruct>".into()),256 let param_name = param.0.name().unwrap_or_else(|| "<destruct>".into());272 params.clone(),257 let params = params.clone();258 Thunk!(move || Err(FunctionParameterNotBoundInCall(259 Some(param_name),260 params261 .iter()262 .map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))263 .collect(),264 )265 .into()))273 )),266 },274 fctx.clone(),267 fctx.clone(),275 &mut bindings,268 &mut bindings,276 )?;269 )?;crates/jrsonnet-evaluator/src/gc.rsdiffbeforeafterboth159 }159 }160}160}161162pub fn assert_trace<T: Trace>(_v: &T) {}161163crates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth20 in_frame,20 in_frame,21 operator::evaluate_add_op,21 operator::evaluate_add_op,22 tb,22 tb,23 val::{ArrValue, ThunkValue},23 val::ArrValue,24 MaybeUnbound, Result, Thunk, Unbound, Val,24 MaybeUnbound, Result, Thunk, Unbound, Val,25};25};2626444 })444 })445 }445 }446 pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {446 pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {447 #[derive(Trace)]448 struct ThunkGet {449 obj: ObjValue,450 key: IStr,451 }452 impl ThunkValue for ThunkGet {453 type Output = Val;454455 fn get(self: Box<Self>) -> Result<Self::Output> {456 Ok(self.obj.get(self.key)?.expect("field exists"))457 }458 }459460 if !self.has_field_ex(key.clone(), true) {447 if !self.has_field_ex(key.clone(), true) {461 return None;448 return None;462 }449 }463 Some(Thunk::new(ThunkGet {464 obj: self.clone(),450 let obj = self.clone();451465 key,452 Some(Thunk!(move || Ok(obj.get(key)?.expect("field exists"))))466 }))467 }453 }468 pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {454 pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {469 #[derive(Trace)]470 struct ThunkGet {471 obj: ObjValue,455 let obj = self.clone();472 key: IStr,473 }474 impl ThunkValue for ThunkGet {475 type Output = Val;476477 fn get(self: Box<Self>) -> Result<Self::Output> {478 self.obj.get_or_bail(self.key)479 }480 }481482 Thunk::new(ThunkGet {456 Thunk!(move || obj.get_or_bail(key))483 obj: self.clone(),484 key,485 })486 }457 }487 pub fn ptr_eq(a: &Self, b: &Self) -> bool {458 pub fn ptr_eq(a: &Self, b: &Self) -> bool {488 Cc::ptr_eq(&a.0, &b.0)459 Cc::ptr_eq(&a.0, &b.0)733 self.value_cache704 self.value_cache734 .borrow_mut()705 .borrow_mut()735 .insert(cache_key.clone(), CacheValue::Pending);706 .insert(cache_key.clone(), CacheValue::Pending);736 let value = self.get_for_uncached(key, this).map_err(|e| {707 let value = self.get_for_uncached(key, this).inspect_err(|e| {737 self.value_cache708 self.value_cache738 .borrow_mut()709 .borrow_mut()739 .insert(cache_key.clone(), CacheValue::Errored(e.clone()));710 .insert(cache_key.clone(), CacheValue::Errored(e.clone()));740 e741 })?;711 })?;742 self.value_cache.borrow_mut().insert(712 self.value_cache.borrow_mut().insert(743 cache_key,713 cache_key,crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth11use derivative::Derivative;11use derivative::Derivative;12use jrsonnet_gcmodule::{Cc, Trace};12use jrsonnet_gcmodule::{Cc, Trace};13use jrsonnet_interner::IStr;13use jrsonnet_interner::IStr;14pub use jrsonnet_macros::Thunk;14use jrsonnet_types::ValType;15use jrsonnet_types::ValType;15use thiserror::Error;16use thiserror::Error;161731 fn get(self: Box<Self>) -> Result<Self::Output>;32 fn get(self: Box<Self>) -> Result<Self::Output>;32}33}3435#[derive(Trace)]36pub struct ThunkValueClosure<D: Trace, O: 'static> {37 env: D,38 // Carries no data, as it is not a real closure, all the39 // captured environment is stored in `env` field.40 #[trace(skip)]41 closure: fn(D) -> Result<O>,42}43impl<D: Trace, O: 'static> ThunkValueClosure<D, O> {44 pub fn new(env: D, closure: fn(D) -> Result<O>) -> Self {45 Self { env, closure }46 }47}48impl<D: Trace, O: 'static> ThunkValue for ThunkValueClosure<D, O> {49 type Output = O;5051 fn get(self: Box<Self>) -> Result<Self::Output> {52 (self.closure)(self.env)53 }54}335534#[derive(Trace)]56#[derive(Trace)]35enum ThunkInner<T: Trace> {57enum ThunkInner<T: Trace> {113 M: ThunkMapper<Input>,135 M: ThunkMapper<Input>,114 M::Output: Trace,136 M::Output: Trace,115 {137 {116 #[derive(Trace)]117 struct Mapped<Input: Trace, Mapper: Trace> {118 inner: Thunk<Input>,119 mapper: Mapper,120 }121 impl<Input, Mapper> ThunkValue for Mapped<Input, Mapper>122 where123 Input: Trace + Clone,124 Mapper: ThunkMapper<Input>,125 {126 type Output = Mapper::Output;138 let inner = self;127128 fn get(self: Box<Self>) -> Result<Self::Output> {139 Thunk!(move || {129 let value = self.inner.evaluate()?;140 let value = inner.evaluate()?;130 let mapped = self.mapper.map(value)?;141 let mapped = mapper.map(value)?;131 Ok(mapped)142 Ok(mapped)132 }143 })133 }134135 Thunk::new(Mapped::<Input, M> {136 inner: self,137 mapper,138 })139 }144 }140}145}141146crates/jrsonnet-macros/Cargo.tomldiffbeforeafterboth17proc-macro2.workspace = true17proc-macro2.workspace = true18quote.workspace = true18quote.workspace = true19syn = { workspace = true, features = ["full"] }19syn = { workspace = true, features = ["full"] }20syn-dissect-closure.workspace = true2021crates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth1use std::string::String;1use std::string::String;223use proc_macro2::TokenStream;3use proc_macro2::TokenStream;4use quote::quote;4use quote::{quote, quote_spanned};5use syn::{5use syn::{6 parenthesized,6 parenthesized,7 parse::{Parse, ParseStream},7 parse::{Parse, ParseStream},8 parse_macro_input,8 parse_macro_input,9 punctuated::Punctuated,9 punctuated::Punctuated,10 spanned::Spanned,10 spanned::Spanned,11 token::{self, Comma},11 token::{self, Comma},12 Attribute, DeriveInput, Error, Expr, FnArg, GenericArgument, Ident, ItemFn, LitStr, Pat, Path,12 Attribute, DeriveInput, Error, Expr, ExprClosure, FnArg, GenericArgument, Ident, ItemFn,13 PathArguments, Result, ReturnType, Token, Type,13 LitStr, Pat, Path, PathArguments, Result, ReturnType, Token, Type,14};14};1515816 input.expand().into()816 input.expand().into()817}817}818819/// Create Thunk using closure syntax820#[proc_macro]821#[allow(non_snake_case)]822pub fn Thunk(input: proc_macro::TokenStream) -> proc_macro::TokenStream {823 let input = parse_macro_input!(input as ExprClosure);824825 let span = input.inputs.span();826 let move_check = input.capture.is_none().then(|| {827 quote_spanned! {span => {828 compile_error!("Thunk! needs to be called with move closure");829 }}830 });831832 let (env, closure, args) = syn_dissect_closure::split_env(input);833834 let trace_check = args.iter().map(|el| {835 let span = el.span();836 quote_spanned! {span => ::jrsonnet_evaluator::gc::assert_trace(&#el);}837 });838839 quote! {{840 #move_check841 #(#trace_check)*842 ::jrsonnet_evaluator::Thunk::new(::jrsonnet_evaluator::val::ThunkValueClosure::new(#env, #closure))843 }}.into()844}818845