rune/runtime/
const_value.rs

1#[macro_use]
2mod macros;
3
4use core::any;
5use core::cmp::Ordering;
6use core::fmt;
7
8#[cfg(feature = "musli")]
9use musli::{Decode, Encode};
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Serialize};
12
13use crate::alloc;
14use crate::alloc::prelude::*;
15use crate::runtime;
16use crate::{self as rune};
17use crate::{declare_dyn_trait, hash_in, Hash, TypeHash};
18
19use super::{
20    AnyTypeInfo, Bytes, ExpectedType, FromValue, Inline, Object, OwnedTuple, Repr, RuntimeError,
21    ToValue, Tuple, Type, TypeInfo, Value, VmErrorKind, VmIntegerRepr,
22};
23
24/// Derive for the [`ToConstValue`] trait.
25///
26/// This is principally used for associated constants in native modules, since
27/// Rune has to be provided a constant-compatible method for constructing values
28/// of the given type.
29///
30/// [`ToConstValue`]: trait@crate::ToConstValue
31///
32/// # Examples
33///
34/// ```
35/// use rune::{docstring, Any, ContextError, Module, ToConstValue};
36///
37/// #[derive(Any, ToConstValue)]
38/// pub struct Duration {
39///     #[const_value(with = const_duration)]
40///     inner: std::time::Duration,
41/// }
42///
43/// mod const_duration {
44///     use rune::runtime::{ConstValue, RuntimeError, Value};
45///     use std::time::Duration;
46///
47///     #[inline]
48///     pub(super) fn to_const_value(duration: Duration) -> Result<ConstValue, RuntimeError> {
49///         let secs = duration.as_secs();
50///         let nanos = duration.subsec_nanos();
51///         rune::to_const_value((secs, nanos))
52///     }
53///
54///     #[inline]
55///     pub(super) fn from_const_value(value: &ConstValue) -> Result<Duration, RuntimeError> {
56///         let (secs, nanos) = rune::from_const_value::<(u64, u32)>(value)?;
57///         Ok(Duration::new(secs, nanos))
58///     }
59///
60///     #[inline]
61///     pub(super) fn from_value(value: Value) -> Result<Duration, RuntimeError> {
62///         let (secs, nanos) = rune::from_value::<(u64, u32)>(value)?;
63///         Ok(Duration::new(secs, nanos))
64///     }
65/// }
66///
67/// #[rune::module(::time)]
68/// pub fn module() -> Result<Module, ContextError> {
69///     let mut m = Module::from_meta(module__meta)?;
70///     m.ty::<Duration>()?;
71///
72///     m
73///         .constant(
74///             "SECOND",
75///             Duration {
76///                 inner: std::time::Duration::from_secs(1),
77///             },
78///         )
79///         .build_associated::<Duration>()?
80///         .docs(docstring! {
81///             /// The duration of one second.
82///             ///
83///             /// # Examples
84///             ///
85///             /// ```rune
86///             /// use time::Duration;
87///             ///
88///             /// let duration = Duration::SECOND;
89///             /// ```
90///         })?;
91///
92///     Ok(m)
93/// }
94/// ```
95pub use rune_macros::ToConstValue;
96
97/// Cheap conversion trait to convert something infallibly into a [`ConstValue`].
98pub trait IntoConstValue {
99    /// Convert into a dynamic [`ConstValue`].
100    #[doc(hidden)]
101    fn into_const_value(self) -> alloc::Result<ConstValue>;
102}
103
104impl IntoConstValue for ConstValue {
105    #[inline]
106    fn into_const_value(self) -> alloc::Result<ConstValue> {
107        Ok(self)
108    }
109}
110
111impl IntoConstValue for &ConstValue {
112    #[inline]
113    fn into_const_value(self) -> alloc::Result<ConstValue> {
114        self.try_clone()
115    }
116}
117
118/// Convert something into a [`ConstValue`].
119///
120/// # Examples
121///
122/// ```
123/// let value = rune::to_const_value((i32::MIN, u64::MAX))?;
124/// let (a, b) = rune::from_const_value::<(i32, u64)>(value)?;
125///
126/// assert_eq!(a, i32::MIN);
127/// assert_eq!(b, u64::MAX);
128/// # Ok::<_, rune::support::Error>(())
129/// ```
130pub fn from_const_value<T>(value: impl IntoConstValue) -> Result<T, RuntimeError>
131where
132    T: FromConstValue,
133{
134    T::from_const_value(value.into_const_value()?)
135}
136
137/// Convert something into a [`ConstValue`].
138///
139/// # Examples
140///
141/// ```
142/// let value = rune::to_const_value((i32::MIN, u64::MAX))?;
143/// let (a, b) = rune::from_const_value::<(i32, u64)>(value)?;
144///
145/// assert_eq!(a, i32::MIN);
146/// assert_eq!(b, u64::MAX);
147/// # Ok::<_, rune::support::Error>(())
148/// ```
149pub fn to_const_value(value: impl ToConstValue) -> Result<ConstValue, RuntimeError> {
150    value.to_const_value()
151}
152
153/// Trait to perform a conversion to a [`ConstValue`].
154pub trait ToConstValue: Sized {
155    /// Convert into a constant value.
156    fn to_const_value(self) -> Result<ConstValue, RuntimeError>;
157
158    /// Return the constant constructor for the given type.
159    #[inline]
160    #[doc(hidden)]
161    fn construct() -> alloc::Result<Option<ConstConstructImpl>> {
162        Ok(None)
163    }
164}
165
166impl ToConstValue for ConstValue {
167    #[inline]
168    fn to_const_value(self) -> Result<ConstValue, RuntimeError> {
169        Ok(self)
170    }
171}
172
173impl ToConstValue for Value {
174    #[inline]
175    fn to_const_value(self) -> Result<ConstValue, RuntimeError> {
176        ConstValue::from_value_ref(&self)
177    }
178}
179
180/// A dynamic constant value.
181#[derive(Debug, TryClone)]
182#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
183#[cfg_attr(feature = "musli", derive(Decode, Encode))]
184pub(crate) struct ConstInstance {
185    /// The type hash of the value.
186    ///
187    /// If the value is a variant, this is the type hash of the enum.
188    #[try_clone(copy)]
189    pub(crate) hash: Hash,
190    /// The type hash of the variant.
191    ///
192    /// If this is not an enum, this is `Hash::EMPTY`.
193    #[try_clone(copy)]
194    pub(crate) variant_hash: Hash,
195    /// The fields the value is constituted of.
196    pub(crate) fields: Box<[ConstValue]>,
197}
198
199impl ConstInstance {
200    fn type_info(&self) -> TypeInfo {
201        fn struct_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
202            write!(f, "unknown constant struct")
203        }
204
205        fn variant_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
206            write!(f, "unknown constant variant")
207        }
208
209        match self.hash {
210            Option::<Value>::HASH => TypeInfo::any::<Option<Value>>(),
211            runtime::Vec::HASH => TypeInfo::any::<runtime::Vec>(),
212            OwnedTuple::HASH => TypeInfo::any::<OwnedTuple>(),
213            Object::HASH => TypeInfo::any::<Object>(),
214            hash if self.variant_hash == Hash::EMPTY => {
215                TypeInfo::any_type_info(AnyTypeInfo::new(struct_name, hash))
216            }
217            hash => TypeInfo::any_type_info(AnyTypeInfo::new(variant_name, hash)),
218        }
219    }
220}
221
222#[derive(Debug, TryClone)]
223#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
224#[cfg_attr(feature = "musli", derive(Decode, Encode))]
225pub(crate) enum ConstValueKind {
226    /// An inline constant value.
227    Inline(#[try_clone(copy)] Inline),
228    /// A string constant designated by its slot.
229    String(Box<str>),
230    /// A byte string.
231    Bytes(Box<[u8]>),
232    /// An instance of some type of value.
233    Instance(Box<ConstInstance>),
234}
235
236impl ConstValueKind {
237    #[inline]
238    fn type_info(&self) -> TypeInfo {
239        match self {
240            ConstValueKind::Inline(value) => value.type_info(),
241            ConstValueKind::String(..) => TypeInfo::any::<String>(),
242            ConstValueKind::Bytes(..) => TypeInfo::any::<Bytes>(),
243            ConstValueKind::Instance(instance) => instance.type_info(),
244        }
245    }
246}
247
248/// A constant value.
249#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(transparent))]
250#[cfg_attr(feature = "musli", derive(Encode, Decode), musli(transparent))]
251pub struct ConstValue {
252    kind: ConstValueKind,
253}
254
255impl ConstValue {
256    /// Construct a constant value that is a string.
257    pub fn string(value: impl AsRef<str>) -> Result<ConstValue, RuntimeError> {
258        let value = Box::try_from(value.as_ref())?;
259        Ok(Self::from(ConstValueKind::String(value)))
260    }
261
262    /// Construct a constant value that is bytes.
263    pub fn bytes(value: impl AsRef<[u8]>) -> Result<ConstValue, RuntimeError> {
264        let value = Box::try_from(value.as_ref())?;
265        Ok(Self::from(ConstValueKind::Bytes(value)))
266    }
267
268    /// Construct a new tuple constant value.
269    pub fn tuple(fields: Box<[ConstValue]>) -> Result<ConstValue, RuntimeError> {
270        let instance = ConstInstance {
271            hash: OwnedTuple::HASH,
272            variant_hash: Hash::EMPTY,
273            fields,
274        };
275
276        let instance = Box::try_new(instance)?;
277        Ok(Self::from(ConstValueKind::Instance(instance)))
278    }
279
280    /// Construct a constant value for a struct.
281    pub fn for_struct<const N: usize>(
282        hash: Hash,
283        fields: [ConstValue; N],
284    ) -> Result<ConstValue, RuntimeError> {
285        let fields = Box::<[ConstValue]>::try_from(fields)?;
286
287        let instance = ConstInstance {
288            hash,
289            variant_hash: Hash::EMPTY,
290            fields,
291        };
292
293        let instance = Box::try_new(instance)?;
294        Ok(Self::from(ConstValueKind::Instance(instance)))
295    }
296
297    /// Try to coerce the current value as the specified integer `T`.
298    ///
299    /// # Examples
300    ///
301    /// ```
302    /// let value = rune::to_const_value(u32::MAX)?;
303    ///
304    /// assert_eq!(value.as_integer::<u64>()?, u32::MAX as u64);
305    /// assert!(value.as_integer::<i32>().is_err());
306    ///
307    /// # Ok::<(), rune::support::Error>(())
308    /// ```
309    pub fn as_integer<T>(&self) -> Result<T, RuntimeError>
310    where
311        T: TryFrom<i64> + TryFrom<u64>,
312    {
313        match self.kind {
314            ConstValueKind::Inline(Inline::Signed(value)) => match value.try_into() {
315                Ok(number) => Ok(number),
316                Err(..) => Err(RuntimeError::new(
317                    VmErrorKind::ValueToIntegerCoercionError {
318                        from: VmIntegerRepr::from(value),
319                        to: any::type_name::<T>(),
320                    },
321                )),
322            },
323            ConstValueKind::Inline(Inline::Unsigned(value)) => match value.try_into() {
324                Ok(number) => Ok(number),
325                Err(..) => Err(RuntimeError::new(
326                    VmErrorKind::ValueToIntegerCoercionError {
327                        from: VmIntegerRepr::from(value),
328                        to: any::type_name::<T>(),
329                    },
330                )),
331            },
332            ref kind => Err(RuntimeError::new(VmErrorKind::ExpectedNumber {
333                actual: kind.type_info(),
334            })),
335        }
336    }
337
338    inline_macros!(inline_into);
339
340    /// Coerce into tuple.
341    #[inline]
342    pub fn into_tuple(self) -> Result<Box<[ConstValue]>, ExpectedType> {
343        match self.kind {
344            ConstValueKind::Instance(instance) => {
345                let instance = Box::into_inner(instance);
346
347                match instance.hash {
348                    OwnedTuple::HASH => Ok(instance.fields),
349                    _ => Err(ExpectedType::new::<Tuple>(instance.type_info())),
350                }
351            }
352            kind => Err(ExpectedType::new::<Tuple>(kind.type_info())),
353        }
354    }
355
356    /// Access the interior value.
357    pub(crate) fn as_kind(&self) -> &ConstValueKind {
358        &self.kind
359    }
360
361    /// Construct a constant value from a reference to a value..
362    pub(crate) fn from_value_ref(value: &Value) -> Result<ConstValue, RuntimeError> {
363        let kind = match value.as_ref() {
364            Repr::Inline(value) => ConstValueKind::Inline(*value),
365            Repr::Dynamic(value) => {
366                return Err(RuntimeError::from(VmErrorKind::ConstNotSupported {
367                    actual: value.type_info(),
368                }));
369            }
370            Repr::Any(value) => match value.type_hash() {
371                String::HASH => {
372                    return ConstValue::string(value.borrow_ref::<String>()?.as_str());
373                }
374                Bytes::HASH => {
375                    return ConstValue::bytes(value.borrow_ref::<Bytes>()?.as_slice());
376                }
377                runtime::OwnedTuple::HASH => {
378                    let tuple = value.borrow_ref::<runtime::OwnedTuple>()?;
379                    let mut const_tuple = Vec::try_with_capacity(tuple.len())?;
380
381                    for value in tuple.iter() {
382                        const_tuple.try_push(Self::from_value_ref(value)?)?;
383                    }
384
385                    return ConstValue::tuple(const_tuple.try_into_boxed_slice()?);
386                }
387                Object::HASH => {
388                    let object = value.borrow_ref::<Object>()?;
389                    let mut fields = Vec::try_with_capacity(object.len())?;
390
391                    for (key, value) in object.iter() {
392                        let key = ConstValue::string(key.as_str())?;
393                        let value = Self::from_value_ref(value)?;
394                        fields.try_push(ConstValue::tuple(Box::try_from([key, value])?)?)?;
395                    }
396
397                    let instance = ConstInstance {
398                        hash: Object::HASH,
399                        variant_hash: Hash::EMPTY,
400                        fields: fields.try_into_boxed_slice()?,
401                    };
402
403                    let instance = Box::try_new(instance)?;
404                    ConstValueKind::Instance(instance)
405                }
406                Option::<Value>::HASH => {
407                    let option = value.borrow_ref::<Option<Value>>()?;
408
409                    let (variant_hash, fields) = match &*option {
410                        Some(some) => (
411                            hash_in!(crate, ::std::option::Option::Some),
412                            Box::try_from([Self::from_value_ref(some)?])?,
413                        ),
414                        None => (hash_in!(crate, ::std::option::Option::None), Box::default()),
415                    };
416
417                    let instance = ConstInstance {
418                        hash: Option::<Value>::HASH,
419                        variant_hash,
420                        fields,
421                    };
422                    ConstValueKind::Instance(Box::try_new(instance)?)
423                }
424                runtime::Vec::HASH => {
425                    let vec = value.borrow_ref::<runtime::Vec>()?;
426                    let mut const_vec = Vec::try_with_capacity(vec.len())?;
427
428                    for value in vec.iter() {
429                        const_vec.try_push(Self::from_value_ref(value)?)?;
430                    }
431
432                    let fields = Box::try_from(const_vec)?;
433
434                    let instance = ConstInstance {
435                        hash: runtime::Vec::HASH,
436                        variant_hash: Hash::EMPTY,
437                        fields,
438                    };
439                    ConstValueKind::Instance(Box::try_new(instance)?)
440                }
441                _ => {
442                    return Err(RuntimeError::from(VmErrorKind::ConstNotSupported {
443                        actual: value.type_info(),
444                    }));
445                }
446            },
447        };
448
449        Ok(Self { kind })
450    }
451
452    #[inline]
453    #[cfg(test)]
454    pub(crate) fn to_value(&self) -> Result<Value, RuntimeError> {
455        self.to_value_with(&EmptyConstContext)
456    }
457
458    /// Convert into a pair of tuples.
459    pub(crate) fn as_string(&self) -> Result<&str, ExpectedType> {
460        let ConstValueKind::String(value) = &self.kind else {
461            return Err(ExpectedType::new::<String>(self.kind.type_info()));
462        };
463
464        Ok(value)
465    }
466
467    /// Convert into a pair of tuples.
468    pub(crate) fn as_pair(&self) -> Result<(&ConstValue, &ConstValue), ExpectedType> {
469        let ConstValueKind::Instance(instance) = &self.kind else {
470            return Err(ExpectedType::new::<Tuple>(self.kind.type_info()));
471        };
472
473        if !matches!(instance.hash, OwnedTuple::HASH) {
474            return Err(ExpectedType::new::<Tuple>(instance.type_info()));
475        }
476
477        let [a, b] = instance.fields.as_ref() else {
478            return Err(ExpectedType::new::<Tuple>(instance.type_info()));
479        };
480
481        Ok((a, b))
482    }
483
484    /// Convert into virtual machine value.
485    ///
486    /// We provide this associated method since a constant value can be
487    /// converted into a value infallibly, which is not captured by the trait
488    /// otherwise.
489    pub(crate) fn to_value_with(&self, cx: &dyn ConstContext) -> Result<Value, RuntimeError> {
490        match &self.kind {
491            ConstValueKind::Inline(value) => Ok(Value::from(*value)),
492            ConstValueKind::String(string) => Ok(Value::try_from(string.as_ref())?),
493            ConstValueKind::Bytes(b) => Ok(Value::try_from(b.as_ref())?),
494            ConstValueKind::Instance(instance) => match &**instance {
495                ConstInstance {
496                    hash,
497                    variant_hash: Hash::EMPTY,
498                    fields,
499                } => match *hash {
500                    runtime::OwnedTuple::HASH => {
501                        let mut t = Vec::try_with_capacity(fields.len())?;
502
503                        for value in fields.iter() {
504                            t.try_push(Self::to_value_with(value, cx)?)?;
505                        }
506
507                        Ok(Value::try_from(OwnedTuple::try_from(t)?)?)
508                    }
509                    runtime::Vec::HASH => {
510                        let mut v = runtime::Vec::with_capacity(fields.len())?;
511
512                        for value in fields.iter() {
513                            v.push(Self::to_value_with(value, cx)?)?;
514                        }
515
516                        Ok(Value::try_from(v)?)
517                    }
518                    runtime::Object::HASH => {
519                        let mut o = Object::with_capacity(fields.len())?;
520
521                        for value in fields.iter() {
522                            let (key, value) = value.as_pair()?;
523                            let key = key.as_string()?.try_to_string()?;
524                            let value = Self::to_value_with(value, cx)?;
525                            o.insert(key, value)?;
526                        }
527
528                        Ok(Value::try_from(o)?)
529                    }
530                    _ => {
531                        let Some(constructor) = cx.get(*hash) else {
532                            return Err(RuntimeError::missing_constant_constructor(*hash));
533                        };
534
535                        constructor.const_construct(fields)
536                    }
537                },
538                ConstInstance {
539                    hash,
540                    variant_hash,
541                    fields,
542                } => {
543                    match (*variant_hash, &fields[..]) {
544                        // If the hash is `Option`, we can return a value directly.
545                        (hash_in!(crate, ::std::option::Option::Some), [value]) => {
546                            let value = Self::to_value_with(value, cx)?;
547                            Ok(Value::try_from(Some(value))?)
548                        }
549                        (hash_in!(crate, ::std::option::Option::None), []) => {
550                            Ok(Value::try_from(None)?)
551                        }
552                        _ => Err(RuntimeError::missing_constant_constructor(*hash)),
553                    }
554                }
555            },
556        }
557    }
558
559    /// Get the type information of the value.
560    pub(crate) fn type_info(&self) -> TypeInfo {
561        self.kind.type_info()
562    }
563}
564
565impl TryClone for ConstValue {
566    fn try_clone(&self) -> alloc::Result<Self> {
567        Ok(Self {
568            kind: self.kind.try_clone()?,
569        })
570    }
571}
572
573impl FromValue for ConstValue {
574    #[inline]
575    fn from_value(value: Value) -> Result<Self, RuntimeError> {
576        ConstValue::from_value_ref(&value)
577    }
578}
579
580impl ToValue for ConstValue {
581    #[inline]
582    fn to_value(self) -> Result<Value, RuntimeError> {
583        ConstValue::to_value_with(&self, &EmptyConstContext)
584    }
585}
586
587impl From<ConstValueKind> for ConstValue {
588    #[inline]
589    fn from(kind: ConstValueKind) -> Self {
590        Self { kind }
591    }
592}
593
594impl From<Inline> for ConstValue {
595    #[inline]
596    fn from(value: Inline) -> Self {
597        Self::from(ConstValueKind::Inline(value))
598    }
599}
600
601impl TryFrom<String> for ConstValue {
602    type Error = alloc::Error;
603
604    #[inline]
605    fn try_from(value: String) -> Result<Self, Self::Error> {
606        Ok(Self::from(Box::<str>::try_from(value)?))
607    }
608}
609
610impl From<Box<str>> for ConstValue {
611    #[inline]
612    fn from(value: Box<str>) -> Self {
613        Self::from(ConstValueKind::String(value))
614    }
615}
616
617impl TryFrom<Bytes> for ConstValue {
618    type Error = alloc::Error;
619
620    #[inline]
621    fn try_from(value: Bytes) -> Result<Self, Self::Error> {
622        Self::try_from(value.as_slice())
623    }
624}
625
626impl TryFrom<&str> for ConstValue {
627    type Error = alloc::Error;
628
629    #[inline]
630    fn try_from(value: &str) -> Result<Self, Self::Error> {
631        Ok(ConstValue::from(Box::<str>::try_from(value)?))
632    }
633}
634
635impl ToConstValue for &str {
636    #[inline]
637    fn to_const_value(self) -> Result<ConstValue, RuntimeError> {
638        Ok(ConstValue::try_from(self)?)
639    }
640}
641
642impl From<Box<[u8]>> for ConstValue {
643    #[inline]
644    fn from(value: Box<[u8]>) -> Self {
645        Self::from(ConstValueKind::Bytes(value))
646    }
647}
648
649impl TryFrom<&[u8]> for ConstValue {
650    type Error = alloc::Error;
651
652    #[inline]
653    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
654        Ok(Self::from(Box::<[u8]>::try_from(value)?))
655    }
656}
657
658impl ToConstValue for &[u8] {
659    #[inline]
660    fn to_const_value(self) -> Result<ConstValue, RuntimeError> {
661        Ok(ConstValue::try_from(self)?)
662    }
663}
664
665impl fmt::Debug for ConstValue {
666    #[inline]
667    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
668        self.kind.fmt(f)
669    }
670}
671
672/// Trait to perform a conversion from a [`ConstValue`].
673pub trait FromConstValue: Sized {
674    /// Convert from a constant value.
675    fn from_const_value(value: ConstValue) -> Result<Self, RuntimeError>;
676}
677
678impl FromConstValue for ConstValue {
679    #[inline]
680    fn from_const_value(value: ConstValue) -> Result<Self, RuntimeError> {
681        Ok(value)
682    }
683}
684
685impl FromConstValue for bool {
686    #[inline]
687    fn from_const_value(value: ConstValue) -> Result<Self, RuntimeError> {
688        value.as_bool()
689    }
690}
691
692impl FromConstValue for char {
693    #[inline]
694    fn from_const_value(value: ConstValue) -> Result<Self, RuntimeError> {
695        value.as_char()
696    }
697}
698
699macro_rules! impl_integer {
700    ($($ty:ty),* $(,)?) => {
701        $(
702            impl FromConstValue for $ty {
703                #[inline]
704                fn from_const_value(value: ConstValue) -> Result<Self, RuntimeError> {
705                    value.as_integer()
706                }
707            }
708        )*
709    };
710}
711
712impl_integer!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
713
714declare_dyn_trait! {
715    /// The vtable for constant constructors.
716    struct ConstConstructVtable;
717
718    /// The implementation wrapper for a constant constructor.
719    pub struct ConstConstructImpl;
720
721    /// Implementation of a constant constructor.
722    ///
723    /// Do not implement manually, this is provided when deriving [`ToConstValue`].
724    ///
725    /// [`ToConstValue`]: derive@ToConstValue
726    pub trait ConstConstruct {
727        /// Construct from values.
728        #[doc(hidden)]
729        fn const_construct(&self, fields: &[ConstValue]) -> Result<Value, RuntimeError>;
730
731        /// Construct from values.
732        #[doc(hidden)]
733        fn runtime_construct(&self, fields: &mut [Value]) -> Result<Value, RuntimeError>;
734    }
735}
736
737pub(crate) trait ConstContext {
738    fn get(&self, hash: Hash) -> Option<&ConstConstructImpl>;
739}
740
741pub(crate) struct EmptyConstContext;
742
743impl ConstContext for EmptyConstContext {
744    #[inline]
745    fn get(&self, _: Hash) -> Option<&ConstConstructImpl> {
746        None
747    }
748}