1mod custom;
4
5use std::borrow::Cow;
6use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher};
8
9use super::{NamedOp, OpName, OpTrait, StaticTag};
10use super::{OpTag, OpType};
11use crate::envelope::serde_with::AsStringEnvelope;
12use crate::types::{CustomType, EdgeKind, Signature, SumType, SumTypeError, Type, TypeRow};
13use crate::{Hugr, HugrView};
14
15use delegate::delegate;
16use itertools::Itertools;
17use serde::{Deserialize, Serialize};
18use serde_with::serde_as;
19use smol_str::SmolStr;
20use thiserror::Error;
21
22pub use custom::{
23 CustomConst, CustomSerialized, TryHash, downcast_equal_consts, get_pair_of_input_values,
24 get_single_input_value,
25};
26
27#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
28#[non_exhaustive]
32#[cfg_attr(test, derive(proptest_derive::Arbitrary))]
33pub struct Const {
34 #[serde(rename = "v")]
36 pub value: Value,
37}
38
39impl Const {
40 #[must_use]
42 pub fn new(value: Value) -> Self {
43 Self { value }
44 }
45
46 #[must_use]
48 pub fn value(&self) -> &Value {
49 &self.value
50 }
51
52 delegate! {
53 to self.value {
54 #[must_use] pub fn get_type(&self) -> Type;
56 #[must_use] pub fn get_custom_value<T: CustomConst>(&self) -> Option<&T>;
59
60 pub fn validate(&self) -> Result<(), ConstTypeError>;
62 }
63 }
64}
65
66impl From<Value> for Const {
67 fn from(value: Value) -> Self {
68 Self::new(value)
69 }
70}
71
72impl NamedOp for Const {
73 fn name(&self) -> OpName {
74 self.value().name()
75 }
76}
77
78impl StaticTag for Const {
79 const TAG: OpTag = OpTag::Const;
80}
81
82impl OpTrait for Const {
83 fn description(&self) -> &'static str {
84 "Constant value"
85 }
86
87 fn tag(&self) -> OpTag {
88 <Self as StaticTag>::TAG
89 }
90
91 fn static_output(&self) -> Option<EdgeKind> {
92 Some(EdgeKind::Const(self.get_type()))
93 }
94
95 }
97
98impl From<Const> for Value {
99 fn from(konst: Const) -> Self {
100 konst.value
101 }
102}
103
104impl AsRef<Value> for Const {
105 fn as_ref(&self) -> &Value {
106 self.value()
107 }
108}
109
110#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
111struct SerialSum {
112 #[serde(default)]
113 tag: usize,
114 #[serde(rename = "vs")]
115 values: Vec<Value>,
116 #[serde(default, rename = "typ")]
117 sum_type: Option<SumType>,
118}
119
120#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
121#[serde(try_from = "SerialSum")]
122#[serde(into = "SerialSum")]
123pub struct Sum {
126 pub tag: usize,
128 pub values: Vec<Value>,
132 pub sum_type: SumType,
134}
135
136impl Sum {
137 #[must_use]
139 pub fn as_tuple(&self) -> Option<&[Value]> {
140 self.sum_type.as_tuple().map(|_| self.values.as_ref())
142 }
143
144 fn try_hash<H: Hasher>(&self, st: &mut H) -> bool {
145 maybe_hash_values(&self.values, st) && {
146 st.write_usize(self.tag);
147 self.sum_type.hash(st);
148 true
149 }
150 }
151}
152
153pub(crate) fn maybe_hash_values<H: Hasher>(vals: &[Value], st: &mut H) -> bool {
154 let mut hasher = DefaultHasher::new();
157 vals.iter().all(|e| e.try_hash(&mut hasher)) && {
158 st.write_u64(hasher.finish());
159 true
160 }
161}
162
163impl TryFrom<SerialSum> for Sum {
164 type Error = &'static str;
165
166 fn try_from(value: SerialSum) -> Result<Self, Self::Error> {
167 let SerialSum {
168 tag,
169 values,
170 sum_type,
171 } = value;
172
173 let sum_type = if let Some(sum_type) = sum_type {
174 sum_type
175 } else {
176 if tag != 0 {
177 return Err("Sum type must be provided if tag is not 0");
178 }
179 SumType::new_tuple(values.iter().map(Value::get_type).collect_vec())
180 };
181
182 Ok(Self {
183 tag,
184 values,
185 sum_type,
186 })
187 }
188}
189
190impl From<Sum> for SerialSum {
191 fn from(value: Sum) -> Self {
192 Self {
193 tag: value.tag,
194 values: value.values,
195 sum_type: Some(value.sum_type),
196 }
197 }
198}
199
200#[serde_as]
201#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
202#[serde(tag = "v")]
203pub enum Value {
206 Extension {
208 #[serde(flatten)]
209 e: OpaqueValue,
211 },
212 Function {
214 #[serde_as(as = "Box<AsStringEnvelope>")]
216 hugr: Box<Hugr>,
217 },
218 #[serde(alias = "Tuple")]
221 Sum(Sum),
222}
223
224#[cfg_attr(not(miri), doc = "```")] #[cfg_attr(miri, doc = "```ignore")]
247#[derive(Debug, Clone, Serialize, Deserialize)]
272pub struct OpaqueValue {
273 #[serde(flatten, with = "self::custom::serde_extension_value")]
274 v: Box<dyn CustomConst>,
275}
276
277impl OpaqueValue {
278 pub fn new(cc: impl CustomConst) -> Self {
280 Self { v: Box::new(cc) }
281 }
282
283 #[must_use]
285 pub fn value(&self) -> &dyn CustomConst {
286 self.v.as_ref()
287 }
288
289 pub(crate) fn value_mut(&mut self) -> &mut dyn CustomConst {
291 self.v.as_mut()
292 }
293
294 delegate! {
295 to self.value() {
296 #[must_use] pub fn get_type(&self) -> Type;
298 #[must_use] pub fn name(&self) -> ValueName;
300 }
301 }
302}
303
304impl<CC: CustomConst> From<CC> for OpaqueValue {
305 fn from(x: CC) -> Self {
306 Self::new(x)
307 }
308}
309
310impl From<Box<dyn CustomConst>> for OpaqueValue {
311 fn from(value: Box<dyn CustomConst>) -> Self {
312 Self { v: value }
313 }
314}
315
316impl PartialEq for OpaqueValue {
317 fn eq(&self, other: &Self) -> bool {
318 self.value().equal_consts(other.value())
319 }
320}
321
322#[derive(Clone, Debug, PartialEq, Eq, Error)]
324#[non_exhaustive]
325pub enum CustomCheckFailure {
326 #[error("Expected type: {expected} but value was of type: {found}")]
328 TypeMismatch {
329 expected: Box<CustomType>,
331 found: Box<Type>,
333 },
334 #[error("{0}")]
336 Message(String),
337}
338
339#[derive(Clone, Debug, PartialEq, Error)]
341#[non_exhaustive]
342pub enum ConstTypeError {
343 #[error("{0}")]
345 SumType(#[from] SumTypeError),
346 #[error(
348 "A function constant cannot be defined using a Hugr with root of type {hugr_root_type}. Must be a monomorphic function."
349 )]
350 NotMonomorphicFunction {
351 hugr_root_type: Box<OpType>,
353 },
354 #[error("Value {1:?} does not match expected type {0}")]
356 ConstCheckFail(Box<Type>, Value),
357 #[error("Error when checking custom type: {0}")]
359 CustomCheckFail(#[from] CustomCheckFailure),
360}
361
362fn mono_fn_type(h: &Hugr) -> Result<Cow<'_, Signature>, ConstTypeError> {
364 let err = || ConstTypeError::NotMonomorphicFunction {
365 hugr_root_type: Box::new(h.entrypoint_optype().clone()),
366 };
367 if let Some(pf) = h.poly_func_type() {
368 match pf.try_into() {
369 Ok(sig) => return Ok(Cow::Owned(sig)),
370 Err(_) => return Err(err()),
371 };
372 }
373
374 h.inner_function_type().ok_or_else(err)
375}
376
377impl Value {
378 #[must_use]
380 pub fn get_type(&self) -> Type {
381 match self {
382 Self::Extension { e } => e.get_type(),
383 Self::Sum(Sum { sum_type, .. }) => sum_type.clone().into(),
384 Self::Function { hugr } => {
385 let func_type = mono_fn_type(hugr).unwrap_or_else(|e| panic!("{}", e));
386 Type::new_function(func_type.into_owned())
387 }
388 }
389 }
390
391 pub fn sum(
395 tag: usize,
396 items: impl IntoIterator<Item = Value>,
397 typ: SumType,
398 ) -> Result<Self, ConstTypeError> {
399 let values: Vec<Value> = items.into_iter().collect();
400 typ.check_type(tag, &values)?;
401 Ok(Self::Sum(Sum {
402 tag,
403 values,
404 sum_type: typ,
405 }))
406 }
407
408 pub fn tuple(items: impl IntoIterator<Item = Value>) -> Self {
410 let vs = items.into_iter().collect_vec();
411 let tys = vs.iter().map(Self::get_type).collect_vec();
412
413 Self::sum(0, vs, SumType::new_tuple(tys)).expect("Tuple type is valid")
414 }
415
416 pub fn function(hugr: impl Into<Hugr>) -> Result<Self, ConstTypeError> {
422 let hugr = hugr.into();
423 mono_fn_type(&hugr)?;
424 Ok(Self::Function {
425 hugr: Box::new(hugr),
426 })
427 }
428
429 #[must_use]
431 pub const fn unit() -> Self {
432 Self::Sum(Sum {
433 tag: 0,
434 values: vec![],
435 sum_type: SumType::Unit { size: 1 },
436 })
437 }
438
439 pub fn unit_sum(tag: usize, size: u8) -> Result<Self, ConstTypeError> {
441 Self::sum(tag, [], SumType::Unit { size })
442 }
443
444 #[must_use]
446 pub fn unary_unit_sum() -> Self {
447 Self::unit_sum(0, 1).expect("0 < 1")
448 }
449
450 #[must_use]
452 pub fn true_val() -> Self {
453 Self::unit_sum(1, 2).expect("1 < 2")
454 }
455
456 #[must_use]
458 pub fn false_val() -> Self {
459 Self::unit_sum(0, 2).expect("0 < 2")
460 }
461
462 pub fn some<V: Into<Value>>(values: impl IntoIterator<Item = V>) -> Self {
465 let values: Vec<Value> = values.into_iter().map(Into::into).collect_vec();
466 let value_types: Vec<Type> = values.iter().map(Value::get_type).collect_vec();
467 let sum_type = SumType::new_option(value_types);
468 Self::sum(1, values, sum_type).unwrap()
469 }
470
471 pub fn none(value_types: impl Into<TypeRow>) -> Self {
474 Self::sum(0, [], SumType::new_option(value_types)).unwrap()
475 }
476
477 #[must_use]
481 pub fn from_bool(b: bool) -> Self {
482 if b {
483 Self::true_val()
484 } else {
485 Self::false_val()
486 }
487 }
488
489 pub fn extension(custom_const: impl CustomConst) -> Self {
491 Self::Extension {
492 e: OpaqueValue::new(custom_const),
493 }
494 }
495
496 #[must_use]
498 pub fn get_custom_value<T: CustomConst>(&self) -> Option<&T> {
499 if let Self::Extension { e } = self {
500 e.v.downcast_ref()
501 } else {
502 None
503 }
504 }
505
506 fn name(&self) -> OpName {
507 match self {
508 Self::Extension { e } => format!("const:custom:{}", e.name()),
509 Self::Function { hugr: h } => {
510 let Ok(t) = mono_fn_type(h) else {
511 panic!("HUGR root node isn't a valid function parent.");
512 };
513 format!("const:function:[{t}]")
514 }
515 Self::Sum(Sum {
516 tag,
517 values,
518 sum_type,
519 }) => {
520 if sum_type.as_tuple().is_some() {
521 let names: Vec<_> = values.iter().map(Value::name).collect();
522 format!("const:seq:{{{}}}", names.iter().join(", "))
523 } else {
524 format!("const:sum:{{tag:{tag}, vals:{values:?}}}")
525 }
526 }
527 }
528 .into()
529 }
530
531 pub fn validate(&self) -> Result<(), ConstTypeError> {
533 match self {
534 Self::Extension { e } => Ok(e.value().validate()?),
535 Self::Function { hugr } => {
536 mono_fn_type(hugr)?;
537 Ok(())
538 }
539 Self::Sum(Sum {
540 tag,
541 values,
542 sum_type,
543 }) => {
544 sum_type.check_type(*tag, values)?;
545 Ok(())
546 }
547 }
548 }
549
550 #[must_use]
552 pub fn as_tuple(&self) -> Option<&[Value]> {
553 if let Self::Sum(sum) = self {
554 sum.as_tuple()
555 } else {
556 None
557 }
558 }
559
560 pub fn try_hash<H: Hasher>(&self, st: &mut H) -> bool {
564 match self {
565 Value::Extension { e } => e.value().try_hash(&mut *st),
566 Value::Function { .. } => false,
567 Value::Sum(s) => s.try_hash(st),
568 }
569 }
570}
571
572impl<T> From<T> for Value
573where
574 T: CustomConst,
575{
576 fn from(value: T) -> Self {
577 Self::extension(value)
578 }
579}
580
581pub type ValueName = SmolStr;
583
584pub type ValueNameRef = str;
586
587#[cfg(test)]
588pub(crate) mod test {
589 use std::collections::HashSet;
590 use std::sync::{Arc, Weak};
591
592 use super::Value;
593 use crate::builder::inout_sig;
594 use crate::builder::test::simple_dfg_hugr;
595 use crate::extension::PRELUDE;
596 use crate::extension::prelude::{bool_t, usize_custom_t};
597 use crate::extension::resolution::{
598 ExtensionResolutionError, WeakExtensionRegistry, resolve_custom_type_extensions,
599 resolve_typearg_extensions,
600 };
601 use crate::std_extensions::arithmetic::int_types::ConstInt;
602 use crate::std_extensions::collections::array::{ArrayValue, array_type};
603 use crate::std_extensions::collections::value_array::{VArrayValue, value_array_type};
604 use crate::{
605 builder::{BuildError, DFGBuilder, Dataflow, DataflowHugr},
606 extension::{
607 ExtensionId,
608 prelude::{ConstUsize, usize_t},
609 },
610 std_extensions::arithmetic::float_types::{ConstF64, float64_type},
611 type_row,
612 types::type_param::TypeArg,
613 types::{Type, TypeBound, TypeRow},
614 };
615 use cool_asserts::assert_matches;
616 use rstest::{fixture, rstest};
617
618 use super::*;
619
620 #[derive(Debug, Clone, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
621 pub(crate) struct CustomTestValue(pub CustomType);
623
624 #[typetag::serde]
625 impl CustomConst for CustomTestValue {
626 fn name(&self) -> ValueName {
627 format!("CustomTestValue({:?})", self.0).into()
628 }
629
630 fn update_extensions(
631 &mut self,
632 extensions: &WeakExtensionRegistry,
633 ) -> Result<(), ExtensionResolutionError> {
634 resolve_custom_type_extensions(&mut self.0, extensions)?;
635 for arg in self.0.args_mut() {
638 resolve_typearg_extensions(arg, extensions)?;
639 }
640 Ok(())
641 }
642
643 fn get_type(&self) -> Type {
644 self.0.clone().into()
645 }
646
647 fn equal_consts(&self, other: &dyn CustomConst) -> bool {
648 crate::ops::constant::downcast_equal_consts(self, other)
649 }
650 }
651
652 pub(crate) fn serialized_float(f: f64) -> Value {
654 CustomSerialized::try_from_custom_const(ConstF64::new(f))
655 .unwrap()
656 .into()
657 }
658
659 #[test]
661 fn test_sum() -> Result<(), BuildError> {
662 use crate::builder::Container;
663 let pred_rows = vec![vec![usize_t(), float64_type()].into(), Type::EMPTY_TYPEROW];
664 let pred_ty = SumType::new(pred_rows.clone());
665
666 let mut b = DFGBuilder::new(inout_sig(
667 type_row![],
668 TypeRow::from(vec![pred_ty.clone().into()]),
669 ))?;
670 let usize_custom_t = usize_custom_t(&Arc::downgrade(&PRELUDE));
671 let c = b.add_constant(Value::sum(
672 0,
673 [
674 CustomTestValue(usize_custom_t.clone()).into(),
675 ConstF64::new(5.1).into(),
676 ],
677 pred_ty.clone(),
678 )?);
679 let w = b.load_const(&c);
680 b.finish_hugr_with_outputs([w]).unwrap();
681
682 let mut b = DFGBuilder::new(Signature::new(
683 type_row![],
684 TypeRow::from(vec![pred_ty.clone().into()]),
685 ))?;
686 let c = b.add_constant(Value::sum(1, [], pred_ty.clone())?);
687 let w = b.load_const(&c);
688 b.finish_hugr_with_outputs([w]).unwrap();
689
690 Ok(())
691 }
692
693 #[test]
694 fn test_bad_sum() {
695 let pred_ty = SumType::new([vec![usize_t(), float64_type()].into(), type_row![]]);
696
697 let good_sum = const_usize();
698 println!("{}", serde_json::to_string_pretty(&good_sum).unwrap());
699
700 let good_sum =
701 Value::sum(0, [const_usize(), serialized_float(5.1)], pred_ty.clone()).unwrap();
702 println!("{}", serde_json::to_string_pretty(&good_sum).unwrap());
703
704 let res = Value::sum(0, [], pred_ty.clone());
705 assert_matches!(
706 res,
707 Err(ConstTypeError::SumType(SumTypeError::WrongVariantLength {
708 tag: 0,
709 expected: 2,
710 found: 0
711 }))
712 );
713
714 let res = Value::sum(4, [], pred_ty.clone());
715 assert_matches!(
716 res,
717 Err(ConstTypeError::SumType(SumTypeError::InvalidTag {
718 tag: 4,
719 num_variants: 2
720 }))
721 );
722
723 let res = Value::sum(0, [const_usize(), const_usize()], pred_ty);
724 assert_matches!(
725 res,
726 Err(ConstTypeError::SumType(SumTypeError::InvalidValueType {
727 tag: 0,
728 index: 1,
729 expected,
730 found,
731 })) if *expected == float64_type() && *found == const_usize()
732 );
733 }
734
735 #[rstest]
736 fn function_value(simple_dfg_hugr: Hugr) {
737 let v = Value::function(simple_dfg_hugr).unwrap();
738
739 let correct_type = Type::new_function(Signature::new_endo(vec![bool_t()]));
740
741 assert_eq!(v.get_type(), correct_type);
742 assert!(v.name().starts_with("const:function:"));
743 }
744
745 #[fixture]
746 fn const_usize() -> Value {
747 ConstUsize::new(257).into()
748 }
749
750 #[fixture]
751 fn const_serialized_usize() -> Value {
752 CustomSerialized::try_from_custom_const(ConstUsize::new(257))
753 .unwrap()
754 .into()
755 }
756
757 #[fixture]
758 fn const_tuple() -> Value {
759 Value::tuple([const_usize(), Value::true_val()])
760 }
761
762 #[fixture]
764 fn const_tuple_serialized() -> Value {
765 Value::tuple([const_serialized_usize(), Value::true_val()])
766 }
767
768 #[fixture]
769 fn const_array_bool() -> Value {
770 ArrayValue::new(bool_t(), [Value::true_val(), Value::false_val()]).into()
771 }
772
773 #[fixture]
774 fn const_value_array_bool() -> Value {
775 VArrayValue::new(bool_t(), [Value::true_val(), Value::false_val()]).into()
776 }
777
778 #[fixture]
779 fn const_array_options() -> Value {
780 let some_true = Value::some([Value::true_val()]);
781 let none = Value::none(vec![bool_t()]);
782 let elem_ty = SumType::new_option(vec![bool_t()]);
783 ArrayValue::new(elem_ty.into(), [some_true, none]).into()
784 }
785
786 #[fixture]
787 fn const_value_array_options() -> Value {
788 let some_true = Value::some([Value::true_val()]);
789 let none = Value::none(vec![bool_t()]);
790 let elem_ty = SumType::new_option(vec![bool_t()]);
791 VArrayValue::new(elem_ty.into(), [some_true, none]).into()
792 }
793
794 #[rstest]
795 #[case(Value::unit(), Type::UNIT, "const:seq:{}")]
796 #[case(const_usize(), usize_t(), "const:custom:ConstUsize(")]
797 #[case(serialized_float(17.4), float64_type(), "const:custom:json:Object")]
798 #[case(const_tuple(), Type::new_tuple(vec![usize_t(), bool_t()]), "const:seq:{")]
799 #[case(const_array_bool(), array_type(2, bool_t()), "const:custom:array")]
800 #[case(
801 const_value_array_bool(),
802 value_array_type(2, bool_t()),
803 "const:custom:value_array"
804 )]
805 #[case(
806 const_array_options(),
807 array_type(2, SumType::new_option(vec![bool_t()]).into()),
808 "const:custom:array"
809 )]
810 #[case(
811 const_value_array_options(),
812 value_array_type(2, SumType::new_option(vec![bool_t()]).into()),
813 "const:custom:value_array"
814 )]
815 fn const_type(
816 #[case] const_value: Value,
817 #[case] expected_type: Type,
818 #[case] name_prefix: &str,
819 ) {
820 assert_eq!(const_value.get_type(), expected_type);
821 let name = const_value.name();
822 assert!(
823 name.starts_with(name_prefix),
824 "{name} does not start with {name_prefix}"
825 );
826 }
827
828 #[rstest]
829 #[case(Value::unit(), Value::unit())]
830 #[case(const_usize(), const_usize())]
831 #[case(const_serialized_usize(), const_usize())]
832 #[case(const_tuple_serialized(), const_tuple())]
833 #[case(const_array_bool(), const_array_bool())]
834 #[case(const_value_array_bool(), const_value_array_bool())]
835 #[case(const_array_options(), const_array_options())]
836 #[case(const_value_array_options(), const_value_array_options())]
837 #[cfg_attr(miri, ignore)]
840 fn const_serde_roundtrip(#[case] const_value: Value, #[case] expected_value: Value) {
841 let serialized = serde_json::to_string(&const_value).unwrap();
842 let deserialized: Value = serde_json::from_str(&serialized).unwrap();
843
844 assert_eq!(deserialized, expected_value);
845 }
846
847 #[rstest]
848 fn const_custom_value(const_usize: Value, const_tuple: Value) {
849 assert_eq!(
850 const_usize.get_custom_value::<ConstUsize>(),
851 Some(&ConstUsize::new(257))
852 );
853 assert_eq!(const_usize.get_custom_value::<ConstInt>(), None);
854 assert_eq!(const_tuple.get_custom_value::<ConstUsize>(), None);
855 assert_eq!(const_tuple.get_custom_value::<ConstInt>(), None);
856 }
857
858 #[test]
859 fn test_json_const() {
860 let ex_id: ExtensionId = "my_extension".try_into().unwrap();
861 let typ_int = CustomType::new(
862 "my_type",
863 vec![TypeArg::BoundedNat(8)],
864 ex_id.clone(),
865 TypeBound::Copyable,
866 &Weak::default(),
868 );
869 let json_const: Value = CustomSerialized::new(typ_int.clone(), 6.into()).into();
870 let classic_t = Type::new_extension(typ_int.clone());
871 assert_matches!(classic_t.least_upper_bound(), TypeBound::Copyable);
872 assert_eq!(json_const.get_type(), classic_t);
873
874 let typ_qb = CustomType::new(
875 "my_type",
876 vec![],
877 ex_id,
878 TypeBound::Copyable,
879 &Weak::default(),
880 );
881 let t = Type::new_extension(typ_qb.clone());
882 assert_ne!(json_const.get_type(), t);
883 }
884
885 #[rstest]
886 fn hash_tuple(const_tuple: Value) {
887 let vals = [
888 Value::unit(),
889 Value::true_val(),
890 Value::false_val(),
891 ConstUsize::new(13).into(),
892 Value::tuple([ConstUsize::new(13).into()]),
893 Value::tuple([ConstUsize::new(13).into(), ConstUsize::new(14).into()]),
894 Value::tuple([ConstUsize::new(13).into(), ConstUsize::new(15).into()]),
895 const_tuple,
896 ];
897
898 let num_vals = vals.len();
899 let hashes = vals.map(|v| {
900 let mut h = DefaultHasher::new();
901 v.try_hash(&mut h).then_some(()).unwrap();
902 h.finish()
903 });
904 assert_eq!(HashSet::from(hashes).len(), num_vals); }
906
907 #[test]
908 fn unhashable_tuple() {
909 let tup = Value::tuple([ConstUsize::new(5).into(), ConstF64::new(4.97).into()]);
910 let mut h1 = DefaultHasher::new();
911 let r = tup.try_hash(&mut h1);
912 assert!(!r);
913
914 h1.write_usize(5);
917 let mut h2 = DefaultHasher::new();
918 h2.write_usize(5);
919 assert_eq!(h1.finish(), h2.finish());
920 }
921
922 mod proptest {
923 use super::super::{OpaqueValue, Sum};
924 use crate::{
925 ops::{Value, constant::CustomSerialized},
926 std_extensions::arithmetic::int_types::ConstInt,
927 std_extensions::collections::list::ListValue,
928 types::{SumType, Type},
929 };
930 use ::proptest::{collection::vec, prelude::*};
931 impl Arbitrary for OpaqueValue {
932 type Parameters = ();
933 type Strategy = BoxedStrategy<Self>;
934 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
935 prop_oneof![
938 any::<ConstInt>().prop_map_into(),
939 any::<CustomSerialized>().prop_map_into()
940 ]
941 .prop_recursive(
942 3, 32, 3, |child_strat| {
946 (any::<Type>(), vec(child_strat, 0..3)).prop_map(|(typ, children)| {
947 Self::new(ListValue::new(
948 typ,
949 children.into_iter().map(|e| Value::Extension { e }),
950 ))
951 })
952 },
953 )
954 .boxed()
955 }
956 }
957
958 impl Arbitrary for Value {
959 type Parameters = ();
960 type Strategy = BoxedStrategy<Self>;
961 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
962 use ::proptest::collection::vec;
963 let leaf_strat = prop_oneof![
964 any::<OpaqueValue>().prop_map(|e| Self::Extension { e }),
965 crate::proptest::any_hugr().prop_map(|x| Value::function(x).unwrap())
966 ];
967 leaf_strat
968 .prop_recursive(
969 3, 32, 3, |element| {
973 prop_oneof![
974 vec(element.clone(), 0..3).prop_map(Self::tuple),
975 (
976 any::<usize>(),
977 vec(element.clone(), 0..3),
978 any_with::<SumType>(1.into()) )
980 .prop_map(
981 |(tag, values, sum_type)| {
982 Self::Sum(Sum {
983 tag,
984 values,
985 sum_type,
986 })
987 }
988 ),
989 ]
990 },
991 )
992 .boxed()
993 }
994 }
995 }
996
997 #[test]
998 fn test_tuple_deserialize() {
999 let json = r#"
1000 {
1001 "v": "Tuple",
1002 "vs": [
1003 {
1004 "v": "Sum",
1005 "tag": 0,
1006 "typ": {
1007 "t": "Sum",
1008 "s": "Unit",
1009 "size": 1
1010 },
1011 "vs": []
1012 },
1013 {
1014 "v": "Sum",
1015 "tag": 1,
1016 "typ": {
1017 "t": "Sum",
1018 "s": "General",
1019 "rows": [
1020 [
1021 {
1022 "t": "Sum",
1023 "s": "Unit",
1024 "size": 1
1025 }
1026 ],
1027 [
1028 {
1029 "t": "Sum",
1030 "s": "Unit",
1031 "size": 2
1032 }
1033 ]
1034 ]
1035 },
1036 "vs": [
1037 {
1038 "v": "Sum",
1039 "tag": 1,
1040 "typ": {
1041 "t": "Sum",
1042 "s": "Unit",
1043 "size": 2
1044 },
1045 "vs": []
1046 }
1047 ]
1048 }
1049 ]
1050}
1051 "#;
1052
1053 let v: Value = serde_json::from_str(json).unwrap();
1054 assert_eq!(
1055 v,
1056 Value::tuple([
1057 Value::unit(),
1058 Value::sum(
1059 1,
1060 [Value::true_val()],
1061 SumType::new([
1062 type_row![Type::UNIT],
1063 vec![Value::true_val().get_type()].into()
1064 ]),
1065 )
1066 .unwrap()
1067 ])
1068 );
1069 }
1070}