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