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