hugr_core/ops/
constant.rs

1//! Constant value definitions.
2
3mod custom;
4mod serialize;
5
6use std::borrow::Cow;
7use std::collections::hash_map::DefaultHasher; // Moves into std::hash in Rust 1.76.
8use std::hash::{Hash, Hasher};
9
10use super::{NamedOp, OpName, OpTrait, StaticTag};
11use super::{OpTag, OpType};
12use crate::envelope::serde_with::AsStringEnvelope;
13use crate::types::{CustomType, EdgeKind, Signature, SumType, SumTypeError, Type, TypeRow};
14use crate::{Hugr, HugrView};
15use serialize::SerialSum;
16
17use delegate::delegate;
18use itertools::Itertools;
19use serde::{Deserialize, Serialize};
20use serde_with::serde_as;
21use smol_str::SmolStr;
22use thiserror::Error;
23
24pub use custom::{
25    CustomConst, CustomSerialized, TryHash, downcast_equal_consts, get_pair_of_input_values,
26    get_single_input_value,
27};
28
29#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
30/// An operation returning a constant value.
31///
32/// Represents core types and extension types.
33#[non_exhaustive]
34#[cfg_attr(test, derive(proptest_derive::Arbitrary))]
35pub struct Const {
36    /// The [Value] of the constant.
37    #[serde(rename = "v")]
38    pub value: Value,
39}
40
41impl Const {
42    /// Create a new [`Const`] operation.
43    #[must_use]
44    pub fn new(value: Value) -> Self {
45        Self { value }
46    }
47
48    /// The inner value of the [`Const`]
49    #[must_use]
50    pub fn value(&self) -> &Value {
51        &self.value
52    }
53
54    delegate! {
55        to self.value {
56            /// Returns the type of this constant.
57            #[must_use] pub fn get_type(&self) -> Type;
58            /// For a Const holding a CustomConst, extract the CustomConst by
59            /// downcasting.
60            #[must_use] pub fn get_custom_value<T: CustomConst>(&self) -> Option<&T>;
61
62            /// Check the value.
63            pub fn validate(&self) -> Result<(), ConstTypeError>;
64        }
65    }
66}
67
68impl From<Value> for Const {
69    fn from(value: Value) -> Self {
70        Self::new(value)
71    }
72}
73
74impl NamedOp for Const {
75    fn name(&self) -> OpName {
76        self.value().name()
77    }
78}
79
80impl StaticTag for Const {
81    const TAG: OpTag = OpTag::Const;
82}
83
84impl OpTrait for Const {
85    fn description(&self) -> &'static str {
86        "Constant value"
87    }
88
89    fn tag(&self) -> OpTag {
90        <Self as StaticTag>::TAG
91    }
92
93    fn static_output(&self) -> Option<EdgeKind> {
94        Some(EdgeKind::Const(self.get_type()))
95    }
96
97    // Constants cannot refer to TypeArgs of the enclosing Hugr, so no substitute().
98}
99
100impl From<Const> for Value {
101    fn from(konst: Const) -> Self {
102        konst.value
103    }
104}
105
106impl AsRef<Value> for Const {
107    fn as_ref(&self) -> &Value {
108        self.value()
109    }
110}
111
112#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
113#[serde(try_from = "SerialSum")]
114#[serde(into = "SerialSum")]
115/// A Sum variant, with a tag indicating the index of the variant and its
116/// value.
117pub struct Sum {
118    /// The tag index of the variant.
119    pub tag: usize,
120    /// The value of the variant.
121    ///
122    /// Sum variants are always a row of values, hence the Vec.
123    pub values: Vec<Value>,
124    /// The full type of the Sum, including the other variants.
125    pub sum_type: SumType,
126}
127
128impl Sum {
129    /// If value is a sum with a single row variant, return the row.
130    #[must_use]
131    pub fn as_tuple(&self) -> Option<&[Value]> {
132        // For valid instances, the type row will not have any row variables.
133        self.sum_type.as_tuple().map(|_| self.values.as_ref())
134    }
135
136    fn try_hash<H: Hasher>(&self, st: &mut H) -> bool {
137        maybe_hash_values(&self.values, st) && {
138            st.write_usize(self.tag);
139            self.sum_type.hash(st);
140            true
141        }
142    }
143}
144
145pub(crate) fn maybe_hash_values<H: Hasher>(vals: &[Value], st: &mut H) -> bool {
146    // We can't mutate the Hasher with the first element
147    // if any element, even the last, fails.
148    let mut hasher = DefaultHasher::new();
149    vals.iter().all(|e| e.try_hash(&mut hasher)) && {
150        st.write_u64(hasher.finish());
151        true
152    }
153}
154
155#[serde_as]
156#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
157#[serde(tag = "v")]
158/// A value that can be stored as a static constant. Representing core types and
159/// extension types.
160pub enum Value {
161    /// An extension constant value, that can check it is of a given [CustomType].
162    Extension {
163        #[serde(flatten)]
164        /// The custom constant value.
165        e: OpaqueValue,
166    },
167    /// A higher-order function value.
168    Function {
169        /// A Hugr defining the function.
170        #[serde_as(as = "Box<AsStringEnvelope>")]
171        hugr: Box<Hugr>,
172    },
173    /// A Sum variant, with a tag indicating the index of the variant and its
174    /// value.
175    #[serde(alias = "Tuple")]
176    Sum(Sum),
177}
178
179/// An opaque newtype around a [`Box<dyn CustomConst>`](CustomConst).
180///
181/// This type has special serialization behaviour in order to support
182/// serialization and deserialization of unknown impls of [CustomConst].
183///
184/// During serialization we first serialize the internal [`dyn` CustomConst](CustomConst)
185/// into a [serde_json::Value]. We then create a [CustomSerialized] wrapping
186/// that value.  That [CustomSerialized] is then serialized in place of the
187/// [OpaqueValue].
188///
189/// During deserialization, first we deserialize a [CustomSerialized]. We
190/// attempt to deserialize the internal [serde_json::Value] using the [`Box<dyn
191/// CustomConst>`](CustomConst) impl. This will fail if the appropriate `impl CustomConst`
192/// is not linked into the running program, in which case we coerce the
193/// [CustomSerialized] into a [`Box<dyn CustomConst>`](CustomConst). The [OpaqueValue] is
194/// then produced from the [`Box<dyn [CustomConst]>`](CustomConst).
195///
196/// In the case where the internal serialized value of a `CustomSerialized`
197/// is another `CustomSerialized` we do not attempt to recurse. This behaviour
198/// may change in future.
199///
200#[cfg_attr(not(miri), doc = "```")] // this doctest depends on typetag, so fails with miri
201#[cfg_attr(miri, doc = "```ignore")]
202/// use serde::{Serialize,Deserialize};
203/// use hugr::{
204///   types::Type,ops::constant::{OpaqueValue, ValueName, CustomConst, CustomSerialized},
205///   extension::{ExtensionSet, prelude::{usize_t, ConstUsize}},
206///   std_extensions::arithmetic::int_types};
207/// use serde_json::json;
208///
209/// let expected_json = json!({
210///     "typ": usize_t(),
211///     "value": {'c': "ConstUsize", 'v': 1}
212/// });
213/// let ev = OpaqueValue::new(ConstUsize::new(1));
214/// assert_eq!(&serde_json::to_value(&ev).unwrap(), &expected_json);
215/// assert_eq!(ev, serde_json::from_value(expected_json).unwrap());
216///
217/// let ev = OpaqueValue::new(CustomSerialized::new(usize_t().clone(), serde_json::Value::Null));
218/// let expected_json = json!({
219///     "typ": usize_t(),
220///     "value": null
221/// });
222///
223/// assert_eq!(&serde_json::to_value(ev.clone()).unwrap(), &expected_json);
224/// assert_eq!(ev, serde_json::from_value(expected_json).unwrap());
225/// ```
226#[derive(Debug, Clone, Serialize, Deserialize)]
227pub struct OpaqueValue {
228    #[serde(flatten, with = "self::custom::serde_extension_value")]
229    v: Box<dyn CustomConst>,
230}
231
232impl OpaqueValue {
233    /// Create a new [`OpaqueValue`] from any [`CustomConst`].
234    pub fn new(cc: impl CustomConst) -> Self {
235        Self { v: Box::new(cc) }
236    }
237
238    /// Returns a reference to the internal [`CustomConst`].
239    #[must_use]
240    pub fn value(&self) -> &dyn CustomConst {
241        self.v.as_ref()
242    }
243
244    /// Returns a reference to the internal [`CustomConst`].
245    pub(crate) fn value_mut(&mut self) -> &mut dyn CustomConst {
246        self.v.as_mut()
247    }
248
249    delegate! {
250        to self.value() {
251            /// Returns the type of the internal [`CustomConst`].
252            #[must_use] pub fn get_type(&self) -> Type;
253            /// An identifier of the internal [`CustomConst`].
254            #[must_use] pub fn name(&self) -> ValueName;
255        }
256    }
257}
258
259impl<CC: CustomConst> From<CC> for OpaqueValue {
260    fn from(x: CC) -> Self {
261        Self::new(x)
262    }
263}
264
265impl From<Box<dyn CustomConst>> for OpaqueValue {
266    fn from(value: Box<dyn CustomConst>) -> Self {
267        Self { v: value }
268    }
269}
270
271impl PartialEq for OpaqueValue {
272    fn eq(&self, other: &Self) -> bool {
273        self.value().equal_consts(other.value())
274    }
275}
276
277/// Struct for custom type check fails.
278#[derive(Clone, Debug, PartialEq, Eq, Error)]
279#[non_exhaustive]
280pub enum CustomCheckFailure {
281    /// The value had a specific type that was not what was expected
282    #[error("Expected type: {expected} but value was of type: {found}")]
283    TypeMismatch {
284        /// The expected custom type.
285        expected: Box<CustomType>,
286        /// The custom type found when checking.
287        found: Box<Type>,
288    },
289    /// Any other message
290    #[error("{0}")]
291    Message(String),
292}
293
294/// Errors that arise from typechecking constants
295#[derive(Clone, Debug, PartialEq, Error)]
296#[non_exhaustive]
297pub enum ConstTypeError {
298    /// Invalid sum type definition.
299    #[error("{0}")]
300    SumType(#[from] SumTypeError),
301    /// Function constant missing a function type.
302    #[error(
303        "A function constant cannot be defined using a Hugr with root of type {hugr_root_type}. Must be a monomorphic function."
304    )]
305    NotMonomorphicFunction {
306        /// The root node type of the Hugr that (claims to) define the function constant.
307        hugr_root_type: Box<OpType>,
308    },
309    /// A mismatch between the type expected and the value.
310    #[error("Value {1:?} does not match expected type {0}")]
311    ConstCheckFail(Box<Type>, Value),
312    /// Error when checking a custom value.
313    #[error("Error when checking custom type: {0}")]
314    CustomCheckFail(#[from] CustomCheckFailure),
315}
316
317/// Hugrs (even functions) inside Consts must be monomorphic
318fn mono_fn_type(h: &Hugr) -> Result<Cow<'_, Signature>, ConstTypeError> {
319    let err = || ConstTypeError::NotMonomorphicFunction {
320        hugr_root_type: Box::new(h.entrypoint_optype().clone()),
321    };
322    if let Some(pf) = h.poly_func_type() {
323        match pf.try_into() {
324            Ok(sig) => return Ok(Cow::Owned(sig)),
325            Err(_) => return Err(err()),
326        };
327    }
328
329    h.inner_function_type().ok_or_else(err)
330}
331
332impl Value {
333    /// Returns the type of this [`Value`].
334    #[must_use]
335    pub fn get_type(&self) -> Type {
336        match self {
337            Self::Extension { e } => e.get_type(),
338            Self::Sum(Sum { sum_type, .. }) => sum_type.clone().into(),
339            Self::Function { hugr } => {
340                let func_type = mono_fn_type(hugr).unwrap_or_else(|e| panic!("{}", e));
341                Type::new_function(func_type.into_owned())
342            }
343        }
344    }
345
346    /// Returns a Sum constant. The value is determined by `items` and is
347    /// type-checked `typ`. The `tag`th variant of `typ` should match the types
348    /// of `items`.
349    pub fn sum(
350        tag: usize,
351        items: impl IntoIterator<Item = Value>,
352        typ: SumType,
353    ) -> Result<Self, ConstTypeError> {
354        let values: Vec<Value> = items.into_iter().collect();
355        typ.check_type(tag, &values)?;
356        Ok(Self::Sum(Sum {
357            tag,
358            values,
359            sum_type: typ,
360        }))
361    }
362
363    /// Returns a tuple constant of constant values.
364    pub fn tuple(items: impl IntoIterator<Item = Value>) -> Self {
365        let vs = items.into_iter().collect_vec();
366        let tys = vs.iter().map(Self::get_type).collect_vec();
367
368        Self::sum(0, vs, SumType::new_tuple(tys)).expect("Tuple type is valid")
369    }
370
371    /// Returns a constant function defined by a Hugr.
372    ///
373    /// # Errors
374    ///
375    /// Returns an error if the Hugr root node does not define a function.
376    pub fn function(hugr: impl Into<Hugr>) -> Result<Self, ConstTypeError> {
377        let hugr = hugr.into();
378        mono_fn_type(&hugr)?;
379        Ok(Self::Function {
380            hugr: Box::new(hugr),
381        })
382    }
383
384    /// Returns a constant unit type (empty Tuple).
385    #[must_use]
386    pub const fn unit() -> Self {
387        Self::Sum(Sum {
388            tag: 0,
389            values: vec![],
390            sum_type: SumType::Unit { size: 1 },
391        })
392    }
393
394    /// Returns a constant Sum over units. Used as branching values.
395    pub fn unit_sum(tag: usize, size: u8) -> Result<Self, ConstTypeError> {
396        Self::sum(tag, [], SumType::Unit { size })
397    }
398
399    /// Returns a constant Sum over units, with only one variant.
400    #[must_use]
401    pub fn unary_unit_sum() -> Self {
402        Self::unit_sum(0, 1).expect("0 < 1")
403    }
404
405    /// Returns a constant "true" value, i.e. the second variant of Sum((), ()).
406    #[must_use]
407    pub fn true_val() -> Self {
408        Self::unit_sum(1, 2).expect("1 < 2")
409    }
410
411    /// Returns a constant "false" value, i.e. the first variant of Sum((), ()).
412    #[must_use]
413    pub fn false_val() -> Self {
414        Self::unit_sum(0, 2).expect("0 < 2")
415    }
416
417    /// Returns an optional with some values. This is a Sum with two variants, the
418    /// first being empty and the second being the values.
419    pub fn some<V: Into<Value>>(values: impl IntoIterator<Item = V>) -> Self {
420        let values: Vec<Value> = values.into_iter().map(Into::into).collect_vec();
421        let value_types: Vec<Type> = values.iter().map(Value::get_type).collect_vec();
422        let sum_type = SumType::new_option(value_types);
423        Self::sum(1, values, sum_type).unwrap()
424    }
425
426    /// Returns an optional with no value. This is a Sum with two variants, the
427    /// first being empty and the second being the value.
428    pub fn none(value_types: impl Into<TypeRow>) -> Self {
429        Self::sum(0, [], SumType::new_option(value_types)).unwrap()
430    }
431
432    /// Returns a constant `bool` value.
433    ///
434    /// see [`Value::true_val`] and [`Value::false_val`].
435    #[must_use]
436    pub fn from_bool(b: bool) -> Self {
437        if b {
438            Self::true_val()
439        } else {
440            Self::false_val()
441        }
442    }
443
444    /// Returns a [`Value::Extension`] holding `custom_const`.
445    pub fn extension(custom_const: impl CustomConst) -> Self {
446        Self::Extension {
447            e: OpaqueValue::new(custom_const),
448        }
449    }
450
451    /// For a [Value] holding a [`CustomConst`], extract the `CustomConst` by downcasting.
452    #[must_use]
453    pub fn get_custom_value<T: CustomConst>(&self) -> Option<&T> {
454        if let Self::Extension { e } = self {
455            e.v.downcast_ref()
456        } else {
457            None
458        }
459    }
460
461    fn name(&self) -> OpName {
462        match self {
463            Self::Extension { e } => format!("const:custom:{}", e.name()),
464            Self::Function { hugr: h } => {
465                let Ok(t) = mono_fn_type(h) else {
466                    panic!("HUGR root node isn't a valid function parent.");
467                };
468                format!("const:function:[{t}]")
469            }
470            Self::Sum(Sum {
471                tag,
472                values,
473                sum_type,
474            }) => {
475                if sum_type.as_tuple().is_some() {
476                    let names: Vec<_> = values.iter().map(Value::name).collect();
477                    format!("const:seq:{{{}}}", names.iter().join(", "))
478                } else {
479                    format!("const:sum:{{tag:{tag}, vals:{values:?}}}")
480                }
481            }
482        }
483        .into()
484    }
485
486    /// Check the value.
487    pub fn validate(&self) -> Result<(), ConstTypeError> {
488        match self {
489            Self::Extension { e } => Ok(e.value().validate()?),
490            Self::Function { hugr } => {
491                mono_fn_type(hugr)?;
492                Ok(())
493            }
494            Self::Sum(Sum {
495                tag,
496                values,
497                sum_type,
498            }) => {
499                sum_type.check_type(*tag, values)?;
500                Ok(())
501            }
502        }
503    }
504
505    /// If value is a sum with a single row variant, return the row.
506    #[must_use]
507    pub fn as_tuple(&self) -> Option<&[Value]> {
508        if let Self::Sum(sum) = self {
509            sum.as_tuple()
510        } else {
511            None
512        }
513    }
514
515    /// Hashes this value, if possible. [`Value::Extension`]s are hashable according
516    /// to their implementation of [`TryHash`]; [`Value::Function`]s never are;
517    /// [`Value::Sum`]s are if their contents are.
518    pub fn try_hash<H: Hasher>(&self, st: &mut H) -> bool {
519        match self {
520            Value::Extension { e } => e.value().try_hash(&mut *st),
521            Value::Function { .. } => false,
522            Value::Sum(s) => s.try_hash(st),
523        }
524    }
525}
526
527impl<T> From<T> for Value
528where
529    T: CustomConst,
530{
531    fn from(value: T) -> Self {
532        Self::extension(value)
533    }
534}
535
536/// A unique identifier for a constant value.
537pub type ValueName = SmolStr;
538
539/// Slice of a [`ValueName`] constant value identifier.
540pub type ValueNameRef = str;
541
542#[cfg(test)]
543pub(crate) mod test {
544    use std::collections::HashSet;
545    use std::sync::{Arc, Weak};
546
547    use super::Value;
548    use crate::builder::inout_sig;
549    use crate::builder::test::simple_dfg_hugr;
550    use crate::extension::PRELUDE;
551    use crate::extension::prelude::{bool_t, usize_custom_t};
552    use crate::extension::resolution::{
553        ExtensionResolutionError, WeakExtensionRegistry, resolve_custom_type_extensions,
554        resolve_typearg_extensions,
555    };
556    use crate::std_extensions::arithmetic::int_types::ConstInt;
557    use crate::std_extensions::collections::array::{ArrayValue, array_type};
558    use crate::std_extensions::collections::value_array::{VArrayValue, value_array_type};
559    use crate::{
560        builder::{BuildError, DFGBuilder, Dataflow, DataflowHugr},
561        extension::{
562            ExtensionId,
563            prelude::{ConstUsize, usize_t},
564        },
565        std_extensions::arithmetic::float_types::{ConstF64, float64_type},
566        type_row,
567        types::type_param::TypeArg,
568        types::{Type, TypeBound, TypeRow},
569    };
570    use cool_asserts::assert_matches;
571    use rstest::{fixture, rstest};
572
573    use super::*;
574
575    #[derive(Debug, Clone, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
576    /// A custom constant value used in testing
577    pub(crate) struct CustomTestValue(pub CustomType);
578
579    #[typetag::serde]
580    impl CustomConst for CustomTestValue {
581        fn name(&self) -> ValueName {
582            format!("CustomTestValue({:?})", self.0).into()
583        }
584
585        fn update_extensions(
586            &mut self,
587            extensions: &WeakExtensionRegistry,
588        ) -> Result<(), ExtensionResolutionError> {
589            resolve_custom_type_extensions(&mut self.0, extensions)?;
590            // This loop is redundant, but we use it to test the public
591            // function.
592            for arg in self.0.args_mut() {
593                resolve_typearg_extensions(arg, extensions)?;
594            }
595            Ok(())
596        }
597
598        fn get_type(&self) -> Type {
599            self.0.clone().into()
600        }
601
602        fn equal_consts(&self, other: &dyn CustomConst) -> bool {
603            crate::ops::constant::downcast_equal_consts(self, other)
604        }
605    }
606
607    /// A [`CustomSerialized`] encoding a [`float64_type()`] float constant used in testing.
608    pub(crate) fn serialized_float(f: f64) -> Value {
609        CustomSerialized::try_from_custom_const(ConstF64::new(f))
610            .unwrap()
611            .into()
612    }
613
614    /// Constructs a DFG hugr defining a sum constant, and returning the loaded value.
615    #[test]
616    fn test_sum() -> Result<(), BuildError> {
617        use crate::builder::Container;
618        let pred_rows = vec![vec![usize_t(), float64_type()].into(), Type::EMPTY_TYPEROW];
619        let pred_ty = SumType::new(pred_rows.clone());
620
621        let mut b = DFGBuilder::new(inout_sig(
622            type_row![],
623            TypeRow::from(vec![pred_ty.clone().into()]),
624        ))?;
625        let usize_custom_t = usize_custom_t(&Arc::downgrade(&PRELUDE));
626        let c = b.add_constant(Value::sum(
627            0,
628            [
629                CustomTestValue(usize_custom_t.clone()).into(),
630                ConstF64::new(5.1).into(),
631            ],
632            pred_ty.clone(),
633        )?);
634        let w = b.load_const(&c);
635        b.finish_hugr_with_outputs([w]).unwrap();
636
637        let mut b = DFGBuilder::new(Signature::new(
638            type_row![],
639            TypeRow::from(vec![pred_ty.clone().into()]),
640        ))?;
641        let c = b.add_constant(Value::sum(1, [], pred_ty.clone())?);
642        let w = b.load_const(&c);
643        b.finish_hugr_with_outputs([w]).unwrap();
644
645        Ok(())
646    }
647
648    #[test]
649    fn test_bad_sum() {
650        let pred_ty = SumType::new([vec![usize_t(), float64_type()].into(), type_row![]]);
651
652        let good_sum = const_usize();
653        println!("{}", serde_json::to_string_pretty(&good_sum).unwrap());
654
655        let good_sum =
656            Value::sum(0, [const_usize(), serialized_float(5.1)], pred_ty.clone()).unwrap();
657        println!("{}", serde_json::to_string_pretty(&good_sum).unwrap());
658
659        let res = Value::sum(0, [], pred_ty.clone());
660        assert_matches!(
661            res,
662            Err(ConstTypeError::SumType(SumTypeError::WrongVariantLength {
663                tag: 0,
664                expected: 2,
665                found: 0
666            }))
667        );
668
669        let res = Value::sum(4, [], pred_ty.clone());
670        assert_matches!(
671            res,
672            Err(ConstTypeError::SumType(SumTypeError::InvalidTag {
673                tag: 4,
674                num_variants: 2
675            }))
676        );
677
678        let res = Value::sum(0, [const_usize(), const_usize()], pred_ty);
679        assert_matches!(
680            res,
681            Err(ConstTypeError::SumType(SumTypeError::InvalidValueType {
682                tag: 0,
683                index: 1,
684                expected,
685                found,
686            })) if *expected == float64_type() && *found == const_usize()
687        );
688    }
689
690    #[rstest]
691    fn function_value(simple_dfg_hugr: Hugr) {
692        let v = Value::function(simple_dfg_hugr).unwrap();
693
694        let correct_type = Type::new_function(Signature::new_endo(vec![bool_t()]));
695
696        assert_eq!(v.get_type(), correct_type);
697        assert!(v.name().starts_with("const:function:"));
698    }
699
700    #[fixture]
701    fn const_usize() -> Value {
702        ConstUsize::new(257).into()
703    }
704
705    #[fixture]
706    fn const_serialized_usize() -> Value {
707        CustomSerialized::try_from_custom_const(ConstUsize::new(257))
708            .unwrap()
709            .into()
710    }
711
712    #[fixture]
713    fn const_tuple() -> Value {
714        Value::tuple([const_usize(), Value::true_val()])
715    }
716
717    /// Equivalent to [`const_tuple`], but uses a non-resolved opaque op for the usize element.
718    #[fixture]
719    fn const_tuple_serialized() -> Value {
720        Value::tuple([const_serialized_usize(), Value::true_val()])
721    }
722
723    #[fixture]
724    fn const_array_bool() -> Value {
725        ArrayValue::new(bool_t(), [Value::true_val(), Value::false_val()]).into()
726    }
727
728    #[fixture]
729    fn const_value_array_bool() -> Value {
730        VArrayValue::new(bool_t(), [Value::true_val(), Value::false_val()]).into()
731    }
732
733    #[fixture]
734    fn const_array_options() -> Value {
735        let some_true = Value::some([Value::true_val()]);
736        let none = Value::none(vec![bool_t()]);
737        let elem_ty = SumType::new_option(vec![bool_t()]);
738        ArrayValue::new(elem_ty.into(), [some_true, none]).into()
739    }
740
741    #[fixture]
742    fn const_value_array_options() -> Value {
743        let some_true = Value::some([Value::true_val()]);
744        let none = Value::none(vec![bool_t()]);
745        let elem_ty = SumType::new_option(vec![bool_t()]);
746        VArrayValue::new(elem_ty.into(), [some_true, none]).into()
747    }
748
749    #[rstest]
750    #[case(Value::unit(), Type::UNIT, "const:seq:{}")]
751    #[case(const_usize(), usize_t(), "const:custom:ConstUsize(")]
752    #[case(serialized_float(17.4), float64_type(), "const:custom:json:Object")]
753    #[case(const_tuple(), Type::new_tuple(vec![usize_t(), bool_t()]), "const:seq:{")]
754    #[case(const_array_bool(), array_type(2, bool_t()), "const:custom:array")]
755    #[case(
756        const_value_array_bool(),
757        value_array_type(2, bool_t()),
758        "const:custom:value_array"
759    )]
760    #[case(
761        const_array_options(),
762        array_type(2, SumType::new_option(vec![bool_t()]).into()),
763        "const:custom:array"
764    )]
765    #[case(
766        const_value_array_options(),
767        value_array_type(2, SumType::new_option(vec![bool_t()]).into()),
768        "const:custom:value_array"
769    )]
770    fn const_type(
771        #[case] const_value: Value,
772        #[case] expected_type: Type,
773        #[case] name_prefix: &str,
774    ) {
775        assert_eq!(const_value.get_type(), expected_type);
776        let name = const_value.name();
777        assert!(
778            name.starts_with(name_prefix),
779            "{name} does not start with {name_prefix}"
780        );
781    }
782
783    #[rstest]
784    #[case(Value::unit(), Value::unit())]
785    #[case(const_usize(), const_usize())]
786    #[case(const_serialized_usize(), const_usize())]
787    #[case(const_tuple_serialized(), const_tuple())]
788    #[case(const_array_bool(), const_array_bool())]
789    #[case(const_value_array_bool(), const_value_array_bool())]
790    #[case(const_array_options(), const_array_options())]
791    #[case(const_value_array_options(), const_value_array_options())]
792    // Opaque constants don't get resolved into concrete types when running miri,
793    // as the `typetag` machinery is not available.
794    #[cfg_attr(miri, ignore)]
795    fn const_serde_roundtrip(#[case] const_value: Value, #[case] expected_value: Value) {
796        let serialized = serde_json::to_string(&const_value).unwrap();
797        let deserialized: Value = serde_json::from_str(&serialized).unwrap();
798
799        assert_eq!(deserialized, expected_value);
800    }
801
802    #[rstest]
803    fn const_custom_value(const_usize: Value, const_tuple: Value) {
804        assert_eq!(
805            const_usize.get_custom_value::<ConstUsize>(),
806            Some(&ConstUsize::new(257))
807        );
808        assert_eq!(const_usize.get_custom_value::<ConstInt>(), None);
809        assert_eq!(const_tuple.get_custom_value::<ConstUsize>(), None);
810        assert_eq!(const_tuple.get_custom_value::<ConstInt>(), None);
811    }
812
813    #[test]
814    fn test_json_const() {
815        let ex_id: ExtensionId = "my_extension".try_into().unwrap();
816        let typ_int = CustomType::new(
817            "my_type",
818            vec![TypeArg::BoundedNat(8)],
819            ex_id.clone(),
820            TypeBound::Copyable,
821            // Dummy extension reference.
822            &Weak::default(),
823        );
824        let json_const: Value = CustomSerialized::new(typ_int.clone(), 6.into()).into();
825        let classic_t = Type::new_extension(typ_int.clone());
826        assert_matches!(classic_t.least_upper_bound(), TypeBound::Copyable);
827        assert_eq!(json_const.get_type(), classic_t);
828
829        let typ_qb = CustomType::new(
830            "my_type",
831            vec![],
832            ex_id,
833            TypeBound::Copyable,
834            &Weak::default(),
835        );
836        let t = Type::new_extension(typ_qb.clone());
837        assert_ne!(json_const.get_type(), t);
838    }
839
840    #[rstest]
841    fn hash_tuple(const_tuple: Value) {
842        let vals = [
843            Value::unit(),
844            Value::true_val(),
845            Value::false_val(),
846            ConstUsize::new(13).into(),
847            Value::tuple([ConstUsize::new(13).into()]),
848            Value::tuple([ConstUsize::new(13).into(), ConstUsize::new(14).into()]),
849            Value::tuple([ConstUsize::new(13).into(), ConstUsize::new(15).into()]),
850            const_tuple,
851        ];
852
853        let num_vals = vals.len();
854        let hashes = vals.map(|v| {
855            let mut h = DefaultHasher::new();
856            v.try_hash(&mut h).then_some(()).unwrap();
857            h.finish()
858        });
859        assert_eq!(HashSet::from(hashes).len(), num_vals); // all distinct
860    }
861
862    #[test]
863    fn unhashable_tuple() {
864        let tup = Value::tuple([ConstUsize::new(5).into(), ConstF64::new(4.97).into()]);
865        let mut h1 = DefaultHasher::new();
866        let r = tup.try_hash(&mut h1);
867        assert!(!r);
868
869        // Check that didn't do anything, by checking the hasher behaves
870        // just like one which never saw the tuple
871        h1.write_usize(5);
872        let mut h2 = DefaultHasher::new();
873        h2.write_usize(5);
874        assert_eq!(h1.finish(), h2.finish());
875    }
876
877    mod proptest {
878        use super::super::{OpaqueValue, Sum};
879        use crate::{
880            ops::{Value, constant::CustomSerialized},
881            std_extensions::arithmetic::int_types::ConstInt,
882            std_extensions::collections::list::ListValue,
883            types::{SumType, Type},
884        };
885        use ::proptest::{collection::vec, prelude::*};
886        impl Arbitrary for OpaqueValue {
887            type Parameters = ();
888            type Strategy = BoxedStrategy<Self>;
889            fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
890                // We intentionally do not include `ConstF64` because it does not
891                // roundtrip serialize
892                prop_oneof![
893                    any::<ConstInt>().prop_map_into(),
894                    any::<CustomSerialized>().prop_map_into()
895                ]
896                .prop_recursive(
897                    3,  // No more than 3 branch levels deep
898                    32, // Target around 32 total elements
899                    3,  // Each collection is up to 3 elements long
900                    |child_strat| {
901                        (any::<Type>(), vec(child_strat, 0..3)).prop_map(|(typ, children)| {
902                            Self::new(ListValue::new(
903                                typ,
904                                children.into_iter().map(|e| Value::Extension { e }),
905                            ))
906                        })
907                    },
908                )
909                .boxed()
910            }
911        }
912
913        impl Arbitrary for Value {
914            type Parameters = ();
915            type Strategy = BoxedStrategy<Self>;
916            fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
917                use ::proptest::collection::vec;
918                let leaf_strat = prop_oneof![
919                    any::<OpaqueValue>().prop_map(|e| Self::Extension { e }),
920                    crate::proptest::any_hugr().prop_map(|x| Value::function(x).unwrap())
921                ];
922                leaf_strat
923                    .prop_recursive(
924                        3,  // No more than 3 branch levels deep
925                        32, // Target around 32 total elements
926                        3,  // Each collection is up to 3 elements long
927                        |element| {
928                            prop_oneof![
929                                vec(element.clone(), 0..3).prop_map(Self::tuple),
930                                (
931                                    any::<usize>(),
932                                    vec(element.clone(), 0..3),
933                                    any_with::<SumType>(1.into()) // for speed: don't generate large sum types for now
934                                )
935                                    .prop_map(
936                                        |(tag, values, sum_type)| {
937                                            Self::Sum(Sum {
938                                                tag,
939                                                values,
940                                                sum_type,
941                                            })
942                                        }
943                                    ),
944                            ]
945                        },
946                    )
947                    .boxed()
948            }
949        }
950    }
951
952    #[test]
953    fn test_tuple_deserialize() {
954        let json = r#"
955        {
956    "v": "Tuple",
957    "vs": [
958        {
959            "v": "Sum",
960            "tag": 0,
961            "typ": {
962                "t": "Sum",
963                "s": "Unit",
964                "size": 1
965            },
966            "vs": []
967        },
968        {
969            "v": "Sum",
970            "tag": 1,
971            "typ": {
972                "t": "Sum",
973                "s": "General",
974                "rows": [
975                    [
976                        {
977                            "t": "Sum",
978                            "s": "Unit",
979                            "size": 1
980                        }
981                    ],
982                    [
983                        {
984                            "t": "Sum",
985                            "s": "Unit",
986                            "size": 2
987                        }
988                    ]
989                ]
990            },
991            "vs": [
992                {
993                    "v": "Sum",
994                    "tag": 1,
995                    "typ": {
996                        "t": "Sum",
997                        "s": "Unit",
998                        "size": 2
999                    },
1000                    "vs": []
1001                }
1002            ]
1003        }
1004    ]
1005}
1006        "#;
1007
1008        let v: Value = serde_json::from_str(json).unwrap();
1009        assert_eq!(
1010            v,
1011            Value::tuple([
1012                Value::unit(),
1013                Value::sum(
1014                    1,
1015                    [Value::true_val()],
1016                    SumType::new([
1017                        type_row![Type::UNIT],
1018                        vec![Value::true_val().get_type()].into()
1019                    ]),
1020                )
1021                .unwrap()
1022            ])
1023        );
1024    }
1025}