use std::convert::Infallible;
use std::fmt;
use std::fmt::Debug;
use std::fmt::Display;
use std::fmt::Formatter;
use std::marker::PhantomData;
use allocative::Allocative;
use dupe::Clone_;
use dupe::Copy_;
use dupe::Dupe_;
use crate::coerce::Coerce;
use crate::typing::Ty;
use crate::values::type_repr::StarlarkTypeRepr;
use crate::values::AllocFrozenValue;
use crate::values::AllocValue;
use crate::values::Freeze;
use crate::values::FreezeResult;
use crate::values::Freezer;
use crate::values::FrozenHeap;
use crate::values::FrozenValue;
use crate::values::Heap;
use crate::values::Trace;
use crate::values::Tracer;
use crate::values::UnpackValue;
use crate::values::Value;
use crate::values::ValueLifetimeless;
use crate::values::ValueLike;
#[derive(Clone_, Copy_, Dupe_, Allocative)]
#[allocative(bound = "")]
pub struct ValueOfUncheckedGeneric<V: ValueLifetimeless, T: StarlarkTypeRepr>(
V,
PhantomData<fn() -> T>,
);
unsafe impl<V, U, T> Coerce<ValueOfUncheckedGeneric<V, T>> for ValueOfUncheckedGeneric<U, T>
where
V: ValueLifetimeless,
U: ValueLifetimeless,
U: Coerce<V>,
T: StarlarkTypeRepr,
{
}
impl<V: ValueLifetimeless, T: StarlarkTypeRepr> ValueOfUncheckedGeneric<V, T> {
#[inline]
pub fn new(value: V) -> Self {
Self(value, PhantomData)
}
#[inline]
pub fn cast<U: StarlarkTypeRepr<Canonical = T::Canonical>>(
self,
) -> ValueOfUncheckedGeneric<V, U> {
ValueOfUncheckedGeneric::new(self.0)
}
#[inline]
pub fn get(self) -> V {
self.0
}
pub fn unpack<'v>(self) -> crate::Result<T>
where
V: ValueLike<'v>,
T: UnpackValue<'v>,
{
Ok(T::unpack_value_err(self.get().to_value())?)
}
}
impl<V: ValueLifetimeless, T: StarlarkTypeRepr> Debug for ValueOfUncheckedGeneric<V, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_tuple("ValueOfUnchecked")
.field(&self.get())
.finish()
}
}
impl<V: ValueLifetimeless, T: StarlarkTypeRepr> Display for ValueOfUncheckedGeneric<V, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&self.get(), f)
}
}
impl<V: ValueLifetimeless, T: StarlarkTypeRepr> StarlarkTypeRepr for ValueOfUncheckedGeneric<V, T> {
type Canonical = T::Canonical;
fn starlark_type_repr() -> Ty {
<Self as StarlarkTypeRepr>::Canonical::starlark_type_repr()
}
}
impl<'v, V: ValueLike<'v>, T: StarlarkTypeRepr> AllocValue<'v> for ValueOfUncheckedGeneric<V, T> {
fn alloc_value(self, _heap: &'v Heap) -> Value<'v> {
self.0.to_value()
}
}
impl<T: StarlarkTypeRepr> AllocFrozenValue for ValueOfUncheckedGeneric<FrozenValue, T> {
fn alloc_frozen_value(self, _heap: &FrozenHeap) -> FrozenValue {
self.0
}
}
unsafe impl<'v, V, T> Trace<'v> for ValueOfUncheckedGeneric<V, T>
where
V: ValueLifetimeless + Trace<'v>,
T: StarlarkTypeRepr,
{
fn trace(&mut self, tracer: &Tracer<'v>) {
self.0.trace(tracer)
}
}
impl<V: ValueLifetimeless + Freeze, T: StarlarkTypeRepr> Freeze for ValueOfUncheckedGeneric<V, T> {
type Frozen = ValueOfUncheckedGeneric<FrozenValue, T>;
fn freeze(self, freezer: &Freezer) -> FreezeResult<Self::Frozen> {
let frozen = self.0.freeze(freezer)?;
Ok(ValueOfUncheckedGeneric::new(frozen))
}
}
pub type ValueOfUnchecked<'v, T> = ValueOfUncheckedGeneric<Value<'v>, T>;
pub type FrozenValueOfUnchecked<'f, T> = ValueOfUncheckedGeneric<FrozenValue, T>;
impl<'v, T: StarlarkTypeRepr> ValueOfUnchecked<'v, T> {
#[inline]
pub fn new_checked(value: Value<'v>) -> anyhow::Result<Self>
where
T: UnpackValue<'v>,
{
T::unpack_value_err(value)?;
Ok(Self::new(value))
}
}
impl<'v, V: ValueLike<'v>, T: StarlarkTypeRepr> ValueOfUncheckedGeneric<V, T> {
#[inline]
pub fn to_value(self) -> ValueOfUnchecked<'v, T> {
ValueOfUnchecked::new(self.0.to_value())
}
}
impl<'v, T: StarlarkTypeRepr> UnpackValue<'v> for ValueOfUnchecked<'v, T> {
type Error = Infallible;
#[inline]
fn unpack_value_impl(value: Value<'v>) -> Result<Option<Self>, Self::Error> {
Ok(Some(Self::new(value)))
}
}
#[cfg(test)]
mod tests {
use std::rc::Rc;
use crate::const_frozen_string;
use crate::typing::Ty;
use crate::values::type_repr::StarlarkTypeRepr;
use crate::values::FrozenValueOfUnchecked;
use crate::values::ValueOfUnchecked;
#[test]
fn test_cast_example() {
let a =
ValueOfUnchecked::<String>::new_checked(const_frozen_string!("a").to_value()).unwrap();
let _b: ValueOfUnchecked<&str> = a.cast();
}
#[test]
fn test_frozen_value_of_unchecked_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
#[allow(dead_code)]
struct ReprNotSendSync(Rc<String>);
impl StarlarkTypeRepr for ReprNotSendSync {
type Canonical = Self;
fn starlark_type_repr() -> Ty {
panic!("not needed in test")
}
}
assert_send_sync::<FrozenValueOfUnchecked<ReprNotSendSync>>();
}
#[test]
fn test_frozen_value_of_unchecked_covariant() {
fn _assert_covariant<'a>(
_value: FrozenValueOfUnchecked<'static, String>,
) -> FrozenValueOfUnchecked<'a, String> {
panic!()
}
}
}