difftreelog
feat unify Arg and Typed handling for Thunk
in: master
8 files changed
crates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/ctx.rs
+++ b/crates/jrsonnet-evaluator/src/ctx.rs
@@ -81,7 +81,7 @@
#[must_use]
pub fn into_future(self, ctx: Pending<Self>) -> Self {
{
- ctx.0.borrow_mut().replace(self);
+ ctx.clone().fill(self);
}
ctx.unwrap()
}
crates/jrsonnet-evaluator/src/dynamic.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/dynamic.rs
+++ b/crates/jrsonnet-evaluator/src/dynamic.rs
@@ -1,29 +1,49 @@
-use std::cell::RefCell;
+use std::cell::OnceCell;
use jrsonnet_gcmodule::{Cc, Trace};
+use crate::{error::ErrorKind::InfiniteRecursionDetected, throw, val::ThunkValue, Result, Thunk};
+
// TODO: Replace with OnceCell once in std
#[derive(Clone, Trace)]
-pub struct Pending<V: Trace + 'static>(pub Cc<RefCell<Option<V>>>);
+pub struct Pending<V: Trace + 'static>(pub Cc<OnceCell<V>>);
impl<T: Trace + 'static> Pending<T> {
pub fn new() -> Self {
- Self(Cc::new(RefCell::new(None)))
+ Self(Cc::new(OnceCell::new()))
}
pub fn new_filled(v: T) -> Self {
- Self(Cc::new(RefCell::new(Some(v))))
+ let cell = OnceCell::new();
+ let _ = cell.set(v);
+ Self(Cc::new(cell))
}
/// # Panics
/// If wrapper is filled already
pub fn fill(self, value: T) {
- assert!(self.0.borrow().is_none(), "wrapper is filled already");
- self.0.borrow_mut().replace(value);
+ self.0
+ .set(value)
+ .map_err(|_| ())
+ .expect("wrapper is filled already")
}
}
impl<T: Clone + Trace + 'static> Pending<T> {
/// # Panics
/// If wrapper is not yet filled
pub fn unwrap(&self) -> T {
- self.0.borrow().as_ref().cloned().unwrap()
+ self.0.get().cloned().expect("pending was not filled")
+ }
+ pub fn try_get(&self) -> Option<T> {
+ self.0.get().cloned()
+ }
+}
+
+impl<T: Trace + Clone> ThunkValue for Pending<T> {
+ type Output = T;
+
+ fn get(self: Box<Self>) -> Result<Self::Output> {
+ let Some(value) = self.0.get() else {
+ throw!(InfiniteRecursionDetected);
+ };
+ Ok(value.clone())
}
}
@@ -32,3 +52,9 @@
Self::new()
}
}
+
+impl<T: Trace + Clone> Into<Thunk<T>> for Pending<T> {
+ fn into(self) -> Thunk<T> {
+ Thunk::new(self)
+ }
+}
crates/jrsonnet-evaluator/src/function/arglike.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/arglike.rs
+++ b/crates/jrsonnet-evaluator/src/function/arglike.rs
@@ -48,28 +48,22 @@
where
T: Typed + Clone,
{
- fn evaluate_arg(&self, _ctx: Context, _tailstrict: bool) -> Result<Thunk<Val>> {
+ fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {
+ if T::provides_lazy() && !tailstrict {
+ return Ok(T::into_lazy_untyped(self.clone()));
+ }
let val = T::into_untyped(self.clone())?;
Ok(Thunk::evaluated(val))
}
}
impl<T> OptionalContext for T where T: Typed + Clone {}
-impl ArgLike for Thunk<Val> {
- fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {
- if tailstrict {
- self.force()?;
- }
- Ok(self.clone())
- }
-}
-impl OptionalContext for Thunk<Val> {}
-
#[derive(Clone, Trace)]
pub enum TlaArg {
String(IStr),
Code(LocExpr),
Val(Val),
+ Lazy(Thunk<Val>),
}
impl ArgLike for TlaArg {
fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {
@@ -84,6 +78,7 @@
})
}),
TlaArg::Val(val) => Ok(Thunk::evaluated(val.clone())),
+ TlaArg::Lazy(lazy) => Ok(lazy.clone()),
}
}
}
crates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/obj.rs
+++ b/crates/jrsonnet-evaluator/src/obj.rs
@@ -15,7 +15,9 @@
function::CallLocation,
gc::{GcHashMap, GcHashSet, TraceBox},
operator::evaluate_add_op,
- tb, throw, MaybeUnbound, Result, State, Thunk, Unbound, Val,
+ tb, throw,
+ val::ThunkValue,
+ MaybeUnbound, Result, State, Thunk, Unbound, Val,
};
#[cfg(not(feature = "exp-preserve-order"))]
crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth1use std::ops::Deref;1use std::{collections::BTreeMap, marker::PhantomData, ops::Deref};223use jrsonnet_gcmodule::Cc;3use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::{IBytes, IStr};4use jrsonnet_interner::{IBytes, IStr};5pub use jrsonnet_macros::Typed;5pub use jrsonnet_macros::Typed;6use jrsonnet_types::{ComplexValType, ValType};6use jrsonnet_types::{ComplexValType, ValType};11 function::{native::NativeDesc, FuncDesc, FuncVal},11 function::{native::NativeDesc, FuncDesc, FuncVal},12 throw,12 throw,13 typed::CheckType,13 typed::CheckType,14 val::{IndexableVal, StrValue},14 val::{IndexableVal, StrValue, ThunkMapper},15 ObjValue, ObjValueBuilder, Val,15 ObjValue, ObjValueBuilder, Thunk, Val,16};16};1718#[derive(Trace)]19struct FromUntyped<K: Trace>(PhantomData<fn() -> K>);20impl<K> ThunkMapper<Val> for FromUntyped<K>21where22 K: Typed + Trace,23{24 type Output = K;2526 fn map(self, from: Val) -> Result<Self::Output> {27 K::from_untyped(from)28 }29}30impl<K: Trace> Default for FromUntyped<K> {31 fn default() -> Self {32 Self(PhantomData)33 }34}173518pub trait TypedObj: Typed {36pub trait TypedObj: Typed {19 fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;37 fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;28pub trait Typed: Sized {46pub trait Typed: Sized {29 const TYPE: &'static ComplexValType;47 const TYPE: &'static ComplexValType;30 fn into_untyped(typed: Self) -> Result<Val>;48 fn into_untyped(typed: Self) -> Result<Val>;49 fn into_lazy_untyped(typed: Self) -> Thunk<Val> {50 Thunk::from(Self::into_untyped(typed))51 }31 fn from_untyped(untyped: Val) -> Result<Self>;52 fn from_untyped(untyped: Val) -> Result<Self>;53 fn from_lazy_untyped(lazy: Thunk<Val>) -> Result<Self> {54 Self::from_untyped(lazy.evaluate()?)55 }5657 // Whatever caller should use `into_lazy_untyped` instead of `into_untyped`58 fn provides_lazy() -> bool {59 false60 }6162 // Whatever caller should use `from_lazy_untyped` instead of `from_untyped` when possible63 fn wants_lazy() -> bool {64 false65 }326633 /// Hack to make builtins be able to return non-result values, and make macros able to convert those values to result67 /// Hack to make builtins be able to return non-result values, and make macros able to convert those values to result34 /// This method returns identity in impl Typed for Result, and should not be overriden68 /// This method returns identity in impl Typed for Result, and should not be overriden39 }73 }40}74}7576impl<T> Typed for Thunk<T>77where78 T: Typed + Trace + Clone,79{80 const TYPE: &'static ComplexValType = &ComplexValType::Lazy(T::TYPE);8182 fn into_untyped(typed: Self) -> Result<Val> {83 T::into_untyped(typed.evaluate()?)84 }8586 fn from_untyped(untyped: Val) -> Result<Self> {87 Self::from_lazy_untyped(Thunk::evaluated(untyped))88 }8990 fn provides_lazy() -> bool {91 true92 }9394 fn into_lazy_untyped(inner: Self) -> Thunk<Val> {95 #[derive(Trace)]96 struct IntoUntyped<K: Trace>(PhantomData<fn() -> K>);97 impl<K> ThunkMapper<K> for IntoUntyped<K>98 where99 K: Typed + Trace,100 {101 type Output = Val;102103 fn map(self, from: K) -> Result<Self::Output> {104 K::into_untyped(from)105 }106 }107 impl<K: Trace> Default for IntoUntyped<K> {108 fn default() -> Self {109 Self(PhantomData)110 }111 }112 inner.map(<IntoUntyped<T>>::default())113 }114115 fn wants_lazy() -> bool {116 true117 }118119 fn from_lazy_untyped(inner: Thunk<Val>) -> Result<Self> {120 Ok(inner.map(<FromUntyped<T>>::default()))121 }122}4112342const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS + 1)) - 1) as f64;124const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS + 1)) - 1) as f64;43125crates/jrsonnet-evaluator/src/typed/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/mod.rs
+++ b/crates/jrsonnet-evaluator/src/typed/mod.rs
@@ -252,6 +252,7 @@
}
Ok(())
}
+ Self::Lazy(_lazy) => Ok(()),
}
}
}
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -88,6 +88,54 @@
}
}
+pub trait ThunkMapper<Input>: Trace {
+ type Output;
+ fn map(self, from: Input) -> Result<Self::Output>;
+}
+impl<Input> Thunk<Input>
+where
+ Input: Trace + Clone,
+{
+ pub fn map<M>(self, mapper: M) -> Thunk<M::Output>
+ where
+ M: ThunkMapper<Input>,
+ M::Output: Trace,
+ {
+ #[derive(Trace)]
+ struct Mapped<Input: Trace, Mapper: Trace> {
+ inner: Thunk<Input>,
+ mapper: Mapper,
+ }
+ impl<Input, Mapper> ThunkValue for Mapped<Input, Mapper>
+ where
+ Input: Trace + Clone,
+ Mapper: ThunkMapper<Input>,
+ {
+ type Output = Mapper::Output;
+
+ fn get(self: Box<Self>) -> Result<Self::Output> {
+ let value = self.inner.evaluate()?;
+ let mapped = self.mapper.map(value)?;
+ Ok(mapped)
+ }
+ }
+
+ Thunk::new(Mapped::<Input, M> {
+ inner: self,
+ mapper,
+ })
+ }
+}
+
+impl<T: Trace> From<Result<T>> for Thunk<T> {
+ fn from(value: Result<T>) -> Self {
+ match value {
+ Ok(o) => Self::evaluated(o),
+ Err(e) => Self::errored(e),
+ }
+ }
+}
+
type CacheKey = (Option<WeakObjValue>, Option<WeakObjValue>);
#[derive(Trace, Clone)]
@@ -272,6 +320,11 @@
Self::Flat(value.into())
}
}
+impl From<IStr> for StrValue {
+ fn from(value: IStr) -> Self {
+ Self::Flat(value)
+ }
+}
impl Display for StrValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
crates/jrsonnet-types/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-types/src/lib.rs
+++ b/crates/jrsonnet-types/src/lib.rs
@@ -128,10 +128,12 @@
Array(Box<ComplexValType>),
ArrayRef(&'static ComplexValType),
ObjectRef(&'static [(&'static str, &'static ComplexValType)]),
+ AttrsOf(&'static ComplexValType),
Union(Vec<ComplexValType>),
UnionRef(&'static [&'static ComplexValType]),
Sum(Vec<ComplexValType>),
SumRef(&'static [&'static ComplexValType]),
+ Lazy(&'static ComplexValType),
}
impl From<ValType> for ComplexValType {
@@ -195,10 +197,18 @@
}
write!(f, "}}")?;
}
+ ComplexValType::AttrsOf(a) => {
+ if matches!(a, ComplexValType::Any) {
+ write!(f, "object")?;
+ } else {
+ write!(f, "AttrsOf<{a}>")?;
+ }
+ }
ComplexValType::Union(v) => write_union(f, true, v.iter())?,
ComplexValType::UnionRef(v) => write_union(f, true, v.iter().copied())?,
ComplexValType::Sum(v) => write_union(f, false, v.iter())?,
ComplexValType::SumRef(v) => write_union(f, false, v.iter().copied())?,
+ ComplexValType::Lazy(lazy) => write!(f, "Lazy<{lazy}>")?,
};
Ok(())
}