Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions hugr-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ semver = { workspace = true, features = ["serde"] }
zstd = { workspace = true, optional = true }
ordered-float = { workspace = true, features = ["serde"] }
base64.workspace = true
relrc = { workspace = true, features = ["petgraph", "serde"] }
smallvec = "1.15.0"
tracing = "0.1.41"

[dev-dependencies]
rstest = { workspace = true }
Expand Down
29 changes: 20 additions & 9 deletions hugr-core/src/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -945,22 +945,17 @@ impl<'a> Context<'a> {

self.make_term_apply(model::CORE_TYPE, &[])
}
Term::BoundedNatType { .. } => self.make_term_apply(model::CORE_NAT_TYPE, &[]),
Term::BoundedNatType(_) => self.make_term_apply(model::CORE_NAT_TYPE, &[]),
Term::StringType => self.make_term_apply(model::CORE_STR_TYPE, &[]),
Term::BytesType => self.make_term_apply(model::CORE_BYTES_TYPE, &[]),
Term::FloatType => self.make_term_apply(model::CORE_FLOAT_TYPE, &[]),
Term::ListType(item_type) => {
let item_type = self.export_term(item_type, None);
self.make_term_apply(model::CORE_LIST_TYPE, &[item_type])
}
Term::TupleType(params) => {
let item_types = self.bump.alloc_slice_fill_iter(
params
.iter()
.map(|param| table::SeqPart::Item(self.export_term(param, None))),
);
let types = self.make_term(table::Term::List(item_types));
self.make_term_apply(model::CORE_TUPLE_TYPE, &[types])
Term::TupleType(item_types) => {
let item_types = self.export_term(item_types, None);
self.make_term_apply(model::CORE_TUPLE_TYPE, &[item_types])
}
Term::Runtime(ty) => self.export_type(ty),
Term::BoundedNat(value) => self.make_term(model::Literal::Nat(*value).into()),
Expand All @@ -975,6 +970,14 @@ impl<'a> Context<'a> {
);
self.make_term(table::Term::List(parts))
}
Term::ListConcat(lists) => {
let parts = self.bump.alloc_slice_fill_iter(
lists
.iter()
.map(|elem| table::SeqPart::Splice(self.export_term(elem, None))),
);
self.make_term(table::Term::List(parts))
}
Term::Tuple(elems) => {
let parts = self.bump.alloc_slice_fill_iter(
elems
Expand All @@ -983,6 +986,14 @@ impl<'a> Context<'a> {
);
self.make_term(table::Term::Tuple(parts))
}
Term::TupleConcat(tuples) => {
let parts = self.bump.alloc_slice_fill_iter(
tuples
.iter()
.map(|elem| table::SeqPart::Splice(self.export_term(elem, None))),
);
self.make_term(table::Term::Tuple(parts))
}
Term::Variable(v) => self.export_type_arg_var(v),
Term::StaticType => self.make_term_apply(model::CORE_STATIC, &[]),
}
Expand Down
12 changes: 10 additions & 2 deletions hugr-core/src/extension/resolution/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,16 @@ pub(super) fn collect_term_exts(
collect_term_exts(item_type, used_extensions, missing_extensions)
}
Term::TupleType(item_types) => {
for item_type in item_types {
collect_term_exts(item_type, used_extensions, missing_extensions);
collect_term_exts(item_types, used_extensions, missing_extensions)
}
Term::ListConcat(lists) => {
for list in lists {
collect_term_exts(list, used_extensions, missing_extensions);
}
}
Term::TupleConcat(tuples) => {
for tuple in tuples {
collect_term_exts(tuple, used_extensions, missing_extensions);
}
}
Term::Variable(_)
Expand Down
20 changes: 8 additions & 12 deletions hugr-core/src/extension/resolution/types_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,23 +222,19 @@ pub(super) fn resolve_term_exts(
) -> Result<(), ExtensionResolutionError> {
match term {
Term::Runtime(ty) => resolve_type_exts(node, ty, extensions, used_extensions)?,
Term::List(elems) => {
for elem in elems.iter_mut() {
resolve_term_exts(node, elem, extensions, used_extensions)?;
}
}
Term::Tuple(elems) => {
for elem in elems.iter_mut() {
resolve_term_exts(node, elem, extensions, used_extensions)?;
Term::List(children)
| Term::ListConcat(children)
| Term::Tuple(children)
| Term::TupleConcat(children) => {
for child in children.iter_mut() {
resolve_term_exts(node, child, extensions, used_extensions)?;
}
}
Term::ListType(item_type) => {
resolve_term_exts(node, item_type, extensions, used_extensions)?;
resolve_term_exts(node, item_type.as_mut(), extensions, used_extensions)?;
}
Term::TupleType(item_types) => {
for item_type in item_types.iter_mut() {
resolve_term_exts(node, item_type, extensions, used_extensions)?;
}
resolve_term_exts(node, item_types.as_mut(), extensions, used_extensions)?;
}
Term::Variable(_)
| Term::RuntimeType(_)
Expand Down
4 changes: 2 additions & 2 deletions hugr-core/src/hugr/serialize/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ fn polyfunctype2() -> PolyFuncTypeRV {
#[case(PolyFuncType::new([TypeParam::StringType], Signature::new_endo(vec![Type::new_var_use(0, TypeBound::Copyable)])))]
#[case(PolyFuncType::new([TypeBound::Copyable.into()], Signature::new_endo(vec![Type::new_var_use(0, TypeBound::Copyable)])))]
#[case(PolyFuncType::new([TypeParam::new_list_type(TypeBound::Any)], Signature::new_endo(type_row![])))]
#[case(PolyFuncType::new([TypeParam::TupleType([TypeBound::Any.into(), TypeParam::bounded_nat_type(2.try_into().unwrap())].into())], Signature::new_endo(type_row![])))]
#[case(PolyFuncType::new([TypeParam::new_tuple_type([TypeBound::Any.into(), TypeParam::bounded_nat_type(2.try_into().unwrap())])], Signature::new_endo(type_row![])))]
#[case(PolyFuncType::new(
[TypeParam::new_list_type(TypeBound::Any)],
Signature::new_endo(Type::new_tuple(TypeRV::new_row_var_use(0, TypeBound::Any)))))]
Expand All @@ -495,7 +495,7 @@ fn roundtrip_polyfunctype_fixedlen(#[case] poly_func_type: PolyFuncType) {
#[case(PolyFuncTypeRV::new([TypeParam::StringType], FuncValueType::new_endo(vec![Type::new_var_use(0, TypeBound::Copyable)])))]
#[case(PolyFuncTypeRV::new([TypeBound::Copyable.into()], FuncValueType::new_endo(vec![Type::new_var_use(0, TypeBound::Copyable)])))]
#[case(PolyFuncTypeRV::new([TypeParam::new_list_type(TypeBound::Any)], FuncValueType::new_endo(type_row![])))]
#[case(PolyFuncTypeRV::new([TypeParam::TupleType([TypeBound::Any.into(), TypeParam::bounded_nat_type(2.try_into().unwrap())].into())], FuncValueType::new_endo(type_row![])))]
#[case(PolyFuncTypeRV::new([TypeParam::new_tuple_type([TypeBound::Any.into(), TypeParam::bounded_nat_type(2.try_into().unwrap())])], FuncValueType::new_endo(type_row![])))]
#[case(PolyFuncTypeRV::new(
[TypeParam::new_list_type(TypeBound::Any)],
FuncValueType::new_endo(TypeRV::new_row_var_use(0, TypeBound::Any))))]
Expand Down
63 changes: 33 additions & 30 deletions hugr-core/src/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@ use crate::{
types::{
CustomType, FuncTypeBase, MaybeRV, PolyFuncType, PolyFuncTypeBase, RowVariable, Signature,
Term, Type, TypeArg, TypeBase, TypeBound, TypeEnum, TypeName, TypeRow,
type_param::TypeParam, type_row::TypeRowBase,
type_param::{SeqPart, TypeParam},
type_row::TypeRowBase,
},
};
use fxhash::FxHashMap;
use hugr_model::v0 as model;
use hugr_model::v0::table;
use itertools::Either;
use itertools::{Either, Itertools};
use smol_str::{SmolStr, ToSmolStr};
use thiserror::Error;

Expand Down Expand Up @@ -1255,14 +1256,10 @@ impl<'a> Context<'a> {
if let Some([item_types]) = self.match_symbol(term_id, model::CORE_TUPLE_TYPE)? {
// At present `hugr-model` has no way to express that the item
// types of a tuple must be copyable. Therefore we import it as `Any`.
let item_types = (|| {
self.import_closed_list(item_types)?
.into_iter()
.map(|param| self.import_term(param))
.collect::<Result<_, _>>()
})()
.map_err(|err| error_context!(err, "item types of tuple type"))?;
return Ok(TypeParam::TupleType(item_types));
let item_types = self
.import_term(item_types)
.map_err(|err| error_context!(err, "item types of tuple type"))?;
return Ok(TypeParam::new_tuple_type(item_types));
}

match self.get_term(term_id)? {
Expand All @@ -1277,28 +1274,24 @@ impl<'a> Context<'a> {
Ok(Term::new_var_use(var.1 as _, decl))
}

table::Term::List { .. } => {
let elems = (|| {
self.import_closed_list(term_id)?
.iter()
.map(|item| self.import_term(*item))
.collect::<Result<_, _>>()
})()
.map_err(|err| error_context!(err, "list items"))?;

Ok(Term::List(elems))
table::Term::List(parts) => {
// PERFORMANCE: Can we do this without the additional allocation?
let parts: Vec<_> = parts
.iter()
.map(|part| self.import_seq_part(part))
.collect::<Result<_, _>>()
.map_err(|err| error_context!(err, "list parts"))?;
Ok(TypeArg::new_list_from_parts(parts))
}

table::Term::Tuple { .. } => {
let elems = (|| {
self.import_closed_list(term_id)?
.iter()
.map(|item| self.import_term(*item))
.collect::<Result<_, _>>()
})()
.map_err(|err| error_context!(err, "tuple items"))?;

Ok(Term::Tuple(elems))
table::Term::Tuple(parts) => {
// PERFORMANCE: Can we do this without the additional allocation?
let parts: Vec<_> = parts
.iter()
.map(|part| self.import_seq_part(part))
.try_collect()
.map_err(|err| error_context!(err, "tuple parts"))?;
Ok(TypeArg::new_tuple_from_parts(parts))
}

table::Term::Literal(model::Literal::Str(value)) => {
Expand All @@ -1322,6 +1315,16 @@ impl<'a> Context<'a> {
.map_err(|err| error_context!(err, "term {}", term_id))
}

fn import_seq_part(
&mut self,
seq_part: &'a table::SeqPart,
) -> Result<SeqPart<TypeArg>, ImportError> {
Ok(match seq_part {
table::SeqPart::Item(term_id) => SeqPart::Item(self.import_term(*term_id)?),
table::SeqPart::Splice(term_id) => SeqPart::Splice(self.import_term(*term_id)?),
})
}

/// Import a `Type` from a term that represents a runtime type.
fn import_type<RV: MaybeRV>(
&mut self,
Expand Down
2 changes: 1 addition & 1 deletion hugr-core/src/types/poly_func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ pub(crate) mod test {
for decl in [
Term::new_list_type(Term::max_nat_type()),
Term::StringType,
Term::TupleType(vec![TypeBound::Any.into(), Term::max_nat_type()]),
Term::new_tuple_type([TypeBound::Any.into(), Term::max_nat_type()]),
] {
let invalid_ts = PolyFuncTypeBase::new_validated([decl.clone()], body_type.clone());
assert_eq!(
Expand Down
44 changes: 41 additions & 3 deletions hugr-core/src/types/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ pub(super) enum TypeParamSer {
Float,
StaticType,
List { param: Box<Term> },
Tuple { params: Vec<Term> },
Tuple { params: ArrayOrTermSer },
}

#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
Expand All @@ -104,9 +104,15 @@ pub(super) enum TypeArgSer {
List {
elems: Vec<Term>,
},
ListConcat {
lists: Vec<Term>,
},
Tuple {
elems: Vec<Term>,
},
TupleConcat {
tuples: Vec<Term>,
},
Variable {
#[serde(flatten)]
v: TermVar,
Expand All @@ -130,15 +136,19 @@ impl From<Term> for TermSer {
Term::BytesType => TermSer::TypeParam(TypeParamSer::Bytes),
Term::FloatType => TermSer::TypeParam(TypeParamSer::Float),
Term::ListType(param) => TermSer::TypeParam(TypeParamSer::List { param }),
Term::TupleType(params) => TermSer::TypeParam(TypeParamSer::Tuple { params }),
Term::Runtime(ty) => TermSer::TypeArg(TypeArgSer::Type { ty }),
Term::TupleType(params) => TermSer::TypeParam(TypeParamSer::Tuple {
params: (*params).into(),
}),
Term::BoundedNat(n) => TermSer::TypeArg(TypeArgSer::BoundedNat { n }),
Term::String(arg) => TermSer::TypeArg(TypeArgSer::String { arg }),
Term::Bytes(value) => TermSer::TypeArg(TypeArgSer::Bytes { value }),
Term::Float(value) => TermSer::TypeArg(TypeArgSer::Float { value }),
Term::List(elems) => TermSer::TypeArg(TypeArgSer::List { elems }),
Term::Tuple(elems) => TermSer::TypeArg(TypeArgSer::Tuple { elems }),
Term::Variable(v) => TermSer::TypeArg(TypeArgSer::Variable { v }),
Term::ListConcat(lists) => TermSer::TypeArg(TypeArgSer::ListConcat { lists }),
Term::TupleConcat(tuples) => TermSer::TypeArg(TypeArgSer::TupleConcat { tuples }),
}
}
}
Expand All @@ -154,7 +164,7 @@ impl From<TermSer> for Term {
TypeParamSer::Bytes => Term::BytesType,
TypeParamSer::Float => Term::FloatType,
TypeParamSer::List { param } => Term::ListType(param),
TypeParamSer::Tuple { params } => Term::TupleType(params),
TypeParamSer::Tuple { params } => Term::TupleType(Box::new(params.into())),
},
TermSer::TypeArg(arg) => match arg {
TypeArgSer::Type { ty } => Term::Runtime(ty),
Expand All @@ -165,11 +175,39 @@ impl From<TermSer> for Term {
TypeArgSer::List { elems } => Term::List(elems),
TypeArgSer::Tuple { elems } => Term::Tuple(elems),
TypeArgSer::Variable { v } => Term::Variable(v),
TypeArgSer::ListConcat { lists } => Term::ListConcat(lists),
TypeArgSer::TupleConcat { tuples } => Term::TupleConcat(tuples),
},
}
}
}

/// Helper type that serialises lists as JSON arrays for compatibility.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
pub(super) enum ArrayOrTermSer {
Array(Vec<Term>),
Term(Box<Term>),
}

impl From<ArrayOrTermSer> for Term {
fn from(value: ArrayOrTermSer) -> Self {
match value {
ArrayOrTermSer::Array(terms) => Term::new_list(terms),
ArrayOrTermSer::Term(term) => *term,
}
}
}

impl From<Term> for ArrayOrTermSer {
fn from(term: Term) -> Self {
match term {
Term::List(terms) => ArrayOrTermSer::Array(terms),
term => ArrayOrTermSer::Term(Box::new(term)),
}
}
}

/// Helper for to serialize and deserialize the byte string in [`TypeArg::Bytes`] via base64.
mod base64 {
use std::sync::Arc;
Expand Down
Loading
Loading