--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs +++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs @@ -1,27 +1,27 @@ -use std::{collections::BTreeMap, marker::PhantomData, ops::Deref}; +use std::{any::TypeId, collections::BTreeMap, marker::PhantomData, mem::transmute, ops::Deref}; use jrsonnet_gcmodule::Trace; use jrsonnet_interner::{IBytes, IStr}; use jrsonnet_types::{ComplexValType, ValType}; use crate::{ - ObjValue, ObjValueBuilder, Result, ResultExt, Thunk, Val, arr::{ArrValue, BytesArray}, bail, function::FuncVal, typed::CheckType, val::{IndexableVal, NumValue, StrValue, ThunkMapper}, + ObjValue, ObjValueBuilder, Result, ResultExt, Thunk, Val, }; #[doc(hidden)] pub mod __typed_macro_prelude { pub use ::jrsonnet_evaluator::{ - IStr, ObjValue, ObjValueBuilder, State, Val, error::{ErrorKind, Result as JrResult}, typed::{ CheckType, ComplexValType, FromUntyped, IntoUntyped, ParseTypedObj, SerializeTypedObj, Typed, }, + IStr, ObjValue, ObjValueBuilder, State, Val, }; } pub use jrsonnet_macros::{FromUntyped, IntoUntyped, Typed}; @@ -127,19 +127,47 @@ const TYPE: &'static ComplexValType = &ComplexValType::Lazy(T::TYPE); } -impl IntoUntyped for Thunk { +fn try_cast_thunk_val(typed: Thunk) -> Result, Thunk> { + if TypeId::of::() == TypeId::of::() { + // SAFETY: We know that it is exactly the same type, and we discard the original after that + // to avoid double-free. + let transmuted = unsafe { transmute::, Thunk>(typed) }; + Ok(transmuted) + } else { + Err(typed) + } +} +impl IntoUntyped for Thunk +where + T: IntoUntyped + Trace + Clone, +{ fn into_untyped(typed: Self) -> Result { - typed.evaluate() + T::into_untyped(typed.evaluate()?) } fn provides_lazy() -> bool { true } fn into_lazy_untyped(inner: Self) -> Thunk { - inner + // Avoid lazy mapping + let inner = match try_cast_thunk_val(inner) { + Ok(v) => return v, + Err(e) => e, + }; + inner.map(>::default()) } } +fn try_cast_thunk_t(typed: Thunk) -> Result, Thunk> { + if TypeId::of::() == TypeId::of::() { + // SAFETY: We know that it is exactly the same type, and we discard the original after that + // to avoid double-free. + let transmuted = unsafe { transmute::, Thunk>(typed) }; + Ok(transmuted) + } else { + Err(typed) + } +} impl FromUntyped for Thunk where T: Typed + FromUntyped + Trace + Clone, @@ -153,6 +181,11 @@ } fn from_lazy_untyped(inner: Thunk) -> Result { + // Avoid lazy mapping + let inner = match try_cast_thunk_t(inner) { + Ok(v) => return Ok(v), + Err(e) => e, + }; Ok(inner.map(>::default())) } }