From c45e6fc67334768ea55c4bd5223af0b7b0cc47ec Mon Sep 17 00:00:00 2001 From: doug-q <141026920+doug-q@users.noreply.github.com> Date: Thu, 9 May 2024 17:22:18 +0100 Subject: [PATCH] feat!: Update serialisation schema, implement `CustomConst` serialisation (#1005) We fix `model_rebuild` in `tys.py` to update the `model_config` rather than overwrite it. This prevents our config, i.e. `json_scheme_extra.required` from being removed during a `model_rebuild`. We remove most `json_scheme_extra.required` from the schema, using these only for `RootModel`s. This allows us to remove `TaggedSumType`, as well as alleviating us from the need of introducing `TaggedOpaqueType`. The serialisation schema is updated, and is `proptest`ed in #981. Reviewers should verify that the `serde` annotations, the pydantic schema definition, and the generated schemas exactly match that branch. BREAKING CHANGE: * Serialization schema * `Const::const_type` and `Value::const_type` are renamed to `Const::get_type` and `Value::get_type`. These now match several other `get_type` functions. --------- Co-authored-by: Craig Roy --- hugr-py/src/hugr/serialization/ops.py | 71 ++-- .../src/hugr/serialization/testing_hugr.py | 3 - hugr-py/src/hugr/serialization/tys.py | 48 +-- hugr/src/algorithm/const_fold.rs | 4 +- hugr/src/builder.rs | 1 - hugr/src/builder/build_traits.rs | 2 +- hugr/src/builder/cfg.rs | 38 +- hugr/src/builder/handle.rs | 1 - hugr/src/builder/tail_loop.rs | 2 +- hugr/src/extension/declarative.rs | 1 - hugr/src/lib.rs | 1 - hugr/src/macros.rs | 12 +- hugr/src/ops/constant.rs | 197 +++++++--- hugr/src/ops/constant/custom.rs | 354 +++++++++++++++++- hugr/src/ops/handle.rs | 1 - hugr/src/std_extensions/collections.rs | 2 +- hugr/src/std_extensions/logic.rs | 2 +- hugr/src/types.rs | 3 - hugr/src/types/check.rs | 4 +- hugr/src/types/serialize.rs | 12 +- specification/schema/.gitattributes | 2 +- .../schema/hugr_schema_strict_v1.json | 161 ++++---- specification/schema/hugr_schema_v1.json | 161 ++++---- .../schema/testing_hugr_schema_strict_v1.json | 161 ++++---- .../schema/testing_hugr_schema_v1.json | 161 ++++---- 25 files changed, 957 insertions(+), 448 deletions(-) diff --git a/hugr-py/src/hugr/serialization/ops.py b/hugr-py/src/hugr/serialization/ops.py index b818602b0..9ce52e5bd 100644 --- a/hugr-py/src/hugr/serialization/ops.py +++ b/hugr-py/src/hugr/serialization/ops.py @@ -70,44 +70,33 @@ class FuncDecl(BaseOp): signature: PolyFuncType -CustomConst = Any # TODO +class CustomConst(ConfiguredBaseModel): + c: str + v: Any class ExtensionValue(ConfiguredBaseModel): """An extension constant value, that can check it is of a given [CustomType].""" - c: Literal["Extension"] = Field("Extension", title="ValueTag") - e: CustomConst = Field(title="CustomConst") - - class Config: - json_schema_extra = { - "required": ["c", "e"], - } + v: Literal["Extension"] = Field("Extension", title="ValueTag") + extensions: ExtensionSet + typ: Type + value: CustomConst class FunctionValue(ConfiguredBaseModel): """A higher-order function value.""" - c: Literal["Function"] = Field("Function", title="ValueTag") + v: Literal["Function"] = Field("Function", title="ValueTag") hugr: Any # TODO - class Config: - json_schema_extra = { - "required": ["c", "hugr"], - } - class TupleValue(ConfiguredBaseModel): """A constant tuple value.""" - c: Literal["Tuple"] = Field("Tuple", title="ValueTag") + v: Literal["Tuple"] = Field("Tuple", title="ValueTag") vs: list["Value"] - class Config: - json_schema_extra = { - "required": ["c", "vs"], - } - class SumValue(ConfiguredBaseModel): """A Sum variant @@ -115,7 +104,7 @@ class SumValue(ConfiguredBaseModel): For any Sum type where this value meets the type of the variant indicated by the tag """ - c: Literal["Sum"] = Field("Sum", title="ValueTag") + v: Literal["Sum"] = Field("Sum", title="ValueTag") tag: int typ: SumType vs: list["Value"] @@ -127,7 +116,6 @@ class Config: "A Sum variant For any Sum type where this value meets the type " "of the variant indicated by the tag." ), - "required": ["c", "tag", "typ", "vs"], } @@ -135,9 +123,12 @@ class Value(RootModel): """A constant Value.""" root: ExtensionValue | FunctionValue | TupleValue | SumValue = Field( - discriminator="c" + discriminator="v" ) + class Config: + json_schema_extra = {"required": ["v"]} + class Const(BaseOp): """A Const operation definition.""" @@ -145,11 +136,6 @@ class Const(BaseOp): op: Literal["Const"] = "Const" v: Value = Field() - class Config: - json_schema_extra = { - "required": ["op", "parent", "v"], - } - # ----------------------------------------------- # --------------- BasicBlock types ------------------ @@ -163,7 +149,7 @@ class DataflowBlock(BaseOp): op: Literal["DataflowBlock"] = "DataflowBlock" inputs: TypeRow = Field(default_factory=list) other_outputs: TypeRow = Field(default_factory=list) - sum_rows: list[TypeRow] = Field(default_factory=list) + sum_rows: list[TypeRow] extension_delta: ExtensionSet = Field(default_factory=list) def insert_port_types(self, in_types: TypeRow, out_types: TypeRow) -> None: @@ -173,26 +159,18 @@ def insert_port_types(self, in_types: TypeRow, out_types: TypeRow) -> None: def insert_child_dfg_signature(self, inputs: TypeRow, outputs: TypeRow) -> None: self.inputs = inputs pred = outputs[0].root - assert isinstance(pred, tys.TaggedSumType) - if isinstance(pred.st.root, tys.UnitSum): - self.sum_rows = [[] for _ in range(pred.st.root.size)] + assert isinstance(pred, tys.SumType) + if isinstance(pred.root, tys.UnitSum): + self.sum_rows = [[] for _ in range(pred.root.size)] else: self.sum_rows = [] - for variant in pred.st.root.rows: + for variant in pred.root.rows: self.sum_rows.append(variant) self.other_outputs = outputs[1:] class Config: # Needed to avoid random '\n's in the pydantic description json_schema_extra = { - "required": [ - "parent", - "op", - "inputs", - "other_outputs", - "sum_rows", - "extension_delta", - ], "description": "A CFG basic block node. The signature is that of the internal Dataflow graph.", } @@ -205,9 +183,9 @@ class ExitBlock(BaseOp): cfg_outputs: TypeRow class Config: - # Needed to avoid random '\n's in the pydantic description json_schema_extra = { - "description": "The single exit node of the CFG, has no children, stores the types of the CFG node output." + # Needed to avoid random '\n's in the pydantic description + "description": "The single exit node of the CFG, has no children, stores the types of the CFG node output.", } @@ -334,8 +312,8 @@ def insert_port_types(self, in_types: TypeRow, out_types: TypeRow) -> None: # First port is a predicate, i.e. a sum of tuple types. We need to unpack # those into a list of type rows pred = in_types[0] - assert isinstance(pred.root, tys.TaggedSumType) - sum = pred.root.st.root + assert isinstance(pred.root, tys.SumType) + sum = pred.root.root if isinstance(sum, tys.UnitSum): self.sum_rows = [[] for _ in range(sum.size)] else: @@ -513,6 +491,9 @@ class OpType(RootModel): | AliasDefn ) = Field(discriminator="op") + class Config: + json_schema_extra = {"required": ["parent", "op"]} + # -------------------------------------- # --------------- OpDef ---------------- diff --git a/hugr-py/src/hugr/serialization/testing_hugr.py b/hugr-py/src/hugr/serialization/testing_hugr.py index 59db4b80d..bd10146b3 100644 --- a/hugr-py/src/hugr/serialization/testing_hugr.py +++ b/hugr-py/src/hugr/serialization/testing_hugr.py @@ -25,6 +25,3 @@ def _pydantic_rebuild(cls, config: ConfigDict = ConfigDict(), **kwargs): my_classes = dict(ops_classes) my_classes[cls.__name__] = cls model_rebuild(my_classes, config=config, **kwargs) - - class Config: - title = "HugrTesting" diff --git a/hugr-py/src/hugr/serialization/tys.py b/hugr-py/src/hugr/serialization/tys.py index c9f116af5..86078fc27 100644 --- a/hugr-py/src/hugr/serialization/tys.py +++ b/hugr-py/src/hugr/serialization/tys.py @@ -48,8 +48,8 @@ class ConfiguredBaseModel(BaseModel): model_config = default_model_config @classmethod - def set_model_config(cls, config: ConfigDict): - cls.model_config = config + def update_model_config(cls, config: ConfigDict): + cls.model_config.update(config) # -------------------------------------------- @@ -99,6 +99,9 @@ class TypeParam(RootModel): WrapValidator(_json_custom_error_validator), ] = Field(discriminator="tp") + class Config: + json_schema_extra = {"required": ["tp"]} + # ------------------------------------------ # --------------- TypeArg ------------------ @@ -150,6 +153,9 @@ class TypeArg(RootModel): WrapValidator(_json_custom_error_validator), ] = Field(discriminator="tya") + class Config: + json_schema_extra = {"required": ["tya"]} + # -------------------------------------------- # --------------- Container ------------------ @@ -170,6 +176,7 @@ class Array(MultiContainer): class UnitSum(ConfiguredBaseModel): """Simple sum type where all variants are empty tuples.""" + t: Literal["Sum"] = "Sum" s: Literal["Unit"] = "Unit" size: int @@ -177,17 +184,21 @@ class UnitSum(ConfiguredBaseModel): class GeneralSum(ConfiguredBaseModel): """General sum type that explicitly stores the types of the variants.""" + t: Literal["Sum"] = "Sum" s: Literal["General"] = "General" rows: list["TypeRow"] class SumType(RootModel): - root: Union[UnitSum, GeneralSum] = Field(discriminator="s") + root: Annotated[Union[UnitSum, GeneralSum], Field(discriminator="s")] + # This seems to be required for nested discriminated unions to work + @property + def t(self) -> str: + return self.root.t -class TaggedSumType(ConfiguredBaseModel): - t: Literal["Sum"] = "Sum" - st: SumType + class Config: + json_schema_extra = {"required": ["s"]} # ---------------------------------------------- @@ -280,17 +291,13 @@ def join(*bs: "TypeBound") -> "TypeBound": class Opaque(ConfiguredBaseModel): """An opaque Type that can be downcasted by the extensions that define it.""" + t: Literal["Opaque"] = "Opaque" extension: ExtensionId id: str # Unique identifier of the opaque type. args: list[TypeArg] bound: TypeBound -class TaggedOpaque(ConfiguredBaseModel): - t: Literal["Opaque"] = "Opaque" - o: Opaque - - class Alias(ConfiguredBaseModel): """An Alias Type""" @@ -314,16 +321,13 @@ class Type(RootModel): """A HUGR type.""" root: Annotated[ - Qubit - | Variable - | USize - | FunctionType - | Array - | TaggedSumType - | TaggedOpaque - | Alias, + Qubit | Variable | USize | FunctionType | Array | SumType | Opaque | Alias, WrapValidator(_json_custom_error_validator), - ] = Field(discriminator="t") + Field(discriminator="t"), + ] + + class Config: + json_schema_extra = {"required": ["t"]} # ------------------------------------------- @@ -365,11 +369,9 @@ def model_rebuild( config: ConfigDict = ConfigDict(), **kwargs, ): - new_config = default_model_config.copy() - new_config.update(config) for c in classes.values(): if issubclass(c, ConfiguredBaseModel): - c.set_model_config(new_config) + c.update_model_config(config) c.model_rebuild(**kwargs) diff --git a/hugr/src/algorithm/const_fold.rs b/hugr/src/algorithm/const_fold.rs index 46ca3b499..884002307 100644 --- a/hugr/src/algorithm/const_fold.rs +++ b/hugr/src/algorithm/const_fold.rs @@ -75,7 +75,7 @@ pub fn fold_leaf_op(op: &OpType, consts: &[(IncomingPort, Value)]) -> ConstFoldR /// Generate a graph that loads and outputs `consts` in order, validating /// against `reg`. fn const_graph(consts: Vec, reg: &ExtensionRegistry) -> Hugr { - let const_types = consts.iter().map(Value::const_type).collect_vec(); + let const_types = consts.iter().map(Value::get_type).collect_vec(); let mut b = DFGBuilder::new(FunctionType::new(type_row![], const_types)).unwrap(); let outputs = consts @@ -338,7 +338,7 @@ mod test { let list: Value = ListValue::new(BOOL_T, [Value::unit_sum(0, 1).unwrap()]).into(); let mut build = DFGBuilder::new(FunctionType::new( type_row![], - vec![list.const_type().clone()], + vec![list.get_type().clone()], )) .unwrap(); diff --git a/hugr/src/builder.rs b/hugr/src/builder.rs index a93e7149d..c186b57cd 100644 --- a/hugr/src/builder.rs +++ b/hugr/src/builder.rs @@ -85,7 +85,6 @@ //! # } //! # doctest().unwrap(); //! ``` -//! use thiserror::Error; use crate::extension::SignatureError; diff --git a/hugr/src/builder/build_traits.rs b/hugr/src/builder/build_traits.rs index 3554a87c5..e3ef10de8 100644 --- a/hugr/src/builder/build_traits.rs +++ b/hugr/src/builder/build_traits.rs @@ -358,7 +358,7 @@ pub trait Dataflow: Container { let load_n = self .add_dataflow_op( ops::LoadConstant { - datatype: op.const_type().clone(), + datatype: op.get_type().clone(), }, // Constant wire from the constant value node vec![Wire::new(const_node, OutgoingPort::from(0))], diff --git a/hugr/src/builder/cfg.rs b/hugr/src/builder/cfg.rs index 2066f39f0..1ac852b45 100644 --- a/hugr/src/builder/cfg.rs +++ b/hugr/src/builder/cfg.rs @@ -46,12 +46,11 @@ use crate::{ /// +------------+ /// */ /// use hugr::{ -/// builder::{BuildError, CFGBuilder, Container, Dataflow, HugrBuilder}, -/// Hugr, -/// extension::{ExtensionSet, prelude}, -/// types::{FunctionType, Type, SumType}, -/// ops, -/// type_row, +/// builder::{BuildError, CFGBuilder, Container, Dataflow, HugrBuilder}, +/// extension::{prelude, ExtensionSet}, +/// ops, type_row, +/// types::{FunctionType, SumType, Type}, +/// Hugr, /// }; /// /// const NAT: Type = prelude::USIZE_T; @@ -75,7 +74,7 @@ use crate::{ /// let left_42 = ops::Value::sum( /// 0, /// [prelude::ConstUsize::new(42).into()], -/// SumType::new(sum_variants.clone()) +/// SumType::new(sum_variants.clone()), /// )?; /// let sum = entry_b.add_load_value(left_42); /// @@ -85,11 +84,10 @@ use crate::{ /// // This block will be the first successor of the entry node. It takes two /// // `NAT` arguments: one from the `sum_variants` type, and another from the /// // entry node's `other_outputs`. -/// let mut successor_builder = -/// cfg_builder.simple_block_builder( -/// FunctionType::new(type_row![NAT, NAT], type_row![NAT]), -/// 1 // only one successor to this block -/// )?; +/// let mut successor_builder = cfg_builder.simple_block_builder( +/// FunctionType::new(type_row![NAT, NAT], type_row![NAT]), +/// 1, // only one successor to this block +/// )?; /// let successor_a = { /// // This block has one successor. The choice is denoted by a unary sum. /// let sum_unary = successor_builder.add_load_const(ops::Value::unary_unit_sum()); @@ -100,14 +98,14 @@ use crate::{ /// successor_builder.finish_with_outputs(sum_unary, [in_wire])? /// }; /// -/// // The only argument to this block is the entry node's `other_outputs`. -/// let mut successor_builder = -/// cfg_builder.simple_block_builder(FunctionType::new(type_row![NAT], type_row![NAT]), 1)?; -/// let successor_b = { -/// let sum_unary = successor_builder.add_load_value(ops::Value::unary_unit_sum()); -/// let [in_wire] = successor_builder.input_wires_arr(); -/// successor_builder.finish_with_outputs(sum_unary, [in_wire])? -/// }; +/// // The only argument to this block is the entry node's `other_outputs`. +/// let mut successor_builder = cfg_builder +/// .simple_block_builder(FunctionType::new(type_row![NAT], type_row![NAT]), 1)?; +/// let successor_b = { +/// let sum_unary = successor_builder.add_load_value(ops::Value::unary_unit_sum()); +/// let [in_wire] = successor_builder.input_wires_arr(); +/// successor_builder.finish_with_outputs(sum_unary, [in_wire])? +/// }; /// let exit = cfg_builder.exit_block(); /// cfg_builder.branch(&entry, 0, &successor_a)?; // branch 0 goes to successor_a /// cfg_builder.branch(&entry, 1, &successor_b)?; // branch 1 goes to successor_b diff --git a/hugr/src/builder/handle.rs b/hugr/src/builder/handle.rs index 3ec337037..1530392a0 100644 --- a/hugr/src/builder/handle.rs +++ b/hugr/src/builder/handle.rs @@ -1,5 +1,4 @@ //! Handles to nodes in HUGR used during the building phase. -//! use crate::ops::handle::{BasicBlockID, CaseID, DfgID, FuncID, NodeHandle, TailLoopID}; use crate::ops::OpTag; use crate::utils::collect_array; diff --git a/hugr/src/builder/tail_loop.rs b/hugr/src/builder/tail_loop.rs index 40baf8657..901324fe9 100644 --- a/hugr/src/builder/tail_loop.rs +++ b/hugr/src/builder/tail_loop.rs @@ -148,7 +148,7 @@ mod test { let const_wire = loop_b.add_load_const(Value::true_val()); let lift_node = loop_b.add_dataflow_op( ops::Lift { - type_row: vec![const_val.const_type().clone()].into(), + type_row: vec![const_val.get_type().clone()].into(), new_extension: PRELUDE_ID, }, [const_wire], diff --git a/hugr/src/extension/declarative.rs b/hugr/src/extension/declarative.rs index 3dc698e47..80c44b40f 100644 --- a/hugr/src/extension/declarative.rs +++ b/hugr/src/extension/declarative.rs @@ -13,7 +13,6 @@ //! ``` //! //! The definition can be loaded into a registry using the [`load_extensions`] or [`load_extensions_file`] functions. -//! //! ```rust //! # const DECLARATIVE_YAML: &str = include_str!("../../examples/extension/declarative.yaml"); //! # use hugr::extension::declarative::load_extensions; diff --git a/hugr/src/lib.rs b/hugr/src/lib.rs index 0147cf483..2016c62b3 100644 --- a/hugr/src/lib.rs +++ b/hugr/src/lib.rs @@ -131,7 +131,6 @@ //! let serialized = serde_json::to_string(&h).unwrap(); //! println!("{}", serialized); //! ``` -//! // Unstable check, may cause false positives. // https://github.com/rust-lang/rust-clippy/issues/5112 diff --git a/hugr/src/macros.rs b/hugr/src/macros.rs index fdeacfe2d..e6b511daa 100644 --- a/hugr/src/macros.rs +++ b/hugr/src/macros.rs @@ -87,12 +87,12 @@ pub use type_row; /// ```rust /// # mod test { /// # use hugr::macros::const_extension_ids; -/// const_extension_ids! { -/// pub const EXT_A: ExtensionId = "A"; -/// /// A doc comment -/// #[cfg(foobar)] pub (super) const EXT_A_B: ExtensionId = "A.B"; -/// const EXT_BAD: ExtensionId = "..55"; // this will generate a failing #[test] fn .... -/// } +/// const_extension_ids! { +/// pub const EXT_A: ExtensionId = "A"; +/// /// A doc comment +/// #[cfg(foobar)] pub (super) const EXT_A_B: ExtensionId = "A.B"; +/// const EXT_BAD: ExtensionId = "..55"; // this will generate a failing #[test] fn .... +/// } /// # } /// ``` #[macro_export] diff --git a/hugr/src/ops/constant.rs b/hugr/src/ops/constant.rs index 8b4d0f46f..1f91b6935 100644 --- a/hugr/src/ops/constant.rs +++ b/hugr/src/ops/constant.rs @@ -8,7 +8,9 @@ use crate::extension::ExtensionSet; use crate::types::{CustomType, EdgeKind, FunctionType, SumType, SumTypeError, Type}; use crate::{Hugr, HugrView}; +use delegate::delegate; use itertools::Itertools; +use serde::{Deserialize, Serialize}; use smol_str::SmolStr; use thiserror::Error; @@ -38,9 +40,11 @@ impl Const { &self.value } - /// Returns a reference to the type of this constant. - pub fn const_type(&self) -> Type { - self.value.const_type() + delegate! { + to self.value { + /// Returns the type of this constant. + pub fn get_type(&self) -> Type; + } } } @@ -50,6 +54,34 @@ impl From for Const { } } +impl NamedOp for Const { + fn name(&self) -> OpName { + self.value().name() + } +} + +impl StaticTag for Const { + const TAG: OpTag = OpTag::Const; +} + +impl OpTrait for Const { + fn description(&self) -> &str { + "Constant value" + } + + fn extension_delta(&self) -> ExtensionSet { + self.value().extension_reqs() + } + + fn tag(&self) -> OpTag { + ::TAG + } + + fn static_output(&self) -> Option { + Some(EdgeKind::Const(self.get_type())) + } +} + impl From for Value { fn from(konst: Const) -> Self { konst.value @@ -63,12 +95,13 @@ impl AsRef for Const { } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] -#[serde(tag = "c")] +#[serde(tag = "v")] /// A value that can be stored as a static constant. Representing core types and /// extension types. pub enum Value { /// An extension constant value, that can check it is of a given [CustomType]. Extension { + #[serde(flatten)] /// The custom constant value. e: ExtensionValue, }, @@ -99,20 +132,92 @@ pub enum Value { }, } -/// Boxed [`CustomConst`] trait object. +/// An opaque newtype around a [`Box`](CustomConst). /// -/// Use [`Value::extension`] to create a new variant of this type. +/// This type has special serialization behaviour in order to support +/// serialisation and deserialisation of unknown impls of [CustomConst]. /// -/// This is required to avoid in -/// [`Value::Extension`], while implementing a transparent encoding into a -/// `CustomConst`. -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(transparent)] -pub struct ExtensionValue(pub(super) Box); +/// During serialization we first serialize the internal [`dyn` CustomConst](CustomConst) +/// into a [serde_yaml::Value]. We then create a [CustomSerialized] wrapping +/// that value. That [CustomSerialized] is then serialized in place of the +/// [ExtensionValue]. +/// +/// During deserialization, first we deserialize a [CustomSerialized]. We +/// attempt to deserialize the internal [serde_yaml::Value] using the [`Box`](CustomConst) impl. This will fail if the appropriate `impl CustomConst` +/// is not linked into the running program, in which case we coerce the +/// [CustomSerialized] into a [`Box`](CustomConst). The [ExtensionValue] is +/// then produced from the [`Box`](CustomConst). +/// +/// In the case where the internal serialised value of a `CustomSerialized` +/// is another `CustomSerialized` we do not attempt to recurse. This behaviour +/// may change in future. +/// +/// ```rust +/// use serde::{Serialize,Deserialize}; +/// use hugr::{ +/// types::Type,ops::constant::{ExtensionValue, ValueName, CustomConst, CustomSerialized}, +/// extension::{ExtensionSet, prelude::{USIZE_T, ConstUsize}}, +/// std_extensions::arithmetic::int_types}; +/// use serde_json::json; +/// +/// let expected_json = json!({ +/// "extensions": ["prelude"], +/// "typ": USIZE_T, +/// "value": {'c': "ConstUsize", 'v': 1} +/// }); +/// let ev = ExtensionValue::new(ConstUsize::new(1)); +/// assert_eq!(&serde_json::to_value(&ev).unwrap(), &expected_json); +/// assert_eq!(ev, serde_json::from_value(expected_json).unwrap()); +/// +/// let ev = ExtensionValue::new(CustomSerialized::new(USIZE_T.clone(), serde_yaml::Value::Null, ExtensionSet::default())); +/// let expected_json = json!({ +/// "extensions": [], +/// "typ": USIZE_T, +/// "value": null +/// }); +/// +/// assert_eq!(&serde_json::to_value(ev.clone()).unwrap(), &expected_json); +/// assert_eq!(ev, serde_json::from_value(expected_json).unwrap()); +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ExtensionValue { + #[serde(flatten, with = "self::custom::serde_extension_value")] + v: Box, +} + +impl ExtensionValue { + /// Create a new [`ExtensionValue`] from any [`CustomConst`]. + pub fn new(cc: impl CustomConst) -> Self { + Self { v: Box::new(cc) } + } + + /// Returns a reference to the internal [`CustomConst`]. + pub fn value(&self) -> &dyn CustomConst { + self.v.as_ref() + } + + delegate! { + to self.value() { + /// Returns the type of the internal [`CustomConst`]. + pub fn get_type(&self) -> Type; + /// An identifier of the internal [`CustomConst`]. + pub fn name(&self) -> ValueName; + /// The extension(s) defining the internal [`CustomConst`]. + pub fn extension_reqs(&self) -> ExtensionSet; + } + } +} + +impl From for ExtensionValue { + fn from(x: CC) -> Self { + Self::new(x) + } +} impl PartialEq for ExtensionValue { fn eq(&self, other: &Self) -> bool { - self.0.equal_consts(other.0.as_ref()) + self.value().equal_consts(other.value()) } } @@ -169,11 +274,11 @@ fn mono_fn_type(h: &Hugr) -> Result { } impl Value { - /// Returns a reference to the type of this [`Value`]. - pub fn const_type(&self) -> Type { + /// Returns the type of this [`Value`]. + pub fn get_type(&self) -> Type { match self { - Self::Extension { e } => e.0.get_type(), - Self::Tuple { vs } => Type::new_tuple(vs.iter().map(Self::const_type).collect_vec()), + Self::Extension { e } => e.get_type(), + Self::Tuple { vs } => Type::new_tuple(vs.iter().map(Self::get_type).collect_vec()), Self::Sum { sum_type, .. } => sum_type.clone().into(), Self::Function { hugr } => { let func_type = mono_fn_type(hugr).unwrap_or_else(|e| panic!("{}", e)); @@ -256,14 +361,14 @@ impl Value { /// Returns a tuple constant of constant values. pub fn extension(custom_const: impl CustomConst) -> Self { Self::Extension { - e: ExtensionValue(Box::new(custom_const)), + e: ExtensionValue::new(custom_const), } } /// For a Const holding a CustomConst, extract the CustomConst by downcasting. pub fn get_custom_value(&self) -> Option<&T> { if let Self::Extension { e } = self { - e.0.downcast_ref() + e.v.downcast_ref() } else { None } @@ -271,7 +376,7 @@ impl Value { fn name(&self) -> OpName { match self { - Self::Extension { e } => format!("const:custom:{}", e.0.name()), + Self::Extension { e } => format!("const:custom:{}", e.name()), Self::Function { hugr: h } => { let Some(t) = h.get_function_type() else { panic!("HUGR root node isn't a valid function parent."); @@ -292,7 +397,7 @@ impl Value { /// The extensions required by a [`Value`] pub fn extension_reqs(&self) -> ExtensionSet { match self { - Self::Extension { e } => e.0.extension_reqs().clone(), + Self::Extension { e } => e.extension_reqs().clone(), Self::Function { .. } => ExtensionSet::new(), // no extensions required to load Hugr (only to run) Self::Tuple { vs } => ExtensionSet::union_over(vs.iter().map(Value::extension_reqs)), Self::Sum { values, .. } => { @@ -302,35 +407,6 @@ impl Value { } } -impl NamedOp for Const { - fn name(&self) -> OpName { - self.value().name() - } -} - -impl StaticTag for Const { - const TAG: OpTag = OpTag::Const; -} -impl OpTrait for Const { - fn description(&self) -> &str { - "Constant value" - } - - fn extension_delta(&self) -> ExtensionSet { - self.value().extension_reqs() - } - - fn tag(&self) -> OpTag { - ::TAG - } - - fn static_output(&self) -> Option { - Some(EdgeKind::Const(self.const_type())) - } -} - -// [KnownTypeConst] is guaranteed to be the right type, so can be constructed -// without initial type check. impl From for Value where T: CustomConst, @@ -392,12 +468,9 @@ mod test { /// A [`CustomSerialized`] encoding a [`FLOAT64_TYPE`] float constant used in testing. pub(crate) fn serialized_float(f: f64) -> Value { - CustomSerialized::new( - FLOAT64_TYPE, - serde_yaml::Value::Number(f.into()), - float_types::EXTENSION_ID, - ) - .into() + CustomSerialized::try_from_custom_const(ConstF64::new(f)) + .unwrap() + .into() } fn test_registry() -> ExtensionRegistry { @@ -419,7 +492,7 @@ mod test { 0, [ CustomTestValue(USIZE_CUSTOM_T).into(), - serialized_float(5.1), + ConstF64::new(5.1).into(), ], pred_ty.clone(), )?); @@ -487,7 +560,7 @@ mod test { crate::extension::prelude::BOOL_T ])); - assert_eq!(v.const_type(), correct_type); + assert_eq!(v.get_type(), correct_type); assert!(v.name().starts_with("const:function:")) } @@ -504,14 +577,14 @@ mod test { #[rstest] #[case(Value::unit(), Type::UNIT, "const:seq:{}")] #[case(const_usize(), USIZE_T, "const:custom:ConstUsize(")] - #[case(serialized_float(17.4), FLOAT64_TYPE, "const:custom:yaml:Number(17.4)")] + #[case(serialized_float(17.4), FLOAT64_TYPE, "const:custom:yaml:Mapping")] #[case(const_tuple(), Type::new_tuple(type_row![USIZE_T, FLOAT64_TYPE]), "const:seq:{")] fn const_type( #[case] const_value: Value, #[case] expected_type: Type, #[case] name_prefix: &str, ) { - assert_eq!(const_value.const_type(), expected_type); + assert_eq!(const_value.get_type(), expected_type); let name = const_value.name(); assert!( name.starts_with(name_prefix), @@ -544,10 +617,10 @@ mod test { .into(); let classic_t = Type::new_extension(typ_int.clone()); assert_matches!(classic_t.least_upper_bound(), TypeBound::Eq); - assert_eq!(yaml_const.const_type(), classic_t); + assert_eq!(yaml_const.get_type(), classic_t); let typ_qb = CustomType::new("my_type", vec![], ex_id, TypeBound::Eq); let t = Type::new_extension(typ_qb.clone()); - assert_ne!(yaml_const.const_type(), t); + assert_ne!(yaml_const.get_type(), t); } } diff --git a/hugr/src/ops/constant/custom.rs b/hugr/src/ops/constant/custom.rs index 9ed59fca4..ae7bd57e5 100644 --- a/hugr/src/ops/constant/custom.rs +++ b/hugr/src/ops/constant/custom.rs @@ -7,6 +7,7 @@ use std::any::Any; use downcast_rs::{impl_downcast, Downcast}; +use thiserror::Error; use crate::extension::ExtensionSet; use crate::macros::impl_box_clone; @@ -18,13 +19,40 @@ use super::Value; use super::ValueName; -/// Constant value for opaque [`CustomType`]s. +/// Extensible constant values. /// -/// When implementing this trait, include the `#[typetag::serde]` attribute to -/// enable serialization. +/// We use [typetag] to provide an `impl Serialize for dyn CustomConst`, and +/// similarly [serde::Deserialize]. When implementing this trait, include the +/// [`#[typetag::serde]`](typetag) attribute to enable serialization. /// -/// [`CustomType`]: crate::types::CustomType -#[typetag::serde(tag = "c")] +/// Note that when serializing through the [`dyn CustomConst`] a dictionary will +/// be serialized with two attributes, `"c"` the tag and `"v"` the +/// `CustomConst`: +/// +/// ```rust +/// use serde::{Serialize,Deserialize}; +/// use hugr::{ +/// types::Type,ops::constant::{ExtensionValue, ValueName, CustomConst}, +/// extension::ExtensionSet, std_extensions::arithmetic::int_types}; +/// use serde_json::json; +/// +/// #[derive(std::fmt::Debug, Clone, Serialize,Deserialize)] +/// struct CC(i64); +/// +/// #[typetag::serde] +/// impl CustomConst for CC { +/// fn name(&self) -> ValueName { "CC".into() } +/// fn extension_reqs(&self) -> ExtensionSet { ExtensionSet::singleton(&int_types::EXTENSION_ID) } +/// fn get_type(&self) -> Type { int_types::INT_TYPES[5].clone() } +/// } +/// +/// assert_eq!(serde_json::to_value(CC(2)).unwrap(), json!(2)); +/// assert_eq!(serde_json::to_value(&CC(2) as &dyn CustomConst).unwrap(), json!({ +/// "c": "CC", +/// "v": 2 +/// })); +/// ```` +#[typetag::serde(tag = "c", content = "v")] pub trait CustomConst: Send + Sync + std::fmt::Debug + CustomConstBoxClone + Any + Downcast { @@ -38,7 +66,7 @@ pub trait CustomConst: /// [USize]: crate::extension::prelude::USIZE_T fn extension_reqs(&self) -> ExtensionSet; - /// Check the value is a valid instance of the provided type. + /// Check the value. fn validate(&self) -> Result<(), CustomCheckFailure> { Ok(()) } @@ -51,10 +79,16 @@ pub trait CustomConst: false } - /// report the type + /// Report the type. fn get_type(&self) -> Type; } +impl PartialEq for dyn CustomConst { + fn eq(&self, other: &Self) -> bool { + (*self).equal_consts(other) + } +} + /// Const equality for types that have PartialEq pub fn downcast_equal_consts( constant: &T, @@ -67,6 +101,33 @@ pub fn downcast_equal_consts( } } +/// Serialize any CustomConst using the `impl Serialize for &dyn CustomConst`. +fn serialize_custom_const(cc: &dyn CustomConst) -> Result { + serde_yaml::to_value(cc) +} + +/// Deserialize a `Box<&dyn CustomConst>` and attempt to downcast it to `CC`; +/// propagating failure. +fn deserialize_custom_const( + value: serde_yaml::Value, +) -> Result { + match deserialize_dyn_custom_const(value)?.downcast::() { + Ok(cc) => Ok(*cc), + Err(dyn_cc) => Err(::custom(format!( + "Failed to deserialize [{}]: {:?}", + std::any::type_name::(), + dyn_cc + ))), + } +} + +/// Deserialize a `Box<&dyn CustomConst>`. +fn deserialize_dyn_custom_const( + value: serde_yaml::Value, +) -> Result, serde_yaml::Error> { + serde_yaml::from_value(value) +} + impl_downcast!(CustomConst); impl_box_clone!(CustomConst, CustomConstBoxClone); @@ -78,6 +139,22 @@ pub struct CustomSerialized { extensions: ExtensionSet, } +#[derive(Debug, Error)] +#[error("Error serializing value into CustomSerialised: err: {err}, value: {payload:?}")] +pub struct SerializeError { + #[source] + err: serde_yaml::Error, + payload: Box, +} + +#[derive(Debug, Error)] +#[error("Error deserialising value from CustomSerialised: err: {err}, value: {payload:?}")] +pub struct DeserializeError { + #[source] + err: serde_yaml::Error, + payload: serde_yaml::Value, +} + impl CustomSerialized { /// Creates a new [`CustomSerialized`]. pub fn new( @@ -96,6 +173,77 @@ impl CustomSerialized { pub fn value(&self) -> &serde_yaml::Value { &self.value } + + /// If `cc` is a [Self], returns a clone of `cc` coerced to [Self]. + /// Otherwise, returns a [Self] with `cc` serialised in it's value. + pub fn try_from_custom_const_ref(cc: &impl CustomConst) -> Result { + Self::try_from_dyn_custom_const(cc) + } + + /// If `cc` is a [Self], returns a clone of `cc` coerced to [Self]. + /// Otherwise, returns a [Self] with `cc` serialised in it's value. + pub fn try_from_dyn_custom_const(cc: &dyn CustomConst) -> Result { + Ok(match cc.as_any().downcast_ref::() { + Some(cs) => cs.clone(), + None => Self::new( + cc.get_type(), + serialize_custom_const(cc).map_err(|err| SerializeError { + err, + payload: cc.clone_box(), + })?, + cc.extension_reqs(), + ), + }) + } + + /// If `cc` is a [Self], return `cc` coerced to [Self]. Otherwise, + /// returns a [Self] with `cc` serialized in it's value. + /// Never clones `cc` outside of error paths. + pub fn try_from_custom_const(cc: impl CustomConst) -> Result { + Self::try_from_custom_const_box(Box::new(cc)) + } + + /// If `cc` is a [Self], return `cc` coerced to [Self]. Otherwise, + /// returns a [Self] with `cc` serialized in it's value. + /// Never clones `cc` outside of error paths. + pub fn try_from_custom_const_box(cc: Box) -> Result { + match cc.downcast::() { + Ok(x) => Ok(*x), + Err(cc) => { + let (typ, extension_reqs) = (cc.get_type(), cc.extension_reqs()); + let value = serialize_custom_const(cc.as_ref()) + .map_err(|err| SerializeError { err, payload: cc })?; + Ok(Self::new(typ, value, extension_reqs)) + } + } + } + + /// Attempts to deserialize the value in self into a `Box`. + /// This can fail, in particular when the `impl CustomConst` for the trait + /// is not linked into the running executable. + /// If deserialisation fails, returns self in a box. + /// + /// Note that if the inner value is a [Self] we do not recursively + /// deserialize it. + pub fn into_custom_const_box(self) -> Box { + // ideally we would not have to clone, but serde_json does not allow us + // to recover the value from the error + deserialize_dyn_custom_const(self.value.clone()).unwrap_or_else(|_| Box::new(self)) + } + + /// Attempts to deserialize the value in self into a `CC`. Propagates failure. + /// + /// Note that if the inner value is a [Self] we do not recursively + /// deserialize it. In particular if that inner value were a [Self] whose + /// inner value were a `CC`, then we would still fail. + pub fn try_into_custom_const(self) -> Result { + // ideally we would not have to clone, but serde_yaml does not allow us + // to recover the value from the error + deserialize_custom_const(self.value.clone()).map_err(|err| DeserializeError { + err, + payload: self.value, + }) + } } #[typetag::serde] @@ -116,9 +264,33 @@ impl CustomConst for CustomSerialized { } } -impl PartialEq for dyn CustomConst { - fn eq(&self, other: &Self) -> bool { - (*self).equal_consts(other) +/// This module is used by the serde annotations on `super::ExtensionValue` +pub(super) mod serde_extension_value { + use serde::{Deserializer, Serializer}; + + use super::{CustomConst, CustomSerialized}; + + pub fn deserialize<'de, D: Deserializer<'de>>( + deserializer: D, + ) -> Result, D::Error> { + use serde::Deserialize; + // We deserialize a CustomSerialized, i.e. not a dyn CustomConst. + let cs = CustomSerialized::deserialize(deserializer)?; + // We return the inner serialised CustomConst if we can, otherwise the + // CustomSerialized itself. + Ok(cs.into_custom_const_box()) + } + + pub fn serialize( + konst: impl AsRef, + serializer: S, + ) -> Result { + use serde::Serialize; + // we create a CustomSerialized, then serialize it. Note we do not + // serialize it as a dyn CustomConst. + let cs = CustomSerialized::try_from_dyn_custom_const(konst.as_ref()) + .map_err(::custom)?; + cs.serialize(serializer) } } @@ -139,3 +311,165 @@ pub fn get_pair_of_input_values( }; Some((c0.get_custom_value()?, c1.get_custom_value()?)) } + +#[cfg(test)] +mod test { + + use rstest::rstest; + + use crate::{ + extension::prelude::{ConstUsize, USIZE_T}, + ops::{constant::custom::serialize_custom_const, Value}, + std_extensions::collections::ListValue, + }; + + use super::{super::ExtensionValue, CustomConst, CustomConstBoxClone, CustomSerialized}; + + struct SerializeCustomConstExample { + cc: CC, + tag: &'static str, + yaml: serde_yaml::Value, + } + + impl SerializeCustomConstExample { + fn new(cc: CC, tag: &'static str) -> Self { + let yaml = serde_yaml::to_value(&cc).unwrap(); + Self { cc, tag, yaml } + } + } + + fn scce_usize() -> SerializeCustomConstExample { + SerializeCustomConstExample::new(ConstUsize::new(12), "ConstUsize") + } + + fn scce_list() -> SerializeCustomConstExample { + let cc = ListValue::new( + USIZE_T, + [ConstUsize::new(1), ConstUsize::new(2)] + .into_iter() + .map(Value::extension), + ); + SerializeCustomConstExample::new(cc, "ListValue") + } + + #[rstest] + #[case(scce_usize())] + #[case(scce_list())] + fn test_custom_serialized_try_from< + CC: CustomConst + serde::Serialize + Clone + PartialEq + 'static + Sized, + >( + #[case] example: SerializeCustomConstExample, + ) { + assert_eq!(example.yaml, serde_yaml::to_value(&example.cc).unwrap()); // sanity check + let expected_yaml: serde_yaml::Value = [ + ("c".into(), example.tag.into()), + ("v".into(), example.yaml.clone()), + ] + .into_iter() + .collect::() + .into(); + + // check serialize_custom_const + assert_eq!(expected_yaml, serialize_custom_const(&example.cc).unwrap()); + + let expected_custom_serialized = CustomSerialized::new( + example.cc.get_type(), + expected_yaml, + example.cc.extension_reqs(), + ); + + // check all the try_from/try_into/into variations + assert_eq!( + &expected_custom_serialized, + &CustomSerialized::try_from_custom_const(example.cc.clone()).unwrap() + ); + assert_eq!( + &expected_custom_serialized, + &CustomSerialized::try_from_custom_const_ref(&example.cc).unwrap() + ); + assert_eq!( + &expected_custom_serialized, + &CustomSerialized::try_from_custom_const_box(example.cc.clone_box()).unwrap() + ); + assert_eq!( + &expected_custom_serialized, + &CustomSerialized::try_from_dyn_custom_const(example.cc.clone_box().as_ref()).unwrap() + ); + assert_eq!( + &example.cc.clone_box(), + &expected_custom_serialized.clone().into_custom_const_box() + ); + assert_eq!( + &example.cc, + &expected_custom_serialized + .clone() + .try_into_custom_const() + .unwrap() + ); + + // check ExtensionValue serializes/deserializes as a CustomSerialized + let ev: ExtensionValue = example.cc.clone().into(); + let ev_val = serde_yaml::to_value(&ev).unwrap(); + assert_eq!( + &ev_val, + &serde_yaml::to_value(&expected_custom_serialized).unwrap() + ); + assert_eq!(ev, serde_yaml::from_value(ev_val).unwrap()); + } + + fn example_custom_serialized() -> (ConstUsize, CustomSerialized) { + let inner = scce_usize().cc; + ( + inner.clone(), + CustomSerialized::try_from_custom_const(inner).unwrap(), + ) + } + + fn example_nested_custom_serialized() -> (CustomSerialized, CustomSerialized) { + let inner = example_custom_serialized().1; + ( + inner.clone(), + CustomSerialized::new( + inner.get_type(), + serialize_custom_const(&inner).unwrap(), + inner.extension_reqs(), + ), + ) + } + + #[rstest] + #[case(example_custom_serialized())] + #[case(example_nested_custom_serialized())] + fn test_try_from_custom_serialized_recursive( + #[case] example: (CC, CustomSerialized), + ) { + let (inner, cs) = example; + // check all the try_from/try_into/into variations + + assert_eq!( + &cs, + &CustomSerialized::try_from_custom_const(cs.clone()).unwrap() + ); + assert_eq!( + &cs, + &CustomSerialized::try_from_custom_const_ref(&cs).unwrap() + ); + assert_eq!( + &cs, + &CustomSerialized::try_from_custom_const_box(cs.clone_box()).unwrap() + ); + assert_eq!( + &cs, + &CustomSerialized::try_from_dyn_custom_const(cs.clone_box().as_ref()).unwrap() + ); + assert_eq!(&inner.clone_box(), &cs.clone().into_custom_const_box()); + assert_eq!(&inner, &cs.clone().try_into_custom_const().unwrap()); + + let ev: ExtensionValue = cs.clone().into(); + // A serialisation round-trip results in an ExtensionValue with the value of inner + assert_eq!( + ExtensionValue::new(inner), + serde_yaml::from_value(serde_yaml::to_value(&ev).unwrap()).unwrap() + ); + } +} diff --git a/hugr/src/ops/handle.rs b/hugr/src/ops/handle.rs index 5b58dab3c..f7fd31ed8 100644 --- a/hugr/src/ops/handle.rs +++ b/hugr/src/ops/handle.rs @@ -1,5 +1,4 @@ //! Handles to nodes in HUGR. -//! use crate::types::{Type, TypeBound}; use crate::Node; diff --git a/hugr/src/std_extensions/collections.rs b/hugr/src/std_extensions/collections.rs index 4896f1ab7..747841a6b 100644 --- a/hugr/src/std_extensions/collections.rs +++ b/hugr/src/std_extensions/collections.rs @@ -84,7 +84,7 @@ impl CustomConst for ListValue { // check all values are instances of the element type for v in &self.0 { - if v.const_type() != *ty { + if v.get_type() != *ty { return Err(error()); } } diff --git a/hugr/src/std_extensions/logic.rs b/hugr/src/std_extensions/logic.rs index 98e4649ed..50e6921ce 100644 --- a/hugr/src/std_extensions/logic.rs +++ b/hugr/src/std_extensions/logic.rs @@ -266,7 +266,7 @@ pub(crate) mod test { let true_val = r.get_value(&TRUE_NAME).unwrap(); for v in [false_val, true_val] { - let simpl = v.typed_value().const_type(); + let simpl = v.typed_value().get_type(); assert_eq!(simpl, BOOL_T); } } diff --git a/hugr/src/types.rs b/hugr/src/types.rs index 213f74a1a..4862d7552 100644 --- a/hugr/src/types.rs +++ b/hugr/src/types.rs @@ -249,7 +249,6 @@ impl TypeEnum { /// /// let sum = Type::new_sum([type_row![], type_row![]]); /// assert_eq!(sum.least_upper_bound(), TypeBound::Eq); -/// /// ``` /// /// ``` @@ -257,9 +256,7 @@ impl TypeEnum { /// /// let func_type = Type::new_function(FunctionType::new_endo(vec![])); /// assert_eq!(func_type.least_upper_bound(), TypeBound::Copyable); -/// /// ``` -/// pub struct Type(TypeEnum, TypeBound); fn validate_each<'a>( diff --git a/hugr/src/types/check.rs b/hugr/src/types/check.rs index 3de851521..174ff817d 100644 --- a/hugr/src/types/check.rs +++ b/hugr/src/types/check.rs @@ -10,7 +10,7 @@ use crate::ops::Value; #[non_exhaustive] pub enum SumTypeError { /// The type of the variant doesn't match the type of the value. - #[error("Expected type {expected} for element {index} of variant #{tag}, but found {}", .found.const_type())] + #[error("Expected type {expected} for element {index} of variant #{tag}, but found {}", .found.get_type())] InvalidValueType { /// Tag of the variant. tag: usize, @@ -70,7 +70,7 @@ impl super::SumType { } for (index, (t, v)) in itertools::zip_eq(variant.iter(), val.iter()).enumerate() { - if v.const_type() != *t { + if v.get_type() != *t { Err(SumTypeError::InvalidValueType { tag, index, diff --git a/hugr/src/types/serialize.rs b/hugr/src/types/serialize.rs index 83718a5c5..4a263af14 100644 --- a/hugr/src/types/serialize.rs +++ b/hugr/src/types/serialize.rs @@ -11,9 +11,9 @@ pub(super) enum SerSimpleType { Q, I, G(Box), - Sum { st: SumType }, + Sum(SumType), Array { inner: Box, len: u64 }, - Opaque { o: CustomType }, + Opaque(CustomType), Alias(AliasDecl), V { i: usize, b: TypeBound }, } @@ -29,11 +29,11 @@ impl From for SerSimpleType { // TODO short circuiting for array. let Type(value, _) = value; match value { - TypeEnum::Extension(o) => SerSimpleType::Opaque { o }, + TypeEnum::Extension(o) => SerSimpleType::Opaque(o), TypeEnum::Alias(a) => SerSimpleType::Alias(a), TypeEnum::Function(sig) => SerSimpleType::G(sig), TypeEnum::Variable(i, b) => SerSimpleType::V { i, b }, - TypeEnum::Sum(st) => SerSimpleType::Sum { st }, + TypeEnum::Sum(st) => SerSimpleType::Sum(st), } } } @@ -44,11 +44,11 @@ impl From for Type { SerSimpleType::Q => QB_T, SerSimpleType::I => USIZE_T, SerSimpleType::G(sig) => Type::new_function(*sig), - SerSimpleType::Sum { st } => st.into(), + SerSimpleType::Sum(st) => st.into(), SerSimpleType::Array { inner, len } => { array_type(TypeArg::BoundedNat { n: len }, (*inner).into()) } - SerSimpleType::Opaque { o } => Type::new_extension(o), + SerSimpleType::Opaque(o) => Type::new_extension(o), SerSimpleType::Alias(a) => Type::new_alias(a), SerSimpleType::V { i, b } => Type::new_var_use(i, b), } diff --git a/specification/schema/.gitattributes b/specification/schema/.gitattributes index 94ff3e30f..266907b7a 100644 --- a/specification/schema/.gitattributes +++ b/specification/schema/.gitattributes @@ -1 +1 @@ -*schema_v*.json -diff +*schema*.json -diff diff --git a/specification/schema/hugr_schema_strict_v1.json b/specification/schema/hugr_schema_strict_v1.json index 2ed6d0919..056ceeaf2 100644 --- a/specification/schema/hugr_schema_strict_v1.json +++ b/specification/schema/hugr_schema_strict_v1.json @@ -247,7 +247,7 @@ }, "Call": { "additionalProperties": false, - "description": "Call a function directly.\n\nThe first port is connected to the def/declare of the function being called\ndirectly, with a `ConstE` edge. The signature of the remaining ports matches\nthe function being called.", + "description": "Operation to call a function directly. The first port is connected to the def/declare of the function being called directly, with a `Static` edge. The signature of the remaining ports matches the function being called.", "properties": { "parent": { "title": "Parent", @@ -498,9 +498,27 @@ "title": "Const", "type": "object" }, + "CustomConst": { + "additionalProperties": false, + "properties": { + "c": { + "title": "C", + "type": "string" + }, + "v": { + "title": "V" + } + }, + "required": [ + "c", + "v" + ], + "title": "CustomConst", + "type": "object" + }, "CustomOp": { "additionalProperties": false, - "description": "A user-defined operation that can be downcasted by the extensions that define\nit.", + "description": "A user-defined operation that can be downcasted by the extensions that define it.", "properties": { "parent": { "title": "Parent", @@ -606,7 +624,7 @@ }, "DataflowBlock": { "additionalProperties": false, - "description": "A CFG basic block node. The signature is that of the internal Dataflow\ngraph.", + "description": "A CFG basic block node. The signature is that of the internal Dataflow graph.", "properties": { "parent": { "title": "Parent", @@ -669,14 +687,15 @@ } }, "required": [ - "parent" + "parent", + "sum_rows" ], "title": "DataflowBlock", "type": "object" }, "ExitBlock": { "additionalProperties": false, - "description": "The single exit node of the CFG, has no children, stores the types of\nthe CFG node output.", + "description": "The single exit node of the CFG, has no children, stores the types of the CFG node output.", "properties": { "parent": { "title": "Parent", @@ -725,7 +744,7 @@ "additionalProperties": false, "description": "An extension constant value, that can check it is of a given [CustomType].", "properties": { - "c": { + "v": { "const": "Extension", "default": "Extension", "enum": [ @@ -734,12 +753,24 @@ "title": "ValueTag", "type": "string" }, - "e": { - "title": "CustomConst" + "extensions": { + "items": { + "type": "string" + }, + "title": "Extensions", + "type": "array" + }, + "typ": { + "$ref": "#/$defs/Type" + }, + "value": { + "$ref": "#/$defs/CustomConst" } }, "required": [ - "e" + "extensions", + "typ", + "value" ], "title": "ExtensionValue", "type": "object" @@ -884,7 +915,7 @@ }, "FunctionType": { "additionalProperties": false, - "description": "A graph encoded as a value. It contains a concrete signature and a set of\nrequired resources.", + "description": "A graph encoded as a value. It contains a concrete signature and a set of required resources.", "properties": { "t": { "const": "G", @@ -928,7 +959,7 @@ "additionalProperties": false, "description": "A higher-order function value.", "properties": { - "c": { + "v": { "const": "Function", "default": "Function", "enum": [ @@ -951,6 +982,15 @@ "additionalProperties": false, "description": "General sum type that explicitly stores the types of the variants.", "properties": { + "t": { + "const": "Sum", + "default": "Sum", + "enum": [ + "Sum" + ], + "title": "T", + "type": "string" + }, "s": { "const": "General", "default": "General", @@ -1432,12 +1472,25 @@ "$ref": "#/$defs/AliasDefn" } ], + "required": [ + "parent", + "op" + ], "title": "OpType" }, "Opaque": { "additionalProperties": false, "description": "An opaque Type that can be downcasted by the extensions that define it.", "properties": { + "t": { + "const": "Opaque", + "default": "Opaque", + "enum": [ + "Opaque" + ], + "title": "T", + "type": "string" + }, "extension": { "title": "Extension", "type": "string" @@ -1562,7 +1615,7 @@ }, "PolyFuncType": { "additionalProperties": false, - "description": "A polymorphic type scheme, i.e. of a FuncDecl, FuncDefn or OpDef.\n(Nodes/operations in the Hugr are not polymorphic.)", + "description": "A polymorphic type scheme, i.e. of a FuncDecl, FuncDefn or OpDef. (Nodes/operations in the Hugr are not polymorphic.)", "properties": { "params": { "items": { @@ -1641,13 +1694,16 @@ "$ref": "#/$defs/GeneralSum" } ], + "required": [ + "s" + ], "title": "SumType" }, "SumValue": { "additionalProperties": false, - "description": "A Sum variant\n\nFor any Sum type where this value meets the type of the variant indicated by the tag", + "description": "A Sum variant For any Sum type where this value meets the type of the variant indicated by the tag.", "properties": { - "c": { + "v": { "const": "Sum", "default": "Sum", "enum": [ @@ -1734,50 +1790,6 @@ "title": "Tag", "type": "object" }, - "TaggedOpaque": { - "additionalProperties": false, - "properties": { - "t": { - "const": "Opaque", - "default": "Opaque", - "enum": [ - "Opaque" - ], - "title": "T", - "type": "string" - }, - "o": { - "$ref": "#/$defs/Opaque" - } - }, - "required": [ - "o" - ], - "title": "TaggedOpaque", - "type": "object" - }, - "TaggedSumType": { - "additionalProperties": false, - "properties": { - "t": { - "const": "Sum", - "default": "Sum", - "enum": [ - "Sum" - ], - "title": "T", - "type": "string" - }, - "st": { - "$ref": "#/$defs/SumType" - } - }, - "required": [ - "st" - ], - "title": "TaggedSumType", - "type": "object" - }, "TailLoop": { "additionalProperties": false, "description": "Tail-controlled loop.", @@ -1868,7 +1880,7 @@ "additionalProperties": false, "description": "A constant tuple value.", "properties": { - "c": { + "v": { "const": "Tuple", "default": "Tuple", "enum": [ @@ -1899,9 +1911,9 @@ "Array": "#/$defs/Array", "G": "#/$defs/FunctionType", "I": "#/$defs/USize", - "Opaque": "#/$defs/TaggedOpaque", + "Opaque": "#/$defs/Opaque", "Q": "#/$defs/Qubit", - "Sum": "#/$defs/TaggedSumType", + "Sum": "#/$defs/SumType", "V": "#/$defs/Variable" }, "propertyName": "t" @@ -1923,15 +1935,18 @@ "$ref": "#/$defs/Array" }, { - "$ref": "#/$defs/TaggedSumType" + "$ref": "#/$defs/SumType" }, { - "$ref": "#/$defs/TaggedOpaque" + "$ref": "#/$defs/Opaque" }, { "$ref": "#/$defs/Alias" } ], + "required": [ + "t" + ], "title": "Type" }, "TypeArg": { @@ -1967,6 +1982,9 @@ "$ref": "#/$defs/VariableArg" } ], + "required": [ + "tya" + ], "title": "TypeArg" }, "TypeBound": { @@ -2011,6 +2029,9 @@ "$ref": "#/$defs/ExtensionsParam" } ], + "required": [ + "tp" + ], "title": "TypeParam" }, "TypeTypeArg": { @@ -2078,6 +2099,15 @@ "additionalProperties": false, "description": "Simple sum type where all variants are empty tuples.", "properties": { + "t": { + "const": "Sum", + "default": "Sum", + "enum": [ + "Sum" + ], + "title": "T", + "type": "string" + }, "s": { "const": "Unit", "default": "Unit", @@ -2153,7 +2183,7 @@ "Sum": "#/$defs/SumValue", "Tuple": "#/$defs/TupleValue" }, - "propertyName": "c" + "propertyName": "v" }, "oneOf": [ { @@ -2169,6 +2199,9 @@ "$ref": "#/$defs/SumValue" } ], + "required": [ + "v" + ], "title": "Value" }, "Variable": { diff --git a/specification/schema/hugr_schema_v1.json b/specification/schema/hugr_schema_v1.json index 9f6979cf4..27d58db73 100644 --- a/specification/schema/hugr_schema_v1.json +++ b/specification/schema/hugr_schema_v1.json @@ -247,7 +247,7 @@ }, "Call": { "additionalProperties": true, - "description": "Call a function directly.\n\nThe first port is connected to the def/declare of the function being called\ndirectly, with a `ConstE` edge. The signature of the remaining ports matches\nthe function being called.", + "description": "Operation to call a function directly. The first port is connected to the def/declare of the function being called directly, with a `Static` edge. The signature of the remaining ports matches the function being called.", "properties": { "parent": { "title": "Parent", @@ -498,9 +498,27 @@ "title": "Const", "type": "object" }, + "CustomConst": { + "additionalProperties": true, + "properties": { + "c": { + "title": "C", + "type": "string" + }, + "v": { + "title": "V" + } + }, + "required": [ + "c", + "v" + ], + "title": "CustomConst", + "type": "object" + }, "CustomOp": { "additionalProperties": true, - "description": "A user-defined operation that can be downcasted by the extensions that define\nit.", + "description": "A user-defined operation that can be downcasted by the extensions that define it.", "properties": { "parent": { "title": "Parent", @@ -606,7 +624,7 @@ }, "DataflowBlock": { "additionalProperties": true, - "description": "A CFG basic block node. The signature is that of the internal Dataflow\ngraph.", + "description": "A CFG basic block node. The signature is that of the internal Dataflow graph.", "properties": { "parent": { "title": "Parent", @@ -669,14 +687,15 @@ } }, "required": [ - "parent" + "parent", + "sum_rows" ], "title": "DataflowBlock", "type": "object" }, "ExitBlock": { "additionalProperties": true, - "description": "The single exit node of the CFG, has no children, stores the types of\nthe CFG node output.", + "description": "The single exit node of the CFG, has no children, stores the types of the CFG node output.", "properties": { "parent": { "title": "Parent", @@ -725,7 +744,7 @@ "additionalProperties": true, "description": "An extension constant value, that can check it is of a given [CustomType].", "properties": { - "c": { + "v": { "const": "Extension", "default": "Extension", "enum": [ @@ -734,12 +753,24 @@ "title": "ValueTag", "type": "string" }, - "e": { - "title": "CustomConst" + "extensions": { + "items": { + "type": "string" + }, + "title": "Extensions", + "type": "array" + }, + "typ": { + "$ref": "#/$defs/Type" + }, + "value": { + "$ref": "#/$defs/CustomConst" } }, "required": [ - "e" + "extensions", + "typ", + "value" ], "title": "ExtensionValue", "type": "object" @@ -884,7 +915,7 @@ }, "FunctionType": { "additionalProperties": true, - "description": "A graph encoded as a value. It contains a concrete signature and a set of\nrequired resources.", + "description": "A graph encoded as a value. It contains a concrete signature and a set of required resources.", "properties": { "t": { "const": "G", @@ -928,7 +959,7 @@ "additionalProperties": true, "description": "A higher-order function value.", "properties": { - "c": { + "v": { "const": "Function", "default": "Function", "enum": [ @@ -951,6 +982,15 @@ "additionalProperties": true, "description": "General sum type that explicitly stores the types of the variants.", "properties": { + "t": { + "const": "Sum", + "default": "Sum", + "enum": [ + "Sum" + ], + "title": "T", + "type": "string" + }, "s": { "const": "General", "default": "General", @@ -1432,12 +1472,25 @@ "$ref": "#/$defs/AliasDefn" } ], + "required": [ + "parent", + "op" + ], "title": "OpType" }, "Opaque": { "additionalProperties": true, "description": "An opaque Type that can be downcasted by the extensions that define it.", "properties": { + "t": { + "const": "Opaque", + "default": "Opaque", + "enum": [ + "Opaque" + ], + "title": "T", + "type": "string" + }, "extension": { "title": "Extension", "type": "string" @@ -1562,7 +1615,7 @@ }, "PolyFuncType": { "additionalProperties": true, - "description": "A polymorphic type scheme, i.e. of a FuncDecl, FuncDefn or OpDef.\n(Nodes/operations in the Hugr are not polymorphic.)", + "description": "A polymorphic type scheme, i.e. of a FuncDecl, FuncDefn or OpDef. (Nodes/operations in the Hugr are not polymorphic.)", "properties": { "params": { "items": { @@ -1641,13 +1694,16 @@ "$ref": "#/$defs/GeneralSum" } ], + "required": [ + "s" + ], "title": "SumType" }, "SumValue": { "additionalProperties": true, - "description": "A Sum variant\n\nFor any Sum type where this value meets the type of the variant indicated by the tag", + "description": "A Sum variant For any Sum type where this value meets the type of the variant indicated by the tag.", "properties": { - "c": { + "v": { "const": "Sum", "default": "Sum", "enum": [ @@ -1734,50 +1790,6 @@ "title": "Tag", "type": "object" }, - "TaggedOpaque": { - "additionalProperties": true, - "properties": { - "t": { - "const": "Opaque", - "default": "Opaque", - "enum": [ - "Opaque" - ], - "title": "T", - "type": "string" - }, - "o": { - "$ref": "#/$defs/Opaque" - } - }, - "required": [ - "o" - ], - "title": "TaggedOpaque", - "type": "object" - }, - "TaggedSumType": { - "additionalProperties": true, - "properties": { - "t": { - "const": "Sum", - "default": "Sum", - "enum": [ - "Sum" - ], - "title": "T", - "type": "string" - }, - "st": { - "$ref": "#/$defs/SumType" - } - }, - "required": [ - "st" - ], - "title": "TaggedSumType", - "type": "object" - }, "TailLoop": { "additionalProperties": true, "description": "Tail-controlled loop.", @@ -1868,7 +1880,7 @@ "additionalProperties": true, "description": "A constant tuple value.", "properties": { - "c": { + "v": { "const": "Tuple", "default": "Tuple", "enum": [ @@ -1899,9 +1911,9 @@ "Array": "#/$defs/Array", "G": "#/$defs/FunctionType", "I": "#/$defs/USize", - "Opaque": "#/$defs/TaggedOpaque", + "Opaque": "#/$defs/Opaque", "Q": "#/$defs/Qubit", - "Sum": "#/$defs/TaggedSumType", + "Sum": "#/$defs/SumType", "V": "#/$defs/Variable" }, "propertyName": "t" @@ -1923,15 +1935,18 @@ "$ref": "#/$defs/Array" }, { - "$ref": "#/$defs/TaggedSumType" + "$ref": "#/$defs/SumType" }, { - "$ref": "#/$defs/TaggedOpaque" + "$ref": "#/$defs/Opaque" }, { "$ref": "#/$defs/Alias" } ], + "required": [ + "t" + ], "title": "Type" }, "TypeArg": { @@ -1967,6 +1982,9 @@ "$ref": "#/$defs/VariableArg" } ], + "required": [ + "tya" + ], "title": "TypeArg" }, "TypeBound": { @@ -2011,6 +2029,9 @@ "$ref": "#/$defs/ExtensionsParam" } ], + "required": [ + "tp" + ], "title": "TypeParam" }, "TypeTypeArg": { @@ -2078,6 +2099,15 @@ "additionalProperties": true, "description": "Simple sum type where all variants are empty tuples.", "properties": { + "t": { + "const": "Sum", + "default": "Sum", + "enum": [ + "Sum" + ], + "title": "T", + "type": "string" + }, "s": { "const": "Unit", "default": "Unit", @@ -2153,7 +2183,7 @@ "Sum": "#/$defs/SumValue", "Tuple": "#/$defs/TupleValue" }, - "propertyName": "c" + "propertyName": "v" }, "oneOf": [ { @@ -2169,6 +2199,9 @@ "$ref": "#/$defs/SumValue" } ], + "required": [ + "v" + ], "title": "Value" }, "Variable": { diff --git a/specification/schema/testing_hugr_schema_strict_v1.json b/specification/schema/testing_hugr_schema_strict_v1.json index 2747617b1..776715a99 100644 --- a/specification/schema/testing_hugr_schema_strict_v1.json +++ b/specification/schema/testing_hugr_schema_strict_v1.json @@ -247,7 +247,7 @@ }, "Call": { "additionalProperties": false, - "description": "Call a function directly.\n\nThe first port is connected to the def/declare of the function being called\ndirectly, with a `ConstE` edge. The signature of the remaining ports matches\nthe function being called.", + "description": "Operation to call a function directly. The first port is connected to the def/declare of the function being called directly, with a `Static` edge. The signature of the remaining ports matches the function being called.", "properties": { "parent": { "title": "Parent", @@ -498,9 +498,27 @@ "title": "Const", "type": "object" }, + "CustomConst": { + "additionalProperties": false, + "properties": { + "c": { + "title": "C", + "type": "string" + }, + "v": { + "title": "V" + } + }, + "required": [ + "c", + "v" + ], + "title": "CustomConst", + "type": "object" + }, "CustomOp": { "additionalProperties": false, - "description": "A user-defined operation that can be downcasted by the extensions that define\nit.", + "description": "A user-defined operation that can be downcasted by the extensions that define it.", "properties": { "parent": { "title": "Parent", @@ -606,7 +624,7 @@ }, "DataflowBlock": { "additionalProperties": false, - "description": "A CFG basic block node. The signature is that of the internal Dataflow\ngraph.", + "description": "A CFG basic block node. The signature is that of the internal Dataflow graph.", "properties": { "parent": { "title": "Parent", @@ -669,14 +687,15 @@ } }, "required": [ - "parent" + "parent", + "sum_rows" ], "title": "DataflowBlock", "type": "object" }, "ExitBlock": { "additionalProperties": false, - "description": "The single exit node of the CFG, has no children, stores the types of\nthe CFG node output.", + "description": "The single exit node of the CFG, has no children, stores the types of the CFG node output.", "properties": { "parent": { "title": "Parent", @@ -725,7 +744,7 @@ "additionalProperties": false, "description": "An extension constant value, that can check it is of a given [CustomType].", "properties": { - "c": { + "v": { "const": "Extension", "default": "Extension", "enum": [ @@ -734,12 +753,24 @@ "title": "ValueTag", "type": "string" }, - "e": { - "title": "CustomConst" + "extensions": { + "items": { + "type": "string" + }, + "title": "Extensions", + "type": "array" + }, + "typ": { + "$ref": "#/$defs/Type" + }, + "value": { + "$ref": "#/$defs/CustomConst" } }, "required": [ - "e" + "extensions", + "typ", + "value" ], "title": "ExtensionValue", "type": "object" @@ -884,7 +915,7 @@ }, "FunctionType": { "additionalProperties": false, - "description": "A graph encoded as a value. It contains a concrete signature and a set of\nrequired resources.", + "description": "A graph encoded as a value. It contains a concrete signature and a set of required resources.", "properties": { "t": { "const": "G", @@ -928,7 +959,7 @@ "additionalProperties": false, "description": "A higher-order function value.", "properties": { - "c": { + "v": { "const": "Function", "default": "Function", "enum": [ @@ -951,6 +982,15 @@ "additionalProperties": false, "description": "General sum type that explicitly stores the types of the variants.", "properties": { + "t": { + "const": "Sum", + "default": "Sum", + "enum": [ + "Sum" + ], + "title": "T", + "type": "string" + }, "s": { "const": "General", "default": "General", @@ -1432,12 +1472,25 @@ "$ref": "#/$defs/AliasDefn" } ], + "required": [ + "parent", + "op" + ], "title": "OpType" }, "Opaque": { "additionalProperties": false, "description": "An opaque Type that can be downcasted by the extensions that define it.", "properties": { + "t": { + "const": "Opaque", + "default": "Opaque", + "enum": [ + "Opaque" + ], + "title": "T", + "type": "string" + }, "extension": { "title": "Extension", "type": "string" @@ -1562,7 +1615,7 @@ }, "PolyFuncType": { "additionalProperties": false, - "description": "A polymorphic type scheme, i.e. of a FuncDecl, FuncDefn or OpDef.\n(Nodes/operations in the Hugr are not polymorphic.)", + "description": "A polymorphic type scheme, i.e. of a FuncDecl, FuncDefn or OpDef. (Nodes/operations in the Hugr are not polymorphic.)", "properties": { "params": { "items": { @@ -1641,13 +1694,16 @@ "$ref": "#/$defs/GeneralSum" } ], + "required": [ + "s" + ], "title": "SumType" }, "SumValue": { "additionalProperties": false, - "description": "A Sum variant\n\nFor any Sum type where this value meets the type of the variant indicated by the tag", + "description": "A Sum variant For any Sum type where this value meets the type of the variant indicated by the tag.", "properties": { - "c": { + "v": { "const": "Sum", "default": "Sum", "enum": [ @@ -1734,50 +1790,6 @@ "title": "Tag", "type": "object" }, - "TaggedOpaque": { - "additionalProperties": false, - "properties": { - "t": { - "const": "Opaque", - "default": "Opaque", - "enum": [ - "Opaque" - ], - "title": "T", - "type": "string" - }, - "o": { - "$ref": "#/$defs/Opaque" - } - }, - "required": [ - "o" - ], - "title": "TaggedOpaque", - "type": "object" - }, - "TaggedSumType": { - "additionalProperties": false, - "properties": { - "t": { - "const": "Sum", - "default": "Sum", - "enum": [ - "Sum" - ], - "title": "T", - "type": "string" - }, - "st": { - "$ref": "#/$defs/SumType" - } - }, - "required": [ - "st" - ], - "title": "TaggedSumType", - "type": "object" - }, "TailLoop": { "additionalProperties": false, "description": "Tail-controlled loop.", @@ -1868,7 +1880,7 @@ "additionalProperties": false, "description": "A constant tuple value.", "properties": { - "c": { + "v": { "const": "Tuple", "default": "Tuple", "enum": [ @@ -1899,9 +1911,9 @@ "Array": "#/$defs/Array", "G": "#/$defs/FunctionType", "I": "#/$defs/USize", - "Opaque": "#/$defs/TaggedOpaque", + "Opaque": "#/$defs/Opaque", "Q": "#/$defs/Qubit", - "Sum": "#/$defs/TaggedSumType", + "Sum": "#/$defs/SumType", "V": "#/$defs/Variable" }, "propertyName": "t" @@ -1923,15 +1935,18 @@ "$ref": "#/$defs/Array" }, { - "$ref": "#/$defs/TaggedSumType" + "$ref": "#/$defs/SumType" }, { - "$ref": "#/$defs/TaggedOpaque" + "$ref": "#/$defs/Opaque" }, { "$ref": "#/$defs/Alias" } ], + "required": [ + "t" + ], "title": "Type" }, "TypeArg": { @@ -1967,6 +1982,9 @@ "$ref": "#/$defs/VariableArg" } ], + "required": [ + "tya" + ], "title": "TypeArg" }, "TypeBound": { @@ -2011,6 +2029,9 @@ "$ref": "#/$defs/ExtensionsParam" } ], + "required": [ + "tp" + ], "title": "TypeParam" }, "TypeTypeArg": { @@ -2078,6 +2099,15 @@ "additionalProperties": false, "description": "Simple sum type where all variants are empty tuples.", "properties": { + "t": { + "const": "Sum", + "default": "Sum", + "enum": [ + "Sum" + ], + "title": "T", + "type": "string" + }, "s": { "const": "Unit", "default": "Unit", @@ -2153,7 +2183,7 @@ "Sum": "#/$defs/SumValue", "Tuple": "#/$defs/TupleValue" }, - "propertyName": "c" + "propertyName": "v" }, "oneOf": [ { @@ -2169,6 +2199,9 @@ "$ref": "#/$defs/SumValue" } ], + "required": [ + "v" + ], "title": "Value" }, "Variable": { diff --git a/specification/schema/testing_hugr_schema_v1.json b/specification/schema/testing_hugr_schema_v1.json index 62c062626..ca1d49c8f 100644 --- a/specification/schema/testing_hugr_schema_v1.json +++ b/specification/schema/testing_hugr_schema_v1.json @@ -247,7 +247,7 @@ }, "Call": { "additionalProperties": true, - "description": "Call a function directly.\n\nThe first port is connected to the def/declare of the function being called\ndirectly, with a `ConstE` edge. The signature of the remaining ports matches\nthe function being called.", + "description": "Operation to call a function directly. The first port is connected to the def/declare of the function being called directly, with a `Static` edge. The signature of the remaining ports matches the function being called.", "properties": { "parent": { "title": "Parent", @@ -498,9 +498,27 @@ "title": "Const", "type": "object" }, + "CustomConst": { + "additionalProperties": true, + "properties": { + "c": { + "title": "C", + "type": "string" + }, + "v": { + "title": "V" + } + }, + "required": [ + "c", + "v" + ], + "title": "CustomConst", + "type": "object" + }, "CustomOp": { "additionalProperties": true, - "description": "A user-defined operation that can be downcasted by the extensions that define\nit.", + "description": "A user-defined operation that can be downcasted by the extensions that define it.", "properties": { "parent": { "title": "Parent", @@ -606,7 +624,7 @@ }, "DataflowBlock": { "additionalProperties": true, - "description": "A CFG basic block node. The signature is that of the internal Dataflow\ngraph.", + "description": "A CFG basic block node. The signature is that of the internal Dataflow graph.", "properties": { "parent": { "title": "Parent", @@ -669,14 +687,15 @@ } }, "required": [ - "parent" + "parent", + "sum_rows" ], "title": "DataflowBlock", "type": "object" }, "ExitBlock": { "additionalProperties": true, - "description": "The single exit node of the CFG, has no children, stores the types of\nthe CFG node output.", + "description": "The single exit node of the CFG, has no children, stores the types of the CFG node output.", "properties": { "parent": { "title": "Parent", @@ -725,7 +744,7 @@ "additionalProperties": true, "description": "An extension constant value, that can check it is of a given [CustomType].", "properties": { - "c": { + "v": { "const": "Extension", "default": "Extension", "enum": [ @@ -734,12 +753,24 @@ "title": "ValueTag", "type": "string" }, - "e": { - "title": "CustomConst" + "extensions": { + "items": { + "type": "string" + }, + "title": "Extensions", + "type": "array" + }, + "typ": { + "$ref": "#/$defs/Type" + }, + "value": { + "$ref": "#/$defs/CustomConst" } }, "required": [ - "e" + "extensions", + "typ", + "value" ], "title": "ExtensionValue", "type": "object" @@ -884,7 +915,7 @@ }, "FunctionType": { "additionalProperties": true, - "description": "A graph encoded as a value. It contains a concrete signature and a set of\nrequired resources.", + "description": "A graph encoded as a value. It contains a concrete signature and a set of required resources.", "properties": { "t": { "const": "G", @@ -928,7 +959,7 @@ "additionalProperties": true, "description": "A higher-order function value.", "properties": { - "c": { + "v": { "const": "Function", "default": "Function", "enum": [ @@ -951,6 +982,15 @@ "additionalProperties": true, "description": "General sum type that explicitly stores the types of the variants.", "properties": { + "t": { + "const": "Sum", + "default": "Sum", + "enum": [ + "Sum" + ], + "title": "T", + "type": "string" + }, "s": { "const": "General", "default": "General", @@ -1432,12 +1472,25 @@ "$ref": "#/$defs/AliasDefn" } ], + "required": [ + "parent", + "op" + ], "title": "OpType" }, "Opaque": { "additionalProperties": true, "description": "An opaque Type that can be downcasted by the extensions that define it.", "properties": { + "t": { + "const": "Opaque", + "default": "Opaque", + "enum": [ + "Opaque" + ], + "title": "T", + "type": "string" + }, "extension": { "title": "Extension", "type": "string" @@ -1562,7 +1615,7 @@ }, "PolyFuncType": { "additionalProperties": true, - "description": "A polymorphic type scheme, i.e. of a FuncDecl, FuncDefn or OpDef.\n(Nodes/operations in the Hugr are not polymorphic.)", + "description": "A polymorphic type scheme, i.e. of a FuncDecl, FuncDefn or OpDef. (Nodes/operations in the Hugr are not polymorphic.)", "properties": { "params": { "items": { @@ -1641,13 +1694,16 @@ "$ref": "#/$defs/GeneralSum" } ], + "required": [ + "s" + ], "title": "SumType" }, "SumValue": { "additionalProperties": true, - "description": "A Sum variant\n\nFor any Sum type where this value meets the type of the variant indicated by the tag", + "description": "A Sum variant For any Sum type where this value meets the type of the variant indicated by the tag.", "properties": { - "c": { + "v": { "const": "Sum", "default": "Sum", "enum": [ @@ -1734,50 +1790,6 @@ "title": "Tag", "type": "object" }, - "TaggedOpaque": { - "additionalProperties": true, - "properties": { - "t": { - "const": "Opaque", - "default": "Opaque", - "enum": [ - "Opaque" - ], - "title": "T", - "type": "string" - }, - "o": { - "$ref": "#/$defs/Opaque" - } - }, - "required": [ - "o" - ], - "title": "TaggedOpaque", - "type": "object" - }, - "TaggedSumType": { - "additionalProperties": true, - "properties": { - "t": { - "const": "Sum", - "default": "Sum", - "enum": [ - "Sum" - ], - "title": "T", - "type": "string" - }, - "st": { - "$ref": "#/$defs/SumType" - } - }, - "required": [ - "st" - ], - "title": "TaggedSumType", - "type": "object" - }, "TailLoop": { "additionalProperties": true, "description": "Tail-controlled loop.", @@ -1868,7 +1880,7 @@ "additionalProperties": true, "description": "A constant tuple value.", "properties": { - "c": { + "v": { "const": "Tuple", "default": "Tuple", "enum": [ @@ -1899,9 +1911,9 @@ "Array": "#/$defs/Array", "G": "#/$defs/FunctionType", "I": "#/$defs/USize", - "Opaque": "#/$defs/TaggedOpaque", + "Opaque": "#/$defs/Opaque", "Q": "#/$defs/Qubit", - "Sum": "#/$defs/TaggedSumType", + "Sum": "#/$defs/SumType", "V": "#/$defs/Variable" }, "propertyName": "t" @@ -1923,15 +1935,18 @@ "$ref": "#/$defs/Array" }, { - "$ref": "#/$defs/TaggedSumType" + "$ref": "#/$defs/SumType" }, { - "$ref": "#/$defs/TaggedOpaque" + "$ref": "#/$defs/Opaque" }, { "$ref": "#/$defs/Alias" } ], + "required": [ + "t" + ], "title": "Type" }, "TypeArg": { @@ -1967,6 +1982,9 @@ "$ref": "#/$defs/VariableArg" } ], + "required": [ + "tya" + ], "title": "TypeArg" }, "TypeBound": { @@ -2011,6 +2029,9 @@ "$ref": "#/$defs/ExtensionsParam" } ], + "required": [ + "tp" + ], "title": "TypeParam" }, "TypeTypeArg": { @@ -2078,6 +2099,15 @@ "additionalProperties": true, "description": "Simple sum type where all variants are empty tuples.", "properties": { + "t": { + "const": "Sum", + "default": "Sum", + "enum": [ + "Sum" + ], + "title": "T", + "type": "string" + }, "s": { "const": "Unit", "default": "Unit", @@ -2153,7 +2183,7 @@ "Sum": "#/$defs/SumValue", "Tuple": "#/$defs/TupleValue" }, - "propertyName": "c" + "propertyName": "v" }, "oneOf": [ { @@ -2169,6 +2199,9 @@ "$ref": "#/$defs/SumValue" } ], + "required": [ + "v" + ], "title": "Value" }, "Variable": {