1mod custom;
4mod serialize;
5
6use std::borrow::Cow;
7use std::collections::hash_map::DefaultHasher; use 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#[non_exhaustive]
34#[cfg_attr(test, derive(proptest_derive::Arbitrary))]
35pub struct Const {
36 #[serde(rename = "v")]
38 pub value: Value,
39}
40
41impl Const {
42 #[must_use]
44 pub fn new(value: Value) -> Self {
45 Self { value }
46 }
47
48 #[must_use]
50 pub fn value(&self) -> &Value {
51 &self.value
52 }
53
54 delegate! {
55 to self.value {
56 #[must_use] pub fn get_type(&self) -> Type;
58 #[must_use] pub fn get_custom_value<T: CustomConst>(&self) -> Option<&T>;
61
62 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 }
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")]
115pub struct Sum {
118 pub tag: usize,
120 pub values: Vec<Value>,
124 pub sum_type: SumType,
126}
127
128impl Sum {
129 #[must_use]
131 pub fn as_tuple(&self) -> Option<&[Value]> {
132 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 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")]
158pub enum Value {
161 Extension {
163 #[serde(flatten)]
164 e: OpaqueValue,
166 },
167 Function {
169 #[serde_as(as = "Box<AsStringEnvelope>")]
171 hugr: Box<Hugr>,
172 },
173 #[serde(alias = "Tuple")]
176 Sum(Sum),
177}
178
179#[cfg_attr(not(miri), doc = "```")] #[cfg_attr(miri, doc = "```ignore")]
202#[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 pub fn new(cc: impl CustomConst) -> Self {
235 Self { v: Box::new(cc) }
236 }
237
238 #[must_use]
240 pub fn value(&self) -> &dyn CustomConst {
241 self.v.as_ref()
242 }
243
244 pub(crate) fn value_mut(&mut self) -> &mut dyn CustomConst {
246 self.v.as_mut()
247 }
248
249 delegate! {
250 to self.value() {
251 #[must_use] pub fn get_type(&self) -> Type;
253 #[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#[derive(Clone, Debug, PartialEq, Eq, Error)]
279#[non_exhaustive]
280pub enum CustomCheckFailure {
281 #[error("Expected type: {expected} but value was of type: {found}")]
283 TypeMismatch {
284 expected: Box<CustomType>,
286 found: Box<Type>,
288 },
289 #[error("{0}")]
291 Message(String),
292}
293
294#[derive(Clone, Debug, PartialEq, Error)]
296#[non_exhaustive]
297pub enum ConstTypeError {
298 #[error("{0}")]
300 SumType(#[from] SumTypeError),
301 #[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 hugr_root_type: Box<OpType>,
308 },
309 #[error("Value {1:?} does not match expected type {0}")]
311 ConstCheckFail(Box<Type>, Value),
312 #[error("Error when checking custom type: {0}")]
314 CustomCheckFail(#[from] CustomCheckFailure),
315}
316
317fn 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 #[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 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 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 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 #[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 pub fn unit_sum(tag: usize, size: u8) -> Result<Self, ConstTypeError> {
396 Self::sum(tag, [], SumType::Unit { size })
397 }
398
399 #[must_use]
401 pub fn unary_unit_sum() -> Self {
402 Self::unit_sum(0, 1).expect("0 < 1")
403 }
404
405 #[must_use]
407 pub fn true_val() -> Self {
408 Self::unit_sum(1, 2).expect("1 < 2")
409 }
410
411 #[must_use]
413 pub fn false_val() -> Self {
414 Self::unit_sum(0, 2).expect("0 < 2")
415 }
416
417 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 pub fn none(value_types: impl Into<TypeRow>) -> Self {
429 Self::sum(0, [], SumType::new_option(value_types)).unwrap()
430 }
431
432 #[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 pub fn extension(custom_const: impl CustomConst) -> Self {
446 Self::Extension {
447 e: OpaqueValue::new(custom_const),
448 }
449 }
450
451 #[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 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 #[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 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
536pub type ValueName = SmolStr;
538
539pub 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 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 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 pub(crate) fn serialized_float(f: f64) -> Value {
609 CustomSerialized::try_from_custom_const(ConstF64::new(f))
610 .unwrap()
611 .into()
612 }
613
614 #[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 #[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 #[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 &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); }
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 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 prop_oneof![
893 any::<ConstInt>().prop_map_into(),
894 any::<CustomSerialized>().prop_map_into()
895 ]
896 .prop_recursive(
897 3, 32, 3, |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, 32, 3, |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()) )
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}