Skip to main content

nickel_lang_core/eval/value/
mod.rs

1//! Runtime representation of Nickel values.
2//!
3//! This module implements a custom memory layout for a memory-efficient representation of Nickel
4//! values. See
5//! [RFC007](https://github.com/tweag/nickel/blob/master/rfcs/007-bytecode-interpreter.md) for more
6//! details.
7use crate::{
8    identifier::LocIdent,
9    impl_display_from_pretty,
10    label::Label,
11    metrics::increment,
12    position::{PosIdx, PosTable, TermPos},
13    term::{
14        ForeignIdPayload, IndexMap, Number, RecordOpKind, RuntimeContract, SealingKey, Term,
15        record::Field, string::NickelString,
16    },
17    traverse::{Traverse, TraverseControl, TraverseOrder},
18    typ::Type,
19};
20
21use malachite::base::num::conversion::traits::ToSci as _;
22use nickel_lang_vector::Slice;
23use std::{
24    alloc::{Layout, alloc, dealloc},
25    borrow::ToOwned,
26    cell::{Cell, RefCell},
27    convert::Infallible,
28    fmt,
29    mem::{ManuallyDrop, size_of, transmute},
30    num::NonZero,
31    ptr::{self, NonNull},
32};
33
34pub use super::cache::lazy::{self, Thunk};
35pub use crate::term::record::RecordData;
36
37pub mod lens;
38
39/// A Nickel array.
40pub type Array = Slice<NickelValue, 32>;
41
42/// A mismatch between an expected tag and the tag found during the decoding process of a value.
43#[derive(Clone, Copy, Debug, Eq, PartialEq)]
44pub struct TagMismatchError;
45
46/// The unified representation of Nickel values.
47///
48/// A tagged pointer to a [reference-counted Nickel value block][ValueBlockRc], or an inline value.
49/// The two least significant bits of the pointer are used as the tag. See [ValueTag] for more
50/// details.
51#[derive(Eq)]
52pub struct NickelValue {
53    // According to [Strict provenance
54    // rules](https://doc.rust-lang.org/std/ptr/index.html#using-strict-provenance), as `data` can
55    // contain a pointer and thus require provenance information, it must either be itself a
56    // pointer, or we have to keep another pointer around that carries this information (which we
57    // surely don't want to do). On the other hand, according to the official documentation of
58    // `std::ptr`:
59    //
60    // > In general, it is always sound for an integer to pretend to be a pointer “for fun” as long
61    // > as you don’t use operations on it which require it to be valid (non-zero-sized offset,
62    // > read, write, etc).
63    //
64    // Thus, since `data` can represent either a integer (inline value) that can masquerade as a
65    // pointer or an actual pointer with provenance requirements, we always represent it as a
66    // pointer.
67    //
68    // Additionally, since pointers to value blocks are always initialized (and in particular
69    // non-null), and the tag for non-pointers values is non-zero, `data` is never zero. We use
70    // `NonNull`, which saves some null checks and enables layout optimizations.
71    data: NonNull<u8>,
72    // On 64-bits pointer-width archs, we can fit everything into one word. Otherwise, we stay safe
73    // and use a separate field for the position index of inline values.
74    #[cfg(not(target_pointer_width = "64"))]
75    pos_idx: PosIdx,
76}
77
78// PartialEq is mostly used for tests, when it's handy to compare something to an expected result.
79// Most of the instances aren't really meaningful to use outside of very simple cases, and you
80// should avoid comparing terms directly.
81impl PartialEq for NickelValue {
82    fn eq(&self, other: &Self) -> bool {
83        match (self.content_ref(), other.content_ref()) {
84            (ValueContentRef::Null, ValueContentRef::Null) => true,
85            (ValueContentRef::Bool(b1), ValueContentRef::Bool(b2)) => b1 == b2,
86
87            (ValueContentRef::Number(number1), ValueContentRef::Number(number2)) => {
88                number1 == number2
89            }
90            (ValueContentRef::Array(array1), ValueContentRef::Array(array2)) => array1 == array2,
91            (ValueContentRef::Record(record1), ValueContentRef::Record(record2)) => {
92                record1 == record2
93            }
94            (ValueContentRef::String(string1), ValueContentRef::String(string2)) => {
95                string1 == string2
96            }
97            (ValueContentRef::Thunk(thunk1), ValueContentRef::Thunk(thunk2)) => {
98                *thunk1.borrow() == *thunk2.borrow()
99            }
100            (ValueContentRef::Term(term1), ValueContentRef::Term(term2)) => term1 == term2,
101            (ValueContentRef::Label(label1), ValueContentRef::Label(label2)) => label1 == label2,
102            (
103                ValueContentRef::EnumVariant(enum_variant1),
104                ValueContentRef::EnumVariant(enum_variant2),
105            ) => enum_variant1 == enum_variant2,
106            (ValueContentRef::ForeignId(foreign_id1), ValueContentRef::ForeignId(foreign_id2)) => {
107                foreign_id1 == foreign_id2
108            }
109            (
110                ValueContentRef::SealingKey(sealing_key1),
111                ValueContentRef::SealingKey(sealing_key2),
112            ) => sealing_key1 == sealing_key2,
113            (
114                ValueContentRef::CustomContract(custom_contract1),
115                ValueContentRef::CustomContract(custom_contract2),
116            ) => custom_contract1 == custom_contract2,
117            (ValueContentRef::Type(type1), ValueContentRef::Type(type2)) => type1 == type2,
118            _ => false,
119        }
120    }
121}
122
123impl fmt::Debug for NickelValue {
124    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125        write!(f, "NickelValue {{ pos_idx: {:?}, data: ", self.pos_idx())?;
126
127        match self.content_ref() {
128            ValueContentRef::Null => write!(f, "InlineValue(null)"),
129            ValueContentRef::Bool(b) => write!(f, "InlineValue({b})"),
130            ValueContentRef::Number(number) => write!(f, "Number({number})"),
131            ValueContentRef::Array(container) => write!(f, "Array({container:?})"),
132            ValueContentRef::Record(container) => write!(f, "Record({container:?})"),
133            ValueContentRef::String(string) => write!(f, "String({string})"),
134            ValueContentRef::Thunk(thunk) => write!(f, "Thunk({:?})", thunk.borrow()),
135            ValueContentRef::Term(term) => write!(f, "Term({term:?})"),
136            ValueContentRef::Label(label) => write!(f, "Label({label:?})"),
137            ValueContentRef::EnumVariant(enum_variant_data) => {
138                write!(f, "EnumVariant({enum_variant_data:?})")
139            }
140            ValueContentRef::ForeignId(foreign_id) => {
141                write!(f, "ForeignId({foreign_id:?})")
142            }
143            ValueContentRef::SealingKey(sealing_key) => {
144                write!(f, "SealingKey({sealing_key:?})")
145            }
146            ValueContentRef::CustomContract(custom_contract) => {
147                write!(f, "CustomContract({custom_contract:?})")
148            }
149            ValueContentRef::Type(type_data) => write!(f, "Type({type_data:?})"),
150        }?;
151
152        write!(f, "}}")
153    }
154}
155
156impl std::fmt::Pointer for NickelValue {
157    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158        if self.is_inline() {
159            write!(f, "<inline>")
160        } else {
161            std::fmt::Pointer::fmt(&self.data, f)
162        }
163    }
164}
165
166// pointer-width-specific implementation
167#[cfg(target_pointer_width = "64")]
168impl NickelValue {
169    /// The mask for the position index bits in an inline value.
170    const INLINE_POS_IDX_MASK: usize = 0xFF_FF_FF_FF_00_00_00_00;
171
172    /// Returns `self` with the inline position index set to `idx` if `self` is an inline value, or
173    /// returns `self` unchanged otherwise.
174    fn with_inline_pos_idx(mut self, idx: PosIdx) -> Self {
175        if self.tag() == ValueTag::Inline {
176            unsafe {
177                self.data = self.data.map_addr(|data| {
178                    // Safety: the lower part of the data an inline value is non-zero, and is
179                    // preserved by the LHS of the bitwise or.
180                    NonZero::new_unchecked(
181                        (usize::from(data) & !Self::INLINE_POS_IDX_MASK) | (usize::from(idx) << 32),
182                    )
183                });
184            }
185        }
186        self
187    }
188
189    /// Returns the position index of `self` if it is an inline value, or `None` otherwise.
190    #[inline]
191    fn inline_pos_idx(&self) -> Option<PosIdx> {
192        (self.tag() == ValueTag::Inline).then(|| {
193            PosIdx::from_usize_truncate((self.as_usize() & Self::INLINE_POS_IDX_MASK) >> 32)
194        })
195    }
196
197    /// Creates a new inline value with an associated position index.
198    #[inline]
199    pub const fn inline(inline: InlineValue, idx: PosIdx) -> Self {
200        NickelValue {
201            // Safety: inline values are tagged, the `Inline` tag is non-zero and smaller than 4,
202            // so the result of the bitwise or operation is non-zero.
203            data: NonNull::without_provenance(unsafe {
204                NonZero::new_unchecked(inline as usize | (idx.to_usize() << 32))
205            }),
206        }
207    }
208
209    /// Converts `self` to an inline value bypassing any safety checks.
210    ///
211    /// # Safety
212    ///
213    /// `self` must hold a valid inline value.
214    #[inline]
215    pub unsafe fn as_inline_unchecked(&self) -> InlineValue {
216        // Safety: `InlineValue` is `#[repr(usize)]`, ensuring that it has the same layout as
217        // `self.0`. The precondition of the function ensures that `self.0` is a valid value for
218        // [InlineValue].
219        //
220        // Since `!Self::inline_pos_idx_MASK` is `FF_FF_FF_FF`, `AND`ing `self.data` with this mask
221        // ensures that the result fit in a u32, so we can use `as` fearlessly.
222        unsafe {
223            transmute::<u32, InlineValue>((self.as_usize() & !Self::INLINE_POS_IDX_MASK) as u32)
224        }
225    }
226
227    /// Makes a raw byte-to-byte copy of another Nickel value, entirely ignoring reference counting
228    /// operations.
229    ///
230    /// # Safety
231    ///
232    /// - the ref count of the value block must be adjusted manually to compensate for dropping `self`. Possible solutions are:
233    ///   - manually increment the reference count
234    ///   - prevent `other` from being dropped (e.g. using [std::mem::ManuallyDrop])
235    ///   - prevent `self` from being dropped
236    #[inline]
237    unsafe fn raw_copy(other: &Self) -> Self {
238        NickelValue { data: other.data }
239    }
240
241    /// Create a Nickel value from a raw pointer to a value block.
242    ///
243    /// # Safety
244    ///
245    /// - `ptr` must be a valid pointer to a [ValueBlockRc].
246    /// - the ref count of the value block must be adjusted manually to compensate for dropping `self`. Possible solutions are:
247    ///   - manually increment the reference count
248    ///   - prevent the original block or value of `ptr` from being dropped (e.g. using
249    ///     [std::mem::ManuallyDrop])
250    #[inline]
251    unsafe fn block(data: NonNull<u8>) -> Self {
252        NickelValue { data }
253    }
254}
255
256// pointer-width-specific implementation
257#[cfg(not(target_pointer_width = "64"))]
258impl NickelValue {
259    /// Returns `self` with the position index set to `idx` if `self` is an inline value, or
260    /// returns `self` unchanged otherwise.
261    #[inline]
262    fn with_inline_pos_idx(mut self, idx: PosIdx) -> Self {
263        // Although the inline position index is just dead data for value blocks on non-64 bits
264        // architecture, we unconditionally update it to avoid branching
265        self.pos_idx = idx;
266        self
267    }
268
269    /// Returns the inline position index of `self` if it's an inline value, or `None` otherwise.
270    fn inline_pos_idx(&self) -> Option<PosIdx> {
271        if self.tag() == ValueTag::Inline {
272            Some(self.pos_idx)
273        } else {
274            None
275        }
276    }
277
278    /// Creates a new inline value.
279    #[inline]
280    pub const fn inline(inline: InlineValue, pos_idx: PosIdx) -> Self {
281        NickelValue {
282            // Safety: inline values are guaranteed to be tagged with a 1 bit, and thus are never
283            // zero.
284            data: unsafe { NonNull::without_provenance(NonZero::new_unchecked(inline as usize)) },
285            pos_idx,
286        }
287    }
288
289    /// Converts the inner value of `self` to an inline value bypassing any safety checks.
290    ///
291    /// # Safety
292    ///
293    /// `self` must hold a valid inline value.
294    #[inline]
295    pub unsafe fn as_inline_unchecked(&self) -> InlineValue {
296        // Safety: `InlineValue` is `#[repr(usize)]`, ensuring that it has the same layout as
297        // `self.0`. The precondition of the function ensures that `self.0` is a valid value for
298        // [InlineValue].
299        unsafe { transmute::<usize, InlineValue>(self.as_usize()) }
300    }
301
302    /// Makes a raw byte-to-byte copy of another Nickel value, entirely ignoring reference counting
303    /// operations.
304    ///
305    /// # Safety
306    ///
307    /// - the ref count of the value block must be adjusted manually to compensate for dropping `self`. Possible solutions are:
308    ///   - manually increment the reference count
309    ///   - prevent `other` from being dropped (e.g. using [std::mem::ManuallyDrop])
310    ///   - prevent `self` from being dropped
311    #[inline]
312    unsafe fn raw_copy(other: &Self) -> Self {
313        NickelValue {
314            data: other.data,
315            pos_idx: other.pos_idx,
316        }
317    }
318
319    /// Creates a Nickel value from a raw pointer to a value block.
320    ///
321    /// # Safety
322    ///
323    /// - `ptr` must be a valid pointer to a [ValueBlockRc].
324    /// - the ref count of the value block must be adjusted manually to compensate for dropping `self`. Possible solutions are:
325    ///   - manually increment the reference count,
326    ///   - prevent `self` from being dropped (e.g. using [std::mem::ManuallyDrop])
327    ///   - use a `ptr` coming from a call to [ValueBlockRc::into_raw]
328    ///   - derive `ptr` from a value or a value block that won't be dropped
329    #[inline]
330    unsafe fn block(data: NonNull<u8>) -> Self {
331        NickelValue {
332            data,
333            pos_idx: PosIdx::NONE,
334        }
335    }
336}
337
338impl NickelValue {
339    /// The mask for the tag bits in a value pointer.
340    const VALUE_TAG_MASK: usize = 0b11;
341
342    /// Returns the inner data as a bare `usize`. This loses the provenance of `self.data`, and is
343    /// supposed to be used for inline values.
344    #[inline]
345    fn as_usize(&self) -> usize {
346        self.data.addr().into()
347    }
348
349    /// Assuming this value is a value block, returns a reference to the corresponding header.
350    ///
351    /// # Safety
352    ///
353    /// `self` must be a value block, that is `self.tag()` must be `Pointer`.
354    #[inline]
355    unsafe fn header(&self) -> &ValueBlockHeader {
356        // Safety: function pre-conditions, plus the lifetime of the returned header is tied to
357        // `&self`, guaranteeing that the block won't be de-allocated.
358        unsafe { ValueBlockRc::header_from_raw(self.data) }
359    }
360
361    /// Returns the tag bits of this value.
362    #[inline]
363    pub fn tag(&self) -> ValueTag {
364        (self.as_usize() & Self::VALUE_TAG_MASK).try_into().unwrap()
365    }
366
367    /// Creates a new inline value without an associated position index. Same as
368    /// `Self::inline(inline, PosIdx::NONE)`.
369    #[inline]
370    pub const fn inline_posless(inline: InlineValue) -> Self {
371        Self::inline(inline, PosIdx::NONE)
372    }
373
374    /// Creates a new null value with the index set to [PosIdx::NONE].
375    #[inline]
376    pub const fn null() -> Self {
377        Self::inline_posless(InlineValue::Null)
378    }
379
380    /// Creates a new true value with the index set to [PosIdx::NONE].
381    #[inline]
382    pub const fn bool_true() -> Self {
383        Self::inline_posless(InlineValue::True)
384    }
385
386    /// Creates a new false value with the index set to [PosIdx::NONE].
387    #[inline]
388    pub const fn bool_false() -> Self {
389        Self::inline_posless(InlineValue::False)
390    }
391
392    /// Creates a new boolean value.
393    pub fn bool_value(value: bool, pos_idx: PosIdx) -> Self {
394        if value {
395            Self::inline(InlineValue::True, pos_idx)
396        } else {
397            Self::inline(InlineValue::False, pos_idx)
398        }
399    }
400
401    /// Creates a new boolean value with the index set to [PosIdx::NONE].
402    #[inline]
403    pub fn bool_value_posless(value: bool) -> Self {
404        Self::bool_value(value, PosIdx::NONE)
405    }
406
407    /// Creates a new empty array with the index set to [PosIdx::NONE].
408    #[inline]
409    pub const fn empty_array() -> Self {
410        Self::inline_posless(InlineValue::EmptyArray)
411    }
412
413    /// Creates a new empty record with the index set to [PosIdx::NONE].
414    #[inline]
415    pub const fn empty_record() -> Self {
416        Self::inline_posless(InlineValue::EmptyRecord)
417    }
418
419    /// Allocates a new number value.
420    #[inline]
421    pub fn number(value: impl Into<Number>, pos_idx: PosIdx) -> Self {
422        ValueBlockRc::encode(value.into(), pos_idx).into()
423    }
424
425    /// Allocates a new number value without any position set. Equivalent to `Self::number(value,
426    /// PosIdx::NONE`
427    #[inline]
428    pub fn number_posless(value: impl Into<Number>) -> Self {
429        Self::number(value, PosIdx::NONE)
430    }
431
432    /// Allocates a new string value.
433    #[inline]
434    pub fn string(value: impl Into<NickelString>, pos_idx: PosIdx) -> Self {
435        ValueBlockRc::encode(value.into(), pos_idx).into()
436    }
437
438    /// Allocates a new string value without any position set. Equivalent to `Self::string(value,
439    /// PosIdx::NONE`
440    #[inline]
441    pub fn string_posless(value: impl Into<NickelString>) -> Self {
442        Self::string(value, PosIdx::NONE)
443    }
444
445    /// Allocates a new array value. If the array is empty, it is automatically inlined as
446    /// [InlineValue::EmptyArray].
447    pub fn array(value: Array, pending_contracts: Vec<RuntimeContract>, pos_idx: PosIdx) -> Self {
448        if value.is_empty() {
449            Self::inline(InlineValue::EmptyArray, pos_idx)
450        } else {
451            ValueBlockRc::encode(
452                ArrayData {
453                    array: value,
454                    pending_contracts,
455                },
456                pos_idx,
457            )
458            .into()
459        }
460    }
461
462    /// Allocates a new array value without any position set. If the array is empty, it is
463    /// automatically inlined as [InlineValue::EmptyArray].
464    #[inline]
465    pub fn array_posless(value: Array, pending_contracts: Vec<RuntimeContract>) -> Self {
466        Self::array(value, pending_contracts, PosIdx::NONE)
467    }
468
469    /// Creates a new empty array, but doesn't inline it. This forces the allocation of a value
470    /// block. [Self::empty_array_block] can be useful when one needs to have a pre-allocated array
471    /// that will be mutated in a second step.
472    #[inline]
473    pub fn empty_array_block(pos_idx: PosIdx) -> Self {
474        ValueBlockRc::encode(ArrayData::default(), pos_idx).into()
475    }
476
477    /// Allocates a new record value. If the record is empty, it is automatically inlined as
478    /// [InlineValue::EmptyRecord].
479    pub fn record(value: RecordData, pos_idx: PosIdx) -> Self {
480        // We need to exclude empty open records here, because we would otherwise lose this bit of
481        // information: an inline empty record is assumed to be closed.
482        if value.is_empty() && !value.attrs.open {
483            Self::inline(InlineValue::EmptyRecord, pos_idx)
484        } else {
485            ValueBlockRc::encode(value, pos_idx).into()
486        }
487    }
488
489    /// Allocates a new record value without any position set. If the record is empty, it is
490    /// automatically inlined as [InlineValue::EmptyRecord].
491    pub fn record_posless(value: RecordData) -> Self {
492        if value.is_empty() {
493            Self::empty_record()
494        } else {
495            ValueBlockRc::encode(value, PosIdx::NONE).into()
496        }
497    }
498
499    /// Creates a new empty record, but doesn't inline it. This forces the allocation of a value
500    /// block. [Self::empty_record] can be useful when one needs to have a pre-allocated record
501    /// that will be mutated in a second step.
502    #[inline]
503    pub fn empty_record_block(pos_idx: PosIdx) -> Self {
504        ValueBlockRc::encode(RecordData::empty(), pos_idx).into()
505    }
506
507    /// Allocates a new thunk value.
508    #[inline]
509    pub fn thunk(data: lazy::ThunkData, pos_idx: PosIdx) -> Self {
510        ValueBlockRc::encode(RefCell::new(data), pos_idx).into()
511    }
512
513    /// Allocates a new thunk value without any position set. Equivalent to `Self::thunk(value,
514    /// PosIdx::NONE)`.
515    #[inline]
516    pub fn thunk_posless(data: lazy::ThunkData) -> Self {
517        Self::thunk(data, PosIdx::NONE)
518    }
519
520    /// Allocates a new term value.
521    #[inline]
522    pub fn term(value: Term, pos_idx: PosIdx) -> Self {
523        ValueBlockRc::encode(value, pos_idx).into()
524    }
525
526    /// Allocates a new term value without any position set. Equivalent to `Self::term(value,
527    /// PosIdx::NONE)`.
528    #[inline]
529    pub fn term_posless(value: Term) -> Self {
530        Self::term(value, PosIdx::NONE)
531    }
532
533    /// Allocates a new label value.
534    #[inline]
535    pub fn label(value: Label, pos_idx: PosIdx) -> Self {
536        ValueBlockRc::encode(value, pos_idx).into()
537    }
538
539    /// Allocates a new label value without any position set. Equivalent to `Self::label(value,
540    /// PosIdx::NONE)`.
541    #[inline]
542    pub fn label_posless(value: Label) -> Self {
543        Self::label(value, PosIdx::NONE)
544    }
545
546    /// Allocates a new enum variant value.
547    pub fn enum_variant(
548        tag: impl Into<LocIdent>,
549        arg: Option<NickelValue>,
550        pos_idx: PosIdx,
551    ) -> Self {
552        ValueBlockRc::encode(
553            EnumVariantData {
554                tag: tag.into(),
555                arg,
556            },
557            pos_idx,
558        )
559        .into()
560    }
561
562    /// Allocates a new enum variant value without any position set. Equivalent to
563    /// `Self::enum_variant(tag, arg, PosIdx::NONE)`.
564    #[inline]
565    pub fn enum_variant_posless(tag: impl Into<LocIdent>, arg: Option<NickelValue>) -> Self {
566        Self::enum_variant(tag.into(), arg, PosIdx::NONE)
567    }
568
569    /// Allocates a new enum tag value. Same as `Self::enum_variant(tag, None, pos_idx)`.
570    #[inline]
571    pub fn enum_tag(tag: impl Into<LocIdent>, pos_idx: PosIdx) -> Self {
572        Self::enum_variant(tag.into(), None, pos_idx)
573    }
574
575    /// Allocates a new enum tag value without any position set. Equivalent to `Self::enum_tag(tag,
576    /// arg, PosIdx::NONE)`.
577    #[inline]
578    pub fn enum_tag_posless(tag: impl Into<LocIdent>) -> Self {
579        Self::enum_tag(tag.into(), PosIdx::NONE)
580    }
581
582    /// Allocates a new foreign ID value.
583    #[inline]
584    pub fn foreign_id(value: ForeignIdPayload, pos_idx: PosIdx) -> Self {
585        ValueBlockRc::encode(value, pos_idx).into()
586    }
587
588    /// Allocates a new foreign ID value without any position set. Equivalent to
589    /// `Self::foreign_id(value, PosIdx::NONE)`.
590    #[inline]
591    pub fn foreign_id_posless(value: ForeignIdPayload) -> Self {
592        Self::foreign_id(value, PosIdx::NONE)
593    }
594
595    /// Allocates a new sealing key value.
596    #[inline]
597    pub fn sealing_key(value: SealingKey, pos_idx: PosIdx) -> Self {
598        ValueBlockRc::encode(value, pos_idx).into()
599    }
600
601    /// Allocates a new sealing key value without any position set. Equivalent to
602    /// `Self::sealing_key(value, PosIdx::NONE)`.
603    #[inline]
604    pub fn sealing_key_posless(value: SealingKey) -> Self {
605        Self::sealing_key(value, PosIdx::NONE)
606    }
607
608    /// Allocates a new custom contract value.
609    #[inline]
610    pub fn custom_contract(value: NickelValue, pos_idx: PosIdx) -> Self {
611        ValueBlockRc::encode(value, pos_idx).into()
612    }
613
614    /// Allocates a new custom contract value without any position set. Equivalent to
615    /// `Self::custom_contract(value, PosIdx::NONE)`.
616    #[inline]
617    pub fn custom_contract_posless(value: NickelValue) -> Self {
618        Self::custom_contract(value, PosIdx::NONE)
619    }
620
621    /// Allocates a new type.
622    #[inline]
623    pub fn typ(typ: Type, contract: NickelValue, pos_idx: PosIdx) -> Self {
624        ValueBlockRc::encode(TypeData { typ, contract }, pos_idx).into()
625    }
626
627    /// Returns the inner value of `self` if it's an inline value, or `None` otherwise.
628    #[inline]
629    pub fn as_inline(&self) -> Option<InlineValue> {
630        InlineValue::try_from(self).ok()
631    }
632
633    /// Returns the inner value of `self` if it's a (inlined) boolean value, or `None` otherwise.
634    #[inline]
635    pub fn as_bool(&self) -> Option<bool> {
636        match self.as_inline()? {
637            InlineValue::True => Some(true),
638            InlineValue::False => Some(false),
639            _ => None,
640        }
641    }
642
643    /// Returns a reference to the inner number stored in this value if `self` is a value block
644    /// with tag number, or `None` otherwise.
645    #[inline]
646    pub fn as_number(&self) -> Option<&NumberData> {
647        self.as_value_data()
648    }
649
650    /// Returns a reference to the inner string stored in this value if `self` is a value block
651    /// with tag string, or `None` otherwise.
652    #[inline]
653    pub fn as_string(&self) -> Option<&StringData> {
654        self.as_value_data()
655    }
656
657    /// Returns a reference to the inner array stored in this value if `self` is a value block
658    /// with tag array or an inline empty array. Returns `None` otherwise.
659    #[inline]
660    pub fn as_array(&self) -> Option<Container<&ArrayData>> {
661        if self.is_inline_empty_array() {
662            Some(Container::Empty)
663        } else {
664            self.as_value_data().map(Container::Alloc)
665        }
666    }
667
668    /// Returns a reference to the inner record stored in this value if `self` is a value block
669    /// with tag record or an inline empty record. Returns `None` otherwise.
670    pub fn as_record(&self) -> Option<Container<&RecordData>> {
671        if self.is_inline_empty_record() {
672            Some(Container::Empty)
673        } else {
674            self.as_value_data().map(Container::Alloc)
675        }
676    }
677
678    /// Returns a reference to the inner thunk data stored in this value if `self` is a value block
679    /// with tag record or an inline empty record. Returns `None` otherwise.
680    ///
681    /// # Safety
682    ///
683    /// `self` must be a thunk value block, that is `self.data_tag()` must be [DataTag::Thunk].
684    #[inline]
685    pub unsafe fn as_thunk_data_unchecked(&self) -> &ThunkData {
686        // Safety: function safety pre-condition
687        unsafe { ValueBlockRc::decode_from_raw_unchecked(self.data) }
688    }
689
690    /// Returns `&self` seen as a thunk if `self` is a value block with tag thunk, or
691    /// `None` otherwise.
692    ///
693    /// While it would be more in line with other `as_xxx` functions to return [ThunkData], in
694    /// practice you often want [Thunk] instead, the latter being harder to work with.
695    pub fn as_thunk(&self) -> Option<&Thunk> {
696        if let Some(DataTag::Thunk) = self.data_tag() {
697            // Safety: function safety pre-condition
698            unsafe { Some(self.as_thunk_unchecked()) }
699        } else {
700            None
701        }
702    }
703
704    /// Returns the same value but wrapped as a [crate::eval::cache::lazy::Thunk] if `self` is a
705    /// value block with tag thunk, or `Err(self)` otherwise.
706    pub fn try_into_thunk(self) -> Result<Thunk, NickelValue> {
707        if let Some(DataTag::Thunk) = self.data_tag() {
708            Ok(Thunk(self))
709        } else {
710            Err(self)
711        }
712    }
713
714    /// Same as [Self::as_thunk], but doesn't check that the value is a block nor that the data tag
715    /// is [DataTag::Thunk].
716    ///
717    /// This method is mostly intended to be used internally and from [Thunk] only. You should
718    /// almost always use [Self::as_thunk] instead.
719    ///
720    /// # Safety
721    ///
722    /// `self` must be a value block with a thunk inside, that is `self.data_tag()` must be
723    /// [DataTag::Thunk].
724    #[inline]
725    pub(super) unsafe fn as_thunk_unchecked(&self) -> &Thunk {
726        // Safety: `Thunk` is a struct with a single field that is a `NickelValue` and has the
727        // `transparent` representation, guaranteeing the same layout as `NickelValue`.
728        unsafe { transmute::<&NickelValue, &Thunk>(self) }
729    }
730
731    /// Mutable version of [Self::as_thunk_unchecked].
732    ///
733    /// # Safety
734    ///
735    /// `self` must be a value block with a thunk inside, that is `self.data_tag()` must be
736    /// [DataTag::Thunk].
737    #[inline]
738    pub(super) unsafe fn as_thunk_mut_unchecked(&mut self) -> &mut Thunk {
739        // Safety: `Thunk` is a struct with a single field that is a `NickelValue` and has the
740        // `transparent` representation, guaranteeing the same layout as `NickelValue`.
741        unsafe { transmute::<&mut NickelValue, &mut Thunk>(self) }
742    }
743
744    /// Interprets this value as a thunk, without checking that the value is a block nor that the
745    /// data tag is [DataTag::Thunk].
746    ///
747    /// This method is mostly intended to be used internally and from [Thunk] only. You should
748    /// almost always use [Self::as_thunk] instead.
749    ///
750    /// # Safety
751    ///
752    /// `self` must be a value block with a thunk inside, that is `self.data_tag()` must be
753    /// [DataTag::Thunk].
754    #[inline]
755    pub(super) unsafe fn into_thunk_unchecked(self) -> Thunk {
756        // Safety: this function pre-condition.
757        Thunk(self)
758    }
759
760    /// Returns a reference to the inner term stored in this value if `self` is a value block with
761    /// a tag term, or `None` otherwise.
762    #[inline]
763    pub fn as_term(&self) -> Option<&TermData> {
764        self.as_value_data()
765    }
766
767    /// Returns a reference to the inner label stored in this value if `self` is a value block with
768    /// tag label, or `None` otherwise.
769    #[inline]
770    pub fn as_label(&self) -> Option<&LabelData> {
771        self.as_value_data()
772    }
773
774    /// Returns a reference to the inner enum variant stored in this value if `self` is a value
775    /// block with tag enum variant, or `None` otherwise.
776    #[inline]
777    pub fn as_enum_variant(&self) -> Option<&EnumVariantData> {
778        self.as_value_data()
779    }
780
781    /// Returns a reference to the inner enum tag stored in this value if `self` is a value is a
782    /// block with an enum variant inside, which has no arguments, or `None` otherwise.
783    pub fn as_enum_tag(&self) -> Option<LocIdent> {
784        let enum_var = self.as_enum_variant()?;
785        enum_var.arg.is_none().then_some(enum_var.tag)
786    }
787
788    /// Returns a reference to the inner sealing key stored in this value if `self` is a value
789    /// block with sealing key inside, or `None` otherwise.
790    #[inline]
791    pub fn as_sealing_key(&self) -> Option<&SealingKeyData> {
792        self.as_value_data()
793    }
794
795    /// Returns a reference to the inner type stored in this value if `self` is a value with a type
796    /// inside, or `None` otherwise.
797    #[inline]
798    pub fn as_type(&self) -> Option<&TypeData> {
799        self.as_value_data()
800    }
801
802    /// Returns a reference to the inner foreign id stored in this value if `self` is a value
803    /// block with a foreign value inside, or `None` otherwise.
804    #[inline]
805    pub fn as_foreign_id(&self) -> Option<&ForeignIdData> {
806        self.as_value_data()
807    }
808
809    /// Returns the value block pointed to by this Nickel value if it's a value block, or `Err`
810    /// otherwise. This is equivalent to `<Self as TryInto<ValueBlockRc>>::try_into`, but is more
811    /// succinct and type-inference-friendly. Since this method must consume `self` in the case
812    /// it's a value block, in order to keep the reference count correct, the caller can get `self`
813    /// back in case of error.
814    #[inline]
815    pub fn into_block(self) -> Result<ValueBlockRc, Self> {
816        ValueBlockRc::try_from(self)
817    }
818
819    /// Returns a reference to the content of this value if it's a value block of type `T`, or
820    /// `None` otherwise.
821    fn as_value_data<T: ValueBlockData>(&self) -> Option<&T> {
822        if self.tag() == ValueTag::Pointer {
823            // Safety: if `tag` is `Pointer`, the content of `self` must be a valid non-null
824            // pointer to a value block.
825            //
826            // Since the value block won't be dropped until self is dropped, and since the output
827            // lifetime of `&T` is tied to `&self`, `&T` won't outlive the value block.
828            unsafe { ValueBlockRc::try_decode_from_raw(self.data) }
829        } else {
830            None
831        }
832    }
833
834    /// Checks if this value is an inline value or is an allocated block.
835    #[inline]
836    pub fn is_inline(&self) -> bool {
837        self.tag() == ValueTag::Inline
838    }
839
840    /// Returns a typed reference to the content of the value, or the content of the inline value
841    /// directly.
842    pub fn content_ref(&self) -> ValueContentRef<'_> {
843        match self.tag() {
844            // Safety: if `self.tag()` is `Pointer`, then the content of self must be a valid
845            // non-null pointer to a value block.
846            ValueTag::Pointer => unsafe {
847                let tag = ValueBlockRc::tag_from_raw(self.data);
848
849                // Safety: additionally, the lifetime of the return `ValueContentRef<'_>` is tied to
850                // `&self`, so the former won't outlive the value block.
851                match tag {
852                    DataTag::Number => {
853                        ValueContentRef::Number(ValueBlockRc::decode_from_raw_unchecked(self.data))
854                    }
855                    DataTag::Array => ValueContentRef::Array(Container::Alloc(
856                        ValueBlockRc::decode_from_raw_unchecked(self.data),
857                    )),
858                    DataTag::Record => ValueContentRef::Record(Container::Alloc(
859                        ValueBlockRc::decode_from_raw_unchecked(self.data),
860                    )),
861                    DataTag::String => {
862                        ValueContentRef::String(ValueBlockRc::decode_from_raw_unchecked(self.data))
863                    }
864                    DataTag::Thunk => {
865                        // Safety: we checked that value's tag is `DataTag::Thunk`
866                        ValueContentRef::Thunk(self.as_thunk_unchecked())
867                    }
868                    DataTag::Term => {
869                        ValueContentRef::Term(ValueBlockRc::decode_from_raw_unchecked(self.data))
870                    }
871                    DataTag::Label => {
872                        ValueContentRef::Label(ValueBlockRc::decode_from_raw_unchecked(self.data))
873                    }
874                    DataTag::EnumVariant => ValueContentRef::EnumVariant(
875                        ValueBlockRc::decode_from_raw_unchecked(self.data),
876                    ),
877                    DataTag::ForeignId => ValueContentRef::ForeignId(
878                        ValueBlockRc::decode_from_raw_unchecked(self.data),
879                    ),
880                    DataTag::SealingKey => ValueContentRef::SealingKey(
881                        ValueBlockRc::decode_from_raw_unchecked(self.data),
882                    ),
883                    DataTag::CustomContract => ValueContentRef::CustomContract(
884                        ValueBlockRc::decode_from_raw_unchecked(self.data),
885                    ),
886                    DataTag::Type => {
887                        ValueContentRef::Type(ValueBlockRc::decode_from_raw_unchecked(self.data))
888                    }
889                }
890            },
891            ValueTag::Inline => {
892                // Safety: `self.tag()` is `ValueTag::Inline`
893                match unsafe { self.as_inline_unchecked() } {
894                    InlineValue::EmptyArray => ValueContentRef::Array(Container::Empty),
895                    InlineValue::EmptyRecord => ValueContentRef::Record(Container::Empty),
896                    InlineValue::Null => ValueContentRef::Null,
897                    InlineValue::True => ValueContentRef::Bool(true),
898                    InlineValue::False => ValueContentRef::Bool(false),
899                }
900            }
901        }
902    }
903
904    /// Returns a mutable typed reference to the content of the value. Returns `None` if the
905    /// underlying value block has a reference count greater than one. See [Self::content_make_mut]
906    /// in this case.
907    ///
908    /// # Thunks
909    ///
910    /// [ValueContentRefMut::Thunk] is special in that it returns a wrapper around `self`, and not
911    /// a reference into the block. It's the only case that doesn't actually require to
912    /// copy-on-write, and thus this method always return `Some` for thunks even if the block is
913    /// shared.
914    pub fn content_mut(&mut self) -> Option<ValueContentRefMut<'_>> {
915        match self.tag() {
916            // Safety: if `self.tag()` is `Pointer`, then the content of self must be a valid
917            // non-null pointer to a value block.
918            ValueTag::Pointer => unsafe {
919                let header = self.header();
920
921                // [^thunk-copy-on-mut-ref]: We must be careful with thunks. Not only we don't need
922                // to copy-on-write, since we're handing back a `Thunk` which is just a wrapper
923                // around `self` (instead of a mutable reference inside the block), but we must
924                // actually _avoid_ making copies, which would break the sharing of thunks.
925                if header.ref_count() != 1 && header.tag() != DataTag::Thunk {
926                    return None;
927                }
928
929                // Safety:
930                //  - additionally, the lifetime of the return `ValueContentRef<'_>` is tied to
931                //    `&mut self`, so the former won't outlive the value block.
932                //  - we've checked above that `ref_count` is `1`, and we hold a mutable borrow of
933                //  `  self`, so there can't be other active mutable borrows to the block
934                Some(match header.tag() {
935                    DataTag::Number => ValueContentRefMut::Number(
936                        ValueBlockRc::decode_mut_from_raw_unchecked(self.data),
937                    ),
938                    DataTag::Array => ValueContentRefMut::Array(Container::Alloc(
939                        ValueBlockRc::decode_mut_from_raw_unchecked(self.data),
940                    )),
941                    DataTag::Record => ValueContentRefMut::Record(Container::Alloc(
942                        ValueBlockRc::decode_mut_from_raw_unchecked(self.data),
943                    )),
944                    DataTag::String => ValueContentRefMut::String(
945                        ValueBlockRc::decode_mut_from_raw_unchecked(self.data),
946                    ),
947                    // Safety: `Thunk` is a struct with a single field that is a `NickelValue` and has the
948                    // `transparent` representation, guaranteeing the same layout as `NickelValue`.
949                    DataTag::Thunk => ValueContentRefMut::Thunk(self.as_thunk_mut_unchecked()),
950                    DataTag::Term => ValueContentRefMut::Term(
951                        ValueBlockRc::decode_mut_from_raw_unchecked(self.data),
952                    ),
953                    DataTag::Label => ValueContentRefMut::Label(
954                        ValueBlockRc::decode_mut_from_raw_unchecked(self.data),
955                    ),
956                    DataTag::EnumVariant => ValueContentRefMut::EnumVariant(
957                        ValueBlockRc::decode_mut_from_raw_unchecked(self.data),
958                    ),
959                    DataTag::ForeignId => ValueContentRefMut::ForeignId(
960                        ValueBlockRc::decode_mut_from_raw_unchecked(self.data),
961                    ),
962                    DataTag::SealingKey => ValueContentRefMut::SealingKey(
963                        ValueBlockRc::decode_mut_from_raw_unchecked(self.data),
964                    ),
965                    DataTag::CustomContract => ValueContentRefMut::CustomContract(
966                        ValueBlockRc::decode_mut_from_raw_unchecked(self.data),
967                    ),
968                    DataTag::Type => ValueContentRefMut::Type(
969                        ValueBlockRc::decode_mut_from_raw_unchecked(self.data),
970                    ),
971                })
972            },
973            ValueTag::Inline => {
974                // Safety: `self.tag()` is `ValueTag::Inline`
975                Some(match unsafe { self.as_inline_unchecked() } {
976                    InlineValue::EmptyArray => ValueContentRefMut::Array(Container::Empty),
977                    InlineValue::EmptyRecord => ValueContentRefMut::Record(Container::Empty),
978                    InlineValue::Null => ValueContentRefMut::Null(self),
979                    InlineValue::True => ValueContentRefMut::Bool(self),
980                    InlineValue::False => ValueContentRefMut::Bool(self),
981                })
982            }
983        }
984    }
985
986    /// Returns a lazy, owned handle to the content of this value.
987    pub fn content(self) -> ValueContent {
988        use lens::{TermContent, ValueLens};
989
990        match self.tag() {
991            ValueTag::Pointer => {
992                // Safety: in each branch, the tag of the value block matches the type parameter of
993                // the lens built with `content_lens()`.
994                unsafe {
995                    // Safety: if `self.tag()` is `ValueTag::Pointer`, `self.data` must be a valid
996                    // pointer to a block.
997                    match self.header().tag() {
998                        DataTag::Number => ValueContent::Number(ValueLens::content_lens(self)),
999                        DataTag::Array => ValueContent::Array(ValueLens::container_lens(self)),
1000                        DataTag::Record => ValueContent::Record(ValueLens::container_lens(self)),
1001                        DataTag::String => ValueContent::String(ValueLens::content_lens(self)),
1002                        DataTag::Thunk => ValueContent::Thunk(ValueLens::thunk_lens(self)),
1003                        DataTag::Term => {
1004                            let term: &TermData =
1005                                ValueBlockRc::decode_from_raw_unchecked(self.data);
1006
1007                            ValueContent::Term(match term {
1008                                Term::StrChunks(_) => {
1009                                    TermContent::StrChunks(ValueLens::term_str_chunks_lens(self))
1010                                }
1011                                Term::Fun(..) => TermContent::Fun(ValueLens::term_fun_lens(self)),
1012                                Term::Let(..) => TermContent::Let(ValueLens::term_let_lens(self)),
1013                                Term::App(..) => TermContent::App(ValueLens::term_app_lens(self)),
1014                                Term::Var(..) => TermContent::Var(ValueLens::term_var_lens(self)),
1015                                Term::RecRecord(..) => {
1016                                    TermContent::RecRecord(ValueLens::term_rec_record_lens(self))
1017                                }
1018                                Term::Closurize(_) => {
1019                                    TermContent::Closurize(ValueLens::term_closurize_lens(self))
1020                                }
1021                                Term::Op1(..) => TermContent::Op1(ValueLens::term_op1_lens(self)),
1022                                Term::Op2(..) => TermContent::Op2(ValueLens::term_op2_lens(self)),
1023                                Term::OpN(..) => TermContent::OpN(ValueLens::term_opn_lens(self)),
1024                                Term::Sealed(..) => {
1025                                    TermContent::Sealed(ValueLens::term_sealed_lens(self))
1026                                }
1027                                Term::Annotated(..) => {
1028                                    TermContent::Annotated(ValueLens::term_annotated_lens(self))
1029                                }
1030                                Term::Import(_) => {
1031                                    TermContent::Import(ValueLens::term_import_lens(self))
1032                                }
1033                                Term::ResolvedImport(_) => TermContent::ResolvedImport(
1034                                    ValueLens::term_resolved_import_lens(self),
1035                                ),
1036                                Term::ParseError(_) => {
1037                                    TermContent::ParseError(ValueLens::term_parse_error_lens(self))
1038                                }
1039                                Term::RuntimeError(_) => TermContent::RuntimeError(
1040                                    ValueLens::term_runtime_error_lens(self),
1041                                ),
1042                            })
1043                        }
1044                        DataTag::Label => ValueContent::Label(ValueLens::content_lens(self)),
1045                        DataTag::EnumVariant => {
1046                            ValueContent::EnumVariant(ValueLens::content_lens(self))
1047                        }
1048                        DataTag::ForeignId => {
1049                            ValueContent::ForeignId(ValueLens::content_lens(self))
1050                        }
1051                        DataTag::SealingKey => {
1052                            ValueContent::SealingKey(ValueLens::content_lens(self))
1053                        }
1054                        DataTag::CustomContract => {
1055                            ValueContent::CustomContract(ValueLens::content_lens(self))
1056                        }
1057                        DataTag::Type => ValueContent::Type(ValueLens::content_lens(self)),
1058                    }
1059                }
1060            }
1061            ValueTag::Inline => {
1062                // Safety:
1063                //
1064                // as_inline_unchecked: `self.tag()` is `ValueTag::Inline`
1065                //
1066                // ValueLens::xxx_lens: in each branch, the type of the inline value matches the
1067                // type of the lens built with `xxx_lens()`.
1068                unsafe {
1069                    match self.as_inline_unchecked() {
1070                        InlineValue::Null => ValueContent::Null(ValueLens::null_lens(self)),
1071                        InlineValue::True | InlineValue::False => {
1072                            ValueContent::Bool(ValueLens::bool_lens(self))
1073                        }
1074                        InlineValue::EmptyArray => {
1075                            ValueContent::Array(ValueLens::container_lens(self))
1076                        }
1077                        InlineValue::EmptyRecord => {
1078                            ValueContent::Record(ValueLens::container_lens(self))
1079                        }
1080                    }
1081                }
1082            }
1083        }
1084    }
1085
1086    /// Returns a mutable typed reference to the content of the value. This method is
1087    /// copy-on-write, same as [std::rc::Rc::make_mut] and [ValueBlockRc::make_mut]: if the
1088    /// underlying value block has a reference count greater than one, `self` is assigned to a fresh
1089    /// copy which is guaranteed to be 1-reference counted, and a mutable reference to the content
1090    /// of this copy is returned. Thunks are exception, and will never be copied; see
1091    /// [Self::content_mut].
1092    pub fn content_make_mut(&mut self) -> ValueContentRefMut<'_> {
1093        // Safety: `value.tag()` must be `Pointer` and `value.data_tag()` must be equal to `T::Tag`
1094        unsafe fn make_mut<T: ValueBlockData + Clone>(value: &mut NickelValue) -> &mut T {
1095            unsafe {
1096                if value.header().ref_count() != 1 {
1097                    increment!("value::content_make_mut::strong clone");
1098                    // Safety: header doesn't become dangling because the ref count is strictly
1099                    // greater than one, so the reassignment of `*value` doesn't invalidate it.
1100                    *value = ValueBlockRc::encode(
1101                        // Safety: `value.data_tag()` is `T::Tag` (precondition)
1102                        ValueBlockRc::decode_from_raw_unchecked::<T>(value.data).clone(),
1103                        value.header().pos_idx,
1104                    )
1105                    .into();
1106                } else {
1107                    increment!("value::content_make_mut::no clone");
1108                }
1109
1110                // Safety: we've made sure `value` is now unique
1111                ValueBlockRc::decode_mut_from_raw_unchecked::<T>(value.data)
1112            }
1113        }
1114
1115        match self.tag() {
1116            // Safety: if `self.tag()` is `Pointer`, then the content of self must be a valid
1117            // non-null pointer to a value block.
1118            ValueTag::Pointer => unsafe {
1119                let tag = ValueBlockRc::tag_from_raw(self.data);
1120
1121                match tag {
1122                    DataTag::Number => ValueContentRefMut::Number(make_mut(self)),
1123                    DataTag::Array => ValueContentRefMut::Array(Container::Alloc(make_mut(self))),
1124                    DataTag::Record => ValueContentRefMut::Record(Container::Alloc(make_mut(self))),
1125                    DataTag::String => ValueContentRefMut::String(make_mut(self)),
1126                    // See [^thunk-copy-on-mut-ref].
1127                    DataTag::Thunk => {
1128                        // Safety: we just checked that the value's tag is `Thunk`.
1129                        ValueContentRefMut::Thunk(self.as_thunk_mut_unchecked())
1130                    }
1131                    DataTag::Term => ValueContentRefMut::Term(make_mut(self)),
1132                    DataTag::Label => ValueContentRefMut::Label(make_mut(self)),
1133                    DataTag::EnumVariant => ValueContentRefMut::EnumVariant(make_mut(self)),
1134                    DataTag::ForeignId => ValueContentRefMut::ForeignId(make_mut(self)),
1135                    DataTag::SealingKey => ValueContentRefMut::SealingKey(make_mut(self)),
1136                    DataTag::CustomContract => ValueContentRefMut::CustomContract(make_mut(self)),
1137                    DataTag::Type => ValueContentRefMut::Type(make_mut(self)),
1138                }
1139            },
1140            ValueTag::Inline => {
1141                // Safety: `self.tag()` is `ValueTag::Inline`
1142                match unsafe { self.as_inline_unchecked() } {
1143                    InlineValue::Null => ValueContentRefMut::Null(self),
1144                    InlineValue::True | InlineValue::False => ValueContentRefMut::Bool(self),
1145                    InlineValue::EmptyArray => ValueContentRefMut::Array(Container::Empty),
1146                    InlineValue::EmptyRecord => ValueContentRefMut::Record(Container::Empty),
1147                }
1148            }
1149        }
1150    }
1151
1152    /// Returns the data tag of the underlying value block, or `None` if `self` is an inline value.
1153    pub fn data_tag(&self) -> Option<DataTag> {
1154        (self.tag() == ValueTag::Pointer).then(|| {
1155            // Safety: if `self.tag()` is `Pointer`, then `self.data` must be valid pointer to a
1156            // value block.
1157            unsafe { ValueBlockRc::tag_from_raw(self.data) }
1158        })
1159    }
1160
1161    /// Checks if this value is the inline empty array. Caution: note that `self` could also be an
1162    /// empty array, but allocated as a block. In that case, [Self::is_inline_empty_array] would
1163    /// still return `false`.
1164    pub fn is_inline_empty_array(&self) -> bool {
1165        self.as_inline()
1166            .is_some_and(|inl| matches!(inl, InlineValue::EmptyArray))
1167    }
1168
1169    /// Checks if this value is the inline empty record. Caution: note that `self` could also be an
1170    /// empty record, but allocated as a block. In that case, [Self::is_inline_empty_record] would still
1171    /// return `false`.
1172    pub fn is_inline_empty_record(&self) -> bool {
1173        self.as_inline()
1174            .is_some_and(|inl| matches!(inl, InlineValue::EmptyRecord))
1175    }
1176
1177    /// Checks if this value is the inlined boolean `true`.
1178    pub fn is_bool_true(&self) -> bool {
1179        self.as_inline()
1180            .is_some_and(|inl| matches!(inl, InlineValue::True))
1181    }
1182
1183    /// Checks if this value is the inlined boolean `false`.
1184    pub fn is_bool_false(&self) -> bool {
1185        self.as_inline()
1186            .is_some_and(|inl| matches!(inl, InlineValue::False))
1187    }
1188
1189    /// Checks if this value is the inlined constant `null`.
1190    pub fn is_null(&self) -> bool {
1191        self.as_inline()
1192            .is_some_and(|inl| matches!(inl, InlineValue::Null))
1193    }
1194
1195    /// Determines if a value is in evaluated form, called weak head normal form (WHNF). See
1196    /// [crate::term::Term::is_whnf] for more detais.
1197    #[inline]
1198    pub fn is_whnf(&self) -> bool {
1199        !matches!(self.data_tag(), Some(DataTag::Thunk | DataTag::Term))
1200    }
1201
1202    /// Returns the class of an expression in WHNF.
1203    ///
1204    /// The class of an expression is an approximation of its type used in error reporting. Class
1205    /// and type coincide for constants (numbers, strings and booleans) and arrays. Otherwise the
1206    /// class is less precise than the type and indicates the general shape of the term: `Record`
1207    /// for records, `Array` for arrays, etc. If the term is not a WHNF, `None` is returned.
1208    pub fn type_of(&self) -> Option<&'static str> {
1209        match self.content_ref() {
1210            ValueContentRef::Null => Some("Other"),
1211            ValueContentRef::Bool(_) => Some("Bool"),
1212            ValueContentRef::Number(_) => Some("Number"),
1213            ValueContentRef::Array(_) => Some("Array"),
1214            ValueContentRef::Record(_) => Some("Record"),
1215            ValueContentRef::String(_) => Some("String"),
1216            ValueContentRef::Term(term) => match term {
1217                Term::Closurize(v) => v.type_of(),
1218                Term::RecRecord(..) => Some("Record"),
1219                Term::Fun(..) => Some("Function"),
1220                Term::Sealed(..) => Some("Sealed"),
1221                Term::Annotated(..) => Some("Annotated"),
1222                Term::Let(..)
1223                | Term::App(_)
1224                | Term::Var(_)
1225                | Term::Op1(_)
1226                | Term::Op2(_)
1227                | Term::OpN(_)
1228                | Term::Import(_)
1229                | Term::ResolvedImport(_)
1230                | Term::StrChunks(_)
1231                | Term::ParseError(_)
1232                | Term::RuntimeError(_) => None,
1233            },
1234            ValueContentRef::Label(_) => Some("Label"),
1235            ValueContentRef::EnumVariant(EnumVariantData { arg: None, tag: _ }) => Some("EnumTag"),
1236            ValueContentRef::EnumVariant(EnumVariantData {
1237                arg: Some(_),
1238                tag: _,
1239            }) => Some("EnumVariant"),
1240            ValueContentRef::ForeignId(_) => Some("ForeignId"),
1241            ValueContentRef::SealingKey(_) => Some("SealingKey"),
1242            ValueContentRef::CustomContract(_) => Some("CustomContract"),
1243            ValueContentRef::Type(_) => Some("Type"),
1244            _ => None,
1245        }
1246    }
1247
1248    /// Determines if a term is a constant.
1249    ///
1250    /// In this context, a constant is an atomic literal of the language that mustn't contain any
1251    /// other sub-expression: null, a boolean, a number, a string, a label, an enum tag, an empty
1252    /// container (array or record), a sealing key or a foreign id.
1253    pub fn is_constant(&self) -> bool {
1254        match self.content_ref() {
1255            ValueContentRef::Null
1256            | ValueContentRef::Bool(_)
1257            | ValueContentRef::Array(Container::Empty)
1258            | ValueContentRef::Record(Container::Empty)
1259            | ValueContentRef::Number(_)
1260            | ValueContentRef::Label(_)
1261            | ValueContentRef::ForeignId(_)
1262            | ValueContentRef::SealingKey(_)
1263            | ValueContentRef::String(_) => true,
1264            ValueContentRef::EnumVariant(enum_variant) => enum_variant.arg.is_none(),
1265            ValueContentRef::Array(_)
1266            | ValueContentRef::Record(_)
1267            | ValueContentRef::Thunk(_)
1268            | ValueContentRef::Term(_)
1269            | ValueContentRef::CustomContract(_)
1270            | ValueContentRef::Type(_) => false,
1271        }
1272    }
1273
1274    /// Determines if a value is an atom of the surface syntax. Atoms are basic elements of the
1275    /// syntax that can freely substituted without being parenthesized.
1276    pub fn fmt_is_atom(&self) -> bool {
1277        let Some(data_tag) = self.data_tag() else {
1278            // All inline values are atoms
1279            // Caution: if we inline some integers in the future as well, negative integers are NOT
1280            // atoms, so this path should be updated.
1281            return true;
1282        };
1283
1284        match data_tag {
1285            DataTag::Array
1286            | DataTag::Record
1287            | DataTag::ForeignId
1288            | DataTag::SealingKey
1289            | DataTag::Label
1290            | DataTag::String => true,
1291            DataTag::Number => self.as_value_data::<NumberData>().unwrap() >= &0,
1292            DataTag::EnumVariant => self
1293                .as_value_data::<EnumVariantData>()
1294                .unwrap()
1295                .arg
1296                .is_none(),
1297            DataTag::Thunk => false,
1298            DataTag::Term => self.as_value_data::<TermData>().unwrap().fmt_is_atom(),
1299            DataTag::CustomContract => self
1300                .as_value_data::<CustomContractData>()
1301                .unwrap()
1302                .fmt_is_atom(),
1303            DataTag::Type => self.as_value_data::<TypeData>().unwrap().typ.fmt_is_atom(),
1304        }
1305    }
1306
1307    /// Converts a primitive value (number, string, boolean, enum tag or null) to a Nickel string,
1308    /// or returns `None` if the value isn't primitive.
1309    pub fn to_nickel_string(&self) -> Option<NickelString> {
1310        match self.content_ref() {
1311            ValueContentRef::Null => Some("null".into()),
1312            ValueContentRef::Bool(b) => Some(b.to_string().into()),
1313            ValueContentRef::String(s) => Some(s.clone()),
1314            ValueContentRef::EnumVariant(EnumVariantData { tag, arg: None }) => Some((*tag).into()),
1315            ValueContentRef::Number(n) => Some(format!("{}", n.to_sci()).into()),
1316            _ => None,
1317        }
1318    }
1319
1320    /// Checks for physical equality of two Nickel values. This is a very fast check that is
1321    /// complete for inline values but partial otherwise (i.e. it only returns `true` for pointers
1322    /// if the values physically point to the same value block).
1323    ///
1324    /// For inline values, this check **ignores** the position index.
1325    pub fn phys_eq(&self, other: &Self) -> bool {
1326        match self.tag() {
1327            ValueTag::Pointer => self.data == other.data,
1328            // Safety: the tag is checked to be `Inline`
1329            ValueTag::Inline => {
1330                other.is_inline()
1331                    && unsafe {
1332                        // Using `as_inline_unchecked` instead of `self.data` directly sidesteps the
1333                        // platform-specific differences
1334                        self.as_inline_unchecked() == other.as_inline_unchecked()
1335                    }
1336            }
1337        }
1338    }
1339
1340    /// Updates the position of this value. First allocates a position index in the position table
1341    /// and use it as the new index.
1342    #[inline]
1343    pub fn with_pos(self, pos_table: &mut PosTable, pos: TermPos) -> Self {
1344        self.with_pos_idx(pos_table.push(pos))
1345    }
1346
1347    /// Updates the position index of this value. If the value is a shared block (the ref
1348    /// count is greater than one), the result will be a new copy of the original value with the
1349    /// position set.
1350    pub fn with_pos_idx(self, pos_idx: PosIdx) -> Self {
1351        match self.tag() {
1352            ValueTag::Pointer => {
1353                // unwrap(): if the tag is `Pointer`, `ValueBlocRc::try_from(self)` must succeed.
1354                let mut block: ValueBlockRc = self.try_into().unwrap();
1355                block.make_unique();
1356                // Safety: `make_unique()` ensures there's no sharing, and we have the exclusive
1357                // access (ownership) of the block.
1358                unsafe { block.0.cast::<ValueBlockHeader>().as_mut().pos_idx = pos_idx }
1359                block.into()
1360            }
1361            // Safety: the tag is checked to be `Inline`
1362            ValueTag::Inline => self.with_inline_pos_idx(pos_idx),
1363        }
1364    }
1365
1366    /// Returns the position index of this value, whether it is inline or not.
1367    pub fn pos_idx(&self) -> PosIdx {
1368        match self.tag() {
1369            // Safety: if `self.tag()` is `Pointer`, then `self.data` must be a valid non-null
1370            // pointer to a value block
1371            ValueTag::Pointer => unsafe { self.header().pos_idx },
1372            // unwrap(): if the tag is `Inline`, then `inline_pos_idx()` must be `Some`
1373            ValueTag::Inline => self.inline_pos_idx().unwrap(),
1374        }
1375    }
1376
1377    /// Returns the position associated to this value.
1378    #[inline]
1379    pub fn pos(&self, table: &PosTable) -> TermPos {
1380        table.get(self.pos_idx())
1381    }
1382
1383    /// Returns the same value with all the positions (recursively) cleared (set to
1384    /// [crate::position::PosIdx::NONE]).
1385    ///
1386    /// This is currently only used in test code, but because it's used from integration
1387    /// tests we cannot hide it behind `#[cfg(test)]`.
1388    pub fn without_pos(self) -> Self {
1389        self.traverse(
1390            &mut |t: Type| {
1391                Ok::<_, Infallible>(Type {
1392                    pos: TermPos::None,
1393                    ..t
1394                })
1395            },
1396            TraverseOrder::BottomUp,
1397        )
1398        .unwrap()
1399        .traverse(
1400            &mut |val: NickelValue| {
1401                //unwrap(): will go away soon
1402                Ok::<_, Infallible>(val.with_pos_idx(PosIdx::NONE))
1403            },
1404            TraverseOrder::BottomUp,
1405        )
1406        .unwrap()
1407    }
1408}
1409
1410impl Default for NickelValue {
1411    #[inline]
1412    fn default() -> Self {
1413        Self::null()
1414    }
1415}
1416
1417// Since a `NickelValue` can be a reference-counted pointer in disguise, we can't just copy it
1418// blindly. We need to make sure the reference count is incremented accordingly.
1419impl Clone for NickelValue {
1420    #[inline]
1421    fn clone(&self) -> Self {
1422        if self.tag() == ValueTag::Pointer {
1423            unsafe {
1424                // Safety: `self.tag()` is `Pointer`.
1425                //
1426                // We need to prevent this value block from being dropped as this would decrement
1427                // the refcount, nullifying our increment.
1428                self.header().inc_ref_count();
1429            }
1430        }
1431
1432        // Safety: we incremented the ref count above, if the inner value is a block.
1433        unsafe { NickelValue::raw_copy(self) }
1434    }
1435}
1436
1437// Same as for `Clone`: since we might be a reference-counted pointer in disguise, we need to
1438// properly decrement the underlying ref count.
1439impl Drop for NickelValue {
1440    #[inline]
1441    fn drop(&mut self) {
1442        if self.tag() == ValueTag::Pointer {
1443            unsafe {
1444                // Safety: if `self.tag()` is `Pointer`, `self.0` must be valid non-null pointer to
1445                // a value block
1446                let _ = ValueBlockRc::from_raw(self.data);
1447            }
1448        }
1449    }
1450}
1451
1452impl From<InlineValue> for NickelValue {
1453    #[inline]
1454    fn from(inline: InlineValue) -> Self {
1455        NickelValue::inline_posless(inline)
1456    }
1457}
1458
1459impl<'a> TryFrom<&'a NickelValue> for InlineValue {
1460    type Error = TagMismatchError;
1461
1462    fn try_from(value: &'a NickelValue) -> Result<Self, Self::Error> {
1463        if value.tag() == ValueTag::Inline {
1464            // Safety: we checked that the tag is `ValueTag::Inline`
1465            unsafe { Ok(value.as_inline_unchecked()) }
1466        } else {
1467            Err(TagMismatchError)
1468        }
1469    }
1470}
1471
1472impl TryFrom<NickelValue> for ValueBlockRc {
1473    /// To avoid consuming the original value needlessly, it is returned upon error.
1474    type Error = NickelValue;
1475
1476    fn try_from(value: NickelValue) -> Result<Self, Self::Error> {
1477        if value.tag() == ValueTag::Pointer {
1478            Ok(unsafe {
1479                // We need to prevent value from being dropped, or this will decrease the refcount.
1480                let value = ManuallyDrop::new(value);
1481                // Safety: if `self.tag()` is `Pointer`, `self.0` must be a valid non-null pointer
1482                // to a value block
1483                ValueBlockRc::from_raw(value.data)
1484            })
1485        } else {
1486            Err(value)
1487        }
1488    }
1489}
1490
1491impl From<ValueBlockRc> for NickelValue {
1492    // Converts this value block to a [NickelValue] pointer. To keep the reference count
1493    // consistent, this function consumes the value block without decrementing the reference
1494    // count, which is left unchanged by the conversion.
1495    #[inline]
1496    fn from(block: ValueBlockRc) -> Self {
1497        // We must avoid dropping `block` here, which would decrement the reference count.
1498        let this = ManuallyDrop::new(block);
1499        // Safety: this is a valid pointer to a `ValueBlockRc`, and the usage of manually drop
1500        // ensures the ref count is left unchanged.
1501        unsafe { NickelValue::block(this.0) }
1502    }
1503}
1504
1505impl_display_from_pretty!(NickelValue);
1506
1507/// Pointer tag used by [NickelValue] to discriminate between the pointer and non-pointer kind of Nickel values.
1508#[repr(usize)]
1509#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1510///////////
1511// CAUTION
1512///////////
1513// unsafe conversion functions from and to numeric types are relying on the precise values
1514// and range of `ValueTag`. If you add or remove tags, make sure to update all the corresponding
1515// code, in particular the `Self::MAX` constant.
1516//
1517// Values must be consecutive.
1518pub enum ValueTag {
1519    /// A heap-allocated value, meaning the tagged data is a valid pointer to [ValueBlockRc].
1520    Pointer = 0,
1521    /// The tag for an [InlineValue], which is not a pointer.
1522    Inline = 1,
1523}
1524
1525impl ValueTag {
1526    /// The highest possible value when a tag is seen as a usize.
1527    pub const MAX: usize = ValueTag::Inline as usize;
1528}
1529
1530impl From<ValueTag> for usize {
1531    #[inline]
1532    fn from(tag: ValueTag) -> Self {
1533        tag as usize
1534    }
1535}
1536
1537/// Out of bounds error when trying to convert a numeric type to a tag.
1538#[derive(Clone, Copy, Eq, PartialEq, Debug)]
1539pub struct TagOutOfBoundsError;
1540
1541impl TryFrom<usize> for ValueTag {
1542    type Error = TagOutOfBoundsError;
1543
1544    fn try_from(value: usize) -> Result<Self, Self::Error> {
1545        if value <= ValueTag::MAX {
1546            // Safety: `#[repr(usize)]` on [ValueTag] guarantees that the enum is safe to transmute
1547            // to and from `usize`, as long as we are in the range of valid tags.
1548            Ok(unsafe { transmute::<usize, ValueTag>(value) })
1549        } else {
1550            Err(TagOutOfBoundsError)
1551        }
1552    }
1553}
1554
1555/// Encode an inline value as a tagged pointer representation (a [NickelValue]).
1556const fn tag_inline(inline: u32) -> u32 {
1557    // Ensures the two highest bits of the inline value aren't set, so that `content << 2` doesn't
1558    // lose any information.
1559    debug_assert!(inline <= (u32::MAX >> 2));
1560    (inline << 2) | (ValueTag::Inline as u32)
1561}
1562
1563/// Small Nickel values that can be inlined in the higher bits (excluding the tag) of the compact
1564/// representation of a Nickel value. Their numeric value is directly encoded with the tag
1565/// included, so that no bit shifting is needed at all when reading or writing them.
1566#[repr(u32)]
1567#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1568pub enum InlineValue {
1569    Null = tag_inline(0),
1570    True = tag_inline(1),
1571    False = tag_inline(2),
1572    EmptyArray = tag_inline(3),
1573    EmptyRecord = tag_inline(4),
1574}
1575
1576/// The discriminating tag for the different kinds of data that can be store in a value block.
1577///////////
1578// CAUTION
1579///////////
1580// unsafe conversion functions from and to numeric types are relying on the `u8` representation,
1581// and precise values and range of `ValueTag`. If you add or remove tags, make sure to update all
1582// the corresponding code, in particular the `Self::MAX` constant.
1583//
1584// Values must be consecutive.
1585#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1586#[repr(u8)]
1587pub enum DataTag {
1588    Number = 0,
1589    Array = 1,
1590    Record = 2,
1591    String = 3,
1592    Thunk = 4,
1593    /// This is a temporary optimization. We could always store a term as a thunk with an empty
1594    /// environment, but this requires additional allocation, indirection, etc. As long as we keep
1595    /// the hybrid term/value representation, we'll use [Self::Term] to inline a term with an empty
1596    /// environment, typically when converting the new AST to the runtime representation.
1597    Term = 5,
1598    Label = 6,
1599    EnumVariant = 7,
1600    ForeignId = 8,
1601    SealingKey = 9,
1602    CustomContract = 10,
1603    Type = 11,
1604}
1605
1606impl DataTag {
1607    /// Returns the offset of the data in a value block from the start pointer (the header). Calls
1608    /// to [ValueBlockRc::data_offset] under the hood instantiated with the right type.
1609    const fn data_offset(&self) -> usize {
1610        match self {
1611            DataTag::Number => ValueBlockRc::data_offset::<NumberData>(),
1612            DataTag::String => ValueBlockRc::data_offset::<StringData>(),
1613            DataTag::Array => ValueBlockRc::data_offset::<ArrayData>(),
1614            DataTag::Record => ValueBlockRc::data_offset::<RecordData>(),
1615            DataTag::Thunk => ValueBlockRc::data_offset::<ThunkData>(),
1616            DataTag::Term => ValueBlockRc::data_offset::<TermData>(),
1617            DataTag::Label => ValueBlockRc::data_offset::<LabelData>(),
1618            DataTag::EnumVariant => ValueBlockRc::data_offset::<EnumVariantData>(),
1619            DataTag::ForeignId => ValueBlockRc::data_offset::<ForeignIdData>(),
1620            DataTag::SealingKey => ValueBlockRc::data_offset::<SealingKeyData>(),
1621            DataTag::CustomContract => ValueBlockRc::data_offset::<CustomContractData>(),
1622            DataTag::Type => ValueBlockRc::data_offset::<TypeData>(),
1623        }
1624    }
1625
1626    /// Returns the layout to be used for (de)allocation of a whole value block for a given type of
1627    /// data. Calls to [ValueBlockRc::block_layout] under the hood instantiated with the right
1628    /// type.
1629    const fn block_layout(&self) -> Layout {
1630        match self {
1631            DataTag::Number => ValueBlockRc::block_layout::<NumberData>(),
1632            DataTag::String => ValueBlockRc::block_layout::<StringData>(),
1633            DataTag::Array => ValueBlockRc::block_layout::<ArrayData>(),
1634            DataTag::Record => ValueBlockRc::block_layout::<RecordData>(),
1635            DataTag::Thunk => ValueBlockRc::block_layout::<ThunkData>(),
1636            DataTag::Term => ValueBlockRc::block_layout::<TermData>(),
1637            DataTag::Label => ValueBlockRc::block_layout::<LabelData>(),
1638            DataTag::EnumVariant => ValueBlockRc::block_layout::<EnumVariantData>(),
1639            DataTag::ForeignId => ValueBlockRc::block_layout::<ForeignIdData>(),
1640            DataTag::SealingKey => ValueBlockRc::block_layout::<SealingKeyData>(),
1641            DataTag::CustomContract => ValueBlockRc::block_layout::<CustomContractData>(),
1642            DataTag::Type => ValueBlockRc::block_layout::<TypeData>(),
1643        }
1644    }
1645
1646    /// The highest possible value (included) for [Self].
1647    const MAX: u8 = DataTag::Type as u8;
1648}
1649
1650impl From<DataTag> for u8 {
1651    #[inline]
1652    fn from(tag: DataTag) -> Self {
1653        tag as u8
1654    }
1655}
1656
1657impl TryFrom<u8> for DataTag {
1658    type Error = TagOutOfBoundsError;
1659
1660    fn try_from(value: u8) -> Result<Self, Self::Error> {
1661        if value <= DataTag::MAX {
1662            // Safety: `#[repr(u8)]` on `DataTag` guarantees that the enum is safe to transmute to
1663            // and from `u8`, as long as we are in the range of valid tags.
1664            Ok(unsafe { transmute::<u8, DataTag>(value) })
1665        } else {
1666            Err(TagOutOfBoundsError)
1667        }
1668    }
1669}
1670
1671/// The header for a heap-allocated Nickel value which is laid out at the beginning of a value
1672/// block directly followed by the value data.
1673// We set a minimal alignment of `4` bytes, so that pointers to the content of a value block
1674// (which are aligned to the max of the alignment of `ValueBlockHeader` and the content) is
1675// guaranteed to have at least the last 2 bits free for tagging (although we currently only use one
1676// bit).
1677#[repr(Rust, align(4))]
1678#[derive(Debug, Clone, PartialEq, Eq)]
1679struct ValueBlockHeader {
1680    /// The tag determining the type and the layout of the data following the header in a value
1681    /// block, and the (strong) reference count of the value, packed in 64 bits. The 8 most
1682    /// significant bits represent the tag, and the remaining 56 bits the reference count.
1683    ///
1684    /// This packed representation proved to perform better (or play better with optimizations at
1685    /// least) than having `data: DataTag` followed by `ref_count: [u8; 7]`. This makes [Self]
1686    /// 8-bytes aligned, meaning that currently we waste 4 unused bytes in the header. However, it
1687    /// was already the case before with a 12 bytes, 4-bytes aligned header (since all data but
1688    /// [SealingKeyData] are 8-bytes aligned anyway, and the latter is very rare). We can also use
1689    /// this space later to store more information.
1690    tag_and_ref_count: Cell<u64>,
1691    /// The position index of the source position of the value.
1692    pos_idx: PosIdx,
1693}
1694
1695impl ValueBlockHeader {
1696    const TAG_MASK: u64 = 0xFF_00_00_00_00_00_00_00;
1697    const MAX_REF_COUNT: u64 = 0x00_FF_FF_FF_FF_FF_FF_FF;
1698
1699    /// Packs a data tagand a reference count together.
1700    ///
1701    /// # Safety
1702    ///
1703    /// This function doesn't check that the reference count is representable on 56 bits.
1704    /// `ref_count` must be smaller or equal to [Self::MAX_REF_COUNT], or the data tag might be
1705    /// later decoded to an invalid value.
1706    const unsafe fn pack(tag: DataTag, ref_count: u64) -> u64 {
1707        ((tag as u64) << 56) | ref_count
1708    }
1709
1710    /// Returns the [DataTag] of this value block.
1711    #[inline]
1712    pub fn tag(&self) -> DataTag {
1713        // Safety: `DataTag` is `repr(u8)`, and we maintain the invariant that the higher 8 bits
1714        // of `self.tag_and_ref_count` stores a valid `DataTag` value.
1715        unsafe { transmute::<u8, DataTag>((self.tag_and_ref_count.get() >> 56) as u8) }
1716    }
1717
1718    /// Creates a new header for a value block with the given tag, a reference count of 1 and the
1719    /// given position index.
1720    #[inline]
1721    pub fn new(tag: DataTag, pos_idx: PosIdx) -> Self {
1722        Self {
1723            // Safety: 1 is smaller than `Self::MAX_REF_COUNT`
1724            tag_and_ref_count: unsafe { Cell::new(Self::pack(tag, 1)) },
1725            pos_idx,
1726        }
1727    }
1728
1729    #[inline]
1730    fn ref_count(&self) -> u64 {
1731        self.tag_and_ref_count.get() & !Self::TAG_MASK
1732    }
1733
1734    #[inline]
1735    fn set_ref_count(&self, count: u64) {
1736        // Safety: we checked above that `count` is smaller or equal to `Self::MAX_REF_COUNT`.
1737        unsafe {
1738            self.tag_and_ref_count.set(Self::pack(self.tag(), count));
1739        }
1740
1741        if count > Self::MAX_REF_COUNT {
1742            panic!("reference count overfow");
1743        }
1744    }
1745
1746    #[inline]
1747    fn inc_ref_count(&self) {
1748        // The code and the tricks of this method are inspired (or downright copied) from
1749        // `std::rc::Rc`.
1750        let count = self.ref_count();
1751
1752        // We insert an `assume` here to hint LLVM at an otherwise missed optimization.
1753        // SAFETY: The reference count will never be zero when this is called.
1754        unsafe {
1755            std::hint::assert_unchecked(count != 0);
1756        }
1757
1758        self.set_ref_count(count + 1);
1759    }
1760
1761    #[inline]
1762    fn dec_ref_count(&self) {
1763        self.set_ref_count(self.ref_count() - 1);
1764    }
1765}
1766
1767/// Marker trait for the data that can be stored in a Nickel value block after the header.
1768pub trait ValueBlockData {
1769    const TAG: DataTag;
1770}
1771
1772pub type NumberData = Number;
1773pub type StringData = NickelString;
1774
1775#[derive(Clone, Debug, PartialEq, Default)]
1776pub struct ArrayData {
1777    pub array: Array,
1778    /// Arrays implement lazy contract application for performance reasons: contracts applied to
1779    /// this array are lazily accumulated in this field and only applied when an element is
1780    /// extracted.
1781    pub pending_contracts: Vec<RuntimeContract>,
1782}
1783
1784pub type ThunkData = RefCell<lazy::ThunkData>;
1785pub type TermData = Term;
1786pub type LabelData = Label;
1787
1788#[derive(Clone, Debug, PartialEq)]
1789pub struct EnumVariantData {
1790    pub tag: LocIdent,
1791    pub arg: Option<NickelValue>,
1792}
1793
1794pub type ForeignIdData = ForeignIdPayload;
1795pub type CustomContractData = NickelValue;
1796pub type SealingKeyData = SealingKey;
1797
1798#[derive(Clone, Debug, PartialEq)]
1799pub struct TypeData {
1800    /// The static type.
1801    pub typ: Type,
1802    /// The conversion of this type to a contract, that is, `typ.contract()?`. This field
1803    /// serves as a caching mechanism so we only run the contract generation code once per type
1804    /// written by the user.
1805    pub contract: NickelValue,
1806}
1807
1808impl ValueBlockData for NumberData {
1809    const TAG: DataTag = DataTag::Number;
1810}
1811
1812impl ValueBlockData for ArrayData {
1813    const TAG: DataTag = DataTag::Array;
1814}
1815
1816impl ValueBlockData for RecordData {
1817    const TAG: DataTag = DataTag::Record;
1818}
1819
1820impl ValueBlockData for StringData {
1821    const TAG: DataTag = DataTag::String;
1822}
1823
1824impl ValueBlockData for ThunkData {
1825    const TAG: DataTag = DataTag::Thunk;
1826}
1827
1828impl ValueBlockData for TermData {
1829    const TAG: DataTag = DataTag::Term;
1830}
1831
1832impl ValueBlockData for LabelData {
1833    const TAG: DataTag = DataTag::Label;
1834}
1835
1836impl ValueBlockData for EnumVariantData {
1837    const TAG: DataTag = DataTag::EnumVariant;
1838}
1839
1840impl ValueBlockData for ForeignIdData {
1841    const TAG: DataTag = DataTag::ForeignId;
1842}
1843
1844impl ValueBlockData for CustomContractData {
1845    const TAG: DataTag = DataTag::CustomContract;
1846}
1847
1848impl ValueBlockData for SealingKeyData {
1849    const TAG: DataTag = DataTag::SealingKey;
1850}
1851
1852impl ValueBlockData for TypeData {
1853    const TAG: DataTag = DataTag::Type;
1854}
1855
1856/// A pointer to a heap-allocated, reference-counted Nickel value of variable size, although the
1857/// size is a deterministic function of the tag stored in the header (first word of the block).
1858///
1859/// # Layout
1860///
1861/// In the following, the type `T : ValueBlockData` is uniquely determined by the tag in the
1862/// header.
1863///
1864/// A value block is laid out as follows:
1865///
1866/// ```text
1867/// -------------------+---------+---+
1868/// | ValueBlockHeader | padding | T |
1869/// -------------------+---------+---+
1870/// ```
1871///
1872/// The base address (`self.0`) is `max(align_of::<ValueBlockHeader>(), align_of::<T>())`-aligned.
1873/// The padding is determined statically and is only necessary if `size_of::<ValueBlockHeader>()`
1874/// isn't a multiple of the total alignment. Currently, for example, there is no padding on x86_64
1875/// in practice. It might happen if the alignment of `T` grows larger than the size of the header.
1876///
1877/// The content of padding is left uninitialized and should not be accessed.
1878pub struct ValueBlockRc(NonNull<u8>);
1879
1880impl ValueBlockRc {
1881    /// Creates a new [Self] from a raw pointer.
1882    ///
1883    /// # Safety
1884    ///
1885    /// `ptr` must be a valid, non-null pointer to a value block.
1886    #[inline]
1887    pub unsafe fn from_raw(ptr: NonNull<u8>) -> Self {
1888        ValueBlockRc(ptr)
1889    }
1890
1891    /// Returns the header of this value block.
1892    #[inline]
1893    fn header(&self) -> &ValueBlockHeader {
1894        // Safety: self.0 is always a valid pointer into a value block, and the lifetime of the
1895        // returned reference is tied to `&self`, guaranteeing that that the block will live as
1896        // long as the `&ValueBlockHeader` reference.
1897        unsafe { Self::header_from_raw(self.0) }
1898    }
1899
1900    /// Returns the tag in the header of this value block.
1901    #[inline]
1902    fn tag(&self) -> DataTag {
1903        self.header().tag()
1904    }
1905
1906    /// Returns the position index of this value.
1907    #[inline]
1908    pub fn pos_idx(&self) -> PosIdx {
1909        self.header().pos_idx
1910    }
1911
1912    /// Returns a reference to the header of a value block pointed to by `ptr`.
1913    ///
1914    /// # Safety
1915    ///
1916    /// - `ptr` must be a valid pointer into a value block.
1917    /// - the corresponding value block must remain alive for `'a`.
1918    #[inline]
1919    unsafe fn header_from_raw<'a>(ptr: NonNull<u8>) -> &'a ValueBlockHeader {
1920        // Safety: the safety precondition of this function
1921        unsafe { ptr.cast::<ValueBlockHeader>().as_ref() }
1922    }
1923
1924    /// Returns the tag in the header of a value block pointed to by `ptr`.
1925    ///
1926    /// # Safety
1927    ///
1928    /// - `ptr` must be a valid pointer into a value block.
1929    #[inline]
1930    unsafe fn tag_from_raw(ptr: NonNull<u8>) -> DataTag {
1931        // Safety: the safety precondition of this function
1932        unsafe { Self::header_from_raw(ptr).tag() }
1933    }
1934
1935    /// Same as [std::rc::Rc::get_mut] but for a value block. Mutably borrows the value block.
1936    /// Returns an error if the tag doesn't match `T::Tag`.
1937    pub fn try_get_mut<T: ValueBlockData>(&mut self) -> Result<Option<&mut T>, TagMismatchError> {
1938        let header = self.header();
1939
1940        if header.tag() != T::TAG {
1941            Err(TagMismatchError)
1942        } else if header.ref_count() != 1 {
1943            Ok(None)
1944        } else {
1945            // Safety: we know that the value block is unique, and we have a mutable borrow to the
1946            // only pointer to it, so we can safely decode the content to a mutable reference
1947            // without any risk of aliasing.
1948            unsafe { Ok(Some(self.decode_mut_unchecked::<T>())) }
1949        }
1950    }
1951
1952    /// Panicking variant of [Self::try_get_mut]. Equivalent to
1953    /// `self.try_get_mut().unwrap().unwrap()`.
1954    #[inline]
1955    pub fn get_mut<T: ValueBlockData>(&mut self) -> &mut T {
1956        self.try_get_mut().unwrap().unwrap()
1957    }
1958
1959    /// Same as [std::rc::Rc::make_mut] but for a value block. Returns `None` if the tag doesn't
1960    /// match `T::Tag`.
1961    pub fn try_make_mut<T: ValueBlockData + Clone>(&mut self) -> Option<&mut T> {
1962        if self.tag() != T::TAG {
1963            return None;
1964        }
1965
1966        self.make_unique();
1967        // Safety: we know that the value block is unique, so we can safely decode the content
1968        // without any risk of aliasing.
1969        unsafe { Some(self.decode_mut_unchecked::<T>()) }
1970    }
1971
1972    /// Panicking variant of [Self::try_make_mut]. Equivalent to `self.try_make_mut().unwrap()`.
1973    #[inline]
1974    pub fn make_mut<T: ValueBlockData + Clone>(&mut self) -> &mut T {
1975        self.try_make_mut().unwrap()
1976    }
1977
1978    /// Makes `self` a unique (non shared) block, that is a 1-reference counted block with the same
1979    /// data. Similar to [Self::make_mut] in spirit but it doesn't require to specify the `T` and
1980    /// doesn't return a mutable reference.
1981    ///
1982    /// This is a no-op if `self` is 1-reference counted. Otherwise, a fresh (strong) copy is
1983    /// allocated and assigned to `self`.
1984    pub fn make_unique(&mut self) {
1985        if self.header().ref_count() == 1 {
1986            increment!("value::make_unique::no clone");
1987        } else {
1988            increment!("value::make_unique::strong clone");
1989            *self = self.strong_clone();
1990        }
1991    }
1992
1993    /// Creates a copy of the content of this value block. As opposed to [Self::clone], which just
1994    /// increments the reference count but encapsulates a pointer to the same block in memory (as
1995    /// [std::rc::Rc::clone]), this method allocates a fresh block with a clone of the content and
1996    /// return a value that is 1-reference counted.
1997    pub fn strong_clone(&self) -> Self {
1998        match self.tag() {
1999            DataTag::Number => Self::encode(self.decode::<NumberData>().clone(), self.pos_idx()),
2000            DataTag::Array => Self::encode(self.decode::<ArrayData>().clone(), self.pos_idx()),
2001            DataTag::Record => Self::encode(self.decode::<RecordData>().clone(), self.pos_idx()),
2002            DataTag::String => Self::encode(self.decode::<StringData>().clone(), self.pos_idx()),
2003            DataTag::Thunk => Self::encode(self.decode::<ThunkData>().clone(), self.pos_idx()),
2004            DataTag::Term => Self::encode(self.decode::<TermData>().clone(), self.pos_idx()),
2005            DataTag::Label => Self::encode(self.decode::<LabelData>().clone(), self.pos_idx()),
2006            DataTag::EnumVariant => {
2007                Self::encode(self.decode::<EnumVariantData>().clone(), self.pos_idx())
2008            }
2009            DataTag::ForeignId => Self::encode(*self.decode::<ForeignIdData>(), self.pos_idx()),
2010            DataTag::SealingKey => Self::encode(*self.decode::<SealingKeyData>(), self.pos_idx()),
2011            DataTag::CustomContract => {
2012                Self::encode(self.decode::<CustomContractData>().clone(), self.pos_idx())
2013            }
2014            DataTag::Type => Self::encode(self.decode::<TypeData>().clone(), self.pos_idx()),
2015        }
2016    }
2017
2018    /// Returns the required padding in bytes between the header and the data in [ValueBlockRc]
2019    /// depending on the tag. Calls to [ValueBlockRc::padding] under the hood instantiated with the
2020    /// right type.
2021    const fn padding<T: ValueBlockData>() -> usize {
2022        let align = Self::block_align::<T>();
2023        let leftover = size_of::<ValueBlockHeader>() % align;
2024
2025        (align - leftover) % align
2026    }
2027
2028    /// Returns the offset to add to the start of a value block (the address of the block header)
2029    /// to reach the data (including padding). Offsetting [Self::0] by [Self::data_offset]
2030    /// yields a valid pointer to a `T`.
2031    const fn data_offset<T: ValueBlockData>() -> usize {
2032        size_of::<ValueBlockHeader>() + Self::padding::<T>()
2033    }
2034
2035    /// Returns the alignment in bytes of a value block for a given value content type `T`. This is
2036    /// the maximum of the alignment of the header and the alignment of `T`, and is guaranteed to
2037    /// be at least 4 bytes.
2038    const fn block_align<T: ValueBlockData>() -> usize {
2039        // Note: we can't use `std::cmp::max` in a const context
2040        if align_of::<ValueBlockHeader>() < align_of::<T>() {
2041            align_of::<T>()
2042        } else {
2043            align_of::<ValueBlockHeader>()
2044        }
2045    }
2046
2047    /// Determines the layout for allocation or de-allocation of value blocks for a given value
2048    /// content type `T`.
2049    const fn block_layout<T: ValueBlockData>() -> Layout {
2050        let header_layout = Layout::new::<ValueBlockHeader>();
2051        let data_layout = Layout::new::<T>();
2052        let size = header_layout.size() + Self::padding::<T>() + data_layout.size();
2053
2054        // The check is not tight (technically, there are a few valid size values for Layout that
2055        // will fail this assert) but it's simpler and the few corner cases don't matter in
2056        // practice.
2057        assert!(
2058            size + Self::block_align::<T>() <= isize::MAX as usize,
2059            "value block size overflow"
2060        );
2061
2062        // Safety: align is the max of existing valid alignments, so it's a power of 2. The assert
2063        // above ensures that the nearest multiple of the alignment after `size` is less than
2064        // `isize::MAX`.
2065        unsafe { Layout::from_size_align_unchecked(size, Self::block_align::<T>()) }
2066    }
2067
2068    /// Allocates a new value block with the given value `T` as content.
2069    fn encode<T: ValueBlockData>(value: T, pos_idx: PosIdx) -> Self {
2070        unsafe {
2071            // Safety: the layout of a block verifies `size > 0`
2072            let start = alloc(Self::block_layout::<T>());
2073
2074            if start.is_null() {
2075                panic!("out of memory: failed to allocate memory for Nickel value")
2076            }
2077
2078            let header_ptr = start as *mut ValueBlockHeader;
2079            header_ptr.write(ValueBlockHeader::new(T::TAG, pos_idx));
2080
2081            // Safety: layout computations are correct (hopefully)
2082            let data_layout = start.add(Self::data_offset::<T>()) as *mut T;
2083            data_layout.write(value);
2084
2085            // Safety: we abort if `start.is_null()` above, so `start` is not null
2086            Self(NonNull::new_unchecked(start))
2087        }
2088    }
2089
2090    /// Tries to decode this value block as a reference to a value of type `T`. Returns `None` if
2091    /// the tag of this value block is not `T::TAG`.
2092    #[inline]
2093    fn try_decode<T: ValueBlockData>(&self) -> Option<&T> {
2094        // Safety: we decode only if `self.tag()` is `T::TAG`
2095        (self.tag() == T::TAG).then(|| unsafe { self.decode_unchecked() })
2096    }
2097
2098    /// Panicking variant of [Self::try_decode]. Same as `self.try_decode().unwrap()`.
2099    #[inline]
2100    #[track_caller]
2101    fn decode<T: ValueBlockData>(&self) -> &T {
2102        self.try_decode().unwrap()
2103    }
2104
2105    /// Unsafe variant of [Self::try_decode]. Doesn't perform any tag check, and blindly tries to
2106    /// decode the content of this block to a `&T`.
2107    ///
2108    /// # Safety
2109    ///
2110    /// The content of this value block must have been encoded from a value of type `T`, that is
2111    /// `self.tag() == T::TAG`.
2112    #[inline]
2113    unsafe fn decode_unchecked<T: ValueBlockData>(&self) -> &T {
2114        // Safety: preconditions
2115        unsafe { Self::decode_from_raw_unchecked(self.0) }
2116    }
2117
2118    /// Mutable variant of [Self::decode_unchecked] (or unsafe variant of [Self::get_mut]).
2119    ///
2120    /// # Safety
2121    ///
2122    /// - The content of this value block must have been encoded from a value of type `T`, that is
2123    ///   `self.tag() == T::TAG`.
2124    /// - You must ensure that there is no active mutable reference inside this value block as long
2125    ///   as the returned mutable reference is alive. This is typically the case if the reference
2126    ///   count of the value block is 1.
2127    #[inline]
2128    unsafe fn decode_mut_unchecked<T: ValueBlockData>(&mut self) -> &mut T {
2129        // Safety: preconditions
2130        unsafe { Self::decode_mut_from_raw_unchecked(self.0) }
2131    }
2132
2133    /// Given a pointer into a value block, blindly tries to decode the content to a `T` bypassing all safety checks.
2134    ///
2135    /// # Safety
2136    ///
2137    /// - The content of this value block must have been encoded from a value of type `T`, that is
2138    ///   `self.tag() == T::TAG`.
2139    /// - The lifetime `'a` of the returned reference must not outlive the value block.
2140    /// - The value block content must not be mutably borrowed during the lifetime `'a`.
2141    #[inline]
2142    unsafe fn decode_from_raw_unchecked<'a, T: ValueBlockData>(ptr: NonNull<u8>) -> &'a T {
2143        // Safety: preconditions
2144        unsafe { ptr.add(Self::data_offset::<T>()).cast::<T>().as_ref() }
2145    }
2146
2147    /// Mutable variant of [Self::decode_from_raw_unchecked].
2148    ///
2149    /// # Safety
2150    ///
2151    /// - The content of this value block must have been encoded from a value of type `T`, that is
2152    ///   `self.tag() == T::TAG`.
2153    /// - The lifetime `'a` of the returned reference must not outlive the value block.
2154    /// - You must ensure that there is no active mutable reference inside this value block as long
2155    ///   as the returned mutable reference is alive (during `'a`). This is typically satisfied if
2156    ///   the reference count of the value block is `1`.
2157    #[inline]
2158    unsafe fn decode_mut_from_raw_unchecked<'a, T: ValueBlockData>(ptr: NonNull<u8>) -> &'a mut T {
2159        // Safety: preconditions
2160        unsafe { ptr.add(Self::data_offset::<T>()).cast::<T>().as_mut() }
2161    }
2162
2163    /// Given a pointer into a value block, tries to decode the content to a `T`. Returns `None` if
2164    /// the tag of this value block is not `T::TAG`.
2165    ///
2166    /// Same as [Self::try_decode], but takes a raw (non-null) pointer instead of a value block.
2167    ///
2168    /// # Safety
2169    ///
2170    /// - The lifetime `'a` of the returned reference must not outlive the value block
2171    /// - The value block content must not be mutably borrowed during the lifetime `'a`.
2172    #[inline]
2173    unsafe fn try_decode_from_raw<'a, T: ValueBlockData>(ptr: NonNull<u8>) -> Option<&'a T> {
2174        // Safety: we've checked that the tag matched `T`. The rest of the safety conditions are
2175        // the pre-conditions of the current function `try_decode`.
2176        unsafe { (Self::tag_from_raw(ptr) == T::TAG).then(|| Self::decode_from_raw_unchecked(ptr)) }
2177    }
2178
2179    // Non-inlined part of drop. Splitting the inlined and slow part is inspired from `std::rc::Rc`
2180    // code.
2181    #[inline(never)]
2182    unsafe fn drop_slow(&mut self) {
2183        unsafe {
2184            let tag = self.tag();
2185            // Safety: the value block is guaranteed to have been allocated with a size of
2186            // `size_of::<ValueBlockHeader>()` + `padding` + `size_of::<T>()`.
2187            let data_ptr = self.0.as_ptr().add(tag.data_offset());
2188
2189            // Safety: `data_ptr` is a valid pointer for the corresponding type and it hasn't
2190            // been dropped before, as it's only dropped once when the last reference goes out
2191            // of scope.
2192            match tag {
2193                DataTag::Number => {
2194                    ptr::drop_in_place(data_ptr as *mut NumberData);
2195                }
2196                DataTag::Array => {
2197                    ptr::drop_in_place(data_ptr as *mut ArrayData);
2198                }
2199                DataTag::Record => {
2200                    ptr::drop_in_place(data_ptr as *mut RecordData);
2201                }
2202                DataTag::String => {
2203                    ptr::drop_in_place(data_ptr as *mut StringData);
2204                }
2205                DataTag::Thunk => {
2206                    ptr::drop_in_place(data_ptr as *mut ThunkData);
2207                }
2208                DataTag::Term => {
2209                    ptr::drop_in_place(data_ptr as *mut TermData);
2210                }
2211                DataTag::Label => {
2212                    ptr::drop_in_place(data_ptr as *mut LabelData);
2213                }
2214                DataTag::EnumVariant => {
2215                    ptr::drop_in_place(data_ptr as *mut EnumVariantData);
2216                }
2217                DataTag::ForeignId => {
2218                    ptr::drop_in_place(data_ptr as *mut ForeignIdData);
2219                }
2220                DataTag::CustomContract => {
2221                    ptr::drop_in_place(data_ptr as *mut CustomContractData);
2222                }
2223                DataTag::SealingKey => {
2224                    ptr::drop_in_place(data_ptr as *mut SealingKeyData);
2225                }
2226                DataTag::Type => {
2227                    ptr::drop_in_place(data_ptr as *mut TypeData);
2228                }
2229            };
2230
2231            dealloc(self.0.as_ptr(), tag.block_layout());
2232        }
2233    }
2234}
2235
2236impl Drop for ValueBlockRc {
2237    #[inline]
2238    fn drop(&mut self) {
2239        self.header().dec_ref_count();
2240
2241        if self.header().ref_count() == 0 {
2242            unsafe {
2243                self.drop_slow();
2244            }
2245        }
2246    }
2247}
2248
2249impl Clone for ValueBlockRc {
2250    #[inline]
2251    fn clone(&self) -> Self {
2252        self.header().inc_ref_count();
2253        Self(self.0)
2254    }
2255}
2256
2257/// Empty containers are inlined, but this should be an implementation detail as much as possible.
2258/// This generic handler is providing a uniform interface to a container, that can either be
2259/// inlined as empty, or allocated (note that an allocated container can still be empty!).
2260#[derive(Clone, PartialEq, Debug, Copy)]
2261pub enum Container<C> {
2262    /// An empty, inlined value.
2263    Empty,
2264    /// An allocated container. **Note that an allocated container can still be empty as a container**:
2265    /// for example, an open record `{..}` needs to be allocated to store the open attribute, but
2266    /// will have an empty underlying map.
2267    Alloc(C),
2268}
2269
2270impl<C> Container<C> {
2271    /// Converts this container to an optional. Returns `None` if `self` is [Container::Empty], and
2272    /// `Some` otherwise.
2273    pub fn into_opt(self) -> Option<C> {
2274        match self {
2275            Container::Empty => None,
2276            Container::Alloc(c) => Some(c),
2277        }
2278    }
2279
2280    /// Unwrap the underlying allocation, or panics if `self` is [Self::Empty].
2281    #[inline]
2282    pub fn unwrap_alloc(self) -> C {
2283        self.into_opt().unwrap()
2284    }
2285}
2286
2287impl<C: Default> Container<C> {
2288    /// Unwrap the underlying container, or allocate a new empty one (via [Default]) if `self` is
2289    /// [Container::Empty].
2290    #[inline]
2291    pub fn unwrap_or_alloc(self) -> C {
2292        self.into_opt().unwrap_or_default()
2293    }
2294}
2295
2296impl<C: ToOwned> Container<&C> {
2297    /// Clones the underlying container if `self` is [Self::Alloc], or returns [Self::Empty]
2298    /// otherwise.
2299    pub fn to_owned(&self) -> Container<C::Owned> {
2300        match self {
2301            Container::Empty => Container::Empty,
2302            Container::Alloc(c) => Container::Alloc((*c).to_owned()),
2303        }
2304    }
2305}
2306
2307impl Container<&RecordData> {
2308    /// Retrieves a field from [crate::term::record::RecordData::fields], or returns `None` if `self`
2309    /// is [Self::Empty].
2310    pub fn get(&self, id: LocIdent) -> Option<&Field> {
2311        self.into_opt().and_then(|record| record.fields.get(&id))
2312    }
2313
2314    /// Returns a vector of all the fields' names of this record sorted alphabetically. Returns an
2315    /// empty vector if `self` is [Self::Empty].
2316    pub fn field_names(&self, op_kind: RecordOpKind) -> Vec<LocIdent> {
2317        self.into_opt()
2318            .map(|record| record.field_names(op_kind))
2319            .unwrap_or_default()
2320    }
2321
2322    /// Returns the length of the underlying record.
2323    pub fn len(&self) -> usize {
2324        self.into_opt().map_or(0, |record| record.fields.len())
2325    }
2326
2327    /// Checks if this record is [Self::Empty], or is [Self::Alloc] where the underlying record is
2328    /// empty (is such that [RecordData::is_empty] returns `true`).
2329    pub fn is_empty(&self) -> bool {
2330        match self {
2331            Container::Empty => true,
2332            Container::Alloc(record) => record.is_empty(),
2333        }
2334    }
2335
2336    /// Checks if this record is [Self::Empty], or is [Self::Alloc] where the underlying record is
2337    /// empty or has only empty optional fields (is such that [RecordData::has_only_empty_opts]
2338    /// returns `true`).
2339    pub fn has_only_empty_opts(&self) -> bool {
2340        match self {
2341            Container::Empty => true,
2342            Container::Alloc(record) => record.has_only_empty_opts(),
2343        }
2344    }
2345}
2346
2347impl Container<&ArrayData> {
2348    /// Iterates over the elements of the array.
2349    pub fn iter(&self) -> impl Iterator<Item = &NickelValue> {
2350        self.into_opt()
2351            .into_iter()
2352            .flat_map(|array_data| array_data.array.iter())
2353    }
2354
2355    /// Iterates over the pending contracts.
2356    pub fn iter_pending_contracts(&self) -> impl Iterator<Item = &RuntimeContract> {
2357        self.into_opt()
2358            .into_iter()
2359            .flat_map(|array_data| array_data.pending_contracts.iter())
2360    }
2361
2362    /// Retrieves an element from the underlying array at the given index, or returns `None` if `self`
2363    /// is [Self::Empty].
2364    pub fn get(&self, idx: usize) -> Option<&NickelValue> {
2365        self.into_opt()
2366            .and_then(|array_data| array_data.array.get(idx))
2367    }
2368
2369    /// Returns the length of the underlying array.
2370    pub fn len(&self) -> usize {
2371        self.into_opt()
2372            .map_or(0, |array_data| array_data.array.len())
2373    }
2374
2375    /// Checks if this record is [Self::Empty], or is [Self::Alloc] where the underlying array is
2376    /// empty (is such that [Array::is_empty] returns `true`).
2377    pub fn is_empty(&self) -> bool {
2378        match self {
2379            Container::Empty => true,
2380            Container::Alloc(array_data) => array_data.array.is_empty(),
2381        }
2382    }
2383}
2384
2385/// An enum representation of a decoded Nickel value. This is useful to match on a general value in
2386/// a typed way.
2387///
2388/// Note that we only ever store references to the content of a value block here, or 1-word inline
2389/// values, so this enum is compact and doesn't defeat the purpose of the whole value encoding
2390/// (beside the fact that [ValueContentRef] is only materialized temporarily by helpers and not a
2391/// standard value representation). This is still true for `Container<ref>` values, thanks to the
2392/// niche optimizatio of rustc: [Self] is 16 bytes wide.
2393#[derive(Clone, Copy, Debug)]
2394pub enum ValueContentRef<'a> {
2395    Null,
2396    Bool(bool),
2397    Number(&'a NumberData),
2398    Array(Container<&'a ArrayData>),
2399    Record(Container<&'a RecordData>),
2400    String(&'a StringData),
2401    Thunk(&'a Thunk),
2402    Term(&'a TermData),
2403    Label(&'a LabelData),
2404    EnumVariant(&'a EnumVariantData),
2405    ForeignId(&'a ForeignIdData),
2406    SealingKey(&'a SealingKeyData),
2407    CustomContract(&'a CustomContractData),
2408    Type(&'a TypeData),
2409}
2410
2411/// Mutable version of [ValueContentRef].
2412pub enum ValueContentRefMut<'a> {
2413    /// Given the encoding of inline values, it's a bad idea to provide a bare Rust reference to
2414    /// the underlying data encoding the inline value. While it's possible currently, we might use
2415    /// a more exotic layout in the future making it impossible to produce a valid Rust reference
2416    /// to an [InlineValue].
2417    ///
2418    /// A mutable reference is mostly useful for value blocks anyway. For inline values, we just
2419    /// return the original value back. It can be overridden directly with a new inline value.
2420    ///
2421    /// Note that the empty array case and the empty record case are handled by the [Container]
2422    /// part in [Self::Array] and [Self::Record].
2423    Null(&'a mut NickelValue),
2424    Bool(&'a mut NickelValue),
2425    Number(&'a mut NumberData),
2426    Array(Container<&'a mut ArrayData>),
2427    Record(Container<&'a mut RecordData>),
2428    String(&'a mut StringData),
2429    Thunk(&'a mut Thunk),
2430    Term(&'a mut TermData),
2431    Label(&'a mut LabelData),
2432    EnumVariant(&'a mut EnumVariantData),
2433    ForeignId(&'a mut ForeignIdData),
2434    SealingKey(&'a mut SealingKeyData),
2435    CustomContract(&'a mut CustomContractData),
2436    Type(&'a mut TypeData),
2437}
2438
2439/// A lazy handle to the owned content of a value block.
2440///
2441/// It's a common pattern to need to move the content out of a block (copying the content if it's
2442/// shared, but avoiding copy if it's unique, like [std::rc::Rc::unwrap_or_clone]) only if a value
2443/// matches some pattern. Typically during program transformations, where blocks should always be
2444/// 1-reference counted, and where one transformation only affects specific nodes.
2445///
2446/// This is the *lazy* part: while the type of the data is known, the content is hidden behind a
2447/// lazy handle, which can either be unwrapped further - consuming the original value irreversibly
2448/// and producing an owned version of the data - or reverted back to the original value.
2449pub enum ValueContent {
2450    /// It can seem useless to carry a lens here, since there's no real data to take out. However,
2451    /// it's important to keep the ability to restore a [Self] value to the original [NickelValue],
2452    /// which is precisely the role of the lens.
2453    Null(lens::ValueLens<()>),
2454    Bool(lens::ValueLens<bool>),
2455    Number(lens::ValueLens<NumberData>),
2456    Array(lens::ValueLens<Container<ArrayData>>),
2457    Record(lens::ValueLens<Container<RecordData>>),
2458    String(lens::ValueLens<StringData>),
2459    Thunk(lens::ValueLens<Thunk>),
2460    Term(lens::TermContent),
2461    Label(lens::ValueLens<LabelData>),
2462    EnumVariant(lens::ValueLens<EnumVariantData>),
2463    ForeignId(lens::ValueLens<ForeignIdData>),
2464    SealingKey(lens::ValueLens<SealingKeyData>),
2465    CustomContract(lens::ValueLens<CustomContractData>),
2466    Type(lens::ValueLens<TypeData>),
2467}
2468
2469impl ValueContent {
2470    /// Do not access the content and restore the original value unchanged.
2471    pub fn restore(self) -> NickelValue {
2472        match self {
2473            ValueContent::Null(lens) => lens.restore(),
2474            ValueContent::Bool(lens) => lens.restore(),
2475            ValueContent::Number(lens) => lens.restore(),
2476            ValueContent::Array(lens) => lens.restore(),
2477            ValueContent::Record(lens) => lens.restore(),
2478            ValueContent::String(lens) => lens.restore(),
2479            ValueContent::Thunk(lens) => lens.restore(),
2480            ValueContent::Term(lens) => lens.restore(),
2481            ValueContent::Label(lens) => lens.restore(),
2482            ValueContent::EnumVariant(lens) => lens.restore(),
2483            ValueContent::ForeignId(lens) => lens.restore(),
2484            ValueContent::SealingKey(lens) => lens.restore(),
2485            ValueContent::CustomContract(lens) => lens.restore(),
2486            ValueContent::Type(lens) => lens.restore(),
2487        }
2488    }
2489}
2490
2491impl Traverse<NickelValue> for NickelValue {
2492    fn traverse<F, E>(self, f: &mut F, order: TraverseOrder) -> Result<Self, E>
2493    where
2494        F: FnMut(NickelValue) -> Result<NickelValue, E>,
2495    {
2496        let value = match order {
2497            TraverseOrder::TopDown => f(self)?,
2498            TraverseOrder::BottomUp => self,
2499        };
2500
2501        let pos_idx = value.pos_idx();
2502
2503        let result = match value.content() {
2504            lens @ (ValueContent::Null(_)
2505            | ValueContent::Bool(_)
2506            | ValueContent::Number(_)
2507            | ValueContent::String(_)
2508            | ValueContent::Label(_)
2509            | ValueContent::Thunk(_)
2510            | ValueContent::SealingKey(_)
2511            | ValueContent::ForeignId(_)) => lens.restore(),
2512            ValueContent::Array(lens) if lens.value().is_inline_empty_array() => lens.restore(),
2513            ValueContent::Record(lens) if lens.value().is_inline_empty_record() => lens.restore(),
2514            ValueContent::Term(lens) => {
2515                let term = lens.take();
2516                let term = term.traverse(f, order)?;
2517                NickelValue::term(term, pos_idx)
2518            }
2519            ValueContent::Record(lens) => {
2520                //unwrap(): we already treated the empty case above, so if we end up here, the
2521                //container must be non-empty, and `into_opt()` must return `Some`.
2522                let record = lens.take().into_opt().unwrap();
2523
2524                // The annotation on `fields_res` uses Result's corresponding trait to convert from
2525                // Iterator<Result> to a Result<Iterator>
2526                let fields: Result<IndexMap<LocIdent, Field>, E> = record
2527                    .fields
2528                    .into_iter()
2529                    // For the conversion to work, note that we need a Result<(Ident,RichTerm), E>
2530                    .map(|(id, field)| Ok((id, field.traverse(f, order)?)))
2531                    .collect();
2532                NickelValue::record(
2533                    RecordData::new_shared_tail(fields?, record.attrs, record.sealed_tail),
2534                    pos_idx,
2535                )
2536            }
2537            ValueContent::Array(lens) => {
2538                //unwrap(): we already treated the empty case above, so if we end up here, the
2539                //container must be non-empty, and `into_opt()` must return `Some`.
2540                let ArrayData {
2541                    array,
2542                    pending_contracts,
2543                } = lens.take().into_opt().unwrap();
2544
2545                let array = array
2546                    .into_iter()
2547                    .map(|t| t.traverse(f, order))
2548                    .collect::<Result<Array, _>>()?;
2549
2550                // We don't recurse into pending contracts currently
2551                NickelValue::array(array, pending_contracts, pos_idx)
2552            }
2553            ValueContent::Type(lens) => {
2554                let TypeData { typ, contract } = lens.take();
2555                let typ = typ.traverse(f, order)?;
2556                let contract = contract.traverse(f, order)?;
2557
2558                NickelValue::typ(typ, contract, pos_idx)
2559            }
2560            ValueContent::EnumVariant(lens) => {
2561                let EnumVariantData { tag, arg } = lens.take();
2562                let arg = arg.map(|arg| arg.traverse(f, order)).transpose()?;
2563                NickelValue::enum_variant(tag, arg, pos_idx)
2564            }
2565            ValueContent::CustomContract(lens) => {
2566                let ctr = lens.take();
2567                let ctr = ctr.traverse(f, order)?;
2568                NickelValue::custom_contract(ctr, pos_idx)
2569            }
2570        };
2571
2572        match order {
2573            TraverseOrder::TopDown => Ok(result),
2574            TraverseOrder::BottomUp => f(result),
2575        }
2576    }
2577
2578    fn traverse_ref<S, U>(
2579        &self,
2580        f: &mut dyn FnMut(&NickelValue, &S) -> TraverseControl<S, U>,
2581        scope: &S,
2582    ) -> Option<U> {
2583        let child_scope = match f(self, scope) {
2584            TraverseControl::Continue => None,
2585            TraverseControl::ContinueWithScope(s) => Some(s),
2586            TraverseControl::SkipBranch => {
2587                return None;
2588            }
2589            TraverseControl::Return(ret) => {
2590                return Some(ret);
2591            }
2592        };
2593
2594        let scope = child_scope.as_ref().unwrap_or(scope);
2595
2596        match self.content_ref() {
2597            ValueContentRef::Null
2598            | ValueContentRef::Bool(_)
2599            | ValueContentRef::Array(Container::Empty)
2600            | ValueContentRef::Record(Container::Empty)
2601            | ValueContentRef::Number(_)
2602            | ValueContentRef::String(_)
2603            | ValueContentRef::Label(_)
2604            | ValueContentRef::Thunk(_)
2605            | ValueContentRef::SealingKey(_)
2606            | ValueContentRef::EnumVariant(EnumVariantData { arg: None, .. })
2607            | ValueContentRef::ForeignId(_) => None,
2608            ValueContentRef::EnumVariant(EnumVariantData { arg: Some(v), .. })
2609            | ValueContentRef::CustomContract(v) => v.traverse_ref(f, scope),
2610            ValueContentRef::Array(Container::Alloc(array_data)) => array_data
2611                .array
2612                .iter()
2613                .find_map(|t| t.traverse_ref(f, scope)),
2614            ValueContentRef::Type(TypeData { typ, contract }) => {
2615                typ.traverse_ref(f, scope)?;
2616                contract.traverse_ref(f, scope)
2617            }
2618            ValueContentRef::Record(Container::Alloc(data)) => data
2619                .fields
2620                .values()
2621                .find_map(|field| field.traverse_ref(f, scope)),
2622            ValueContentRef::Term(term) => term.traverse_ref(f, scope),
2623        }
2624    }
2625}
2626
2627impl Traverse<Type> for NickelValue {
2628    fn traverse<F, E>(self, f: &mut F, order: TraverseOrder) -> Result<NickelValue, E>
2629    where
2630        F: FnMut(Type) -> Result<Type, E>,
2631    {
2632        self.traverse(
2633            &mut |value: NickelValue| {
2634                let pos_idx = value.pos_idx();
2635
2636                match value.content() {
2637                    ValueContent::Type(lens) => {
2638                        let TypeData { typ, contract } = lens.take();
2639                        let typ = typ.traverse(f, order)?;
2640                        Ok(NickelValue::typ(typ, contract, pos_idx))
2641                    }
2642                    lens => Ok(lens.restore()),
2643                }
2644            },
2645            order,
2646        )
2647    }
2648
2649    fn traverse_ref<S, U>(
2650        &self,
2651        f: &mut dyn FnMut(&Type, &S) -> TraverseControl<S, U>,
2652        state: &S,
2653    ) -> Option<U> {
2654        self.traverse_ref(
2655            &mut |value: &NickelValue, state: &S| {
2656                if let Some(TypeData { typ, contract: _ }) = value.as_type() {
2657                    typ.traverse_ref(f, state).into()
2658                } else {
2659                    TraverseControl::Continue
2660                }
2661            },
2662            state,
2663        )
2664    }
2665}
2666
2667impl From<Term> for NickelValue {
2668    fn from(term: Term) -> Self {
2669        NickelValue::term_posless(term)
2670    }
2671}
2672
2673#[cfg(test)]
2674mod tests {
2675    use super::*;
2676    use crate::position::RawSpan;
2677
2678    #[test]
2679    fn inline_values() {
2680        use crate::files::Files;
2681
2682        let inline_null = NickelValue::null();
2683        let inline_true = NickelValue::bool_true();
2684        let inline_false = NickelValue::bool_false();
2685        let inline_empty_array = NickelValue::empty_array();
2686        let inline_empty_record = NickelValue::empty_record();
2687
2688        assert_eq!(inline_null.tag(), ValueTag::Inline);
2689        assert_eq!(inline_true.tag(), ValueTag::Inline);
2690        assert_eq!(inline_false.tag(), ValueTag::Inline);
2691        assert_eq!(inline_empty_array.tag(), ValueTag::Inline);
2692        assert_eq!(inline_empty_record.tag(), ValueTag::Inline);
2693
2694        assert_eq!(inline_null.as_inline(), Some(InlineValue::Null));
2695        assert_eq!(inline_true.as_inline(), Some(InlineValue::True));
2696        assert_eq!(inline_false.as_inline(), Some(InlineValue::False));
2697        assert_eq!(
2698            inline_empty_array.as_inline(),
2699            Some(InlineValue::EmptyArray)
2700        );
2701        assert_eq!(
2702            inline_empty_record.as_inline(),
2703            Some(InlineValue::EmptyRecord)
2704        );
2705
2706        let dummy_pos = TermPos::Original(RawSpan {
2707            src_id: Files::empty().add("<test>", String::from("empty")),
2708            start: 0.into(),
2709            end: 1.into(),
2710        });
2711
2712        let mut pos_table = PosTable::new();
2713
2714        // Makes sure the values are what we expect, and that `phys_eq` properly ignores position
2715        // information (we create a fresh position index for each value).
2716        assert!(
2717            inline_null
2718                .clone()
2719                .with_inline_pos_idx(pos_table.push(dummy_pos))
2720                .phys_eq(&NickelValue::null().with_inline_pos_idx(pos_table.push(dummy_pos)))
2721        );
2722        assert!(
2723            inline_true
2724                .with_inline_pos_idx(pos_table.push(dummy_pos))
2725                .phys_eq(&NickelValue::bool_true().with_inline_pos_idx(pos_table.push(dummy_pos)))
2726        );
2727        assert!(
2728            inline_false
2729                .with_inline_pos_idx(pos_table.push(dummy_pos))
2730                .phys_eq(&NickelValue::bool_false().with_inline_pos_idx(pos_table.push(dummy_pos)))
2731        );
2732        assert!(
2733            inline_empty_array
2734                .with_inline_pos_idx(pos_table.push(dummy_pos))
2735                .phys_eq(
2736                    &NickelValue::empty_array().with_inline_pos_idx(pos_table.push(dummy_pos))
2737                )
2738        );
2739        assert!(
2740            inline_empty_record
2741                .with_inline_pos_idx(pos_table.push(dummy_pos))
2742                .phys_eq(
2743                    &NickelValue::empty_record().with_inline_pos_idx(pos_table.push(dummy_pos))
2744                )
2745        );
2746
2747        assert!(!inline_null.phys_eq(&NickelValue::bool_true()));
2748    }
2749
2750    #[test]
2751    fn basic_value_blocks() {
2752        let number_value = NickelValue::number_posless(42);
2753        let string_value = NickelValue::string_posless("Hello, World!");
2754
2755        assert_eq!(number_value.tag(), ValueTag::Pointer);
2756        assert_eq!(string_value.tag(), ValueTag::Pointer);
2757
2758        assert_eq!(number_value.as_number().unwrap(), &Number::from(42));
2759        assert_eq!(
2760            string_value.as_string().unwrap(),
2761            &NickelString::from("Hello, World!")
2762        );
2763
2764        assert!(number_value.as_string().is_none());
2765        assert!(string_value.as_number().is_none());
2766    }
2767
2768    #[test]
2769    fn ref_counting() {
2770        let number_value = NickelValue::number_posless(42);
2771
2772        let mut copies =
2773            std::array::from_fn::<_, 20, _>(|_| ManuallyDrop::new(number_value.clone()));
2774
2775        let mut as_val = number_value.into_block().unwrap();
2776        assert_eq!(as_val.header().ref_count(), 21);
2777
2778        let mut block_copy = ManuallyDrop::new(as_val.clone());
2779
2780        assert_eq!(as_val.header().ref_count(), 22);
2781
2782        assert!(as_val.try_get_mut::<NumberData>().unwrap().is_none());
2783
2784        for copy in copies.iter_mut().take(10) {
2785            unsafe {
2786                ManuallyDrop::drop(copy);
2787            }
2788        }
2789
2790        assert_eq!(as_val.header().ref_count(), 12);
2791
2792        for copy in copies.iter_mut().skip(10) {
2793            unsafe {
2794                ManuallyDrop::drop(copy);
2795            }
2796        }
2797
2798        let mut cow = as_val.clone();
2799        *cow.make_mut() = Number::from(0);
2800
2801        // The original value wasn't 1-RCed, so it should be left unchanged and cow must be a
2802        // separate copy.
2803        assert_eq!(as_val.header().ref_count(), 2);
2804        assert_eq!(as_val.decode::<NumberData>(), &Number::from(42));
2805        assert_eq!(cow.header().ref_count(), 1);
2806        assert_eq!(cow.decode::<NumberData>(), &Number::from(0));
2807
2808        unsafe { ManuallyDrop::drop(&mut block_copy) }
2809
2810        *as_val.get_mut() = Number::from(100);
2811        assert_eq!(as_val.header().ref_count(), 1);
2812        assert_eq!(as_val.decode::<NumberData>(), &Number::from(100));
2813    }
2814
2815    #[test]
2816    fn in_place_modification() {
2817        // We make the containers non-empty so that they're not inlined.
2818        let mut record_data = RecordData::default();
2819        record_data
2820            .fields
2821            .insert(LocIdent::from("hello"), Default::default());
2822        let mut array_data = Array::default();
2823        array_data.push(NickelValue::null());
2824
2825        let record = NickelValue::record_posless(record_data);
2826        let array = NickelValue::array_posless(array_data, Vec::new());
2827
2828        let mut record_value = record.into_block().unwrap();
2829        let mut array_value = array.into_block().unwrap();
2830
2831        assert_eq!(record_value.decode::<RecordData>().fields.len(), 1);
2832        assert_eq!(array_value.decode::<ArrayData>().array.len(), 1);
2833
2834        record_value
2835            .get_mut::<RecordData>()
2836            .fields
2837            .insert(LocIdent::from("world"), Default::default());
2838        array_value
2839            .get_mut::<ArrayData>()
2840            .array
2841            .push(NickelValue::null());
2842
2843        let array_copy = array_value.clone();
2844        let record_copy = record_value.clone();
2845
2846        assert_eq!(record_copy.decode::<RecordData>().fields.len(), 2);
2847        assert_eq!(array_copy.decode::<ArrayData>().array.len(), 2);
2848    }
2849
2850    #[test]
2851    fn in_place_modification_with_content_ref() {
2852        // We make the containers non-empty so that they're not inlined.
2853        let mut record_data = RecordData::default();
2854        record_data
2855            .fields
2856            .insert(LocIdent::from("hello"), Default::default());
2857        let mut array_data = Array::default();
2858        array_data.push(NickelValue::null());
2859
2860        let mut record = NickelValue::record_posless(record_data);
2861        let mut array = NickelValue::array_posless(array_data, Vec::new());
2862
2863        assert_eq!(record.as_record().unwrap().unwrap_alloc().fields.len(), 1);
2864        assert_eq!(array.as_array().unwrap().unwrap_alloc().array.len(), 1);
2865
2866        if let Some(ValueContentRefMut::Record(Container::Alloc(record))) = record.content_mut() {
2867            record
2868                .fields
2869                .insert(LocIdent::from("world"), Default::default());
2870        } else {
2871            panic!("Expected RecordData");
2872        }
2873
2874        if let Some(ValueContentRefMut::Array(Container::Alloc(array_data))) = array.content_mut() {
2875            array_data.array.push(NickelValue::null());
2876        } else {
2877            panic!("Expected ArrayData");
2878        }
2879
2880        let array_copy = array.clone();
2881        let record_copy = record.clone();
2882
2883        assert_eq!(record_copy.as_record().unwrap().len(), 2);
2884        assert_eq!(array_copy.as_array().unwrap().len(), 2);
2885    }
2886
2887    #[test]
2888    fn content_make_mut() {
2889        // We make the containers non-empty so that they're not inlined.
2890        let mut record_data = RecordData::default();
2891        record_data
2892            .fields
2893            .insert(LocIdent::from("hello"), Default::default());
2894        let mut array_data = Array::default();
2895        array_data.push(NickelValue::null());
2896
2897        let mut record = NickelValue::record_posless(record_data);
2898        let mut array = NickelValue::array_posless(array_data, Vec::new());
2899        let array_copy = array.clone();
2900
2901        assert_eq!(record.as_record().unwrap().unwrap_alloc().fields.len(), 1);
2902        assert_eq!(array.as_array().unwrap().unwrap_alloc().array.len(), 1);
2903
2904        if let ValueContentRefMut::Record(Container::Alloc(record)) = record.content_make_mut() {
2905            record
2906                .fields
2907                .insert(LocIdent::from("world"), Default::default());
2908        } else {
2909            panic!("Expected RecordData");
2910        }
2911
2912        if let ValueContentRefMut::Array(Container::Alloc(array_data)) = array.content_make_mut() {
2913            array_data.array.push(NickelValue::null());
2914        } else {
2915            panic!("Expected ArrayData");
2916        }
2917
2918        let record_copy = record.clone();
2919
2920        assert_eq!(record.as_record().unwrap().len(), 2);
2921        assert_eq!(record_copy.as_record().unwrap().len(), 2);
2922        assert_eq!(array.as_array().unwrap().len(), 2);
2923        // The copy was made before the call to `content_make_mut`, so it must have been preserved.
2924        assert_eq!(array_copy.as_array().unwrap().len(), 1);
2925    }
2926
2927    #[test]
2928    fn empty_containers_are_inlined() {
2929        let empty_record = NickelValue::record_posless(RecordData::default());
2930        let empty_array = NickelValue::array_posless(Array::default(), Vec::new());
2931
2932        assert_eq!(empty_record.tag(), ValueTag::Inline);
2933        assert_eq!(empty_array.tag(), ValueTag::Inline);
2934
2935        assert_eq!(empty_record.as_inline(), Some(InlineValue::EmptyRecord));
2936        assert_eq!(empty_array.as_inline(), Some(InlineValue::EmptyArray));
2937    }
2938}