1use std::sync::Arc;
7
8use crate::{
9 Direction, Hugr, HugrView, Node, Port,
10 extension::{
11 ExtensionId, ExtensionRegistry, SignatureError, resolution::ExtensionResolutionError,
12 },
13 hugr::{HugrMut, NodeMetadata},
14 ops::{
15 AliasDecl, AliasDefn, CFG, Call, CallIndirect, Case, Conditional, Const, DFG,
16 DataflowBlock, ExitBlock, FuncDecl, FuncDefn, Input, LoadConstant, LoadFunction, OpType,
17 OpaqueOp, Output, Tag, TailLoop, Value,
18 constant::{CustomConst, CustomSerialized, OpaqueValue},
19 },
20 package::Package,
21 std_extensions::{
22 arithmetic::{float_types::ConstF64, int_types::ConstInt},
23 collections::array::ArrayValue,
24 },
25 types::{
26 CustomType, FuncTypeBase, MaybeRV, PolyFuncType, PolyFuncTypeBase, RowVariable, Signature,
27 Term, Type, TypeArg, TypeBase, TypeBound, TypeEnum, TypeName, TypeRow,
28 type_param::{SeqPart, TypeParam},
29 type_row::TypeRowBase,
30 },
31};
32use hugr_model::v0::table;
33use hugr_model::v0::{self as model};
34use itertools::{Either, Itertools};
35use rustc_hash::FxHashMap;
36use smol_str::{SmolStr, ToSmolStr};
37use thiserror::Error;
38
39fn gen_str(generator: &Option<String>) -> String {
40 match generator {
41 Some(g) => format!(" generated by {g}"),
42 None => String::new(),
43 }
44}
45
46#[derive(Debug, Clone, Error)]
48#[error("failed to import hugr{}", gen_str(&self.generator))]
49pub struct ImportError {
50 #[source]
51 inner: ImportErrorInner,
52 generator: Option<String>,
53}
54
55const UNSUPPORTED_HINT: &str = concat!(
56 "Hint: The import process encountered a `hugr-model` feature ",
57 "that is currently unsupported in `hugr-core`. ",
58 "As `hugr-core` evolves towards the same expressivity as `hugr-model` ",
59 "we expect that errors of this kind will be removed.",
60);
61
62const UNINFERRED_HINT: &str = concat!(
63 "Hint: The import process encountered implicit information in the `hugr-model` ",
64 "package that it can not yet fill in. ",
65 "Such implicit information can be signatures for nodes and regions, ",
66 "wildcard terms or symbol applications with fewer arguments than the symbol has parameters. ",
67 "Until more comprehensive inference is implemented, `hugr-model` packages will need to be very explicit. ",
68 "To avoid this error, make sure to include as much information as possible when generating packages."
69);
70
71const CLOSED_TUPLE_HINT: &str = concat!(
73 "Hint: A tuple is closed if all of its items are known. ",
74 "Closed tuples can contain spliced subtuples, as long as they can be recursively flattened ",
75 "into a tuple that only contains individual items."
76);
77
78const CLOSED_LIST_HINT: &str = concat!(
80 "Hint: A list is closed if all of its items are known. ",
81 "Closed lists can contain spliced sublists, as long as they can be recursively flattened ",
82 "into a list that only contains individual items."
83);
84
85#[derive(Debug, Clone, Error)]
86enum ImportErrorInner {
87 #[error("Unsupported: {0}\n{hint}", hint = UNSUPPORTED_HINT)]
91 Unsupported(String),
92
93 #[error("Uninferred implicit: {0}\n{hint}", hint = UNINFERRED_HINT)]
96 Uninferred(String),
97
98 #[error("{0}")]
100 Invalid(String),
101
102 #[error("import failed in context: {1}")]
104 Context(#[source] Box<ImportErrorInner>, String),
105
106 #[error("signature error")]
108 Signature(#[from] SignatureError),
109
110 #[error("extension error")]
112 Extension(#[from] ExtensionError),
113
114 #[error("incorrect order hint")]
116 OrderHint(#[from] OrderHintError),
117
118 #[error("extension resolution error")]
120 ExtensionResolution(#[from] ExtensionResolutionError),
121}
122
123#[derive(Debug, Clone, Error)]
124enum ExtensionError {
125 #[error("Importing the hugr requires extension {missing_ext}, which was not found in the registry. The available extensions are: [{}]",
127 available.iter().map(std::string::ToString::to_string).collect::<Vec<_>>().join(", "))]
128 Missing {
129 missing_ext: ExtensionId,
131 available: Vec<ExtensionId>,
133 },
134
135 #[error(
137 "Importing the hugr requires extension {ext} to have a type named {name}, but it was not found."
138 )]
139 MissingType {
140 ext: ExtensionId,
142 name: TypeName,
144 },
145}
146
147#[derive(Debug, Clone, Error)]
149enum OrderHintError {
150 #[error("duplicate order hint key {0}")]
152 DuplicateKey(table::RegionId, u64),
153 #[error("order hint with unknown key {0}")]
155 UnknownKey(u64),
156 #[error("order hint on node with no order port: {0}")]
158 NoOrderPort(table::NodeId),
159}
160
161macro_rules! error_unsupported {
163 ($($e:expr),*) => { ImportErrorInner::Unsupported(format!($($e),*)) }
164}
165
166macro_rules! error_uninferred {
168 ($($e:expr),*) => { ImportErrorInner::Uninferred(format!($($e),*)) }
169}
170
171macro_rules! error_invalid {
173 ($($e:expr),*) => { ImportErrorInner::Invalid(format!($($e),*)) }
174}
175
176macro_rules! error_context {
178 ($err:expr, $($e:expr),*) => {
179 {
180 ImportErrorInner::Context(Box::new($err), format!($($e),*))
181 }
182 }
183}
184
185pub fn import_package(
188 package: &table::Package,
189 packaged_extensions: ExtensionRegistry,
190 loaded_extensions: &ExtensionRegistry,
191) -> Result<Package, ImportError> {
192 let mut registry = loaded_extensions.clone();
193 registry.extend(&packaged_extensions);
194 let modules = package
195 .modules
196 .iter()
197 .map(|module| import_hugr(module, ®istry))
198 .collect::<Result<Vec<_>, _>>()?;
199
200 let mut package = Package::new(modules);
202 package.extensions = packaged_extensions;
203 Ok(package)
204}
205
206fn get_generator(ctx: &Context<'_>) -> Option<String> {
209 ctx.module
210 .get_region(ctx.module.root)
211 .map(|r| r.meta.iter())
212 .into_iter()
213 .flatten()
214 .find_map(|meta| {
215 let (name, json_val) = ctx.decode_json_meta(*meta).ok()??;
216
217 (name == crate::envelope::GENERATOR_KEY)
218 .then_some(crate::envelope::format_generator(&json_val))
219 })
220}
221
222pub fn import_hugr(
224 module: &table::Module,
225 extensions: &ExtensionRegistry,
226) -> Result<Hugr, ImportError> {
227 let mut ctx = Context {
230 module,
231 hugr: Hugr::new(),
232 link_ports: FxHashMap::default(),
233 static_edges: Vec::new(),
234 extensions,
235 nodes: FxHashMap::default(),
236 local_vars: FxHashMap::default(),
237 custom_name_cache: FxHashMap::default(),
238 region_scope: table::RegionId::default(),
239 };
240
241 let import_steps: [fn(&mut Context) -> _; 3] = [
242 |ctx| ctx.import_root(),
243 |ctx| ctx.link_ports(),
244 |ctx| ctx.link_static_ports(),
245 ];
246
247 for step in import_steps {
248 if let Err(e) = step(&mut ctx) {
249 return Err(ImportError {
250 inner: e,
251 generator: get_generator(&ctx),
252 });
253 }
254 }
255 ctx.hugr
256 .resolve_extension_defs(extensions)
257 .map_err(|e| ImportError {
258 inner: ImportErrorInner::ExtensionResolution(e),
259 generator: get_generator(&ctx),
260 })?;
261 Ok(ctx.hugr)
262}
263
264struct Context<'a> {
265 module: &'a table::Module<'a>,
267
268 hugr: Hugr,
270
271 link_ports: FxHashMap<(table::RegionId, table::LinkIndex), Vec<(Node, Port)>>,
274
275 static_edges: Vec<(table::NodeId, table::NodeId)>,
278
279 extensions: &'a ExtensionRegistry,
281
282 nodes: FxHashMap<table::NodeId, Node>,
284
285 local_vars: FxHashMap<table::VarId, LocalVar>,
286
287 custom_name_cache: FxHashMap<&'a str, (ExtensionId, SmolStr)>,
288
289 region_scope: table::RegionId,
290}
291
292impl<'a> Context<'a> {
293 fn get_node_signature(&mut self, node: table::NodeId) -> Result<Signature, ImportErrorInner> {
295 let node_data = self.get_node(node)?;
296 let signature = node_data
297 .signature
298 .ok_or_else(|| error_uninferred!("node signature"))?;
299 self.import_func_type(signature)
300 }
301
302 #[inline]
304 fn get_node(&self, node_id: table::NodeId) -> Result<&'a table::Node<'a>, ImportErrorInner> {
305 self.module
306 .get_node(node_id)
307 .ok_or_else(|| error_invalid!("unknown node {}", node_id))
308 }
309
310 #[inline]
312 fn get_term(&self, term_id: table::TermId) -> Result<&'a table::Term<'a>, ImportErrorInner> {
313 self.module
314 .get_term(term_id)
315 .ok_or_else(|| error_invalid!("unknown term {}", term_id))
316 }
317
318 #[inline]
320 fn get_region(
321 &self,
322 region_id: table::RegionId,
323 ) -> Result<&'a table::Region<'a>, ImportErrorInner> {
324 self.module
325 .get_region(region_id)
326 .ok_or_else(|| error_invalid!("unknown region {}", region_id))
327 }
328
329 fn make_node(
330 &mut self,
331 node_id: table::NodeId,
332 op: OpType,
333 parent: Node,
334 ) -> Result<Node, ImportErrorInner> {
335 let node = self.hugr.add_node_with_parent(parent, op);
336 self.nodes.insert(node_id, node);
337
338 let node_data = self.get_node(node_id)?;
339 self.record_links(node, Direction::Incoming, node_data.inputs);
340 self.record_links(node, Direction::Outgoing, node_data.outputs);
341
342 for meta_item in node_data.meta {
343 self.import_node_metadata(node, *meta_item)
344 .map_err(|err| error_context!(err, "node metadata"))?;
345 }
346
347 Ok(node)
348 }
349
350 fn import_node_metadata(
351 &mut self,
352 node: Node,
353 meta_item: table::TermId,
354 ) -> Result<(), ImportErrorInner> {
355 if let Some((name, json_value)) = self.decode_json_meta(meta_item)? {
357 self.hugr.set_metadata(node, name, json_value);
358 }
359
360 if let Some([]) = self.match_symbol(meta_item, model::CORE_ENTRYPOINT)? {
362 self.hugr.set_entrypoint(node);
363 }
364
365 Ok(())
366 }
367
368 fn decode_json_meta(
369 &self,
370 meta_item: table::TermId,
371 ) -> Result<Option<(SmolStr, serde_json::Value)>, ImportErrorInner> {
372 Ok(
373 if let Some([name_arg, json_arg]) =
374 self.match_symbol(meta_item, model::COMPAT_META_JSON)?
375 {
376 let table::Term::Literal(model::Literal::Str(name)) = self.get_term(name_arg)?
377 else {
378 return Err(error_invalid!(
379 "`{}` expects a string literal as its first argument",
380 model::COMPAT_META_JSON
381 ));
382 };
383
384 let table::Term::Literal(model::Literal::Str(json_str)) =
385 self.get_term(json_arg)?
386 else {
387 return Err(error_invalid!(
388 "`{}` expects a string literal as its second argument",
389 model::COMPAT_CONST_JSON
390 ));
391 };
392
393 let json_value: NodeMetadata = serde_json::from_str(json_str).map_err(|_| {
394 error_invalid!(
395 "failed to parse JSON string for `{}` metadata",
396 model::COMPAT_CONST_JSON
397 )
398 })?;
399 Some((name.to_owned(), json_value))
400 } else {
401 None
402 },
403 )
404 }
405
406 fn record_links(&mut self, node: Node, direction: Direction, links: &'a [table::LinkIndex]) {
408 let optype = self.hugr.get_optype(node);
409 debug_assert!(links.len() <= optype.port_count(direction));
411
412 for (link, port) in links.iter().zip(self.hugr.node_ports(node, direction)) {
413 self.link_ports
414 .entry((self.region_scope, *link))
415 .or_default()
416 .push((node, port));
417 }
418 }
419
420 fn link_ports(&mut self) -> Result<(), ImportErrorInner> {
423 let mut inputs = Vec::new();
426 let mut outputs = Vec::new();
427
428 for (link_id, link_ports) in std::mem::take(&mut self.link_ports) {
429 if link_ports.is_empty() {
431 continue;
432 }
433
434 for (node, port) in link_ports {
435 match port.as_directed() {
436 Either::Left(input) => inputs.push((node, input)),
437 Either::Right(output) => outputs.push((node, output)),
438 }
439 }
440
441 match (inputs.as_slice(), outputs.as_slice()) {
442 ([], []) => {
443 unreachable!();
444 }
445 (_, [output]) => {
446 for (node, port) in &inputs {
447 self.hugr.connect(output.0, output.1, *node, *port);
448 }
449 }
450 ([input], _) => {
451 for (node, port) in &outputs {
452 self.hugr.connect(*node, *port, input.0, input.1);
453 }
454 }
455 _ => {
456 return Err(error_unsupported!(
457 concat!(
458 "Link {:?} would require a hyperedge because it connects more ",
459 "than one input port with more than one output port."
460 ),
461 link_id
462 ));
463 }
464 }
465
466 inputs.clear();
467 outputs.clear();
468 }
469
470 Ok(())
471 }
472
473 fn link_static_ports(&mut self) -> Result<(), ImportErrorInner> {
475 for (src_id, dst_id) in std::mem::take(&mut self.static_edges) {
476 let src = self.nodes[&src_id];
478 let dst = self.nodes[&dst_id];
479 let src_port = self.hugr.get_optype(src).static_output_port().unwrap();
480 let dst_port = self.hugr.get_optype(dst).static_input_port().unwrap();
481 self.hugr.connect(src, src_port, dst, dst_port);
482 }
483
484 Ok(())
485 }
486
487 fn get_symbol_name(&self, node_id: table::NodeId) -> Result<&'a str, ImportErrorInner> {
488 let node_data = self.get_node(node_id)?;
489 let name = node_data
490 .operation
491 .symbol()
492 .ok_or_else(|| error_invalid!("node {} is expected to be a symbol", node_id))?;
493 Ok(name)
494 }
495
496 fn get_func_signature(
497 &mut self,
498 func_node: table::NodeId,
499 ) -> Result<PolyFuncType, ImportErrorInner> {
500 let symbol = match self.get_node(func_node)?.operation {
501 table::Operation::DefineFunc(symbol) => symbol,
502 table::Operation::DeclareFunc(symbol) => symbol,
503 _ => {
504 return Err(error_invalid!(
505 "node {} is expected to be a function declaration or definition",
506 func_node
507 ));
508 }
509 };
510
511 self.import_poly_func_type(func_node, *symbol, |_, signature| Ok(signature))
512 }
513
514 fn import_root(&mut self) -> Result<(), ImportErrorInner> {
516 self.region_scope = self.module.root;
517 let region_data = self.get_region(self.module.root)?;
518
519 for node in region_data.children {
520 self.import_node(*node, self.hugr.module_root())?;
521 }
522
523 for meta_item in region_data.meta {
524 self.import_node_metadata(self.hugr.module_root(), *meta_item)?;
525 }
526
527 Ok(())
528 }
529
530 fn import_node(
531 &mut self,
532 node_id: table::NodeId,
533 parent: Node,
534 ) -> Result<Option<Node>, ImportErrorInner> {
535 let node_data = self.get_node(node_id)?;
536
537 let result = match node_data.operation {
538 table::Operation::Invalid => {
539 return Err(error_invalid!(concat!(
540 "Tried to import an `invalid` operation.\n",
541 "The `invalid` operation in `hugr-model` is a placeholder indicating a missing operation. ",
542 "It currently has no equivalent in `hugr-core`."
543 )));
544 }
545
546 table::Operation::Dfg => Some(
547 self.import_node_dfg(node_id, parent, node_data)
548 .map_err(|err| error_context!(err, "`dfg` node with id {}", node_id))?,
549 ),
550
551 table::Operation::Cfg => Some(
552 self.import_node_cfg(node_id, parent, node_data)
553 .map_err(|err| error_context!(err, "`cfg` node with id {}", node_id))?,
554 ),
555
556 table::Operation::Block => Some(
557 self.import_node_block(node_id, parent)
558 .map_err(|err| error_context!(err, "`block` node with id {}", node_id))?,
559 ),
560
561 table::Operation::DefineFunc(symbol) => Some(
562 self.import_node_define_func(node_id, symbol, node_data, parent)
563 .map_err(|err| error_context!(err, "`define-func` node with id {}", node_id))?,
564 ),
565
566 table::Operation::DeclareFunc(symbol) => Some(
567 self.import_node_declare_func(node_id, symbol, parent)
568 .map_err(|err| {
569 error_context!(err, "`declare-func` node with id {}", node_id)
570 })?,
571 ),
572
573 table::Operation::TailLoop => Some(
574 self.import_tail_loop(node_id, parent)
575 .map_err(|err| error_context!(err, "`tail-loop` node with id {}", node_id))?,
576 ),
577
578 table::Operation::Conditional => Some(
579 self.import_conditional(node_id, parent)
580 .map_err(|err| error_context!(err, "`cond` node with id {}", node_id))?,
581 ),
582
583 table::Operation::Custom(operation) => Some(
584 self.import_node_custom(node_id, operation, node_data, parent)
585 .map_err(|err| error_context!(err, "custom node with id {}", node_id))?,
586 ),
587
588 table::Operation::DefineAlias(symbol, value) => Some(
589 self.import_node_define_alias(node_id, symbol, value, parent)
590 .map_err(|err| {
591 error_context!(err, "`define-alias` node with id {}", node_id)
592 })?,
593 ),
594
595 table::Operation::DeclareAlias(symbol) => Some(
596 self.import_node_declare_alias(node_id, symbol, parent)
597 .map_err(|err| {
598 error_context!(err, "`declare-alias` node with id {}", node_id)
599 })?,
600 ),
601
602 table::Operation::Import { .. } => None,
603
604 table::Operation::DeclareConstructor { .. } => None,
605 table::Operation::DeclareOperation { .. } => None,
606 };
607
608 Ok(result)
609 }
610
611 fn import_node_dfg(
612 &mut self,
613 node_id: table::NodeId,
614 parent: Node,
615 node_data: &'a table::Node<'a>,
616 ) -> Result<Node, ImportErrorInner> {
617 let signature = self
618 .get_node_signature(node_id)
619 .map_err(|err| error_context!(err, "node signature"))?;
620
621 let optype = OpType::DFG(DFG { signature });
622 let node = self.make_node(node_id, optype, parent)?;
623
624 let [region] = node_data.regions else {
625 return Err(error_invalid!("dfg region expects a single region"));
626 };
627
628 self.import_dfg_region(*region, node)?;
629 Ok(node)
630 }
631
632 fn import_node_cfg(
633 &mut self,
634 node_id: table::NodeId,
635 parent: Node,
636 node_data: &'a table::Node<'a>,
637 ) -> Result<Node, ImportErrorInner> {
638 let signature = self
639 .get_node_signature(node_id)
640 .map_err(|err| error_context!(err, "node signature"))?;
641
642 let optype = OpType::CFG(CFG { signature });
643 let node = self.make_node(node_id, optype, parent)?;
644
645 let [region] = node_data.regions else {
646 return Err(error_invalid!("cfg nodes expect a single region"));
647 };
648
649 self.import_cfg_region(*region, node)?;
650 Ok(node)
651 }
652
653 fn import_dfg_region(
654 &mut self,
655 region: table::RegionId,
656 node: Node,
657 ) -> Result<(), ImportErrorInner> {
658 let region_data = self.get_region(region)?;
659
660 let prev_region = self.region_scope;
661 if region_data.scope.is_some() {
662 self.region_scope = region;
663 }
664
665 if region_data.kind != model::RegionKind::DataFlow {
666 return Err(error_invalid!("expected dfg region"));
667 }
668
669 let signature = self
670 .import_func_type(
671 region_data
672 .signature
673 .ok_or_else(|| error_uninferred!("region signature"))?,
674 )
675 .map_err(|err| error_context!(err, "signature of dfg region with id {}", region))?;
676
677 let input = self.hugr.add_node_with_parent(
679 node,
680 OpType::Input(Input {
681 types: signature.input,
682 }),
683 );
684 let output = self.hugr.add_node_with_parent(
685 node,
686 OpType::Output(Output {
687 types: signature.output,
688 }),
689 );
690
691 self.record_links(input, Direction::Outgoing, region_data.sources);
693 self.record_links(output, Direction::Incoming, region_data.targets);
694
695 for child in region_data.children {
696 self.import_node(*child, node)?;
697 }
698
699 self.create_order_edges(region, input, output)?;
700
701 for meta_item in region_data.meta {
702 self.import_node_metadata(node, *meta_item)?;
703 }
704
705 self.region_scope = prev_region;
706
707 Ok(())
708 }
709
710 fn create_order_edges(
714 &mut self,
715 region_id: table::RegionId,
716 input: Node,
717 output: Node,
718 ) -> Result<(), ImportErrorInner> {
719 let region_data = self.get_region(region_id)?;
720 debug_assert_eq!(region_data.kind, model::RegionKind::DataFlow);
721
722 let mut order_keys = FxHashMap::<u64, Node>::default();
725
726 for child_id in region_data.children {
727 let child_data = self.get_node(*child_id)?;
728
729 for meta_id in child_data.meta {
730 let Some([key]) = self.match_symbol(*meta_id, model::ORDER_HINT_KEY)? else {
731 continue;
732 };
733
734 let table::Term::Literal(model::Literal::Nat(key)) = self.get_term(key)? else {
735 continue;
736 };
737
738 let child_node = self.nodes[child_id];
741 let child_optype = self.hugr.get_optype(child_node);
742
743 if child_optype.other_output_port().is_none() {
746 return Err(OrderHintError::NoOrderPort(*child_id).into());
747 }
748
749 if order_keys.insert(*key, child_node).is_some() {
750 return Err(OrderHintError::DuplicateKey(region_id, *key).into());
751 }
752 }
753 }
754
755 for meta_id in region_data.meta {
757 if let Some([key]) = self.match_symbol(*meta_id, model::ORDER_HINT_INPUT_KEY)? {
758 let table::Term::Literal(model::Literal::Nat(key)) = self.get_term(key)? else {
759 continue;
760 };
761
762 if order_keys.insert(*key, input).is_some() {
763 return Err(OrderHintError::DuplicateKey(region_id, *key).into());
764 }
765 }
766
767 if let Some([key]) = self.match_symbol(*meta_id, model::ORDER_HINT_OUTPUT_KEY)? {
768 let table::Term::Literal(model::Literal::Nat(key)) = self.get_term(key)? else {
769 continue;
770 };
771
772 if order_keys.insert(*key, output).is_some() {
773 return Err(OrderHintError::DuplicateKey(region_id, *key).into());
774 }
775 }
776 }
777
778 for meta_id in region_data.meta {
780 let Some([a, b]) = self.match_symbol(*meta_id, model::ORDER_HINT_ORDER)? else {
781 continue;
782 };
783
784 let table::Term::Literal(model::Literal::Nat(a)) = self.get_term(a)? else {
785 continue;
786 };
787
788 let table::Term::Literal(model::Literal::Nat(b)) = self.get_term(b)? else {
789 continue;
790 };
791
792 let a = order_keys.get(a).ok_or(OrderHintError::UnknownKey(*a))?;
793 let b = order_keys.get(b).ok_or(OrderHintError::UnknownKey(*b))?;
794
795 let a_port = self.hugr.get_optype(*a).other_output_port().unwrap();
799 let b_port = self.hugr.get_optype(*b).other_input_port().unwrap();
800
801 self.hugr.connect(*a, a_port, *b, b_port);
802 }
803
804 Ok(())
805 }
806
807 fn import_adt_and_rest(
811 &mut self,
812 list: table::TermId,
813 ) -> Result<(Vec<TypeRow>, TypeRow), ImportErrorInner> {
814 let items = self.import_closed_list(list)?;
815
816 let Some((first, rest)) = items.split_first() else {
817 return Err(error_invalid!("expected list to have at least one element"));
818 };
819
820 let sum_rows: Vec<_> = {
821 let [variants] = self.expect_symbol(*first, model::CORE_ADT)?;
822 self.import_type_rows(variants)?
823 };
824
825 let rest = rest
826 .iter()
827 .map(|term| self.import_type(*term))
828 .collect::<Result<Vec<_>, _>>()?
829 .into();
830
831 Ok((sum_rows, rest))
832 }
833
834 fn import_tail_loop(
835 &mut self,
836 node_id: table::NodeId,
837 parent: Node,
838 ) -> Result<Node, ImportErrorInner> {
839 let node_data = self.get_node(node_id)?;
840 debug_assert_eq!(node_data.operation, table::Operation::TailLoop);
841
842 let [region] = node_data.regions else {
843 return Err(error_invalid!(
844 "loop node {} expects a single region",
845 node_id
846 ));
847 };
848
849 let region_data = self.get_region(*region)?;
850
851 let (just_inputs, just_outputs, rest) = (|| {
852 let [_, region_outputs] = self.get_func_type(
853 region_data
854 .signature
855 .ok_or_else(|| error_uninferred!("region signature"))?,
856 )?;
857 let (sum_rows, rest) = self.import_adt_and_rest(region_outputs)?;
858
859 if sum_rows.len() != 2 {
860 return Err(error_invalid!(
861 "Loop nodes expect their first target to be an ADT with two variants."
862 ));
863 }
864
865 let mut sum_rows = sum_rows.into_iter();
866 let just_inputs = sum_rows.next().unwrap();
867 let just_outputs = sum_rows.next().unwrap();
868
869 Ok((just_inputs, just_outputs, rest))
870 })()
871 .map_err(|err| error_context!(err, "region signature"))?;
872
873 let optype = OpType::TailLoop(TailLoop {
874 just_inputs,
875 just_outputs,
876 rest,
877 });
878
879 let node = self.make_node(node_id, optype, parent)?;
880
881 self.import_dfg_region(*region, node)?;
882 Ok(node)
883 }
884
885 fn import_conditional(
886 &mut self,
887 node_id: table::NodeId,
888 parent: Node,
889 ) -> Result<Node, ImportErrorInner> {
890 let node_data = self.get_node(node_id)?;
891 debug_assert_eq!(node_data.operation, table::Operation::Conditional);
892
893 let (sum_rows, other_inputs, outputs) = (|| {
894 let [inputs, outputs] = self.get_func_type(
895 node_data
896 .signature
897 .ok_or_else(|| error_uninferred!("node signature"))?,
898 )?;
899 let (sum_rows, other_inputs) = self.import_adt_and_rest(inputs)?;
900 let outputs = self.import_type_row(outputs)?;
901
902 Ok((sum_rows, other_inputs, outputs))
903 })()
904 .map_err(|err| error_context!(err, "node signature"))?;
905
906 let optype = OpType::Conditional(Conditional {
907 sum_rows,
908 other_inputs,
909 outputs,
910 });
911
912 let node = self.make_node(node_id, optype, parent)?;
913
914 for region in node_data.regions {
915 let region_data = self.get_region(*region)?;
916 let signature = self.import_func_type(
917 region_data
918 .signature
919 .ok_or_else(|| error_uninferred!("region signature"))?,
920 )?;
921
922 let case_node = self
923 .hugr
924 .add_node_with_parent(node, OpType::Case(Case { signature }));
925
926 self.import_dfg_region(*region, case_node)?;
927 }
928
929 Ok(node)
930 }
931
932 fn import_cfg_region(
948 &mut self,
949 region: table::RegionId,
950 node: Node,
951 ) -> Result<(), ImportErrorInner> {
952 let region_data = self.get_region(region)?;
953
954 if region_data.kind != model::RegionKind::ControlFlow {
955 return Err(error_invalid!("expected cfg region"));
956 }
957
958 let prev_region = self.region_scope;
959 if region_data.scope.is_some() {
960 self.region_scope = region;
961 }
962
963 let region_target_types = (|| {
964 let [_, region_targets] = self.get_ctrl_type(
965 region_data
966 .signature
967 .ok_or_else(|| error_uninferred!("region signature"))?,
968 )?;
969
970 self.import_closed_list(region_targets)
971 })()
972 .map_err(|err| error_context!(err, "signature of cfg region with id {}", region))?;
973
974 let entry_node = 'find_entry: {
977 let [entry_link] = region_data.sources else {
978 return Err(error_invalid!("cfg region expects a single source"));
979 };
980
981 for child in region_data.children {
982 let child_data = self.get_node(*child)?;
983 let is_entry = child_data.inputs.iter().any(|link| link == entry_link);
984
985 if is_entry {
986 break 'find_entry *child;
987 }
988 }
989
990 return Err(error_invalid!("cfg region without entry node"));
995 };
996
997 self.import_node(entry_node, node)?;
1000
1001 {
1004 let cfg_outputs = {
1005 let [target_types] = region_target_types.as_slice() else {
1006 return Err(error_invalid!("cfg region expects a single target"));
1007 };
1008
1009 self.import_type_row(*target_types)?
1010 };
1011
1012 let exit = self
1013 .hugr
1014 .add_node_with_parent(node, OpType::ExitBlock(ExitBlock { cfg_outputs }));
1015 self.record_links(exit, Direction::Incoming, region_data.targets);
1016 }
1017
1018 for child in region_data.children {
1020 if *child != entry_node {
1021 self.import_node(*child, node)?;
1022 }
1023 }
1024
1025 for meta_item in region_data.meta {
1026 self.import_node_metadata(node, *meta_item)
1027 .map_err(|err| error_context!(err, "node metadata"))?;
1028 }
1029
1030 self.region_scope = prev_region;
1031
1032 Ok(())
1033 }
1034
1035 fn import_node_block(
1036 &mut self,
1037 node_id: table::NodeId,
1038 parent: Node,
1039 ) -> Result<Node, ImportErrorInner> {
1040 let node_data = self.get_node(node_id)?;
1041 debug_assert_eq!(node_data.operation, table::Operation::Block);
1042
1043 let [region] = node_data.regions else {
1044 return Err(error_invalid!("basic block expects a single region"));
1045 };
1046 let region_data = self.get_region(*region)?;
1047 let [inputs, outputs] = self.get_func_type(
1048 region_data
1049 .signature
1050 .ok_or_else(|| error_uninferred!("region signature"))?,
1051 )?;
1052 let inputs = self.import_type_row(inputs)?;
1053 let (sum_rows, other_outputs) = self.import_adt_and_rest(outputs)?;
1054
1055 let optype = OpType::DataflowBlock(DataflowBlock {
1056 inputs,
1057 other_outputs,
1058 sum_rows,
1059 });
1060 let node = self.make_node(node_id, optype, parent)?;
1061
1062 self.import_dfg_region(*region, node).map_err(|err| {
1063 error_context!(err, "block body defined by region with id {}", *region)
1064 })?;
1065 Ok(node)
1066 }
1067
1068 fn import_node_define_func(
1069 &mut self,
1070 node_id: table::NodeId,
1071 symbol: &'a table::Symbol<'a>,
1072 node_data: &'a table::Node<'a>,
1073 parent: Node,
1074 ) -> Result<Node, ImportErrorInner> {
1075 let visibility = symbol.visibility.clone().ok_or(ImportErrorInner::Invalid(
1076 "No visibility for FuncDefn".to_string(),
1077 ))?;
1078 self.import_poly_func_type(node_id, *symbol, |ctx, signature| {
1079 let func_name = ctx.import_title_metadata(node_id)?.unwrap_or(symbol.name);
1080
1081 let optype =
1082 OpType::FuncDefn(FuncDefn::new_vis(func_name, signature, visibility.into()));
1083
1084 let node = ctx.make_node(node_id, optype, parent)?;
1085
1086 let [region] = node_data.regions else {
1087 return Err(error_invalid!(
1088 "function definition nodes expect a single region"
1089 ));
1090 };
1091
1092 ctx.import_dfg_region(*region, node).map_err(|err| {
1093 error_context!(err, "function body defined by region with id {}", *region)
1094 })?;
1095
1096 Ok(node)
1097 })
1098 }
1099
1100 fn import_node_declare_func(
1101 &mut self,
1102 node_id: table::NodeId,
1103 symbol: &'a table::Symbol<'a>,
1104 parent: Node,
1105 ) -> Result<Node, ImportErrorInner> {
1106 let visibility = symbol.visibility.clone().ok_or(ImportErrorInner::Invalid(
1107 "No visibility for FuncDecl".to_string(),
1108 ))?;
1109 self.import_poly_func_type(node_id, *symbol, |ctx, signature| {
1110 let func_name = ctx.import_title_metadata(node_id)?.unwrap_or(symbol.name);
1111
1112 let optype =
1113 OpType::FuncDecl(FuncDecl::new_vis(func_name, signature, visibility.into()));
1114 let node = ctx.make_node(node_id, optype, parent)?;
1115 Ok(node)
1116 })
1117 }
1118
1119 fn import_node_custom(
1132 &mut self,
1133 node_id: table::NodeId,
1134 operation: table::TermId,
1135 node_data: &'a table::Node<'a>,
1136 parent: Node,
1137 ) -> Result<Node, ImportErrorInner> {
1138 if let Some([inputs, outputs]) = self.match_symbol(operation, model::CORE_CALL_INDIRECT)? {
1139 let inputs = self.import_type_row(inputs)?;
1140 let outputs = self.import_type_row(outputs)?;
1141 let signature = Signature::new(inputs, outputs);
1142 let optype = OpType::CallIndirect(CallIndirect { signature });
1143 let node = self.make_node(node_id, optype, parent)?;
1144 return Ok(node);
1145 }
1146
1147 if let Some([_, _, func]) = self.match_symbol(operation, model::CORE_CALL)? {
1148 let (symbol, args) = match self.get_term(func)? {
1149 table::Term::Apply(symbol, args) => (symbol, args),
1150 table::Term::Var(_) => {
1151 return Err(error_unsupported!(
1157 "`{}` does not yet support function variables.",
1158 model::CORE_CALL
1159 ));
1160 }
1161 table::Term::Func(_) => {
1162 return Err(error_unsupported!(
1168 "`{}` does not yet support anonymous functions.",
1169 model::CORE_CALL
1170 ));
1171 }
1172 _ => {
1173 return Err(error_invalid!(
1174 concat!(
1175 "Expected a function to be passed to `{}`.\n",
1176 "Currently this is restricted to symbols that refer to functions."
1177 ),
1178 model::CORE_CALL
1179 ));
1180 }
1181 };
1182
1183 let func_sig = self.get_func_signature(*symbol)?;
1184
1185 let type_args = args
1186 .iter()
1187 .map(|term| self.import_term(*term))
1188 .collect::<Result<Vec<TypeArg>, _>>()?;
1189
1190 self.static_edges.push((*symbol, node_id));
1191 let optype = OpType::Call(
1192 Call::try_new(func_sig, type_args).map_err(ImportErrorInner::Signature)?,
1193 );
1194
1195 let node = self.make_node(node_id, optype, parent)?;
1196 return Ok(node);
1197 }
1198
1199 if let Some([_, value]) = self.match_symbol(operation, model::CORE_LOAD_CONST)? {
1200 if let table::Term::Apply(symbol, args) = self.get_term(value)? {
1202 let func_node_data = self.get_node(*symbol)?;
1203
1204 if let table::Operation::DefineFunc(_) | table::Operation::DeclareFunc(_) =
1205 func_node_data.operation
1206 {
1207 let func_sig = self.get_func_signature(*symbol)?;
1208 let type_args = args
1209 .iter()
1210 .map(|term| self.import_term(*term))
1211 .collect::<Result<Vec<TypeArg>, _>>()?;
1212
1213 self.static_edges.push((*symbol, node_id));
1214
1215 let optype = OpType::LoadFunction(
1216 LoadFunction::try_new(func_sig, type_args)
1217 .map_err(ImportErrorInner::Signature)?,
1218 );
1219
1220 let node = self.make_node(node_id, optype, parent)?;
1221 return Ok(node);
1222 }
1223 }
1224
1225 let signature = node_data
1227 .signature
1228 .ok_or_else(|| error_uninferred!("node signature"))?;
1229 let [_, outputs] = self.get_func_type(signature)?;
1230 let outputs = self.import_closed_list(outputs)?;
1231 let output = outputs.first().ok_or_else(|| {
1232 error_invalid!("`{}` expects a single output", model::CORE_LOAD_CONST)
1233 })?;
1234 let datatype = self.import_type(*output)?;
1235
1236 let imported_value = self.import_value(value, *output)?;
1237
1238 let load_const_node = self.make_node(
1239 node_id,
1240 OpType::LoadConstant(LoadConstant {
1241 datatype: datatype.clone(),
1242 }),
1243 parent,
1244 )?;
1245
1246 let const_node = self
1247 .hugr
1248 .add_node_with_parent(parent, OpType::Const(Const::new(imported_value)));
1249
1250 self.hugr.connect(const_node, 0, load_const_node, 0);
1251
1252 return Ok(load_const_node);
1253 }
1254
1255 if let Some([_, _, tag]) = self.match_symbol(operation, model::CORE_MAKE_ADT)? {
1256 let tag = match self.get_term(tag)? {
1257 table::Term::Literal(model::Literal::Nat(tag)) => tag,
1258 table::Term::Var(_) => {
1259 return Err(error_unsupported!(
1260 concat!(
1261 "`{}` does not yet support passing a variable as the tag.\n",
1262 "The `hugr-core` builtin `Tag` operation expects a concrete value for the tag. ",
1263 "Therefore we must insist on a tag given as a natural number literal on import.",
1264 ),
1265 model::CORE_MAKE_ADT
1266 ));
1267 }
1268 _ => {
1269 return Err(error_invalid!(
1270 "`{}` expects a nat literal tag",
1271 model::CORE_MAKE_ADT
1272 ));
1273 }
1274 };
1275
1276 let signature = node_data
1277 .signature
1278 .ok_or_else(|| error_uninferred!("node signature"))?;
1279 let [_, outputs] = self.get_func_type(signature)?;
1280 let (variants, _) = self.import_adt_and_rest(outputs)?;
1281 let node = self.make_node(
1282 node_id,
1283 OpType::Tag(Tag {
1284 variants,
1285 tag: *tag as usize,
1286 }),
1287 parent,
1288 )?;
1289 return Ok(node);
1290 }
1291
1292 let table::Term::Apply(node, params) = self.get_term(operation)? else {
1293 return Err(error_invalid!(
1294 "custom operations expect a symbol application referencing an operation"
1295 ));
1296 };
1297 let name = self.get_symbol_name(*node)?;
1298 let args = params
1299 .iter()
1300 .map(|param| self.import_term(*param))
1301 .collect::<Result<Vec<_>, _>>()?;
1302 let (extension, name) = self.import_custom_name(name)?;
1303 let signature = self.get_node_signature(node_id)?;
1304
1305 let optype = OpType::OpaqueOp(OpaqueOp::new(extension, name, args, signature));
1311 self.make_node(node_id, optype, parent)
1312 }
1313
1314 fn import_node_define_alias(
1315 &mut self,
1316 node_id: table::NodeId,
1317 symbol: &'a table::Symbol<'a>,
1318 value: table::TermId,
1319 parent: Node,
1320 ) -> Result<Node, ImportErrorInner> {
1321 if !symbol.params.is_empty() {
1322 return Err(error_unsupported!(
1323 "parameters or constraints in alias definition"
1324 ));
1325 }
1326
1327 let optype = OpType::AliasDefn(AliasDefn {
1328 name: symbol.name.to_smolstr(),
1329 definition: self.import_type(value)?,
1330 });
1331
1332 let node = self.make_node(node_id, optype, parent)?;
1333 Ok(node)
1334 }
1335
1336 fn import_node_declare_alias(
1337 &mut self,
1338 node_id: table::NodeId,
1339 symbol: &'a table::Symbol<'a>,
1340 parent: Node,
1341 ) -> Result<Node, ImportErrorInner> {
1342 if !symbol.params.is_empty() {
1343 return Err(error_unsupported!(
1344 "parameters or constraints in alias declaration"
1345 ));
1346 }
1347
1348 let optype = OpType::AliasDecl(AliasDecl {
1349 name: symbol.name.to_smolstr(),
1350 bound: TypeBound::Copyable,
1351 });
1352
1353 let node = self.make_node(node_id, optype, parent)?;
1354 Ok(node)
1355 }
1356
1357 fn import_poly_func_type<RV: MaybeRV, T>(
1358 &mut self,
1359 node: table::NodeId,
1360 symbol: table::Symbol<'a>,
1361 in_scope: impl FnOnce(&mut Self, PolyFuncTypeBase<RV>) -> Result<T, ImportErrorInner>,
1362 ) -> Result<T, ImportErrorInner> {
1363 (|| {
1364 let mut imported_params = Vec::with_capacity(symbol.params.len());
1365
1366 for (index, param) in symbol.params.iter().enumerate() {
1367 self.local_vars
1368 .insert(table::VarId(node, index as _), LocalVar::new(param.r#type));
1369 }
1370
1371 for constraint in symbol.constraints {
1372 if let Some([term]) = self.match_symbol(*constraint, model::CORE_NON_LINEAR)? {
1373 let table::Term::Var(var) = self.get_term(term)? else {
1374 return Err(error_unsupported!(
1375 "constraint on term that is not a variable"
1376 ));
1377 };
1378
1379 self.local_vars
1380 .get_mut(var)
1381 .ok_or_else(|| error_invalid!("unknown variable {}", var))?
1382 .bound = TypeBound::Copyable;
1383 } else {
1384 return Err(error_unsupported!(
1385 concat!(
1386 "Constraints other than `{}` can not yet be imported.\n",
1387 "`hugr-core` does not have support for arbitrary constraints yet, ",
1388 "instead relying on operation-specific Rust code to compute and ",
1389 "validate signatures of custom operations."
1390 ),
1391 model::CORE_NON_LINEAR
1392 ));
1393 }
1394 }
1395
1396 for (index, param) in symbol.params.iter().enumerate() {
1397 let bound = self.local_vars[&table::VarId(node, index as _)].bound;
1398 imported_params.push(
1399 self.import_term_with_bound(param.r#type, bound)
1400 .map_err(|err| error_context!(err, "type of parameter `{}`", param.name))?,
1401 );
1402 }
1403
1404 let body = self.import_func_type::<RV>(symbol.signature)?;
1405 in_scope(self, PolyFuncTypeBase::new(imported_params, body))
1406 })()
1407 .map_err(|err| error_context!(err, "symbol `{}` defined by node {}", symbol.name, node))
1408 }
1409
1410 fn import_term(&mut self, term_id: table::TermId) -> Result<Term, ImportErrorInner> {
1412 self.import_term_with_bound(term_id, TypeBound::Linear)
1413 }
1414
1415 fn import_term_with_bound(
1416 &mut self,
1417 term_id: table::TermId,
1418 bound: TypeBound,
1419 ) -> Result<Term, ImportErrorInner> {
1420 (|| {
1421 if let Some([]) = self.match_symbol(term_id, model::CORE_STR_TYPE)? {
1422 return Ok(Term::StringType);
1423 }
1424
1425 if let Some([]) = self.match_symbol(term_id, model::CORE_NAT_TYPE)? {
1426 return Ok(Term::max_nat_type());
1427 }
1428
1429 if let Some([]) = self.match_symbol(term_id, model::CORE_BYTES_TYPE)? {
1430 return Ok(Term::BytesType);
1431 }
1432
1433 if let Some([]) = self.match_symbol(term_id, model::CORE_FLOAT_TYPE)? {
1434 return Ok(Term::FloatType);
1435 }
1436
1437 if let Some([]) = self.match_symbol(term_id, model::CORE_TYPE)? {
1438 return Ok(TypeParam::RuntimeType(bound));
1439 }
1440
1441 if let Some([]) = self.match_symbol(term_id, model::CORE_CONSTRAINT)? {
1442 return Err(error_unsupported!("`{}`", model::CORE_CONSTRAINT));
1443 }
1444
1445 if let Some([]) = self.match_symbol(term_id, model::CORE_STATIC)? {
1446 return Ok(Term::StaticType);
1447 }
1448
1449 if let Some([ty]) = self.match_symbol(term_id, model::CORE_CONST)? {
1450 let ty = self
1451 .import_type(ty)
1452 .map_err(|err| error_context!(err, "type of a constant"))?;
1453 return Ok(TypeParam::new_const(ty));
1454 }
1455
1456 if let Some([item_type]) = self.match_symbol(term_id, model::CORE_LIST_TYPE)? {
1457 let item_type = self
1460 .import_term(item_type)
1461 .map_err(|err| error_context!(err, "item type of list type"))?;
1462 return Ok(TypeParam::new_list_type(item_type));
1463 }
1464
1465 if let Some([item_types]) = self.match_symbol(term_id, model::CORE_TUPLE_TYPE)? {
1466 let item_types = self
1469 .import_term(item_types)
1470 .map_err(|err| error_context!(err, "item types of tuple type"))?;
1471 return Ok(TypeParam::new_tuple_type(item_types));
1472 }
1473
1474 match self.get_term(term_id)? {
1475 table::Term::Wildcard => Err(error_uninferred!("wildcard")),
1476
1477 table::Term::Var(var) => {
1478 let var_info = self
1479 .local_vars
1480 .get(var)
1481 .ok_or_else(|| error_invalid!("unknown variable {}", var))?;
1482 let decl = self.import_term_with_bound(var_info.r#type, var_info.bound)?;
1483 Ok(Term::new_var_use(var.1 as _, decl))
1484 }
1485
1486 table::Term::List(parts) => {
1487 let parts: Vec<_> = parts
1489 .iter()
1490 .map(|part| self.import_seq_part(part))
1491 .collect::<Result<_, _>>()
1492 .map_err(|err| error_context!(err, "list parts"))?;
1493 Ok(TypeArg::new_list_from_parts(parts))
1494 }
1495
1496 table::Term::Tuple(parts) => {
1497 let parts: Vec<_> = parts
1499 .iter()
1500 .map(|part| self.import_seq_part(part))
1501 .try_collect()
1502 .map_err(|err| error_context!(err, "tuple parts"))?;
1503 Ok(TypeArg::new_tuple_from_parts(parts))
1504 }
1505
1506 table::Term::Literal(model::Literal::Str(value)) => {
1507 Ok(Term::String(value.to_string()))
1508 }
1509
1510 table::Term::Literal(model::Literal::Nat(value)) => Ok(Term::BoundedNat(*value)),
1511
1512 table::Term::Literal(model::Literal::Bytes(value)) => {
1513 Ok(Term::Bytes(value.clone()))
1514 }
1515 table::Term::Literal(model::Literal::Float(value)) => Ok(Term::Float(*value)),
1516 table::Term::Func { .. } => Err(error_unsupported!("function constant")),
1517
1518 table::Term::Apply { .. } => {
1519 let ty: Type = self.import_type(term_id)?;
1520 Ok(ty.into())
1521 }
1522 }
1523 })()
1524 .map_err(|err| error_context!(err, "term {}", term_id))
1525 }
1526
1527 fn import_seq_part(
1528 &mut self,
1529 seq_part: &'a table::SeqPart,
1530 ) -> Result<SeqPart<TypeArg>, ImportErrorInner> {
1531 Ok(match seq_part {
1532 table::SeqPart::Item(term_id) => SeqPart::Item(self.import_term(*term_id)?),
1533 table::SeqPart::Splice(term_id) => SeqPart::Splice(self.import_term(*term_id)?),
1534 })
1535 }
1536
1537 fn import_type<RV: MaybeRV>(
1539 &mut self,
1540 term_id: table::TermId,
1541 ) -> Result<TypeBase<RV>, ImportErrorInner> {
1542 (|| {
1543 if let Some([_, _]) = self.match_symbol(term_id, model::CORE_FN)? {
1544 let func_type = self.import_func_type::<RowVariable>(term_id)?;
1545 return Ok(TypeBase::new_function(func_type));
1546 }
1547
1548 if let Some([variants]) = self.match_symbol(term_id, model::CORE_ADT)? {
1549 let variants = (|| {
1550 self.import_closed_list(variants)?
1551 .iter()
1552 .map(|variant| self.import_type_row::<RowVariable>(*variant))
1553 .collect::<Result<Vec<_>, _>>()
1554 })()
1555 .map_err(|err| error_context!(err, "adt variants"))?;
1556
1557 return Ok(TypeBase::new_sum(variants));
1558 }
1559
1560 match self.get_term(term_id)? {
1561 table::Term::Wildcard => Err(error_uninferred!("wildcard")),
1562
1563 table::Term::Apply(symbol, args) => {
1564 let name = self.get_symbol_name(*symbol)?;
1565
1566 let args = args
1567 .iter()
1568 .map(|arg| self.import_term(*arg))
1569 .collect::<Result<Vec<_>, _>>()
1570 .map_err(|err| {
1571 error_context!(err, "type argument of custom type `{}`", name)
1572 })?;
1573
1574 let (extension, id) = self.import_custom_name(name)?;
1575
1576 let extension_ref =
1577 self.extensions
1578 .get(&extension)
1579 .ok_or_else(|| ExtensionError::Missing {
1580 missing_ext: extension.clone(),
1581 available: self.extensions.ids().cloned().collect(),
1582 })?;
1583
1584 let ext_type =
1585 extension_ref
1586 .get_type(&id)
1587 .ok_or_else(|| ExtensionError::MissingType {
1588 ext: extension.clone(),
1589 name: id.clone(),
1590 })?;
1591
1592 let bound = ext_type.bound(&args);
1593
1594 Ok(TypeBase::new_extension(CustomType::new(
1595 id,
1596 args,
1597 extension,
1598 bound,
1599 &Arc::downgrade(extension_ref),
1600 )))
1601 }
1602
1603 table::Term::Var(var @ table::VarId(_, index)) => {
1604 let local_var = self
1605 .local_vars
1606 .get(var)
1607 .ok_or(error_invalid!("unknown var {}", var))?;
1608 Ok(TypeBase::new_var_use(*index as _, local_var.bound))
1609 }
1610
1611 table::Term::Literal(_)
1614 | table::Term::List { .. }
1615 | table::Term::Tuple { .. }
1616 | table::Term::Func { .. } => Err(error_invalid!("expected a runtime type")),
1617 }
1618 })()
1619 .map_err(|err| error_context!(err, "term {} as `Type`", term_id))
1620 }
1621
1622 fn get_func_type(
1623 &mut self,
1624 term_id: table::TermId,
1625 ) -> Result<[table::TermId; 2], ImportErrorInner> {
1626 self.match_symbol(term_id, model::CORE_FN)?
1627 .ok_or(error_invalid!("expected a function type"))
1628 }
1629
1630 fn get_ctrl_type(
1631 &mut self,
1632 term_id: table::TermId,
1633 ) -> Result<[table::TermId; 2], ImportErrorInner> {
1634 self.match_symbol(term_id, model::CORE_CTRL)?
1635 .ok_or(error_invalid!("expected a control type"))
1636 }
1637
1638 fn import_func_type<RV: MaybeRV>(
1647 &mut self,
1648 term_id: table::TermId,
1649 ) -> Result<FuncTypeBase<RV>, ImportErrorInner> {
1650 (|| {
1651 let [inputs, outputs] = self.get_func_type(term_id)?;
1652 let inputs = self
1653 .import_type_row(inputs)
1654 .map_err(|err| error_context!(err, "function inputs"))?;
1655 let outputs = self
1656 .import_type_row(outputs)
1657 .map_err(|err| error_context!(err, "function outputs"))?;
1658 Ok(FuncTypeBase::new(inputs, outputs))
1659 })()
1660 .map_err(|err| error_context!(err, "function type"))
1661 }
1662
1663 fn import_closed_list(
1675 &mut self,
1676 term_id: table::TermId,
1677 ) -> Result<Vec<table::TermId>, ImportErrorInner> {
1678 fn import_into(
1679 ctx: &mut Context,
1680 term_id: table::TermId,
1681 types: &mut Vec<table::TermId>,
1682 ) -> Result<(), ImportErrorInner> {
1683 match ctx.get_term(term_id)? {
1684 table::Term::List(parts) => {
1685 types.reserve(parts.len());
1686
1687 for part in *parts {
1688 match part {
1689 table::SeqPart::Item(term_id) => {
1690 types.push(*term_id);
1691 }
1692 table::SeqPart::Splice(term_id) => {
1693 import_into(ctx, *term_id, types)?;
1694 }
1695 }
1696 }
1697 }
1698 _ => {
1699 return Err(error_invalid!(
1700 "Expected a closed list.\n{}",
1701 CLOSED_LIST_HINT
1702 ));
1703 }
1704 }
1705
1706 Ok(())
1707 }
1708
1709 let mut types = Vec::new();
1710 import_into(self, term_id, &mut types)?;
1711 Ok(types)
1712 }
1713
1714 fn import_closed_tuple(
1718 &mut self,
1719 term_id: table::TermId,
1720 ) -> Result<Vec<table::TermId>, ImportErrorInner> {
1721 fn import_into(
1722 ctx: &mut Context,
1723 term_id: table::TermId,
1724 types: &mut Vec<table::TermId>,
1725 ) -> Result<(), ImportErrorInner> {
1726 match ctx.get_term(term_id)? {
1727 table::Term::Tuple(parts) => {
1728 types.reserve(parts.len());
1729
1730 for part in *parts {
1731 match part {
1732 table::SeqPart::Item(term_id) => {
1733 types.push(*term_id);
1734 }
1735 table::SeqPart::Splice(term_id) => {
1736 import_into(ctx, *term_id, types)?;
1737 }
1738 }
1739 }
1740 }
1741 _ => {
1742 return Err(error_invalid!(
1743 "Expected a closed tuple term.\n{}",
1744 CLOSED_TUPLE_HINT
1745 ));
1746 }
1747 }
1748
1749 Ok(())
1750 }
1751
1752 let mut types = Vec::new();
1753 import_into(self, term_id, &mut types)?;
1754 Ok(types)
1755 }
1756
1757 fn import_type_rows<RV: MaybeRV>(
1761 &mut self,
1762 term_id: table::TermId,
1763 ) -> Result<Vec<TypeRowBase<RV>>, ImportErrorInner> {
1764 self.import_closed_list(term_id)?
1765 .into_iter()
1766 .map(|term_id| self.import_type_row::<RV>(term_id))
1767 .collect()
1768 }
1769
1770 fn import_type_row<RV: MaybeRV>(
1776 &mut self,
1777 term_id: table::TermId,
1778 ) -> Result<TypeRowBase<RV>, ImportErrorInner> {
1779 fn import_into<RV: MaybeRV>(
1780 ctx: &mut Context,
1781 term_id: table::TermId,
1782 types: &mut Vec<TypeBase<RV>>,
1783 ) -> Result<(), ImportErrorInner> {
1784 match ctx.get_term(term_id)? {
1785 table::Term::List(parts) => {
1786 types.reserve(parts.len());
1787
1788 for item in *parts {
1789 match item {
1790 table::SeqPart::Item(term_id) => {
1791 types.push(ctx.import_type::<RV>(*term_id)?);
1792 }
1793 table::SeqPart::Splice(term_id) => {
1794 import_into(ctx, *term_id, types)?;
1795 }
1796 }
1797 }
1798 }
1799 table::Term::Var(table::VarId(_, index)) => {
1800 let var = RV::try_from_rv(RowVariable(*index as _, TypeBound::Linear))
1801 .map_err(|_| {
1802 error_invalid!("Expected a closed list.\n{}", CLOSED_LIST_HINT)
1803 })?;
1804 types.push(TypeBase::new(TypeEnum::RowVar(var)));
1805 }
1806 _ => return Err(error_invalid!("expected a list")),
1807 }
1808
1809 Ok(())
1810 }
1811
1812 let mut types = Vec::new();
1813 import_into(self, term_id, &mut types)?;
1814 Ok(types.into())
1815 }
1816
1817 fn import_custom_name(
1818 &mut self,
1819 symbol: &'a str,
1820 ) -> Result<(ExtensionId, SmolStr), ImportErrorInner> {
1821 use std::collections::hash_map::Entry;
1822 match self.custom_name_cache.entry(symbol) {
1823 Entry::Occupied(occupied_entry) => Ok(occupied_entry.get().clone()),
1824 Entry::Vacant(vacant_entry) => {
1825 let qualified_name = ExtensionId::new(symbol)
1826 .map_err(|_| error_invalid!("`{}` is not a valid symbol name", symbol))?;
1827
1828 let (extension, id) = qualified_name
1829 .split_last()
1830 .ok_or_else(|| error_invalid!("`{}` is not a valid symbol name", symbol))?;
1831
1832 vacant_entry.insert((extension.clone(), id.clone()));
1833 Ok((extension, id))
1834 }
1835 }
1836 }
1837
1838 fn import_value(
1844 &mut self,
1845 term_id: table::TermId,
1846 type_id: table::TermId,
1847 ) -> Result<Value, ImportErrorInner> {
1848 let term_data = self.get_term(term_id)?;
1849
1850 if let Some([runtime_type, json]) = self.match_symbol(term_id, model::COMPAT_CONST_JSON)? {
1854 let table::Term::Literal(model::Literal::Str(json)) = self.get_term(json)? else {
1855 return Err(error_invalid!(
1856 "`{}` expects a string literal",
1857 model::COMPAT_CONST_JSON
1858 ));
1859 };
1860
1861 let value: Option<Box<dyn CustomConst>> = serde_json::from_str(json).ok();
1865
1866 if let Some(value) = value {
1867 let opaque_value = OpaqueValue::from(value);
1868 return Ok(Value::Extension { e: opaque_value });
1869 } else {
1870 let runtime_type = self.import_type(runtime_type)?;
1871 let value: serde_json::Value = serde_json::from_str(json).map_err(|_| {
1872 error_invalid!(
1873 "unable to parse JSON string for `{}`",
1874 model::COMPAT_CONST_JSON
1875 )
1876 })?;
1877 let custom_const = CustomSerialized::new(runtime_type, value);
1878 let opaque_value = OpaqueValue::new(custom_const);
1879 return Ok(Value::Extension { e: opaque_value });
1880 }
1881 }
1882
1883 if let Some([_, element_type_term, contents]) =
1884 self.match_symbol(term_id, ArrayValue::CTR_NAME)?
1885 {
1886 let element_type = self.import_type(element_type_term)?;
1887 let contents = self.import_closed_list(contents)?;
1888 let contents = contents
1889 .iter()
1890 .map(|item| self.import_value(*item, element_type_term))
1891 .collect::<Result<Vec<_>, _>>()?;
1892 return Ok(ArrayValue::new(element_type, contents).into());
1893 }
1894
1895 if let Some([bitwidth, value]) = self.match_symbol(term_id, ConstInt::CTR_NAME)? {
1896 let bitwidth = {
1897 let table::Term::Literal(model::Literal::Nat(bitwidth)) =
1898 self.get_term(bitwidth)?
1899 else {
1900 return Err(error_invalid!(
1901 "`{}` expects a nat literal in its `bitwidth` argument",
1902 ConstInt::CTR_NAME
1903 ));
1904 };
1905 if *bitwidth > 6 {
1906 return Err(error_invalid!(
1907 "`{}` expects the bitwidth to be at most 6, got {}",
1908 ConstInt::CTR_NAME,
1909 bitwidth
1910 ));
1911 }
1912 *bitwidth as u8
1913 };
1914
1915 let value = {
1916 let table::Term::Literal(model::Literal::Nat(value)) = self.get_term(value)? else {
1917 return Err(error_invalid!(
1918 "`{}` expects a nat literal value",
1919 ConstInt::CTR_NAME
1920 ));
1921 };
1922 *value
1923 };
1924
1925 return Ok(ConstInt::new_u(bitwidth, value)
1926 .map_err(|_| error_invalid!("failed to create int constant"))?
1927 .into());
1928 }
1929
1930 if let Some([value]) = self.match_symbol(term_id, ConstF64::CTR_NAME)? {
1931 let table::Term::Literal(model::Literal::Float(value)) = self.get_term(value)? else {
1932 return Err(error_invalid!(
1933 "`{}` expects a float literal value",
1934 ConstF64::CTR_NAME
1935 ));
1936 };
1937
1938 return Ok(ConstF64::new(value.into_inner()).into());
1939 }
1940
1941 if let Some([_, _, tag, values]) = self.match_symbol(term_id, model::CORE_CONST_ADT)? {
1942 let [variants] = self.expect_symbol(type_id, model::CORE_ADT)?;
1943 let values = self.import_closed_tuple(values)?;
1944 let variants = self.import_closed_list(variants)?;
1945
1946 let table::Term::Literal(model::Literal::Nat(tag)) = self.get_term(tag)? else {
1947 return Err(error_invalid!(
1948 "`{}` expects a nat literal tag",
1949 model::CORE_ADT
1950 ));
1951 };
1952
1953 let variant = variants.get(*tag as usize).ok_or(error_invalid!(
1954 "the tag of a `{}` must be a valid index into the list of variants",
1955 model::CORE_CONST_ADT
1956 ))?;
1957
1958 let variant = self.import_closed_list(*variant)?;
1959
1960 let items = values
1961 .iter()
1962 .zip(variant.iter())
1963 .map(|(value, typ)| self.import_value(*value, *typ))
1964 .collect::<Result<Vec<_>, _>>()?;
1965
1966 let typ = {
1967 let typ: Type = self.import_type(type_id)?;
1969 match typ.as_type_enum() {
1970 TypeEnum::Sum(sum) => sum.clone(),
1971 _ => unreachable!(),
1972 }
1973 };
1974
1975 return Ok(Value::sum(*tag as _, items, typ).unwrap());
1976 }
1977
1978 match term_data {
1979 table::Term::Wildcard => Err(error_uninferred!("wildcard")),
1980 table::Term::Var(_) => Err(error_unsupported!(concat!(
1981 "Constant value containing a variable.\n",
1982 "The constant system in `hugr-core` is not set up yet to support ",
1983 "constants that depend on variables.",
1984 ))),
1985
1986 table::Term::Apply(symbol, _) => {
1987 let symbol_name = self.get_symbol_name(*symbol)?;
1988 Err(error_unsupported!(
1989 concat!(
1990 "Unknown custom constant constructor `{}`.\n",
1991 "Importing constants from `hugr-model` to `hugr-core` currently only supports a small ",
1992 "and hard-coded list of constant constructors. To support JSON encoded constants ",
1993 "use the constant constructor `{}`."
1994 ),
1995 symbol_name,
1996 model::COMPAT_CONST_JSON
1997 ))
1998 }
2002
2003 table::Term::List { .. } | table::Term::Tuple(_) | table::Term::Literal(_) => {
2004 Err(error_invalid!("expected constant"))
2005 }
2006
2007 table::Term::Func { .. } => Err(error_unsupported!("Constant function value.")),
2008 }
2009 }
2010
2011 fn match_symbol<const N: usize>(
2029 &self,
2030 term_id: table::TermId,
2031 name: &str,
2032 ) -> Result<Option<[table::TermId; N]>, ImportErrorInner> {
2033 let term = self.get_term(term_id)?;
2034
2035 let table::Term::Apply(symbol, args) = term else {
2038 return Ok(None);
2039 };
2040
2041 if name != self.get_symbol_name(*symbol)? {
2042 return Ok(None);
2043 }
2044
2045 if args.len() > N {
2046 return Ok(None);
2047 }
2048
2049 let result = std::array::from_fn(|i| {
2050 (i + args.len())
2051 .checked_sub(N)
2052 .map(|i| args[i])
2053 .unwrap_or_default()
2054 });
2055
2056 Ok(Some(result))
2057 }
2058
2059 fn expect_symbol<const N: usize>(
2069 &self,
2070 term_id: table::TermId,
2071 name: &str,
2072 ) -> Result<[table::TermId; N], ImportErrorInner> {
2073 self.match_symbol(term_id, name)?.ok_or(error_invalid!(
2074 "Expected symbol `{}` with arity {}.",
2075 name,
2076 N
2077 ))
2078 }
2079
2080 fn import_title_metadata(
2089 &self,
2090 node_id: table::NodeId,
2091 ) -> Result<Option<&'a str>, ImportErrorInner> {
2092 let node_data = self.get_node(node_id)?;
2093 for meta in node_data.meta {
2094 let Some([name]) = self.match_symbol(*meta, model::CORE_TITLE)? else {
2095 continue;
2096 };
2097
2098 let table::Term::Literal(model::Literal::Str(name)) = self.get_term(name)? else {
2099 return Err(error_invalid!(
2100 "`{}` metadata expected a string literal as argument",
2101 model::CORE_TITLE
2102 ));
2103 };
2104
2105 return Ok(Some(name.as_str()));
2106 }
2107
2108 Ok(None)
2109 }
2110}
2111
2112#[derive(Debug, Clone, Copy)]
2114struct LocalVar {
2115 r#type: table::TermId,
2117 bound: TypeBound,
2119}
2120
2121impl LocalVar {
2122 pub fn new(r#type: table::TermId) -> Self {
2123 Self {
2124 r#type,
2125 bound: TypeBound::Linear,
2126 }
2127 }
2128}