///|
/// Returns `true` when two `Value`s are equal under Starlark semantics,
/// including cross-type `Int`/`Float` equality and NaN inequality.
pub impl Eq for Value with fn equal(a, b) -> Bool {
match starlark_equals_depth(a, b, compare_limit) {
Ok(eq) => eq
Err(_) => false
}
}
///|
/// Returns the Starlark `str(v)` string representation of `v`. Unlike
/// `repr`, string values are returned without quotes.
///
/// Parameters:
///
/// - `v` : The value to convert to a string.
///
/// Returns the Starlark `str()` representation of `v`.
pub fn Value::to_str(v : Value) -> String {
match v.to_str_checked() {
Ok(s) => s
Err(e) => abort(e)
}
}
///|
/// Like `Value::to_str` but returns `Err` instead of aborting when the nesting
/// depth limit is exceeded. For use in the eval engine where the error should
/// surface as a Starlark runtime error.
pub fn Value::to_str_checked(v : Value) -> Result[String, String] {
match v {
None => Ok("None")
Bool(true) => Ok("True")
Bool(false) => Ok("False")
Int(i) => Ok(i.to_string())
Float(f) => Ok(@numeric.format_float(f))
String(s) => Ok(s.raw)
Bytes(b) => Ok(repr_bytes_val(b))
List(l) => repr_inner(List(l), [], [], repr_limit)
Tuple(t) => repr_inner(Tuple(t), [], [], repr_limit)
Dict(d) => repr_inner(Dict(d), [], [], repr_limit)
Set(s) => repr_inner(Set(s), [], [], repr_limit)
Range(_)
| Function(_)
| Builtin(_)
| BoundMethod(_)
| Module(_)
| ExtVal(_) => repr_inner(v, [], [], repr_limit)
StringElems(_) | StringCodepoints(_) | BytesElems(_) =>
repr_inner(v, [], [], repr_limit)
}
}
///|
/// Returns `true` if `a == b` under Starlark semantics, including cross-type
/// `Int`/`Float` equality and NaN inequality.
///
/// Parameters:
///
/// - `a` : The left-hand side value.
/// - `b` : The right-hand side value.
///
/// Returns `true` when the two values are equal under Starlark semantics.
pub fn Value::starlark_equals(a : Value, b : Value) -> Bool {
match starlark_equals_depth(a, b, compare_limit) {
Ok(eq) => eq
Err(_) => false
}
}
///|
/// Returns `true` when the integer `n` is exactly equal to the finite float
/// `f`. Returns `false` for NaN, infinities, and floats with a fractional
/// part.
///
/// Parameters:
///
/// - `n` : The integer value.
/// - `f` : The float value to compare against.
fn int_float_eq(n : BigInt, f : Double) -> Bool {
if f.is_nan() || f.is_inf() {
return false
}
// An int can equal a float only when the float has no fractional part;
// trunc() strips the fraction, so `f != f.trunc()` means one was present.
if f != f.trunc() {
return false
}
n == @numeric.double_to_bigint(f)
}
///|
let fnv_offset_basis : UInt = 2166136261U
///|
let fnv_prime : UInt = 16777619U
// Starlark integer hash multiplier: 12582917 * (lo + 3)
///|
let int_hash_mult : UInt = 12582917U
// Sentinel hash for NaN/Inf floats (golden ratio × 10^6)
///|
let float_special_hash : UInt = 1618033U
// Starlark tuple hash algorithm constants
///|
let tuple_hash_init : UInt = 0x345678U
///|
let tuple_hash_prime : UInt = 1000003U
///|
let tuple_hash_step_base : UInt = 82520U
// 2^32 — used to extract the low 32 bits of a BigInt magnitude
///|
let bigint_uint32_modulus : BigInt = 4294967296N
///|
/// Computes the FNV-1a hash of a byte sequence.
///
/// Parameters:
///
/// - `bytes` : The byte sequence to hash.
fn fnv1a(bytes : Bytes) -> UInt {
let mut h = fnv_offset_basis
for i in 0.. UInt {
n.reinterpret_as_uint() * fnv_prime
}
///|
let min_int64_bigint : BigInt = -9223372036854775808N
///|
let max_int64_bigint : BigInt = 9223372036854775807N
///|
/// Computes the Starlark integer hash for a `BigInt`, matching starlark-go's
/// algorithm. Values in the `int64` range use the two's-complement low 32 bits;
/// values outside that range use the low 32 bits of the magnitude.
///
/// Parameters:
///
/// - `n` : The integer to hash.
fn hash_bigint(n : BigInt) -> UInt {
// Integer hash algorithm:
// small int (int64 range): lo = big.Word(iSmall) = uint64(iSmall)
// big int (outside int64 range): lo = iBig.Bits()[0] = low word of magnitude
// uint32(lo+3) reduces to uint32(lo32+3) regardless of the upper 32 bits,
// so we only need the low 32 bits of the appropriate 64-bit word.
let lo : UInt = if n >= min_int64_bigint && n <= max_int64_bigint {
// Two's-complement low 32 bits, matching uint32(uint64(iSmall)).
n.to_int64().to_int().reinterpret_as_uint()
} else {
// Low 32 bits of the magnitude, matching uint32(iBig.Bits()[0]).
let abs_n = if n < 0N { -n } else { n }
(abs_n % bigint_uint32_modulus).to_int().reinterpret_as_uint()
}
int_hash_mult * (lo + 3U)
}
///|
/// Computes the Starlark hash for a `Double`. NaN and infinities return a fixed
/// sentinel; finite values are hashed via their exact integer equivalent so that
/// `hash(1.0) == hash(1)`.
///
/// Parameters:
///
/// - `f` : The float value to hash.
fn hash_float(f : Double) -> UInt {
if f.is_nan() || f.is_inf() {
return float_special_hash
}
// Convert to exact integer value then hash — ensures
// hash(1e20) == hash(10**20).
hash_bigint(@numeric.double_to_bigint(f))
}
///|
/// Computes the hash of `self` with an explicit recursion-depth cap to guard
/// against deep nesting. Returns `Err` for unhashable types or when the depth
/// limit is reached.
///
/// Parameters:
///
/// - `self` : The value to hash.
/// - `depth` : Maximum remaining recursion depth; returns `Err` when it
/// reaches zero.
fn Value::hash_depth(self : Value, depth : Int) -> Result[UInt, String] {
match self {
None => Ok(0U)
Bool(false) => Ok(0U)
Bool(true) => Ok(1U)
Int(n) => Ok(hash_bigint(n))
Float(f) => Ok(hash_float(f))
String(s) => Ok(fnv1a(s.bytes))
Bytes(b) => Ok(fnv1a(b))
Tuple(t) => hash_tuple(t, depth)
List(_) => Err("unhashable type: list")
Dict(_) => Err("unhashable type: dict")
Set(_) => Err("unhashable type: set")
Range(_) => Err("unhashable: range")
Function(f) => Ok(fnv1a(@utf8.encode(f.name())))
Builtin(b) => Ok(fnv1a(@utf8.encode(b.name())))
BoundMethod(m) => Ok(hash_int(m.id))
Module(_) => Err("unhashable: module")
StringElems(_) => Err("unhashable: string.elems")
StringCodepoints(_) => Err("unhashable: string.codepoints")
BytesElems(_) => Err("unhashable: bytes.elems")
ExtVal(c) => c.get_hash_depth(depth)
}
}
///|
/// Computes the Starlark tuple hash using Python's tuplehash algorithm,
/// respecting the given recursion-depth cap.
///
/// Parameters:
///
/// - `t` : The array of tuple elements to hash.
/// - `depth` : Maximum remaining recursion depth; returns `Err` when it
/// reaches zero.
fn hash_tuple(t : Array[Value], depth : Int) -> Result[UInt, String] {
if depth < 1 {
return Err("hash exceeded maximum recursion depth")
}
let mut x : UInt = tuple_hash_init
let mut mult : UInt = tuple_hash_prime
// Python's tuplehash step: mult += 82520 + len + len per element.
// The "len + len" form is a direct port of CPython's uint arithmetic.
let n = (t.length() + t.length()).reinterpret_as_uint()
for elem in t {
match elem.hash_depth(depth - 1) {
Err(e) => return Err(e)
Ok(y) => {
x = x ^ (y * mult)
mult = mult + tuple_hash_step_base + n
}
}
}
Ok(x)
}
///|
/// Computes the Starlark hash of `self`. Returns `Err` for unhashable types
/// (list, dict, set, range, module). Guarantees `hash(x) == hash(y)` whenever
/// `x == y` under Starlark semantics, including cross-type `Int`/`Float`.
///
/// Parameters:
///
/// - `self` : The value to hash.
///
/// Returns `Ok(hash)` for hashable values, or `Err` with a message like
/// `"unhashable type: list"` for unhashable types.
pub fn Value::hash(self : Value) -> Result[UInt, String] {
self.hash_depth(hash_limit)
}
///|
fn int64_signum(a : Int64, b : Int64) -> Int {
if a < b {
-1
} else if a > b {
1
} else {
0
}
}
///|
fn bigint_signum(a : BigInt, b : BigInt) -> Int {
a.compare(b)
}
///|
fn float_cmp(x : Double, y : Double) -> Int {
if x > y {
1
} else if x < y {
-1
} else if x == y {
0
} else if x == x {
// x is not NaN, so y is NaN; NaN > everything
-1
} else if y == y {
// y is not NaN, so x is NaN; NaN > everything
1
} else {
// both NaN
0
}
}
///|
fn bytes_lex_cmp(a : Bytes, b : Bytes) -> Int {
let len = if a.length() < b.length() { a.length() } else { b.length() }
for i in 0.. cb {
return 1
}
}
int64_signum(a.length().to_int64(), b.length().to_int64())
}
///|
/// Compares an integer `n` to a finite-or-special float `f` using exact
/// arithmetic. NaN is treated as greater than all integers; infinities sort
/// to the expected extremes. Returns a negative int, zero, or positive int.
///
/// Parameters:
///
/// - `n` : The integer value.
/// - `f` : The float value to compare against.
fn int_float_cmp(n : BigInt, f : Double) -> Int {
if f != f {
-1 // NaN > everything in starlark ordering
} else if f == @double.infinity {
-1 // n < +inf
} else if f == @double.neg_infinity {
1 // n > -inf
} else {
// finite f: compare exactly using floor(f)
let ffloor = f.floor()
let f_floor_big = @numeric.double_to_bigint(ffloor)
if f == ffloor {
n.compare(f_floor_big)
} else {
// f is in (ffloor, ffloor+1); n is an integer
let cmp = n.compare(f_floor_big)
if cmp <= 0 {
-1
} else {
1
}
}
}
}
///|
/// Depth cap for recursive comparison and equality operations.
/// Matches starlark-go's `CompareLimit` (10). Every function that recurses
/// over nested `Value`s must accept a `depth : Int` parameter, decrement it
/// before each recursive call, and return `Err` when `depth < 1`. The named
/// limit constants here are the shared roots; never recurse without one.
pub let compare_limit : Int = 10
///|
/// Depth cap for `hash` traversal. Set to 200 — a safe ceiling on all four
/// MoonBit backends. See `compare_limit` for the depth-guard convention.
///
/// `StarlarkDict` and `StarlarkSet` use `dict_key_hash_limit` (not this
/// constant) for their internal hash function. Use `hash_limit` only for
/// standalone hash calls outside of a hash-table context.
pub let hash_limit : Int = 200
///|
/// Depth cap for hashing `StarlarkDict` and `StarlarkSet` keys.
///
/// Set to `compare_limit - 1` so that any key that hashes successfully can
/// also be compared for equality. `starlark_equals_depth` checks `depth < 1`
/// before dispatching — including for leaf types — so equality with budget
/// `compare_limit` accepts nesting only up to `compare_limit - 1`. Using this
/// constant for the hash function keeps both limits in lock-step: a key that
/// hashes at depth D can always be equality-compared at the same depth.
pub let dict_key_hash_limit : Int = compare_limit - 1
///|
/// Depth cap for `freeze` traversal. Set to 200 — a safe ceiling on all four
/// MoonBit backends. See `compare_limit` for the depth-guard convention.
pub let freeze_limit : Int = 200
///|
/// Compares `a` and `b` using Starlark's total ordering, returning a negative
/// int, zero, or positive int. Returns `Err` for incompatible types. `op` is
/// the operator string used in the error message.
///
/// Parameters:
///
/// - `a` : The left-hand side value.
/// - `b` : The right-hand side value.
/// - `op` : The comparison operator string used in error messages (default `"<"`).
///
/// Returns `Ok(n)` where `n < 0`, `n == 0`, or `n > 0`, or `Err` when the
/// types cannot be compared.
#internal(unsafe, "eval engine only; embedders use compare_depth")
pub fn compare_values(
a : Value,
b : Value,
op? : String = "<",
) -> Result[Int, String] {
compare_values_depth(a, b, compare_limit, op~)
}
///|
/// Like `compare_values` but with an explicit recursion-depth cap to guard
/// against cycles in nested structures.
///
/// Parameters:
///
/// - `a` : The left-hand side value.
/// - `b` : The right-hand side value.
/// - `depth` : Maximum remaining recursion depth; returns `Err` when it
/// reaches zero.
/// - `op` : The comparison operator string used in error messages (default `"<"`).
///
/// Returns `Ok(n)` where `n < 0`, `n == 0`, or `n > 0`, or `Err` when types
/// are incompatible or the depth limit is exceeded.
#internal(unsafe, "eval engine only; embedders use compare_depth")
pub fn compare_values_depth(
a : Value,
b : Value,
depth : Int,
op? : String = "<",
) -> Result[Int, String] {
if depth < 1 {
return Err("comparison exceeded maximum recursion depth")
}
match (a, b) {
(Bool(x), Bool(y)) =>
Ok(
match (x, y) {
(false, true) => -1
(true, false) => 1
_ => 0
},
)
(Int(x), Int(y)) => Ok(bigint_signum(x, y))
(Float(x), Float(y)) => Ok(float_cmp(x, y))
(String(x), String(y)) => Ok(bytes_lex_cmp(x.bytes, y.bytes))
(Bytes(x), Bytes(y)) => Ok(bytes_lex_cmp(x, y))
(Tuple(x), Tuple(y)) => slice_cmp_depth(x[:], y[:], depth - 1, op~)
(List(x), List(y)) =>
slice_cmp_depth(x.items[:], y.items[:], depth - 1, op~)
(Int(n), Float(f)) => Ok(int_float_cmp(n, f))
(Float(f), Int(n)) => Ok(-int_float_cmp(n, f))
(ExtVal(ac), _) =>
match ac.get_compare(b) {
Some(c) => Ok(c)
None => Err("\{a.type_name()} \{op} \{b.type_name()} not implemented")
}
(_, ExtVal(rc)) =>
match rc.get_compare(a) {
Some(c) => Ok(-c)
None => Err("\{a.type_name()} \{op} \{b.type_name()} not implemented")
}
_ => Err("\{a.type_name()} \{op} \{b.type_name()} not implemented")
}
}
///|
/// Like `starlark_equals` but with an explicit recursion-depth cap.
///
/// Parameters:
///
/// - `a` : The left-hand side value.
/// - `b` : The right-hand side value.
/// - `depth` : Maximum remaining recursion depth; returns `Err` when it
/// reaches zero.
///
/// Returns `Ok(true)` when equal, `Ok(false)` when not, or `Err` when the
/// depth limit is exceeded.
#internal(unsafe, "eval engine only; not part of the public embedding API")
pub fn starlark_equals_depth(
a : Value,
b : Value,
depth : Int,
) -> Result[Bool, String] {
if depth < 1 {
return Err("comparison exceeded maximum recursion depth")
}
match (a, b) {
(None, None) => Ok(true)
(Bool(x), Bool(y)) => Ok(x == y)
(Int(x), Int(y)) => Ok(x == y)
(Float(x), Float(y)) => Ok(if x.is_nan() { y.is_nan() } else { x == y })
(String(x), String(y)) => Ok(x.equals(y))
(Bytes(x), Bytes(y)) => Ok(x.equal(y))
(List(x), List(y)) => slice_equals_depth(x.items[:], y.items[:], depth - 1)
(Tuple(x), Tuple(y)) => slice_equals_depth(x[:], y[:], depth - 1)
(Int(n), Float(f)) => Ok(int_float_eq(n, f))
(Float(f), Int(n)) => Ok(int_float_eq(n, f))
(Dict(x), Dict(y)) => dict_equals_depth(x, y, depth - 1)
(Set(x), Set(y)) => set_equals_depth(x, y, depth - 1)
(Range(x), Range(y)) => Ok(range_equals(x, y))
(Function(x), Function(y)) => Ok(physical_equal(x, y))
(Builtin(x), Builtin(y)) => Ok(x.name == y.name)
(BoundMethod(x), BoundMethod(y)) => Ok(x.id == y.id)
(Module(x), Module(y)) => Ok(physical_equal(x, y))
(StringElems(x), StringElems(y)) => Ok(x.s.equals(y.s) && x.ords == y.ords)
(StringCodepoints(x), StringCodepoints(y)) =>
Ok(x.s.equals(y.s) && x.ords == y.ords)
(BytesElems(x), BytesElems(y)) => Ok(x.b.equal(y.b))
(ExtVal(a), ExtVal(b)) => a.get_equals(ExtVal(b), depth - 1)
_ => Ok(false)
}
}
///|
/// Returns `Ok(true)` when two array slices of `Value`s are element-wise equal
/// under Starlark semantics, respecting the given recursion-depth cap.
///
/// Parameters:
///
/// - `a` : The left-hand slice.
/// - `b` : The right-hand slice.
/// - `depth` : Maximum remaining recursion depth passed to each element
/// comparison.
fn slice_equals_depth(
a : ArrayView[Value],
b : ArrayView[Value],
depth : Int,
) -> Result[Bool, String] {
if a.length() != b.length() {
return Ok(false)
}
for i in 0.. return Err(e)
Ok(false) => return Ok(false)
Ok(true) => ()
}
}
Ok(true)
}
///|
/// Lexicographically compares two array slices of `Value`s using
/// `compare_values_depth`, respecting the given recursion-depth cap.
///
/// Parameters:
///
/// - `a` : The left-hand slice.
/// - `b` : The right-hand slice.
/// - `depth` : Maximum remaining recursion depth passed to each element
/// comparison.
/// - `op` : The comparison operator string used in error messages (default `"<"`).
fn slice_cmp_depth(
a : ArrayView[Value],
b : ArrayView[Value],
depth : Int,
op? : String = "<",
) -> Result[Int, String] {
let len = if a.length() < b.length() { a.length() } else { b.length() }
for i in 0.. return Err(e)
Ok(c) => if c != 0 { return Ok(c) }
}
}
Ok(int64_signum(a.length().to_int64(), b.length().to_int64()))
}
///|
/// Returns `Ok(true)` when two `StarlarkDict`s have the same set of keys and
/// each corresponding pair of values is equal under Starlark semantics.
///
/// Parameters:
///
/// - `a` : The left-hand dict.
/// - `b` : The right-hand dict.
/// - `depth` : Maximum remaining recursion depth passed to each value
/// comparison.
fn dict_equals_depth(
a : StarlarkDict,
b : StarlarkDict,
depth : Int,
) -> Result[Bool, String] {
if a.length() != b.length() {
return Ok(false)
}
let mut result : Result[Bool, String] = Ok(true)
a.each(fn(k, v) {
if result is Ok(true) {
match b.get(k) {
Ok(Some(bv)) =>
match starlark_equals_depth(v, bv, depth) {
Err(e) => result = Err(e)
Ok(false) => result = Ok(false)
Ok(true) => ()
}
_ => result = Ok(false)
}
}
})
result
}
///|
/// Returns `Ok(true)` when two `StarlarkSet`s contain the same elements under
/// Starlark semantics.
///
/// Parameters:
///
/// - `a` : The left-hand set.
/// - `b` : The right-hand set.
/// - `_depth` : Reserved recursion-depth cap (unused; sets contain only
/// hashable scalars in practice).
fn set_equals_depth(
a : StarlarkSet,
b : StarlarkSet,
_depth : Int,
) -> Result[Bool, String] {
if a.length() != b.length() {
return Ok(false)
}
let mut result : Result[Bool, String] = Ok(true)
a.each(fn(k) {
if result is Ok(true) {
match b.contains(k) {
Ok(true) => ()
Ok(false) => result = Ok(false)
Err(e) => result = Err(e)
}
}
})
result
}
///|
/// Recursively deep-freezes `self` and all values reachable from it (list
/// items, dict keys/values, set elements, function defaults and closures).
/// Aborts if the nesting depth exceeds `freeze_limit`.
///
/// Parameters:
///
/// - `self` : The root value to freeze.
pub fn Value::freeze(self : Value) -> Unit {
match freeze_value_inner(self, [], freeze_limit) {
Ok(_) => ()
Err(e) => abort(e)
}
}
///|
/// Like `Value::freeze` but returns `Err` instead of aborting when the nesting
/// depth limit is exceeded.
///
/// On `Err`, the value is in an indeterminate partially-frozen state and must
/// be discarded. The exec-file entry points do this automatically; embedders
/// calling `freeze_checked` directly must not use the value after an `Err`
/// return.
pub fn Value::freeze_checked(self : Value) -> Result[Unit, String] {
freeze_value_inner(self, [], freeze_limit)
}
///|
/// Recursively freezes `v` and all values reachable from it, using `seen_funcs`
/// to break cycles through `StarlarkFunction` closures and `depth` as a
/// recursion-depth guard. Already-frozen containers and scalar values return
/// immediately without consuming the depth budget.
///
/// Parameters:
///
/// - `v` : The value to freeze.
/// - `seen_funcs` : Accumulator of `StarlarkFunction` objects already visited
/// in the current traversal, used to detect closure cycles.
/// - `depth` : Maximum remaining recursion depth; returns `Err` when it
/// reaches zero.
fn freeze_value_inner(
v : Value,
seen_funcs : Array[StarlarkFunction],
depth : Int,
) -> Result[Unit, String] {
// Phase 1: early exits that do not consume the depth budget.
// Already-frozen containers, the function cycle guard, and scalar / no-op
// values all return immediately without decrementing depth.
match v {
List(l) => if l.is_frozen() { return Ok(()) }
Dict(d) => if d.is_frozen() { return Ok(()) }
Set(s) => if s.is_frozen() { return Ok(()) }
Function(f) =>
if seen_funcs.iter().any(fn(g) { physical_equal(g, f) }) {
return Ok(())
}
None
| Bool(_)
| Int(_)
| Float(_)
| String(_)
| Bytes(_)
| Range(_)
| StringElems(_)
| StringCodepoints(_)
| BytesElems(_)
| Module(_) => return Ok(())
_ => ()
}
// Phase 2: single depth guard for all remaining paths (containers and
// ExtVal). Placing it here — after the frozen/cycle/scalar exits — means
// already-frozen nodes do not consume depth and scalars never trigger
// the limit, matching the semantics of hash_limit and repr_limit.
if depth < 1 {
return Err("freeze exceeded maximum recursion depth")
}
// Phase 3: recursive processing. Every branch here may recurse.
// Note: ExtVal passes through the depth guard above, but do_freeze receives
// no depth argument — the registered freeze_fn is responsible for its own
// recursion. Full depth propagation through ExtVal requires an API change
// (adding a depth parameter to freeze_fn) and is tracked as a known gap.
match v {
List(l) => {
l.freeze()
for item in l.items {
match freeze_value_inner(item, seen_funcs, depth - 1) {
Err(e) => return Err(e)
Ok(_) => ()
}
}
}
Dict(d) => {
d.freeze()
let mut err : String? = None
d.each(fn(k, val) {
if err is None {
match freeze_value_inner(k, seen_funcs, depth - 1) {
Err(e) => err = Some(e)
Ok(_) =>
match freeze_value_inner(val, seen_funcs, depth - 1) {
Err(e) => err = Some(e)
Ok(_) => ()
}
}
}
})
match err {
Some(e) => return Err(e)
None => ()
}
}
Set(s) => {
s.freeze()
let mut err : String? = None
s.each(fn(k) {
if err is None {
match freeze_value_inner(k, seen_funcs, depth - 1) {
Err(e) => err = Some(e)
Ok(_) => ()
}
}
})
match err {
Some(e) => return Err(e)
None => ()
}
}
Tuple(t) =>
for item in t {
match freeze_value_inner(item, seen_funcs, depth - 1) {
Err(e) => return Err(e)
Ok(_) => ()
}
}
Function(f) => {
seen_funcs.push(f)
for default_opt in f.defaults {
match default_opt {
Some(dv) =>
match freeze_value_inner(dv, seen_funcs, depth - 1) {
Err(e) => return Err(e)
Ok(_) => ()
}
None => ()
}
}
for cell in f.vm_freevars {
match cell.get() {
Some(cv) =>
match freeze_value_inner(cv, seen_funcs, depth - 1) {
Err(e) => return Err(e)
Ok(_) => ()
}
None => ()
}
}
}
BoundMethod(m) =>
match freeze_value_inner(m.recv, seen_funcs, depth - 1) {
Err(e) => return Err(e)
Ok(_) => ()
}
Builtin(f) =>
match f.receiver() {
Some(r) =>
match freeze_value_inner(r, seen_funcs, depth - 1) {
Err(e) => return Err(e)
Ok(_) => ()
}
None => ()
}
ExtVal(c) => c.do_freeze()
_ => ()
}
Ok(())
}
///|
/// Returns `true` when two `StarlarkRange` values represent the same sequence
/// of integers. Ranges with different lengths are never equal; empty ranges
/// are always equal; non-empty ranges are equal when they have the same start
/// and, if their length exceeds one, the same step.
///
/// Parameters:
///
/// - `x` : The left-hand range.
/// - `y` : The right-hand range.
fn range_equals(x : StarlarkRange, y : StarlarkRange) -> Bool {
let xl = x.length()
let yl = y.length()
if xl != yl {
return false
}
if xl == 0L {
return true
}
if x.start != y.start {
return false
}
xl == 1L || x.step == y.step
}