/*
 * Copyright 2018 The Starlark in Rust Authors.
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::cell::RefCell;
use std::cell::UnsafeCell;
use std::marker;
use std::marker::PhantomData;

use starlark_map::small_map::SmallMap;
use starlark_map::small_set::SmallSet;
use starlark_map::Hashed;

use crate::slice_vec_ext::VecExt;
use crate::values::Freezer;
use crate::values::FrozenStringValue;
use crate::values::FrozenValue;
use crate::values::StringValue;
use crate::values::Value;

/// Need to be implemented for non-simple `StarlarkValue`.
///
/// This is called on freeze of the heap. Must produce a replacement object to place
/// in the frozen heap.
///
/// For relatively simple cases it can be implemented with `#[derive(Freeze)]`:
///
/// ```
/// # struct AdditionalData;
///
/// use starlark::values::Freeze;
///
/// #[derive(Freeze)]
/// struct MyType<V> {
///     value: V,
///     // This field does not implement `Freeze`, but we can use it as is for freeze.
///     #[freeze(identity)]
///     data: AdditionalData,
/// }
/// ```
pub trait Freeze {
    /// When type is frozen, it is frozen into this type.
    type Frozen;

    /// Freeze a value. The frozen value _must_ be equal to the original,
    /// and produce the same hash.
    ///
    /// Note during freeze, `Value` objects in `Self` might be already special forward-objects,
    /// trying to unpack these objects will crash the process.
    /// So the function is only allowed to access `Value` objects after it froze them.
    fn freeze(self, freezer: &Freezer) -> anyhow::Result<Self::Frozen>;
}

impl Freeze for String {
    type Frozen = String;

    fn freeze(self, _freezer: &Freezer) -> anyhow::Result<String> {
        Ok(self)
    }
}

impl Freeze for i32 {
    type Frozen = i32;

    fn freeze(self, _freezer: &Freezer) -> anyhow::Result<i32> {
        Ok(self)
    }
}

impl Freeze for u32 {
    type Frozen = u32;

    fn freeze(self, _freezer: &Freezer) -> anyhow::Result<u32> {
        Ok(self)
    }
}

impl Freeze for i64 {
    type Frozen = i64;

    fn freeze(self, _freezer: &Freezer) -> anyhow::Result<i64> {
        Ok(self)
    }
}

impl Freeze for u64 {
    type Frozen = u64;

    fn freeze(self, _freezer: &Freezer) -> anyhow::Result<u64> {
        Ok(self)
    }
}

impl Freeze for usize {
    type Frozen = usize;

    fn freeze(self, _freezer: &Freezer) -> anyhow::Result<usize> {
        Ok(self)
    }
}

impl Freeze for bool {
    type Frozen = bool;

    fn freeze(self, _freezer: &Freezer) -> anyhow::Result<bool> {
        Ok(self)
    }
}

impl<'v, T: 'static> Freeze for marker::PhantomData<&'v T> {
    type Frozen = PhantomData<&'static T>;

    fn freeze(self, _freezer: &Freezer) -> anyhow::Result<PhantomData<&'static T>> {
        Ok(marker::PhantomData)
    }
}

impl<T> Freeze for Vec<T>
where
    T: Freeze,
{
    type Frozen = Vec<T::Frozen>;

    fn freeze(self, freezer: &Freezer) -> anyhow::Result<Vec<T::Frozen>> {
        self.into_try_map(|v| v.freeze(freezer))
    }
}

impl<T> Freeze for RefCell<T>
where
    T: Freeze,
{
    type Frozen = T::Frozen;

    fn freeze(self, freezer: &Freezer) -> anyhow::Result<T::Frozen> {
        self.into_inner().freeze(freezer)
    }
}

impl<T> Freeze for UnsafeCell<T>
where
    T: Freeze,
{
    type Frozen = UnsafeCell<T::Frozen>;

    fn freeze(self, freezer: &Freezer) -> anyhow::Result<Self::Frozen> {
        Ok(UnsafeCell::new(self.into_inner().freeze(freezer)?))
    }
}

impl<T> Freeze for Box<T>
where
    T: Freeze,
{
    type Frozen = Box<T::Frozen>;

    fn freeze(self, freezer: &Freezer) -> anyhow::Result<Self::Frozen> {
        Ok(Box::new((*self).freeze(freezer)?))
    }
}

impl<T> Freeze for Box<[T]>
where
    T: Freeze,
{
    type Frozen = Box<[T::Frozen]>;

    fn freeze(self, freezer: &Freezer) -> anyhow::Result<Self::Frozen> {
        self.into_vec()
            .into_try_map(|v| v.freeze(freezer))
            .map(|v| v.into_boxed_slice())
    }
}

impl<T> Freeze for Option<T>
where
    T: Freeze,
{
    type Frozen = Option<T::Frozen>;

    fn freeze(self, freezer: &Freezer) -> anyhow::Result<Option<T::Frozen>> {
        self.map(|v| v.freeze(freezer)).transpose()
    }
}

impl<K: Freeze> Freeze for Hashed<K> {
    type Frozen = Hashed<K::Frozen>;

    fn freeze(self, freezer: &Freezer) -> anyhow::Result<Self::Frozen> {
        // `freeze` must not change hash.
        Ok(Hashed::new_unchecked(
            self.hash(),
            self.into_key().freeze(freezer)?,
        ))
    }
}

impl<K, V> Freeze for SmallMap<K, V>
where
    K: Freeze,
    V: Freeze,
{
    type Frozen = SmallMap<K::Frozen, V::Frozen>;

    fn freeze(self, freezer: &Freezer) -> anyhow::Result<SmallMap<K::Frozen, V::Frozen>> {
        let mut new = SmallMap::with_capacity(self.len());
        for (key, value) in self.into_iter_hashed() {
            let hash = key.hash();
            let key = key.into_key().freeze(freezer)?;
            // TODO(nga): verify hash unchanged after freeze.
            let key = Hashed::new_unchecked(hash, key);
            let value = value.freeze(freezer)?;
            new.insert_hashed_unique_unchecked(key, value);
        }
        Ok(new)
    }
}

impl<T> Freeze for SmallSet<T>
where
    T: Freeze,
{
    type Frozen = SmallSet<T::Frozen>;

    fn freeze(self, freezer: &Freezer) -> anyhow::Result<Self::Frozen> {
        let mut new = SmallSet::with_capacity(self.len());
        for value in self.into_iter_hashed() {
            let value = value.freeze(freezer)?;
            // TODO(nga): verify hash unchanged after freeze.
            new.insert_hashed_unique_unchecked(value);
        }
        Ok(new)
    }
}

impl<'v> Freeze for Value<'v> {
    type Frozen = FrozenValue;

    fn freeze(self, freezer: &Freezer) -> anyhow::Result<FrozenValue> {
        freezer.freeze(self)
    }
}

impl Freeze for FrozenValue {
    type Frozen = FrozenValue;

    fn freeze(self, _freezer: &Freezer) -> anyhow::Result<FrozenValue> {
        Ok(self)
    }
}

impl<'v> Freeze for StringValue<'v> {
    type Frozen = FrozenStringValue;

    fn freeze(self, freezer: &Freezer) -> anyhow::Result<FrozenStringValue> {
        self.freeze(freezer)
    }
}

impl Freeze for () {
    type Frozen = ();

    fn freeze(self, _freezer: &Freezer) -> anyhow::Result<()> {
        Ok(())
    }
}

impl<A: Freeze> Freeze for (A,) {
    type Frozen = (A::Frozen,);

    fn freeze(self, freezer: &Freezer) -> anyhow::Result<(A::Frozen,)> {
        Ok((self.0.freeze(freezer)?,))
    }
}

impl<A: Freeze, B: Freeze> Freeze for (A, B) {
    type Frozen = (A::Frozen, B::Frozen);

    fn freeze(self, freezer: &Freezer) -> anyhow::Result<(A::Frozen, B::Frozen)> {
        Ok((self.0.freeze(freezer)?, self.1.freeze(freezer)?))
    }
}

impl<A: Freeze, B: Freeze, C: Freeze> Freeze for (A, B, C) {
    type Frozen = (A::Frozen, B::Frozen, C::Frozen);

    fn freeze(self, freezer: &Freezer) -> anyhow::Result<(A::Frozen, B::Frozen, C::Frozen)> {
        Ok((
            self.0.freeze(freezer)?,
            self.1.freeze(freezer)?,
            self.2.freeze(freezer)?,
        ))
    }
}

impl<A: Freeze, B: Freeze, C: Freeze, D: Freeze> Freeze for (A, B, C, D) {
    type Frozen = (A::Frozen, B::Frozen, C::Frozen, D::Frozen);

    fn freeze(
        self,
        freezer: &Freezer,
    ) -> anyhow::Result<(A::Frozen, B::Frozen, C::Frozen, D::Frozen)> {
        Ok((
            self.0.freeze(freezer)?,
            self.1.freeze(freezer)?,
            self.2.freeze(freezer)?,
            self.3.freeze(freezer)?,
        ))
    }
}


/*
 * Copyright 2019 The Starlark in Rust Authors.
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::borrow::Borrow;
use std::cmp::Ordering;
use std::fmt;
use std::fmt::Display;
use std::fmt::Formatter;
use std::hash::Hash;
use std::hash::Hasher;
use std::ops::Deref;
use std::ptr;
use std::sync::atomic;

use allocative::Allocative;
use dupe::Clone_;
use dupe::Copy_;
use dupe::Dupe_;

use crate::values::Freeze;
use crate::values::Freezer;
use crate::values::Trace;
use crate::values::Tracer;

/// A [`FrozenRef`] is essentially a [`FrozenValue`](crate::values::FrozenValue),
/// and has the same memory and access guarantees as it.
/// However, this keeps the type of the type `T` of the actual
/// [`FrozenValue`](crate::values::FrozenValue) as a
/// reference, allowing manipulation of the actual typed data.
#[derive(Clone_, Dupe_, Copy_, Debug, Allocative)]
#[allocative(skip)] // Data is owned by heap.
pub struct FrozenRef<'f, T: 'f + ?Sized> {
    pub(crate) value: &'f T,
}

impl<'f, T> Default for FrozenRef<'f, [T]> {
    fn default() -> FrozenRef<'f, [T]> {
        FrozenRef { value: &[] }
    }
}

unsafe impl<'v, 'f, T: 'f + ?Sized> Trace<'v> for FrozenRef<'f, T> {
    fn trace(&mut self, _: &Tracer<'v>) {
        // Do nothing, because `FrozenRef` can only point to frozen value.
    }
}

impl<'f, T: 'f + ?Sized> FrozenRef<'f, T> {
    pub(crate) const fn new(value: &'f T) -> FrozenRef<T> {
        FrozenRef { value }
    }

    /// Returns a reference to the underlying value.
    pub fn as_ref(self) -> &'f T {
        self.value
    }

    /// Converts `self` into a new reference that points at something reachable from the previous.
    pub fn map<F, U: 'f + ?Sized>(self, f: F) -> FrozenRef<'f, U>
    where
        for<'v> F: FnOnce(&'v T) -> &'v U,
    {
        FrozenRef {
            value: f(self.value),
        }
    }
}

impl<'f, T: ?Sized + Display> Display for FrozenRef<'f, T> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        self.value.fmt(f)
    }
}

impl<'f, T: ?Sized> Deref for FrozenRef<'f, T> {
    type Target = T;

    fn deref(&self) -> &T {
        self.value
    }
}

impl<'f, T: 'f + ?Sized> Borrow<T> for FrozenRef<'f, T> {
    fn borrow(&self) -> &T {
        self
    }
}

impl<'f, T: 'f + ?Sized> Borrow<T> for FrozenRef<'f, Box<T>> {
    fn borrow(&self) -> &T {
        self
    }
}

impl<'f, T: 'f + ?Sized> PartialEq for FrozenRef<'f, T>
where
    T: PartialEq,
{
    fn eq(&self, other: &Self) -> bool {
        (self as &T).eq(other as &T)
    }
}

impl<'f, T: 'f + ?Sized> Eq for FrozenRef<'f, T> where T: Eq {}

impl<'f, T: 'f + ?Sized> PartialOrd for FrozenRef<'f, T>
where
    T: PartialOrd,
{
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        (self as &T).partial_cmp(other as &T)
    }
}

impl<'f, T: 'f + ?Sized> Ord for FrozenRef<'f, T>
where
    T: Ord,
{
    fn cmp(&self, other: &Self) -> Ordering {
        (self as &T).cmp(other as &T)
    }
}

impl<'f, T: 'f + ?Sized> Hash for FrozenRef<'f, T>
where
    T: Hash,
{
    fn hash<H: Hasher>(&self, state: &mut H) {
        (self as &T).hash(state);
    }
}

impl<'f, T: 'f + ?Sized> Freeze for FrozenRef<'f, T> {
    type Frozen = Self;

    fn freeze(self, _freezer: &Freezer) -> anyhow::Result<Self::Frozen> {
        Ok(self)
    }
}

/// `Atomic<Option<FrozenRef<T>>>`.
pub(crate) struct AtomicFrozenRefOption<T>(atomic::AtomicPtr<T>);

unsafe impl<'v, T> Trace<'v> for AtomicFrozenRefOption<T> {
    fn trace(&mut self, _: &Tracer<'v>) {
        // Do nothing, because `AtomicFrozenRefOption` holds `FrozenRef`.
    }
}

impl<T> AtomicFrozenRefOption<T> {
    pub(crate) fn new(module: Option<FrozenRef<T>>) -> AtomicFrozenRefOption<T> {
        AtomicFrozenRefOption(atomic::AtomicPtr::new(match module {
            Some(v) => v.as_ref() as *const T as *mut T,
            None => ptr::null_mut(),
        }))
    }

    pub(crate) fn load_relaxed(&self) -> Option<FrozenRef<'static, T>> {
        // Note this is relaxed load which is cheap.
        let ptr = self.0.load(atomic::Ordering::Relaxed);
        if ptr.is_null() {
            None
        } else {
            Some(FrozenRef::new(unsafe { &*ptr }))
        }
    }

    pub(crate) fn store_relaxed(&self, module: FrozenRef<T>) {
        self.0.store(
            module.as_ref() as *const T as *mut T,
            atomic::Ordering::Relaxed,
        );
    }
}


/*
 * Copyright 2018 The Starlark in Rust Authors.
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use crate::values::Value;
use crate::values::ValueError;

// Helper for convert_slice_indices
fn convert_index_aux(
    len: i32,
    v1: Option<Value>,
    default: i32,
    min: i32,
    max: i32,
) -> anyhow::Result<i32> {
    if let Some(v) = v1 {
        if v.is_none() {
            Ok(default)
        } else {
            match v.to_int() {
                Ok(x) => {
                    let i = if x < 0 { len + x } else { x };
                    if i < min {
                        Ok(min)
                    } else if i > max {
                        Ok(max)
                    } else {
                        Ok(i)
                    }
                }
                Err(..) => Err(ValueError::IncorrectParameterTypeWithExpected(
                    "none or int".to_owned(),
                    v.get_type().to_owned(),
                )
                .into()),
            }
        }
    } else {
        Ok(default)
    }
}

/// Function to parse the index for at/set_at methods.
///
/// Return an `i32` from self corresponding to the index recenterd between 0
/// and len. Raise the correct errors if the value is not numeric or the
/// index is out of bound.
pub(crate) fn convert_index(v: Value, len: i32) -> anyhow::Result<i32> {
    match v.to_int() {
        Ok(x) => {
            let i = if x < 0 {
                len.checked_add(x).ok_or(ValueError::IntegerOverflow)?
            } else {
                x
            };
            if i < 0 || i >= len {
                Err(ValueError::IndexOutOfBound(i).into())
            } else {
                Ok(i)
            }
        }
        Err(..) => Err(ValueError::IncorrectParameterTypeWithExpected(
            "int".to_owned(),
            v.get_type().to_owned(),
        )
        .into()),
    }
}

/// Parse indices for slicing.
///
/// Takes the object length and 3 optional values and returns `(i32, i32,
/// i32)` with those index correctly converted in range of length.
/// Return the correct errors if the values are not numeric or the stride is
/// 0.
pub(crate) fn convert_slice_indices(
    len: i32,
    start: Option<Value>,
    stop: Option<Value>,
    stride: Option<Value>,
) -> anyhow::Result<(i32, i32, i32)> {
    let stride = match stride {
        None => 1,
        Some(v) if v.is_none() => 1,
        Some(v) => v.to_int().map_err(|_| {
            ValueError::IncorrectParameterTypeWithExpected(
                "int or None".to_owned(),
                v.get_type().to_owned(),
            )
        })?,
    };
    match stride {
        0 => Err(ValueError::IndexOutOfBound(0).into()),
        stride => {
            let def_start = if stride < 0 { len - 1 } else { 0 };
            let def_end = if stride < 0 { -1 } else { len };
            let clamp = if stride < 0 { -1 } else { 0 };
            let start = convert_index_aux(len, start, def_start, clamp, len + clamp);
            let stop = convert_index_aux(len, stop, def_end, clamp, len + clamp);
            match (start, stop) {
                (Ok(s1), Ok(s2)) => Ok((s1, s2, stride)),
                (Err(x), ..) => Err(x),
                (Ok(..), Err(x)) => Err(x),
            }
        }
    }
}

pub(crate) fn apply_slice<T: Copy>(
    xs: &[T],
    start: Option<Value>,
    stop: Option<Value>,
    stride: Option<Value>,
) -> anyhow::Result<Vec<T>> {
    let (start, stop, stride) = convert_slice_indices(xs.len() as i32, start, stop, stride)?;
    if stride == 1 {
        if start >= stop {
            return Ok(Vec::new());
        } else {
            return Ok(xs[start as usize..stop as usize].to_vec());
        }
    }

    let (start, stop) = if stride < 0 {
        (stop + 1, start + 1)
    } else {
        (start, stop)
    };
    if start >= stop {
        return Ok(Vec::new());
    }
    let mut res = xs[start as usize..stop as usize].to_vec();
    if stride == -1 {
        res.reverse();
        return Ok(res);
    }
    if stride < 0 {
        res.reverse();
    }
    let astride = stride.abs();
    let res = res
        .into_iter()
        .enumerate()
        .filter_map(|x| {
            if 0 == (x.0 as i32 % astride) {
                Some(x.1)
            } else {
                None
            }
        })
        .collect();
    Ok(res)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::values::types::inline_int::InlineInt;
    use crate::values::Heap;

    #[test]
    fn test_convert_index() {
        let heap = Heap::new();
        assert_eq!(Some(6), convert_index(Value::testing_new_int(6), 7).ok());
        assert_eq!(Some(6), convert_index(Value::testing_new_int(-1), 7).ok());
        assert_eq!(
            Some((6, 7, 1)),
            convert_slice_indices(7, Some(Value::testing_new_int(6)), None, None).ok()
        );
        assert_eq!(
            Some((6, -1, -1)),
            convert_slice_indices(
                7,
                Some(Value::testing_new_int(-1)),
                None,
                Some(Value::testing_new_int(-1))
            )
            .ok()
        );
        assert_eq!(
            Some((6, 7, 1)),
            convert_slice_indices(
                7,
                Some(Value::testing_new_int(-1)),
                Some(Value::testing_new_int(10)),
                None
            )
            .ok()
        );
        // Errors
        assert!(convert_index(heap.alloc("a"), 7).is_err());
        assert!(convert_index(Value::testing_new_int(8), 7).is_err()); // 8 > 7 = len
        assert!(convert_index(Value::testing_new_int(-8), 7).is_err()); // -8 + 7 = -1 < 0
    }

    #[test]
    fn test_apply_slice() {
        let s = &[0, 1, 2, 3, 4, 5, 6];

        let x = apply_slice(
            s,
            Some(Value::new_int(InlineInt::MINUS_ONE)),
            None,
            Some(Value::new_int(InlineInt::MINUS_ONE)),
        )
        .unwrap();
        assert_eq!(x, &[6, 5, 4, 3, 2, 1, 0]);

        let x = apply_slice(
            s,
            Some(Value::new_int(InlineInt::MINUS_ONE)),
            None,
            Some(Value::new_int(InlineInt::MINUS_ONE)),
        )
        .unwrap();
        assert_eq!(x, &[6, 5, 4, 3, 2, 1, 0]);

        let x = apply_slice(
            s,
            Some(Value::testing_new_int(0)),
            Some(Value::testing_new_int(3)),
            Some(Value::testing_new_int(2)),
        )
        .unwrap();
        assert_eq!(x, &[0, 2]);

        let x = apply_slice(
            s,
            Some(Value::testing_new_int(5)),
            Some(Value::testing_new_int(2)),
            Some(Value::testing_new_int(-2)),
        )
        .unwrap();
        assert_eq!(x, &[5, 3]);

        let x = apply_slice(
            s,
            Some(Value::testing_new_int(-1)),
            Some(Value::testing_new_int(-5)),
            Some(Value::testing_new_int(-1)),
        )
        .unwrap();
        assert_eq!(x, &[6, 5, 4, 3]);

        let x = apply_slice(
            s,
            Some(Value::testing_new_int(-1)),
            Some(Value::testing_new_int(0)),
            Some(Value::testing_new_int(-1)),
        )
        .unwrap();
        assert_eq!(x, &[6, 5, 4, 3, 2, 1]);

        let x = apply_slice(
            &[1, 2, 3],
            Some(Value::testing_new_int(0)),
            Some(Value::testing_new_int(-2)),
            Some(Value::testing_new_int(-1)),
        )
        .unwrap();
        assert_eq!(x, &[] as &[i32]);
    }
}


/*
 * Copyright 2018 The Starlark in Rust Authors.
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::fmt::Debug;

use crate::values::Heap;
use crate::values::Value;

/// Iterator of starlark values.
#[derive(Debug)]
pub struct StarlarkIterator<'v> {
    /// Iterator implementation. Typically an iterable itself.
    value: Value<'v>,
    /// Current index.
    index: usize,
    /// Heap to allocate values on.
    heap: &'v Heap,
}

impl<'v> StarlarkIterator<'v> {
    /// Construct iterator from the given value.
    #[inline]
    pub(crate) fn new(value: Value<'v>, heap: &'v Heap) -> StarlarkIterator<'v> {
        StarlarkIterator {
            value,
            index: 0,
            heap,
        }
    }

    /// Iterator yielding no values.
    #[inline]
    pub fn empty(heap: &'v Heap) -> StarlarkIterator<'v> {
        Self::new(Value::new_empty_tuple(), heap)
    }
}

impl<'v> Iterator for StarlarkIterator<'v> {
    type Item = Value<'v>;

    #[inline]
    fn next(&mut self) -> Option<Value<'v>> {
        let r = self.value.get_ref().iter_next(self.index, self.heap);
        if r.is_some() {
            self.index += 1;
        } else {
            self.value.get_ref().iter_stop();
            // We must call `iter_stop` exactly once, regardless of whether
            // iterator is exhausted or not, even if `next` is called after `None`.
            // So we replace `value` with empty tuple, for which we know that `iter_stop` is no-op.
            self.value = Value::new_empty_tuple();
            self.index = 0;
        }
        r
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.value.get_ref().iter_size_hint(self.index)
    }
}

impl<'v> Drop for StarlarkIterator<'v> {
    #[inline]
    fn drop(&mut self) {
        // `iter_stop` is no-op for empty tuple, this saves us from virtual call
        // after iterator is exhausted.
        if !self.value.ptr_eq(Value::new_empty_tuple()) {
            self.value.get_ref().iter_stop();
        }
    }
}


/*
 * Copyright 2018 The Starlark in Rust Authors.
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::marker::PhantomData;

use crate::typing::Ty;
use crate::values::type_repr::StarlarkTypeRepr;

enum NonInstantiable {}

/// `StarlarkTypeRepr` for iterable types.
pub struct StarlarkIter<T: StarlarkTypeRepr>(PhantomData<T>, NonInstantiable);

impl<T: StarlarkTypeRepr> StarlarkTypeRepr for StarlarkIter<T> {
    fn starlark_type_repr() -> Ty {
        Ty::iter(T::starlark_type_repr())
    }
}


/*
 * Copyright 2018 The Starlark in Rust Authors.
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! Defines a runtime Starlark value ([`Value`]) and traits for defining custom values ([`StarlarkValue`]).
//!
//! This module contains code for working with Starlark values:
//!
//! * Most code dealing with Starlark will use [`Value`], as it represents the fundamental values used in
//!   Starlark. When frozen, they become [`FrozenValue`].
//! * Values are garbage-collected, so a given [`Value`] lives on a [`Heap`].
//! * Rust values (e.g. [`String`], [`Vec`]) can be added to the [`Heap`] with [`AllocValue`],
//!   and deconstructed from a [`Value`] with [`UnpackValue`]
//!   (or specialised methods like [`unpack_str`](Value::unpack_str)).
//! * To define your own Rust data type that can live in a [`Value`] it must implement the [`StarlarkValue`]
//!   trait.
//! * All the nested modules represent the built-in Starlark values. These are all defined using [`StarlarkValue`],
//!   so may serve as interesting inspiration for writing your own values, in addition to occurring in Starlark programs.

pub use starlark_derive::starlark_attrs;
pub use starlark_derive::starlark_value;
pub use starlark_derive::Freeze;
pub use starlark_derive::NoSerialize;
pub use starlark_derive::StarlarkAttrs;
pub use starlark_derive::Trace;

pub use crate::any::AnyLifetime;
pub use crate::any::ProvidesStaticType;
pub use crate::coerce::Coerce;
pub use crate::values::alloc_value::AllocFrozenValue;
pub use crate::values::alloc_value::AllocValue;
pub use crate::values::demand::Demand;
pub use crate::values::error::ValueError;
pub use crate::values::freeze::Freeze;
pub use crate::values::frozen_ref::FrozenRef;
pub use crate::values::iter::StarlarkIterator;
pub use crate::values::iter_type::StarlarkIter;
pub use crate::values::layout::complex::ValueOfComplex;
pub use crate::values::layout::heap::heap_type::Freezer;
pub use crate::values::layout::heap::heap_type::FrozenHeap;
pub use crate::values::layout::heap::heap_type::FrozenHeapRef;
pub use crate::values::layout::heap::heap_type::Heap;
pub use crate::values::layout::heap::heap_type::Tracer;
pub use crate::values::layout::heap::profile::aggregated::AggregateHeapProfileInfo;
pub use crate::values::layout::identity::ValueIdentity;
pub use crate::values::layout::static_string::constant_string;
pub use crate::values::layout::static_string::StarlarkStrNRepr;
pub use crate::values::layout::typed::string::FrozenStringValue;
pub use crate::values::layout::typed::string::StringValue;
pub use crate::values::layout::typed::string::StringValueLike;
pub use crate::values::layout::typed::FrozenValueTyped;
pub use crate::values::layout::typed::ValueTyped;
pub use crate::values::layout::value::FrozenValue;
pub use crate::values::layout::value::Value;
pub use crate::values::layout::value::ValueLike;
pub use crate::values::owned::OwnedFrozenValue;
pub use crate::values::owned::OwnedFrozenValueTyped;
pub use crate::values::trace::Trace;
pub use crate::values::traits::ComplexValue;
pub use crate::values::traits::StarlarkValue;
pub use crate::values::types::any;
pub use crate::values::types::array;
pub use crate::values::types::bool;
pub use crate::values::types::dict;
pub use crate::values::types::enumeration;
pub use crate::values::types::exported_name;
pub use crate::values::types::float;
pub use crate::values::types::function;
pub use crate::values::types::int;
pub use crate::values::types::list;
pub use crate::values::types::none;
pub use crate::values::types::range;
pub use crate::values::types::record;
pub use crate::values::types::regex;
pub use crate::values::types::starlark_value_as_type;
pub use crate::values::types::string;
pub use crate::values::types::structs;
pub use crate::values::types::tuple;
pub use crate::values::unpack::UnpackValue;
pub use crate::values::unpack::ValueOf;
pub use crate::values::value_of_unchecked::ValueOfUnchecked;

mod alloc_value;
mod comparison;
pub(crate) mod demand;
pub(crate) mod error;
mod freeze;
pub(crate) mod frozen_ref;
mod index;
pub(crate) mod iter;
pub(crate) mod iter_type;
pub(crate) mod layout;
pub(crate) mod never;
pub(crate) mod num;
mod owned;
pub(crate) mod recursive_repr_or_json_guard;
mod stack_guard;
mod trace;
mod traits;
pub mod type_repr;
pub(crate) mod types;
pub(crate) mod typing;
mod unpack;
pub(crate) mod value_of_unchecked;


/*
 * Copyright 2018 The Starlark in Rust Authors.
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use crate::typing::Ty;
use crate::values::type_repr::StarlarkTypeRepr;
use crate::values::AllocValue;

/// Never type, can be used as native function return type.
pub(crate) enum StarlarkNever {}

impl StarlarkTypeRepr for StarlarkNever {
    fn starlark_type_repr() -> Ty {
        Ty::Never
    }
}

impl<'v> AllocValue<'v> for StarlarkNever {
    fn alloc_value(self, _heap: &'v crate::values::Heap) -> crate::values::Value<'v> {
        match self {}
    }
}


/*
 * Copyright 2018 The Starlark in Rust Authors.
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! Helpers for numerical values.

use std::cmp::Ordering;
use std::ops::Add;
use std::ops::Mul;
use std::ops::Sub;

use dupe::Dupe;
use either::Either;

use crate::collections::StarlarkHashValue;
use crate::typing::Ty;
use crate::values::type_repr::StarlarkTypeRepr;
use crate::values::types::float::StarlarkFloat;
use crate::values::types::int_or_big::StarlarkInt;
use crate::values::types::int_or_big::StarlarkIntRef;
use crate::values::AllocFrozenValue;
use crate::values::AllocValue;
use crate::values::FrozenHeap;
use crate::values::FrozenValue;
use crate::values::Heap;
use crate::values::UnpackValue;
use crate::values::Value;
use crate::values::ValueLike;

#[derive(Debug, thiserror::Error)]
enum NumError {
    #[error("float division by zero: {0} / {1}")]
    DivisionByZero(Num, Num),
}

/// [`NumRef`] represents a numerical value that can be unpacked from a [`Value`].
///
/// It's an intermediate representation that facilitates conversions between
/// numerical types and helps in implementation of arithmetical operations
/// between them.
#[derive(Clone, Debug, Dupe, Copy)]
pub(crate) enum NumRef<'v> {
    Int(StarlarkIntRef<'v>),
    Float(f64),
}

#[derive(Debug, derive_more::Display)]
pub(crate) enum Num {
    Int(StarlarkInt),
    Float(f64),
}

impl<'v> StarlarkTypeRepr for NumRef<'v> {
    fn starlark_type_repr() -> Ty {
        Either::<StarlarkIntRef, StarlarkFloat>::starlark_type_repr()
    }
}

impl StarlarkTypeRepr for Num {
    fn starlark_type_repr() -> Ty {
        NumRef::starlark_type_repr()
    }
}

impl<'v> UnpackValue<'v> for NumRef<'v> {
    fn expected() -> String {
        "int or float".to_owned()
    }

    #[allow(clippy::manual_map)]
    fn unpack_value(value: Value<'v>) -> Option<Self> {
        if let Some(i) = StarlarkIntRef::unpack_value(value) {
            Some(NumRef::Int(i))
        } else if let Some(f) = value.downcast_ref::<StarlarkFloat>() {
            Some(NumRef::Float(f.0))
        } else {
            None
        }
    }
}

impl<'v> AllocValue<'v> for Num {
    fn alloc_value(self, heap: &'v Heap) -> Value<'v> {
        match self {
            Self::Int(i) => heap.alloc(i),
            Self::Float(f) => heap.alloc(f),
        }
    }
}

impl AllocFrozenValue for Num {
    fn alloc_frozen_value(self, heap: &FrozenHeap) -> FrozenValue {
        match self {
            Self::Int(i) => heap.alloc(i),
            Self::Float(f) => heap.alloc(f),
        }
    }
}

impl<'v> NumRef<'v> {
    /// Get underlying value as float
    pub(crate) fn as_float(&self) -> f64 {
        match self {
            Self::Int(i) => i.to_f64(),
            Self::Float(f) => *f,
        }
    }

    pub(crate) fn f64_to_i32_exact(f: f64) -> Option<i32> {
        let i = f as i32;
        if i as f64 == f { Some(i) } else { None }
    }

    /// Get underlying value as int (if it can be precisely expressed as int)
    pub(crate) fn as_int(&self) -> Option<i32> {
        match self {
            Self::Int(i) => i.to_i32(),
            Self::Float(f) => Self::f64_to_i32_exact(*f),
        }
    }

    /// Get hash of the underlying number
    pub(crate) fn get_hash_64(self) -> u64 {
        fn float_hash(f: f64) -> u64 {
            if f.is_nan() {
                // all possible NaNs should hash to the same value
                0
            } else if f.is_infinite() {
                u64::MAX
            } else if f == 0.0 {
                // Both 0.0 and -0.0 need the same hash, but are both equal to 0.0
                0.0f64.to_bits()
            } else {
                f.to_bits()
            }
        }

        match (self.as_int(), self) {
            // equal ints and floats should have the same hash
            (Some(i), _) => i as u64,
            (None, Self::Float(f)) => float_hash(f),
            (None, Self::Int(StarlarkIntRef::Small(i))) => {
                // shouldn't happen - as_int() should have resulted in an int
                i.to_i32() as u64
            }
            (None, Self::Int(StarlarkIntRef::Big(b))) => {
                // Not perfect, but OK: `1000000000000000000000003` and `1000000000000000000000005`
                // flush to the same float, and neither is exact float,
                // so we could use better hash for such numbers.
                float_hash(b.to_f64())
            }
        }
    }

    pub(crate) fn get_hash(self) -> StarlarkHashValue {
        StarlarkHashValue::hash_64(self.get_hash_64())
    }

    fn to_owned(self) -> Num {
        match self {
            NumRef::Int(i) => Num::Int(i.to_owned()),
            NumRef::Float(f) => Num::Float(f),
        }
    }

    pub(crate) fn div(self, other: NumRef) -> anyhow::Result<f64> {
        let a = self.as_float();
        let b = other.as_float();
        if b == 0.0 {
            Err(NumError::DivisionByZero(self.to_owned(), other.to_owned()).into())
        } else {
            Ok(a / b)
        }
    }

    pub(crate) fn floor_div(self, other: NumRef) -> anyhow::Result<Num> {
        if let (NumRef::Int(a), NumRef::Int(b)) = (self, other) {
            a.floor_div(b).map(Num::Int)
        } else {
            StarlarkFloat::floor_div_impl(self.as_float(), other.as_float()).map(Num::Float)
        }
    }

    pub(crate) fn percent(self, other: NumRef) -> anyhow::Result<Num> {
        if let (NumRef::Int(a), NumRef::Int(b)) = (self, other) {
            a.percent(b).map(Num::Int)
        } else {
            StarlarkFloat::percent_impl(self.as_float(), other.as_float()).map(Num::Float)
        }
    }
}

impl<'v> From<f64> for NumRef<'v> {
    fn from(f: f64) -> Self {
        Self::Float(f)
    }
}

/// This is total eq per starlark spec, not Rust's partial eq.
impl<'v> PartialEq for NumRef<'v> {
    fn eq(&self, other: &Self) -> bool {
        if let (NumRef::Int(a), NumRef::Int(b)) = (self, other) {
            a == b
        } else {
            StarlarkFloat::compare_impl(self.as_float(), other.as_float()) == Ordering::Equal
        }
    }
}

impl<'v> Eq for NumRef<'v> {}

impl<'v> PartialOrd for NumRef<'v> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl<'v> Ord for NumRef<'v> {
    fn cmp(&self, other: &Self) -> Ordering {
        if let (NumRef::Int(a), NumRef::Int(b)) = (self, other) {
            a.cmp(b)
        } else {
            StarlarkFloat::compare_impl(self.as_float(), other.as_float())
        }
    }
}

impl<'v> Add for NumRef<'v> {
    type Output = Num;

    fn add(self, rhs: Self) -> Self::Output {
        if let (NumRef::Int(a), NumRef::Int(b)) = (self, rhs) {
            return Num::Int(a + b);
        }
        Num::Float(self.as_float() + rhs.as_float())
    }
}

impl<'v> Sub for NumRef<'v> {
    type Output = Num;

    fn sub(self, rhs: Self) -> Self::Output {
        if let (NumRef::Int(a), NumRef::Int(b)) = (self, rhs) {
            return Num::Int(a - b);
        }
        Num::Float(self.as_float() - rhs.as_float())
    }
}

impl<'v> Mul for NumRef<'v> {
    type Output = Num;

    fn mul(self, rhs: Self) -> Self::Output {
        if let (NumRef::Int(a), NumRef::Int(b)) = (self, rhs) {
            return Num::Int(a * b);
        }
        Num::Float(self.as_float() * rhs.as_float())
    }
}

#[cfg(test)]
mod tests {
    use num_bigint::BigInt;

    use super::*;
    use crate::values::types::inline_int::InlineInt;

    #[test]
    fn test_from_value() {
        assert!(NumRef::unpack_value(Value::new_bool(true)).is_none());
        assert!(NumRef::unpack_value(Value::new_bool(false)).is_none());
        assert!(NumRef::unpack_value(Value::new_empty_string()).is_none());
        assert!(NumRef::unpack_value(Value::new_none()).is_none());

        assert_eq!(
            NumRef::unpack_value(Value::testing_new_int(0))
                .unwrap()
                .as_int(),
            Some(0)
        );
        assert_eq!(
            NumRef::unpack_value(Value::testing_new_int(42))
                .unwrap()
                .as_int(),
            Some(42)
        );
        assert_eq!(
            NumRef::unpack_value(Value::testing_new_int(-42))
                .unwrap()
                .as_int(),
            Some(-42)
        );
    }

    #[test]
    fn test_conversion_to_float() {
        assert_eq!(
            NumRef::Int(StarlarkIntRef::Small(InlineInt::ZERO)).as_float(),
            0.0
        );
        assert_eq!(
            NumRef::Int(StarlarkIntRef::Small(InlineInt::MAX)).as_float(),
            InlineInt::MAX.to_f64()
        );
        assert_eq!(
            NumRef::Int(StarlarkIntRef::Small(InlineInt::MIN)).as_float(),
            InlineInt::MIN.to_f64()
        );

        assert_eq!(NumRef::Float(0.0).as_float(), 0.0);
        assert!(NumRef::Float(f64::NAN).as_float().is_nan());
    }

    #[test]
    fn test_conversion_to_int() {
        assert_eq!(
            NumRef::Int(StarlarkIntRef::Small(InlineInt::testing_new(0))).as_int(),
            Some(0)
        );
        assert_eq!(
            NumRef::Int(StarlarkIntRef::Small(InlineInt::testing_new(42))).as_int(),
            Some(42)
        );
        assert_eq!(
            NumRef::Int(StarlarkIntRef::Small(InlineInt::testing_new(-42))).as_int(),
            Some(-42)
        );

        assert_eq!(NumRef::Float(0_f64).as_int(), Some(0));
        assert_eq!(NumRef::Float(42_f64).as_int(), Some(42));
        assert_eq!(NumRef::Float(-42_f64).as_int(), Some(-42));

        assert_eq!(NumRef::Float(i32::MIN as f64).as_int(), Some(i32::MIN));
        assert_eq!(NumRef::Float(i32::MAX as f64).as_int(), Some(i32::MAX));

        assert_eq!(NumRef::Float(42.75).as_int(), None);
        assert_eq!(NumRef::Float(-42.75).as_int(), None);
        assert_eq!(NumRef::Float(f64::NAN).as_int(), None);
        assert_eq!(NumRef::Float(f64::INFINITY).as_int(), None);
        assert_eq!(NumRef::Float(f64::NEG_INFINITY).as_int(), None);
    }

    #[test]
    fn test_hashing() {
        assert_eq!(
            NumRef::Int(StarlarkIntRef::Small(InlineInt::testing_new(0))).get_hash_64(),
            NumRef::Float(0.0).get_hash_64()
        );
        assert_eq!(
            NumRef::Int(StarlarkIntRef::Small(InlineInt::testing_new(42))).get_hash_64(),
            NumRef::Float(42.0).get_hash_64()
        );

        assert_eq!(
            NumRef::Float(f64::INFINITY + f64::NEG_INFINITY).get_hash_64(),
            NumRef::Float(f64::NAN).get_hash_64()
        );
        assert_eq!(
            NumRef::Float("0.25".parse().unwrap()).get_hash_64(),
            NumRef::Float("25e-2".parse().unwrap()).get_hash_64()
        );

        let x = 1u64 << 55;
        assert_eq!(x as f64 as u64, x, "Self-check");
        assert_eq!(
            NumRef::Float(x as f64).get_hash_64(),
            NumRef::Int(StarlarkInt::from(BigInt::from(x)).as_ref()).get_hash_64(),
        )
    }

    #[test]
    fn test_eq() {
        assert_eq!(NumRef::Float(f64::NAN), NumRef::Float(f64::NAN));
        assert_eq!(NumRef::Float(f64::INFINITY), NumRef::Float(f64::INFINITY));
        assert_eq!(
            NumRef::Int(StarlarkIntRef::Small(InlineInt::testing_new(10))),
            NumRef::Float(10.0)
        );
    }
}


/*
 * Copyright 2019 The Starlark in Rust Authors.
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::fmt;
use std::fmt::Display;
use std::ops::Deref;

use allocative::Allocative;
use dupe::Clone_;
use dupe::Dupe;
use dupe::Dupe_;

use crate::values::none::NoneType;
use crate::values::AllocFrozenValue;
use crate::values::FrozenHeap;
use crate::values::FrozenHeapRef;
use crate::values::FrozenValue;
use crate::values::FrozenValueTyped;
use crate::values::StarlarkValue;
use crate::values::Value;

/// A [`FrozenValue`] along with a [`FrozenHeapRef`] that ensures it is kept alive.
/// Obtained from [`FrozenModule::get`](crate::environment::FrozenModule::get) or
/// [`OwnedFrozenValue::alloc`].
///
/// While it is possible to obtain the underlying [`FrozenValue`] with
/// [`unchecked_frozen_value`](OwnedFrozenValue::unchecked_frozen_value), that approach
/// is strongly discouraged. See the other methods which unpack the code, access it as a
/// [`Value`] (which has a suitable lifetime) or add references to other heaps.
#[derive(Debug, Clone, Dupe, Allocative)]
pub struct OwnedFrozenValue {
    owner: FrozenHeapRef,
    // Invariant: this FrozenValue must be kept alive by the `owner` field.
    value: FrozenValue,
}

impl Default for OwnedFrozenValue {
    fn default() -> Self {
        OwnedFrozenValue::alloc(NoneType)
    }
}

impl Display for OwnedFrozenValue {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        Display::fmt(&self.value, f)
    }
}

impl AllocFrozenValue for OwnedFrozenValue {
    fn alloc_frozen_value(self, heap: &FrozenHeap) -> FrozenValue {
        // Safe because this is the standard expectation for alloc_frozen_value
        // - you must keep the heap you allocate it on alive.
        unsafe { self.owned_frozen_value(heap) }
    }
}

impl OwnedFrozenValue {
    /// Create an [`OwnedFrozenValue`] - generally [`OwnedFrozenValue`]s are obtained
    /// from [`FrozenModule::get`](crate::environment::FrozenModule::get).
    /// Safe provided the `value` (and any values it points at) are kept alive by the
    /// `owner`, typically because the value was created on the heap.
    ///
    /// ```
    /// use starlark::values::{FrozenHeap, OwnedFrozenValue};
    /// let heap = FrozenHeap::new();
    /// let value = heap.alloc("test");
    /// unsafe { OwnedFrozenValue::new(heap.into_ref(), value) };
    /// ```
    pub unsafe fn new(owner: FrozenHeapRef, value: FrozenValue) -> Self {
        Self { owner, value }
    }

    /// Create an [`OwnedFrozenValue`] in a new heap.
    pub fn alloc(x: impl AllocFrozenValue) -> Self {
        let heap = FrozenHeap::new();
        let val = heap.alloc(x);
        // Safe because we just created the value on the heap
        unsafe { Self::new(heap.into_ref(), val) }
    }

    /// Unpack the boolean contained in the underlying value, or [`None`] if it is not a boolean.
    pub fn unpack_bool(&self) -> Option<bool> {
        self.value.unpack_bool()
    }

    /// Unpack the int contained in the underlying value, or [`None`] if it is not an int.
    pub fn unpack_int(&self) -> Option<i32> {
        self.value.unpack_i32()
    }

    /// Unpack the string contained in the underlying value, or [`None`] if it is not an string.
    pub fn unpack_str(&self) -> Option<&str> {
        self.value.unpack_str()
    }

    /// Check if `self` references `<T>`.
    pub fn downcast<T: StarlarkValue<'static>>(self) -> Result<OwnedFrozenValueTyped<T>, Self> {
        match FrozenValueTyped::new(self.value) {
            Some(typed) => Ok(OwnedFrozenValueTyped {
                owner: self.owner,
                value: typed,
            }),
            None => Err(self),
        }
    }

    /// Obtain the [`Value`] stored inside.
    pub fn value<'v>(&'v self) -> Value<'v> {
        Value::new_frozen(self.value)
    }

    /// Extract a [`Value`] by passing the [`FrozenHeap`] which will promise to keep it alive.
    /// When using with a [`Module`](crate::environment::Module),
    /// see the [`frozen_heap`](crate::environment::Module::frozen_heap) function.
    /// If you don't care about the resulting lifetime the [`value`](OwnedFrozenValue::value) method is easier.
    pub fn owned_value<'v>(&self, heap: &'v FrozenHeap) -> Value<'v> {
        // Safe because we convert it to a value which is tied to the owning heap
        unsafe { self.owned_frozen_value(heap).to_value() }
    }

    /// Operate on the [`FrozenValue`] stored inside.
    /// Safe provided you don't store the argument [`FrozenValue`] after the closure has returned.
    /// Using this function is discouraged when possible.
    pub fn map(&self, f: impl FnOnce(FrozenValue) -> FrozenValue) -> Self {
        Self {
            owner: self.owner.dupe(),
            value: f(self.value),
        }
    }

    /// Same as [`map`](OwnedFrozenValue::map) above but with [`Result`]
    pub fn try_map<E>(
        &self,
        f: impl FnOnce(FrozenValue) -> Result<FrozenValue, E>,
    ) -> Result<Self, E> {
        Ok(Self {
            owner: self.owner.dupe(),
            value: f(self.value)?,
        })
    }

    /// Obtain a reference to the FrozenHeap that owns this value.
    pub fn owner(&self) -> &FrozenHeapRef {
        &self.owner
    }

    /// Obtain direct access to the [`FrozenValue`] that lives inside. If you drop all
    /// references to the [`FrozenHeap`] keeping it alive, any code using the [`FrozenValue`]
    /// is likely to segfault. If possible use [`value`](OwnedFrozenValue::value) or
    /// [`owned_frozen_value`](OwnedFrozenValue::owned_frozen_value).
    pub unsafe fn unchecked_frozen_value(&self) -> FrozenValue {
        self.value
    }

    /// Extract a [`FrozenValue`] by passing the [`FrozenHeap`] which will keep it alive.
    /// Provided the argument heap does indeed stay alive for the lifetime of the result,
    /// all will be fine. Unsafe if you pass the wrong heap, or don't keep the heap alive
    /// long enough. Where possible, use [`value`](OwnedFrozenValue::value) or
    /// [`owned_value`](OwnedFrozenValue::owned_value).
    pub unsafe fn owned_frozen_value(&self, heap: &FrozenHeap) -> FrozenValue {
        heap.add_reference(&self.owner);
        self.value
    }
}

/// Same as [`OwnedFrozenValue`] but it is known to contain `T`.
#[derive(Debug, Clone_, Dupe_, Allocative)]
pub struct OwnedFrozenValueTyped<T: StarlarkValue<'static>> {
    owner: FrozenHeapRef,
    value: FrozenValueTyped<'static, T>,
}

impl<T: StarlarkValue<'static>> Deref for OwnedFrozenValueTyped<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        self.value.as_ref()
    }
}

impl<T: StarlarkValue<'static>> OwnedFrozenValueTyped<T> {
    /// Erase the type.
    ///
    /// This operation is unsafe because returned value is not bound by the heap lifetime.
    /// So if the heap is dropped, the returned value quetly becomes invalid.
    pub unsafe fn to_frozen_value(&self) -> FrozenValue {
        self.value.to_frozen_value()
    }

    /// Get a value reference.
    pub fn to_value<'v>(&'v self) -> Value<'v> {
        // SAFETY: returned value lifetime is tied to self, so
        //   the heap is guaranteed to outlive the returned value.
        unsafe { self.to_frozen_value().to_value() }
    }

    /// Erase the type.
    pub fn to_owned_frozen_value(&self) -> OwnedFrozenValue {
        OwnedFrozenValue {
            owner: self.owner.dupe(),
            value: self.value.to_frozen_value(),
        }
    }

    /// Obtain a reference to the FrozenHeap that owns this value.
    pub fn owner(&self) -> &FrozenHeapRef {
        &self.owner
    }

    /// Obtain a reference to the value.
    pub fn as_ref(&self) -> &T {
        self.value.as_ref()
    }

    /// Extract a [`FrozenValue`] by passing the [`FrozenHeap`] which will keep it alive.
    ///
    /// See [`OwnedFrozenValue::owned_frozen_value`].
    pub unsafe fn owned_frozen_value(&self, heap: &FrozenHeap) -> FrozenValue {
        heap.add_reference(&self.owner);
        self.value.to_frozen_value()
    }

    /// Extract a [`Value`] by passing the [`FrozenHeap`] which will promise to keep it alive.
    ///
    /// See [`OwnedFrozenValue::owned_value`].
    pub fn owned_value<'v>(&self, heap: &'v FrozenHeap) -> Value<'v> {
        // Safe because we convert it to a value which is tied to the owning heap
        unsafe { self.owned_frozen_value(heap).to_value() }
    }

    /// Operate on the [`FrozenValue`] stored inside.
    /// Safe provided you don't store the argument [`FrozenValue`] after the closure has returned.
    /// Using this function is discouraged when possible.
    pub fn map<U: StarlarkValue<'static>>(
        &self,
        f: impl FnOnce(FrozenValueTyped<T>) -> FrozenValueTyped<U>,
    ) -> OwnedFrozenValueTyped<U> {
        OwnedFrozenValueTyped {
            owner: self.owner.dupe(),
            value: f(self.value),
        }
    }

    /// Same as [`map`](OwnedFrozenValue::map) above but with [`Result`]
    pub fn try_map<U: StarlarkValue<'static>, E>(
        &self,
        f: impl FnOnce(FrozenValueTyped<T>) -> Result<FrozenValueTyped<U>, E>,
    ) -> Result<OwnedFrozenValueTyped<U>, E> {
        Ok(OwnedFrozenValueTyped {
            owner: self.owner.dupe(),
            value: f(self.value)?,
        })
    }
}


/*
 * Copyright 2018 The Starlark in Rust Authors.
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! Detect recursion when doing `repr` or `to_json`.

use std::cell::Cell;

use crate::collections::SmallSet;
use crate::hint::unlikely;
use crate::values::layout::pointer::RawPointer;
use crate::values::Value;

/// Pop the stack on drop.
pub(crate) struct ReprStackGuard;

/// Pop the stack on drop.
pub(crate) struct JsonStackGuard;

impl Drop for ReprStackGuard {
    fn drop(&mut self) {
        REPR_STACK.with(|repr_stack| {
            let mut stack = Cell::take(repr_stack);
            let popped = stack.pop();
            debug_assert!(popped.is_some());
            repr_stack.set(stack);
        })
    }
}

impl Drop for JsonStackGuard {
    fn drop(&mut self) {
        JSON_STACK.with(|json_stack| {
            let mut stack = Cell::take(json_stack);
            let popped = stack.pop();
            debug_assert!(popped.is_some());
            json_stack.set(stack);
        })
    }
}

/// Returned when `repr` is called recursively and a cycle is detected.
pub(crate) struct ReprCycle;

/// Returned when `to_json` is called recursively and a cycle is detected.
pub(crate) struct JsonCycle;

thread_local! {
    static REPR_STACK: Cell<SmallSet<RawPointer>> = const { Cell::new(SmallSet::new()) };
}

thread_local! {
    static JSON_STACK: Cell<SmallSet<RawPointer>> = const { Cell::new(SmallSet::new()) };
}

/// Push a value to the stack, return error if it is already on the stack.
pub(crate) fn repr_stack_push(value: Value) -> Result<ReprStackGuard, ReprCycle> {
    REPR_STACK.with(|repr_stack| {
        let mut stack = Cell::take(repr_stack);
        if unlikely(!stack.insert(value.ptr_value())) {
            repr_stack.set(stack);
            Err(ReprCycle)
        } else {
            repr_stack.set(stack);
            Ok(ReprStackGuard)
        }
    })
}

/// Push a value to the stack, return error if it is already on the stack.
pub(crate) fn json_stack_push(value: Value) -> Result<JsonStackGuard, JsonCycle> {
    JSON_STACK.with(|json_stack| {
        let mut stack = Cell::take(json_stack);
        if unlikely(!stack.insert(value.ptr_value())) {
            json_stack.set(stack);
            Err(JsonCycle)
        } else {
            json_stack.set(stack);
            Ok(JsonStackGuard)
        }
    })
}


/*
 * Copyright 2019 The Starlark in Rust Authors.
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! Guard to check we don't recurse too deeply with nested operations like Equals.

use std::cell::Cell;

use crate::hint::unlikely;
use crate::values::error::ControlError;

// Maximum recursion level for comparison
// TODO(dmarting): those are rather short, maybe make it configurable?
#[cfg(debug_assertions)]
const MAX_RECURSION: u32 = 200;

#[cfg(not(debug_assertions))]
const MAX_RECURSION: u32 = 3000;

// A thread-local counter is used to detect too deep recursion.
//
// Thread-local is chosen instead of explicit function "recursion" parameter
// for two reasons:
// * It's possible to propagate stack depth across external functions like
//   `Display::to_string` where passing a stack depth parameter is hard
// * We need to guarantee that stack depth is not lost in complex invocation
//   chains like function calls compare which calls native function which calls
//   starlark function which calls to_str. We could change all evaluation stack
//   signatures to accept some "context" parameters, but passing it as
//   thread-local is easier.
thread_local! {
    static STACK_DEPTH: Cell<u32> = const { Cell::new(0) };
}

/// Stored previous stack depth before calling `try_inc`.
///
/// Stores that previous stack depths back to thread-local on drop.
#[must_use]
// QUESTION: Is this useful? If we build a deep structure so deep equals
//           dies, won't we just die in `drop` anyway?
pub struct StackGuard {
    prev_depth: u32,
}

impl Drop for StackGuard {
    fn drop(&mut self) {
        STACK_DEPTH.with(|stack_depth| {
            stack_depth.set(self.prev_depth);
        });
    }
}

/// Increment stack depth.
fn inc() -> StackGuard {
    STACK_DEPTH.with(|stack_depth| {
        let prev_depth = stack_depth.get();
        stack_depth.set(prev_depth + 1);
        StackGuard { prev_depth }
    })
}

/// Check stack depth does not exceed configured max stack depth.
fn check() -> anyhow::Result<()> {
    if unlikely(STACK_DEPTH.with(|stack_depth| stack_depth.get()) >= MAX_RECURSION) {
        return Err(ControlError::TooManyRecursionLevel.into());
    }
    Ok(())
}

/// Try increment stack depth.
///
/// Return opaque object which resets stack to previous value
/// on `drop`.
///
/// If stack depth exceeds configured limit, return error.
pub(crate) fn stack_guard() -> anyhow::Result<StackGuard> {
    check()?;
    Ok(inc())
}


/*
 * Copyright 2018 The Starlark in Rust Authors.
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::cell::Cell;
use std::cell::RefCell;
use std::cell::UnsafeCell;
use std::marker;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::AtomicI16;
use std::sync::atomic::AtomicI32;
use std::sync::atomic::AtomicI64;
use std::sync::atomic::AtomicI8;
use std::sync::atomic::AtomicIsize;
use std::sync::atomic::AtomicU16;
use std::sync::atomic::AtomicU32;
use std::sync::atomic::AtomicU64;
use std::sync::atomic::AtomicU8;
use std::sync::atomic::AtomicUsize;
use std::sync::Arc;
use std::sync::Mutex;

use either::Either;
use hashbrown::raw::RawTable;
use starlark_map::small_set::SmallSet;

use crate::collections::SmallMap;
use crate::values::FrozenValue;
use crate::values::Tracer;
use crate::values::Value;

/// Called by the garbage collection, and must walk over every contained `Value` in the type.
/// Marked `unsafe` because if you miss a nested `Value`, it will probably segfault.
///
/// For the most cases `#[derive(Trace)]` is enough to implement this trait:
///
/// ```
/// # use starlark::values::Value;
/// # use starlark::values::Trace;
///
/// #[derive(Trace)]
/// struct MySet<'v> {
///    keys: Vec<Value<'v>>
/// }
/// ```
pub unsafe trait Trace<'v> {
    /// Recursively "trace" the value.
    ///
    /// Note during trace, `Value` objects in `Self` might be already special forward-objects,
    /// trying to unpack these values may crash the process.
    ///
    /// Generally this function should not do anything except calling `trace` on the fields.
    fn trace(&mut self, tracer: &Tracer<'v>);
}

unsafe impl<'v, T: Trace<'v>> Trace<'v> for Vec<T> {
    fn trace(&mut self, tracer: &Tracer<'v>) {
        self.iter_mut().for_each(|x| x.trace(tracer));
    }
}

unsafe impl<'v, T: Trace<'v>> Trace<'v> for [T] {
    fn trace(&mut self, tracer: &Tracer<'v>) {
        self.iter_mut().for_each(|x| x.trace(tracer));
    }
}

unsafe impl<'v, T: Trace<'v>> Trace<'v> for RawTable<T> {
    fn trace(&mut self, tracer: &Tracer<'v>) {
        unsafe {
            self.iter().for_each(|e| e.as_mut().trace(tracer));
        }
    }
}

unsafe impl<'v, K: Trace<'v>, V: Trace<'v>> Trace<'v> for SmallMap<K, V> {
    fn trace(&mut self, tracer: &Tracer<'v>) {
        for (k, v) in self.iter_mut_unchecked() {
            k.trace(tracer);
            v.trace(tracer);
        }
    }
}

unsafe impl<'v, T: Trace<'v>> Trace<'v> for SmallSet<T> {
    fn trace(&mut self, tracer: &Tracer<'v>) {
        for v in self.iter_mut_unchecked() {
            v.trace(tracer);
        }
    }
}

unsafe impl<'v, T: Trace<'v>> Trace<'v> for Option<T> {
    fn trace(&mut self, tracer: &Tracer<'v>) {
        if let Some(x) = self {
            x.trace(tracer)
        }
    }
}

unsafe impl<'v, T: Trace<'v>> Trace<'v> for RefCell<T> {
    fn trace(&mut self, tracer: &Tracer<'v>) {
        self.get_mut().trace(tracer)
    }
}

unsafe impl<'v, T: Trace<'v>> Trace<'v> for Cell<T> {
    fn trace(&mut self, tracer: &Tracer<'v>) {
        self.get_mut().trace(tracer);
    }
}

unsafe impl<'v, T: Trace<'v>> Trace<'v> for UnsafeCell<T> {
    fn trace(&mut self, tracer: &Tracer<'v>) {
        self.get_mut().trace(tracer);
    }
}

unsafe impl<'v, T: Trace<'v> + ?Sized> Trace<'v> for Box<T> {
    fn trace(&mut self, tracer: &Tracer<'v>) {
        Box::as_mut(self).trace(tracer)
    }
}

unsafe impl<'v> Trace<'v> for () {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}

unsafe impl<'v, T1: Trace<'v>> Trace<'v> for (T1,) {
    fn trace(&mut self, tracer: &Tracer<'v>) {
        self.0.trace(tracer);
    }
}

unsafe impl<'v, T1: Trace<'v>, T2: Trace<'v>> Trace<'v> for (T1, T2) {
    fn trace(&mut self, tracer: &Tracer<'v>) {
        self.0.trace(tracer);
        self.1.trace(tracer);
    }
}

unsafe impl<'v, T1: Trace<'v>, T2: Trace<'v>, T3: Trace<'v>> Trace<'v> for (T1, T2, T3) {
    fn trace(&mut self, tracer: &Tracer<'v>) {
        self.0.trace(tracer);
        self.1.trace(tracer);
        self.2.trace(tracer);
    }
}

unsafe impl<'v, T1: Trace<'v>, T2: Trace<'v>, T3: Trace<'v>, T4: Trace<'v>> Trace<'v>
    for (T1, T2, T3, T4)
{
    fn trace(&mut self, tracer: &Tracer<'v>) {
        self.0.trace(tracer);
        self.1.trace(tracer);
        self.2.trace(tracer);
        self.3.trace(tracer);
    }
}

unsafe impl<'v, T1: Trace<'v>, T2: Trace<'v>> Trace<'v> for Either<T1, T2> {
    fn trace(&mut self, tracer: &Tracer<'v>) {
        match self {
            Either::Left(x) => x.trace(tracer),
            Either::Right(x) => x.trace(tracer),
        }
    }
}

unsafe impl<'v> Trace<'v> for Value<'v> {
    fn trace(&mut self, tracer: &Tracer<'v>) {
        tracer.trace(self)
    }
}

unsafe impl<'v> Trace<'v> for FrozenValue {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}

unsafe impl<'v> Trace<'v> for String {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}

unsafe impl<'v> Trace<'v> for usize {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}

unsafe impl<'v> Trace<'v> for i32 {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}

unsafe impl<'v> Trace<'v> for u32 {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}

unsafe impl<'v> Trace<'v> for u64 {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}

unsafe impl<'v> Trace<'v> for bool {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}

unsafe impl<'v> Trace<'v> for AtomicBool {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}

unsafe impl<'v> Trace<'v> for AtomicI8 {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}

unsafe impl<'v> Trace<'v> for AtomicU8 {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}

unsafe impl<'v> Trace<'v> for AtomicI16 {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}

unsafe impl<'v> Trace<'v> for AtomicU16 {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}

unsafe impl<'v> Trace<'v> for AtomicI32 {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}

unsafe impl<'v> Trace<'v> for AtomicU32 {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}

unsafe impl<'v> Trace<'v> for AtomicI64 {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}

unsafe impl<'v> Trace<'v> for AtomicU64 {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}

unsafe impl<'v> Trace<'v> for AtomicUsize {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}

unsafe impl<'v> Trace<'v> for AtomicIsize {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}

unsafe impl<'v> Trace<'v> for std::time::Instant {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}

unsafe impl<'v, T> Trace<'v> for marker::PhantomData<T> {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}

unsafe impl<'v, T: Trace<'v>> Trace<'v> for Arc<Mutex<T>> {
    fn trace(&mut self, tracer: &Tracer<'v>) {
        self.lock().unwrap().trace(tracer);
    }
}

unsafe impl<'v, A, R> Trace<'v> for fn(A) -> R {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}

unsafe impl<'v, A, B, R> Trace<'v> for fn(A, B) -> R {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}

unsafe impl<'v, A, B, C, R> Trace<'v> for fn(A, B, C) -> R {
    fn trace(&mut self, _tracer: &Tracer<'v>) {}
}


/*
 * Copyright 2018 The Starlark in Rust Authors.
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! The values module define a trait `StarlarkValue` that defines the attribute of
//! any value in Starlark and a few macro to help implementing this trait.
//! The `Value` struct defines the actual structure holding a StarlarkValue. It is
//! mostly used to enable mutable and Rc behavior over a StarlarkValue.
//! This modules also defines this traits for the basic immutable values: int,
//! bool and NoneType. Sub-modules implement other common types of all Starlark
//! dialect.
//!
//! __Note__: we use _sequence_, _iterable_ and _indexable_ according to the
//! definition in the [Starlark specification](
//! https://github.com/google/skylark/blob/a0e5de7e63b47e716cca7226662a4c95d47bf873/doc/spec.md#sequence-types).
//! We also use the term _container_ for denoting any of those type that can
//! hold several values.

use std::cmp::Ordering;
use std::fmt::Debug;
use std::fmt::Display;
use std::fmt::Write;

use allocative::Allocative;
use erased_serde::Serialize;
use starlark_derive::starlark_internal_vtable;
use starlark_map::StarlarkHashValue;

use crate::any::ProvidesStaticType;
use crate::collections::Hashed;
use crate::collections::StarlarkHasher;
use crate::docs::DocItem;
use crate::environment::Methods;
use crate::eval::Arguments;
use crate::eval::Evaluator;
use crate::private::Private;
use crate::typing::Ty;
use crate::values::demand::Demand;
use crate::values::error::ControlError;
use crate::values::function::FUNCTION_TYPE;
use crate::values::Freeze;
use crate::values::FrozenStringValue;
use crate::values::Heap;
use crate::values::Trace;
use crate::values::Value;
use crate::values::ValueError;

/// A trait for values which are more complex - because they are either mutable,
/// or contain references to other values.
///
/// For values that contain nested [`Value`] types (mutable or not) there are a bunch of helpers
/// and macros.
///
/// ## Types containing [`Value`]
///
/// A Starlark type containing values will need to exist in two states: one containing [`Value`]
/// and one containing [`FrozenValue`](crate::values::FrozenValue). To deal with that, if we are defining the type
/// containing a single value, let's call it `One`, we'd define `OneGen`
/// (for the general version), and then have the
/// [`starlark_complex_value!`](crate::starlark_complex_value!) macro
/// generate `One` and `FrozenOne` aliases.
///
/// ```
/// use starlark::values::{ProvidesStaticType, ComplexValue, Coerce, Freezer, FrozenValue, StarlarkValue, Value, ValueLike, Trace, Tracer, Freeze, NoSerialize};
/// use starlark::{starlark_complex_value};
/// use derive_more::Display;
/// use allocative::Allocative;
/// use starlark_derive::starlark_value;
///
/// #[derive(Debug, Trace, Coerce, Display, ProvidesStaticType, NoSerialize, Allocative)]
/// #[repr(C)]
/// struct OneGen<V>(V);
/// starlark_complex_value!(One);
///
/// #[starlark_value(type = "one")]
/// impl<'v, V: ValueLike<'v> + 'v> StarlarkValue<'v> for OneGen<V>
///     where Self: ProvidesStaticType<'v>,
/// {
///     // To implement methods which work for both `One` and `FrozenOne`,
///     // use the `ValueLike` trait.
/// }
///
/// impl<'v> Freeze for One<'v> {
///     type Frozen = FrozenOne;
///     fn freeze(self, freezer: &Freezer) -> anyhow::Result<Self::Frozen> {
///         Ok(OneGen(self.0.freeze(freezer)?))
///     }
/// }
/// ```
///
/// The [`starlark_complex_value!`](crate::starlark_complex_value!) requires that
/// the type have an instance for `Coerce`, then the macro defines two type aliases.
///
/// ```
/// # use crate::starlark::values::*;
/// # #[derive(Debug, Trace)]
/// # struct OneGen<V>(V);
/// type One<'v> = OneGen<Value<'v>>;
/// type FrozenOne = OneGen<FrozenValue>;
/// ```
///
/// To make these aliases public (or public to the crate) pass a visibility
/// to the macro, e.g. `starlark_complex_value!(pub One)`.
///
/// The macro also defines instances of [`ProvidesStaticType`] for both,
/// [`AllocValue`](crate::values::AllocValue) for both,
/// [`AllocFrozenValue`](crate::values::AllocFrozenValue) for the frozen one, and
/// [`UnpackValue`](crate::values::UnpackValue) for the non-frozen one.
/// It also defines the methods:
///
/// ```
/// # use crate::starlark::values::*;
/// # use std::cell::RefMut;
/// # struct OneGen<V>(V);
/// # type One<'v> = OneGen<Value<'v>>;
/// impl<'v> One<'v> {
///     // Obtain a reference to `One` from a `Value`, regardless
///     // of whether the underlying `Value` is a `One` or `FrozenOne`.
///     pub fn from_value(x: Value<'v>) -> Option<&'v Self> {
/// # unimplemented!(
/// # r#"
///         ...
/// # "#);
///     }
/// }
/// ```
///
/// ## Different types
///
/// If the types are different between the frozen and non-frozen values you can define your own
/// type specialisations as `type One<'v> = OneGen<Value<'v>>` and `type FrozenOne = OneGen<String>`
/// and use [`starlark_complex_values!`](crate::starlark_complex_values!) which will provide similar facilities to
/// [`starlark_complex_value!`](crate::starlark_simple_value!).
///
/// ## Other types
///
/// The macro [`starlark_complex_value!`](crate::starlark_complex_value!) is applicable
/// when there is a single base type, `FooGen<V>`, with specialisations
/// `FooGen<Value<'v>>` and `FooGen<FrozenValue>`.
/// If you have a type where the difference between frozen and non-frozen does not follow this
/// pattern then you will have to write instances of the traits you need manually.
/// Examples of cases where the macro doesn't work include:
///
/// * If your type doesn't contain any [`Value`] types, but instead implements this trait for mutability.
/// * If the difference between frozen and non-frozen is more complex, e.g. a [`Cell`](std::cell::Cell)
///   when non-frozen and a direct value when frozen.
pub trait ComplexValue<'v>: StarlarkValue<'v> + Trace<'v> + Freeze
where
    <Self as Freeze>::Frozen: StarlarkValue<'static>,
{
}

impl<'v, V> ComplexValue<'v> for V
where
    V: StarlarkValue<'v> + Trace<'v> + Freeze,
    <V as Freeze>::Frozen: StarlarkValue<'static>,
{
}

/// How to put a Rust values into [`Value`]s.
///
/// Every Rust value stored in a [`Value`] must implement this trait.
/// You _must_ also implement [`ComplexValue`] if:
///
/// * A type is _mutable_, if you ever need to get a `&mut self` reference to it.
/// * A type _contains_ nested Starlark [`Value`]s.
///
/// There are only two required members of [`StarlarkValue`], namely
/// [`TYPE`](StarlarkValue::TYPE)
/// and [`get_type_value_static`](StarlarkValue::get_type_value_static).
/// Both these should be implemented with the [`starlark_value`](crate::values::starlark_value)
/// proc macro:
///
/// ```
/// use starlark::values::StarlarkValue;
/// use starlark::values::ProvidesStaticType;
/// use starlark::values::NoSerialize;
/// # use starlark::starlark_simple_value;
/// use derive_more::Display;
/// use allocative::Allocative;
/// use starlark_derive::starlark_value;
///
/// #[derive(Debug, Display, ProvidesStaticType, NoSerialize, Allocative)]
/// #[display(fmt = "Foo")]
/// struct Foo;
/// # starlark_simple_value!(Foo);
/// #[starlark_value(type = "foo")]
/// impl<'v> StarlarkValue<'v> for Foo {
/// }
/// ```
///
/// Every additional field enables further features in Starlark. In most cases the default
/// implementation returns an "unimplemented" [`Err`].
///
/// # Note To Implementors
/// Any additional methods that are added to this trait also need to be added to the
/// [`StarlarkValue`] implementation in `crate::values::layout::avalue::Wrapper`. Otherwise,
/// any implementations other than the default implementation will not be run.
#[starlark_internal_vtable]
pub trait StarlarkValue<'v>:
    'v + ProvidesStaticType<'v> + Allocative + Debug + Display + Serialize + Sized
{
    /// Return a string describing the type of self, as returned by the type()
    /// function.
    ///
    /// This can be only implemented by the [`#[starlark_value]`](crate::values::starlark_value)
    /// proc macro.
    const TYPE: &'static str;

    /// Like [`TYPE`](Self::TYPE), but returns a reusable [`FrozenStringValue`]
    /// pointer to it. This function deliberately doesn't take a heap,
    /// as it would not be performant to allocate a new value each time.
    ///
    /// This can be only implemented by the [`#[starlark_value]`](crate::values::starlark_value)
    /// proc macro.
    fn get_type_value_static() -> FrozenStringValue;

    /// Return a string that is the representation of a type that a user would use in
    /// type annotations. This often will be the same as [`Self::TYPE`], but in
    /// some instances it might be slightly different than what is returned by `TYPE`.
    ///
    /// This can be only implemented by the [`#[starlark_value]`](crate::values::starlark_value)
    /// proc macro.
    fn get_type_starlark_repr() -> Ty {
        Ty::name(Self::TYPE)
    }

    /// Please do not implement this method or `get_type`,
    /// but use [`#[starlark_value]`](crate::values::starlark_value) proc macro.
    #[doc(hidden)]
    #[starlark_internal_vtable(skip)]
    fn please_use_starlark_type_macro();

    /// Type is special in Starlark, it is implemented differently than user defined types.
    /// For example, some special types like `bool` cannon be heap allocated.
    ///
    /// This function must not be implemented outside of starlark crate.
    #[doc(hidden)]
    #[starlark_internal_vtable(skip)]
    fn is_special(_private: Private) -> bool {
        false
    }

    /// Is this value a match for a named type. Usually returns `true` for
    /// values matching `get_type`, but might also work for subtypes it implements.
    fn matches_type(&self, ty: &str) -> bool {
        Self::TYPE == ty
    }

    /// Function is implemented for types values.
    #[doc(hidden)]
    fn type_matches_value(&self, _value: Value<'v>, _private: Private) -> bool {
        unreachable!("`type_matches_value` should only be called on special types")
    }

    /// Get the members associated with this type, accessible via `this_type.x`.
    /// These members will have `dir`/`getattr`/`hasattr` properly implemented,
    /// so it is the preferred way to go if possible. See
    /// [`MethodsStatic`](crate::environment::MethodsStatic) for an example of how
    /// to define this method.
    fn get_methods() -> Option<&'static Methods>
    where
        Self: Sized,
    {
        None
    }

    /// Return structured documentation for self, if available.
    fn documentation(&self) -> Option<DocItem>
    where
        Self: Sized,
    {
        Self::get_methods().map(|methods| DocItem::Object(methods.documentation()))
    }

    /// Type of this instance for typechecker.
    /// Note this can be more precise than generic type.
    #[doc(hidden)]
    fn typechecker_ty(&self, _private: Private) -> Option<Ty> {
        None
    }

    /// Evaluate this value as a type expression. Basically, `eval_type(this)`.
    #[doc(hidden)]
    fn eval_type(&self, _private: Private) -> Option<Ty> {
        None
    }

    /// Return a string representation of self, as returned by the `repr()` function.
    /// Defaults to the `Display` instance - which should be fine for nearly all types.
    /// In many cases the `repr()` representation will also be a Starlark expression
    /// for creating the value.
    ///
    /// ```rust
    /// # starlark::assert::all_true(r#"
    /// repr("test") == '"test"'
    /// repr([1,2,3]) == '[1, 2, 3]'
    /// repr([1,[2,3]]) == '[1, [2, 3]]'
    /// repr([1]) == '[1]'
    /// repr([]) == '[]'
    /// # "#);
    /// ```
    fn collect_repr(&self, collector: &mut String) {
        // Rust won't return Err when writing to a String, so safe unwrap
        write!(collector, "{}", self).unwrap()
    }

    /// Invoked to print `repr` when a cycle is the object stack is detected.
    fn collect_repr_cycle(&self, collector: &mut String) {
        write!(collector, "<{}...>", Self::TYPE).unwrap()
    }

    /// String used when printing call stack. `repr(self)` by default.
    fn name_for_call_stack(&self, me: Value<'v>) -> String {
        me.to_repr()
    }

    /// Convert self to a boolean, as returned by the bool() function.
    /// The default implementation returns [`true`].
    fn to_bool(&self) -> bool {
        // Return `true` by default, because this is default when implementing
        // custom types in Python: https://docs.python.org/release/2.5.2/lib/truth.html
        true
    }

    /// Return a hash data for self to be used when self is placed as a key in a `Dict`.
    /// Return an [`Err`] if there is no hash for this value (e.g. list).
    /// Must be stable between frozen and non-frozen values.
    fn write_hash(&self, hasher: &mut StarlarkHasher) -> anyhow::Result<()> {
        if Self::TYPE == FUNCTION_TYPE {
            // The Starlark spec says values of type "function" must be hashable.
            // We could return the address of the function, but that changes
            // with frozen/non-frozen which breaks freeze for Dict.
            // We could create an atomic counter and use that, but it takes memory,
            // effort, complexity etc, and we don't expect many Dict's keyed by
            // function. Returning 0 as the hash is valid, as Eq will sort it out.
            let _ = hasher;
            Ok(())
        } else {
            Err(ControlError::NotHashableValue(Self::TYPE.to_owned()).into())
        }
    }

    /// Get the hash value. Calls [`write_hash`](Self::write_hash) by default.
    #[doc(hidden)]
    fn get_hash(&self, _private: Private) -> anyhow::Result<StarlarkHashValue> {
        let mut hasher = StarlarkHasher::new();
        self.write_hash(&mut hasher)?;
        Ok(hasher.finish_small())
    }

    /// Compare `self` with `other` for equality.
    /// Should only return an error on excessive recursion.
    ///
    /// This function can only be called when it is known that self pointer
    /// is not equal to the other pointer. Thus, an implementation may assume
    /// that the pointers are not equal. Implementation of `equals` for some
    /// builtin types and default implementation rely on this assumption.
    ///
    /// Equality must be symmetric (`a == b` implies `b == a`).
    /// When evaluating `a == b` (or when using equality in dicts and such),
    /// it is not specified whether `a.equals(b)` or `b.equals(a)` is called.
    fn equals(&self, _other: Value<'v>) -> anyhow::Result<bool> {
        // Type is only equal via a pointer
        Ok(false)
    }

    /// Compare `self` with `other`.
    /// This method returns a result of type [`Ordering`], or an [`Err`]
    /// if the two types differ.
    fn compare(&self, other: Value<'v>) -> anyhow::Result<Ordering> {
        ValueError::unsupported_with(self, "compare", other)
    }

    /// Directly invoke a function.
    /// The number of `named` and `names` arguments are guaranteed to be equal.
    fn invoke(
        &self,
        _me: Value<'v>,
        _args: &Arguments<'v, '_>,
        _eval: &mut Evaluator<'v, '_>,
    ) -> anyhow::Result<Value<'v>> {
        ValueError::unsupported(self, "call()")
    }

    /// Invoke this function as a method (after getattr, so this object might be unbound).
    ///
    /// This is an internal operation, and does not need to be implemented
    /// or used outside of the Starlark crate.
    #[doc(hidden)]
    fn invoke_method(
        &self,
        _me: Value<'v>,
        _this: Value<'v>,
        _args: &Arguments<'v, '_>,
        _eval: &mut Evaluator<'v, '_>,
        _sealed: Private,
    ) -> anyhow::Result<Value<'v>> {
        unreachable!("invoke_method should only be invoked for method or attribute");
    }

    /// Return the result of `a[index]` if `a` is indexable.
    fn at(&self, index: Value<'v>, _heap: &'v Heap) -> anyhow::Result<Value<'v>> {
        ValueError::unsupported_with(self, "[]", index)
    }

    /// Return the result of `a[index0, index1]` if `a` is indexable by two parameters.
    fn at2(
        &self,
        _index0: Value<'v>,
        _index1: Value<'v>,
        _heap: &'v Heap,
        _private: Private,
    ) -> anyhow::Result<Value<'v>> {
        ValueError::unsupported(self, "[,]")
    }

    /// Extract a slice of the underlying object if the object is indexable. The
    /// result will be object between `start` and `stop` (both of them are
    /// added length() if negative and then clamped between 0 and length()).
    /// `stride` indicates the direction.
    ///
    /// # Parameters
    ///
    /// * start: the start of the slice.
    /// * stop: the end of the slice.
    /// * stride: the direction of slice,
    ///
    /// # Examples
    ///
    /// ```rust
    /// # starlark::assert::all_true(r#"
    /// 'abc'[1:] == 'bc'         # Remove the first element
    /// 'abc'[:-1] == 'ab'        # Remove the last element
    /// 'abc'[1:-1] == 'b'        # Remove the first and the last element
    /// 'abc'[-1:] == 'c'         # Take the last letter
    /// 'abc'[:1] == 'a'          # Take the first letter
    /// 'banana'[1::2] == 'aaa'   # Select one element out of 2, skipping the first
    /// 'banana'[4::-2] == 'nnb'  # Select one element out of 2 in reverse order, starting at index 4
    /// # "#);
    /// ```
    fn slice(
        &self,
        _start: Option<Value<'v>>,
        _stop: Option<Value<'v>>,
        _stride: Option<Value<'v>>,
        _heap: &'v Heap,
    ) -> anyhow::Result<Value<'v>> {
        ValueError::unsupported(self, "[::]")
    }

    /// Implement iteration over the value of this container by providing
    /// the values in a `Vec`.
    #[starlark_internal_vtable(skip)]
    fn iterate_collect(&self, _heap: &'v Heap) -> anyhow::Result<Vec<Value<'v>>> {
        ValueError::unsupported(self, "(iter)")
    }

    /// Returns an iterator over the value of this container if this value holds
    /// an iterable container.
    ///
    /// **This function is hard to implement correctly.**
    /// For example, returning a list from this function is memory violation,
    /// because the list object acting as an iterator is assumed
    /// to have the iteration lock acquired.
    ///
    /// Consider implementing [`iterate_collect`](Self::iterate_collect) instead
    /// when possible.
    ///
    /// This function calls [`iterate_collect`](Self::iterate_collect) by default.
    ///
    /// Returned iterator value must implement
    /// [`iter_next`](Self::iter_next) and [`iter_stop`](Self::iter_stop).
    /// Default implementations of these functions panic.
    ///
    /// Starlark-rust guarantees that
    /// * `iter_next` and `iter_stop` are only called on the value returned from `iterate`
    /// * `iter_next` is called only before `iter_stop`
    /// * `iter_stop` is called exactly once
    ///
    /// So implementations of iterators may acquire mutation lock in `iterate`,
    /// assume that it is held in `iter_next`, and release it in `iter_stop`.
    /// Obviously, there are no such guarantees if these functions are called directly.
    unsafe fn iterate(&self, _me: Value<'v>, heap: &'v Heap) -> anyhow::Result<Value<'v>> {
        Ok(heap.alloc_tuple(&self.iterate_collect(heap)?))
    }

    /// Returns the size hint for the iterator.
    unsafe fn iter_size_hint(&self, _index: usize) -> (usize, Option<usize>) {
        (0, None)
    }

    /// Yield the next value from the iterator.
    ///
    /// This function is called on the iterator value returned by [`iterate`](Self::iterate).
    /// This function accepts an index, which starts at 0 and is incremented by 1
    /// for each call to `iter_next`. The index can be used to implement
    /// cheap iteration over simple containers like lists:
    /// list [`iterate`](Self::iterate) just returns the list itself,
    /// and the passed index is used to access the list elements.
    ///
    /// Default implementation panics.
    ///
    /// This function is only called before [`iter_stop`](Self::iter_stop).
    unsafe fn iter_next(&self, _index: usize, _heap: &'v Heap) -> Option<Value<'v>> {
        panic!(
            "iter_next called on non-iterable value of type {}",
            Self::TYPE
        )
    }

    /// Indicate that the iteration is finished.
    ///
    /// This function is typically used to release mutation lock.
    /// The function must be implemented for iterators even if it does nothing.
    ///
    /// This function is called exactly once for the iterator.
    unsafe fn iter_stop(&self) {
        panic!(
            "iter_stop called on non-iterable value of type {}",
            Self::TYPE
        )
    }

    /// Returns the length of the value, if this value is a sequence.
    fn length(&self) -> anyhow::Result<i32> {
        ValueError::unsupported(self, "len()")
    }

    /// Get an attribute for the current value as would be returned by dotted
    /// expression (i.e. `a.attribute`).
    ///
    /// The three methods [`get_attr`](StarlarkValue::get_attr),
    /// [`has_attr`](StarlarkValue::has_attr) and [`dir_attr`](StarlarkValue::dir_attr)
    /// must be consistent - if you implement one, you should probably implement all three.
    ///
    /// This operations must have no side effects, because it can be called speculatively.
    fn get_attr(&self, _attribute: &str, _heap: &'v Heap) -> Option<Value<'v>> {
        None
    }

    /// A version of `get_attr` which takes `BorrowHashed<str>` instead of `&str`,
    /// thus implementation may reuse the hash of the string if this is called
    /// repeatedly with the same string.
    ///
    /// This function is optional, but if it is implemented, it must be consistent with
    /// [`get_attr`](Self::get_attr).
    fn get_attr_hashed(&self, attribute: Hashed<&str>, heap: &'v Heap) -> Option<Value<'v>> {
        self.get_attr(attribute.key(), heap)
    }

    /// Return true if an attribute of name `attribute` exists for the current
    /// value.
    ///
    /// The three methods [`get_attr`](StarlarkValue::get_attr),
    /// [`has_attr`](StarlarkValue::has_attr) and [`dir_attr`](StarlarkValue::dir_attr)
    /// must be consistent.
    ///
    /// Default implementation of this function delegates to [`get_attr`](Self::get_attr).
    fn has_attr(&self, attribute: &str, heap: &'v Heap) -> bool {
        self.get_attr(attribute, heap).is_some()
    }

    /// Return a vector of string listing all attribute of the current value.
    ///
    /// The three methods [`get_attr`](StarlarkValue::get_attr),
    /// [`has_attr`](StarlarkValue::has_attr) and [`dir_attr`](StarlarkValue::dir_attr)
    /// must be consistent - if you implement one, you should probably implement all three.
    fn dir_attr(&self) -> Vec<String> {
        Vec::new()
    }

    /// Tell whether `other` is in the current value, if it is a container.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # starlark::assert::all_true(r#"
    /// ('a' in 'abc') == True
    /// ('b' in 'abc') == True
    /// ('z' in 'abc') == False
    /// # "#);
    /// ```
    fn is_in(&self, other: Value<'v>) -> anyhow::Result<bool> {
        ValueError::unsupported_owned(other.get_type(), "in", Some(Self::TYPE))
    }

    /// Apply the `+` unary operator to the current value.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # starlark::assert::all_true(r#"
    /// +1 == 1
    /// # "#);
    /// ```
    fn plus(&self, _heap: &'v Heap) -> anyhow::Result<Value<'v>> {
        ValueError::unsupported(self, "+")
    }

    /// Apply the `-` unary operator to the current value.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # starlark::assert::all_true(r#"
    /// -(1) == -1
    /// # "#);
    /// ```
    fn minus(&self, _heap: &'v Heap) -> anyhow::Result<Value<'v>> {
        ValueError::unsupported(self, "-")
    }

    /// Add with the arguments the other way around. Should return [`None`]
    /// to fall through to normal add.
    fn radd(&self, _lhs: Value<'v>, _heap: &'v Heap) -> Option<anyhow::Result<Value<'v>>> {
        None
    }

    /// Add `other` to the current value. Pass both self and
    /// the Value form of self as original.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # starlark::assert::all_true(r#"
    /// 1 + 2 == 3
    /// [1, 2, 3] + [2, 3] == [1, 2, 3, 2, 3]
    /// 'abc' + 'def' == 'abcdef'
    /// (1, 2, 3) + (2, 3) == (1, 2, 3, 2, 3)
    /// # "#);
    /// ```
    fn add(&self, _rhs: Value<'v>, _heap: &'v Heap) -> Option<anyhow::Result<Value<'v>>> {
        None
    }

    /// Subtract `other` from the current value.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # starlark::assert::all_true(r#"
    /// 1 - 2 == -1
    /// # "#);
    /// ```
    fn sub(&self, other: Value<'v>, _heap: &'v Heap) -> anyhow::Result<Value<'v>> {
        ValueError::unsupported_with(self, "-", other)
    }

    /// Multiply the current value with `other`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # starlark::assert::all_true(r#"
    /// 2 * 3 == 6
    /// [1, 2, 3] * 3 == [1, 2, 3, 1, 2, 3, 1, 2, 3]
    /// 'abc' * 3 == 'abcabcabc'
    /// (1, 2, 3) * 3 == (1, 2, 3, 1, 2, 3, 1, 2, 3)
    /// # "#);
    /// ```
    fn mul(&self, other: Value<'v>, _heap: &'v Heap) -> anyhow::Result<Value<'v>> {
        ValueError::unsupported_with(self, "*", other)
    }

    /// Divide the current value by `other`. Always results in a float value.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # starlark::assert::all_true(r#"
    /// 4 / 2.0 == 2.0
    /// 7 / 2 == 3.5
    /// # "#);
    /// ```
    fn div(&self, other: Value<'v>, _heap: &'v Heap) -> anyhow::Result<Value<'v>> {
        ValueError::unsupported_with(self, "/", other)
    }

    /// Apply the percent operator between the current value and `other`. Usually used on
    /// strings, as per
    /// [the Starlark spec](https://github.com/google/skylark/blob/a0e5de7e63b47e716cca7226662a4c95d47bf873/doc/spec.md#string-interpolation).
    ///
    /// # Examples
    ///
    /// ```rust
    /// # starlark::assert::all_true(r#"
    /// 5 % 3 == 2
    /// -5 % -3 == -2
    /// 5 % -3 == -1
    /// -5 % 3 == 1
    /// 5.5 % 3.0 == 2.5
    /// -5.5 % 3.0 == 0.5
    /// 5.5 % -3.0 == -0.5
    /// -5.5 % -3.0 == -2.5
    /// "a %s c" % 3 == "a 3 c"
    /// "Hello %s, your score is %d" % ("Bob", 75) == "Hello Bob, your score is 75"
    /// "%d %o %x" % (65, 65, 65) == "65 101 41"
    /// "Hello %s, welcome" % "Bob" == "Hello Bob, welcome"
    /// "%s" % (1,) == "1"
    /// "%s" % ((1,),) == "(1,)"
    /// "%s" % [1] == "[1]"
    /// "test" % () == "test"
    /// # "#);
    /// ```
    fn percent(&self, other: Value<'v>, _heap: &'v Heap) -> anyhow::Result<Value<'v>> {
        ValueError::unsupported_with(self, "%", other)
    }

    /// Floor division between the current value and `other`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # starlark::assert::all_true(r#"
    /// 7 // 2 == 3
    /// -7 // -2 == 3
    /// 7 // -2 == -4
    /// -7 // 2 == -4
    /// 7.0 // 2.0 == 3.0
    /// -7.0 // -2.0 == 3.0
    /// 7.0 // -2.0 == -4.0
    /// -7.0 // 2.0 == -4.0
    /// 3.0 // 2.0 == 1.0
    /// # "#);
    /// ```
    fn floor_div(&self, other: Value<'v>, _heap: &'v Heap) -> anyhow::Result<Value<'v>> {
        ValueError::unsupported_with(self, "//", other)
    }

    /// Bitwise `&` operator.
    fn bit_and(&self, other: Value<'v>, _heap: &'v Heap) -> anyhow::Result<Value<'v>> {
        ValueError::unsupported_with(self, "&", other)
    }

    /// Bitwise `|` operator.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # starlark::assert::all_true(r#"
    /// 0xb00f | 0x0ee0 == 0xbeef
    /// 4 | 7 == 7
    /// {1: 2} | {3: 4} == {1: 2, 3: 4}
    /// {1: 2} | {1: 3} == {1: 3}
    /// # "#);
    /// ```
    fn bit_or(&self, other: Value<'v>, _heap: &'v Heap) -> anyhow::Result<Value<'v>> {
        ValueError::unsupported_with(self, "|", other)
    }

    /// Bitwise `^` operator.
    fn bit_xor(&self, other: Value<'v>, _heap: &'v Heap) -> anyhow::Result<Value<'v>> {
        ValueError::unsupported_with(self, "^", other)
    }

    /// Bitwise `~` operator.
    fn bit_not(&self, _heap: &'v Heap) -> anyhow::Result<Value<'v>> {
        ValueError::unsupported(self, "~")
    }

    /// Bitwise `<<` operator.
    fn left_shift(&self, other: Value<'v>, _heap: &'v Heap) -> anyhow::Result<Value<'v>> {
        ValueError::unsupported_with(self, "<<", other)
    }

    /// Bitwise `>>` operator.
    fn right_shift(&self, other: Value<'v>, _heap: &'v Heap) -> anyhow::Result<Value<'v>> {
        ValueError::unsupported_with(self, ">>", other)
    }

    /// Called when exporting a value under a specific name,
    fn export_as(&self, _variable_name: &str, _eval: &mut Evaluator<'v, '_>) {
        // Most data types ignore how they are exported
        // but rules/providers like to use it as a helpful hint for users
    }

    /// Set the value at `index` with the new value.
    ///
    /// ```rust
    /// # starlark::assert::is_true(r#"
    /// v = [1, 2, 3]
    /// v[1] = 1
    /// v[2] = [2,3]
    /// v == [1, 1, [2, 3]]
    /// # "#);
    /// ```
    fn set_at(&self, _index: Value<'v>, _new_value: Value<'v>) -> anyhow::Result<()> {
        Err(ValueError::CannotMutateImmutableValue.into())
    }

    /// Set the attribute named `attribute` of the current value to
    /// `value` (e.g. `a.attribute = value`).
    fn set_attr(&self, attribute: &str, _new_value: Value<'v>) -> anyhow::Result<()> {
        ValueError::unsupported(self, &format!(".{}=", attribute))
    }

    /// Dynamically provide values based on type.
    ///
    /// Value can be fetched using [`Value::request_value`].
    ///
    /// The API is based on
    /// [std::any::Provider](https://doc.rust-lang.org/std/any/trait.Provider.html).
    fn provide(&'v self, demand: &mut Demand<'_, 'v>) {
        let _ = demand;
    }
}


/*
 * Copyright 2018 The Starlark in Rust Authors.
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! Trait and default implementations of a trait that will show starlark type annotations for a
//! given type.

use std::marker::PhantomData;

use either::Either;

use crate::typing::Ty;
use crate::values::none::NoneType;
use crate::values::string::StarlarkStr;
use crate::values::StarlarkValue;

/// Provides a starlark type representation, even if StarlarkValue is not implemented.
pub trait StarlarkTypeRepr {
    /// The representation of a type that a user would use verbatim in starlark type annotations
    fn starlark_type_repr() -> Ty;
}

/// A dict used just for display purposes.
///
/// `DictOf` requires `Unpack` to be implemented, and `Dict` does not take type parameters so
/// we need something for documentation generation.
pub struct DictType<K: StarlarkTypeRepr, V: StarlarkTypeRepr> {
    k: PhantomData<K>,
    v: PhantomData<V>,
}

impl<K: StarlarkTypeRepr, V: StarlarkTypeRepr> StarlarkTypeRepr for DictType<K, V> {
    fn starlark_type_repr() -> Ty {
        Ty::dict(K::starlark_type_repr(), V::starlark_type_repr())
    }
}

impl<'v, T: StarlarkValue<'v> + ?Sized> StarlarkTypeRepr for T {
    fn starlark_type_repr() -> Ty {
        Self::get_type_starlark_repr()
    }
}

impl StarlarkTypeRepr for String {
    fn starlark_type_repr() -> Ty {
        StarlarkStr::starlark_type_repr()
    }
}

impl StarlarkTypeRepr for &str {
    fn starlark_type_repr() -> Ty {
        StarlarkStr::starlark_type_repr()
    }
}

impl<T: StarlarkTypeRepr> StarlarkTypeRepr for Option<T> {
    fn starlark_type_repr() -> Ty {
        Either::<NoneType, T>::starlark_type_repr()
    }
}

impl<T: StarlarkTypeRepr> StarlarkTypeRepr for Vec<T> {
    fn starlark_type_repr() -> Ty {
        Ty::list(T::starlark_type_repr())
    }
}

impl<TLeft: StarlarkTypeRepr, TRight: StarlarkTypeRepr> StarlarkTypeRepr for Either<TLeft, TRight> {
    fn starlark_type_repr() -> Ty {
        Ty::union2(TLeft::starlark_type_repr(), TRight::starlark_type_repr())
    }
}