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
55#[derive(Debug, Clone, Error)]
56enum ImportErrorInner {
57 #[error("currently unsupported: {0}")]
61 Unsupported(String),
62
63 #[error("uninferred implicit: {0}")]
66 Uninferred(String),
67
68 #[error("{0}")]
70 Invalid(String),
71
72 #[error("import failed in context: {1}")]
74 Context(#[source] Box<ImportErrorInner>, String),
75
76 #[error("signature error")]
78 Signature(#[from] SignatureError),
79
80 #[error("extension error")]
82 Extension(#[from] ExtensionError),
83
84 #[error("incorrect order hint")]
86 OrderHint(#[from] OrderHintError),
87
88 #[error("extension resolution error")]
90 ExtensionResolution(#[from] ExtensionResolutionError),
91}
92
93#[derive(Debug, Clone, Error)]
94enum ExtensionError {
95 #[error("Importing the hugr requires extension {missing_ext}, which was not found in the registry. The available extensions are: [{}]",
97 available.iter().map(std::string::ToString::to_string).collect::<Vec<_>>().join(", "))]
98 Missing {
99 missing_ext: ExtensionId,
101 available: Vec<ExtensionId>,
103 },
104
105 #[error(
107 "Importing the hugr requires extension {ext} to have a type named {name}, but it was not found."
108 )]
109 MissingType {
110 ext: ExtensionId,
112 name: TypeName,
114 },
115}
116
117#[derive(Debug, Clone, Error)]
119enum OrderHintError {
120 #[error("duplicate order hint key {0}")]
122 DuplicateKey(table::RegionId, u64),
123 #[error("order hint with unknown key {0}")]
125 UnknownKey(u64),
126 #[error("order hint on node with no order port: {0}")]
128 NoOrderPort(table::NodeId),
129}
130
131macro_rules! error_unsupported {
133 ($($e:expr),*) => { ImportErrorInner::Unsupported(format!($($e),*)) }
134}
135
136macro_rules! error_uninferred {
138 ($($e:expr),*) => { ImportErrorInner::Uninferred(format!($($e),*)) }
139}
140
141macro_rules! error_invalid {
143 ($($e:expr),*) => { ImportErrorInner::Invalid(format!($($e),*)) }
144}
145
146macro_rules! error_context {
148 ($err:expr, $($e:expr),*) => {
149 {
150 ImportErrorInner::Context(Box::new($err), format!($($e),*))
151 }
152 }
153}
154
155pub fn import_package(
157 package: &table::Package,
158 extensions: &ExtensionRegistry,
159) -> Result<Package, ImportError> {
160 let modules = package
161 .modules
162 .iter()
163 .map(|module| import_hugr(module, extensions))
164 .collect::<Result<Vec<_>, _>>()?;
165
166 let package = Package::new(modules);
168 Ok(package)
169}
170
171fn get_generator(ctx: &Context<'_>) -> Option<String> {
174 ctx.module
175 .get_region(ctx.module.root)
176 .map(|r| r.meta.iter())
177 .into_iter()
178 .flatten()
179 .find_map(|meta| {
180 let (name, json_val) = ctx.decode_json_meta(*meta).ok()??;
181
182 (name == crate::envelope::GENERATOR_KEY)
183 .then_some(crate::envelope::format_generator(&json_val))
184 })
185}
186
187pub fn import_hugr(
189 module: &table::Module,
190 extensions: &ExtensionRegistry,
191) -> Result<Hugr, ImportError> {
192 let mut ctx = Context {
195 module,
196 hugr: Hugr::new(),
197 link_ports: FxHashMap::default(),
198 static_edges: Vec::new(),
199 extensions,
200 nodes: FxHashMap::default(),
201 local_vars: FxHashMap::default(),
202 custom_name_cache: FxHashMap::default(),
203 region_scope: table::RegionId::default(),
204 };
205
206 let import_steps: [fn(&mut Context) -> _; 3] = [
207 |ctx| ctx.import_root(),
208 |ctx| ctx.link_ports(),
209 |ctx| ctx.link_static_ports(),
210 ];
211
212 for step in import_steps {
213 if let Err(e) = step(&mut ctx) {
214 return Err(ImportError {
215 inner: e,
216 generator: get_generator(&ctx),
217 });
218 }
219 }
220 ctx.hugr
221 .resolve_extension_defs(extensions)
222 .map_err(|e| ImportError {
223 inner: ImportErrorInner::ExtensionResolution(e),
224 generator: get_generator(&ctx),
225 })?;
226 Ok(ctx.hugr)
227}
228
229struct Context<'a> {
230 module: &'a table::Module<'a>,
232
233 hugr: Hugr,
235
236 link_ports: FxHashMap<(table::RegionId, table::LinkIndex), Vec<(Node, Port)>>,
239
240 static_edges: Vec<(table::NodeId, table::NodeId)>,
243
244 extensions: &'a ExtensionRegistry,
246
247 nodes: FxHashMap<table::NodeId, Node>,
249
250 local_vars: FxHashMap<table::VarId, LocalVar>,
251
252 custom_name_cache: FxHashMap<&'a str, (ExtensionId, SmolStr)>,
253
254 region_scope: table::RegionId,
255}
256
257impl<'a> Context<'a> {
258 fn get_node_signature(&mut self, node: table::NodeId) -> Result<Signature, ImportErrorInner> {
260 let node_data = self.get_node(node)?;
261 let signature = node_data
262 .signature
263 .ok_or_else(|| error_uninferred!("node signature"))?;
264 self.import_func_type(signature)
265 }
266
267 #[inline]
269 fn get_node(&self, node_id: table::NodeId) -> Result<&'a table::Node<'a>, ImportErrorInner> {
270 self.module
271 .get_node(node_id)
272 .ok_or_else(|| error_invalid!("unknown node {}", node_id))
273 }
274
275 #[inline]
277 fn get_term(&self, term_id: table::TermId) -> Result<&'a table::Term<'a>, ImportErrorInner> {
278 self.module
279 .get_term(term_id)
280 .ok_or_else(|| error_invalid!("unknown term {}", term_id))
281 }
282
283 #[inline]
285 fn get_region(
286 &self,
287 region_id: table::RegionId,
288 ) -> Result<&'a table::Region<'a>, ImportErrorInner> {
289 self.module
290 .get_region(region_id)
291 .ok_or_else(|| error_invalid!("unknown region {}", region_id))
292 }
293
294 fn make_node(
295 &mut self,
296 node_id: table::NodeId,
297 op: OpType,
298 parent: Node,
299 ) -> Result<Node, ImportErrorInner> {
300 let node = self.hugr.add_node_with_parent(parent, op);
301 self.nodes.insert(node_id, node);
302
303 let node_data = self.get_node(node_id)?;
304 self.record_links(node, Direction::Incoming, node_data.inputs);
305 self.record_links(node, Direction::Outgoing, node_data.outputs);
306
307 for meta_item in node_data.meta {
308 self.import_node_metadata(node, *meta_item)
309 .map_err(|err| error_context!(err, "node metadata"))?;
310 }
311
312 Ok(node)
313 }
314
315 fn import_node_metadata(
316 &mut self,
317 node: Node,
318 meta_item: table::TermId,
319 ) -> Result<(), ImportErrorInner> {
320 if let Some((name, json_value)) = self.decode_json_meta(meta_item)? {
322 self.hugr.set_metadata(node, name, json_value);
323 }
324
325 if let Some([]) = self.match_symbol(meta_item, model::CORE_ENTRYPOINT)? {
327 self.hugr.set_entrypoint(node);
328 }
329
330 Ok(())
331 }
332
333 fn decode_json_meta(
334 &self,
335 meta_item: table::TermId,
336 ) -> Result<Option<(SmolStr, serde_json::Value)>, ImportErrorInner> {
337 Ok(
338 if let Some([name_arg, json_arg]) =
339 self.match_symbol(meta_item, model::COMPAT_META_JSON)?
340 {
341 let table::Term::Literal(model::Literal::Str(name)) = self.get_term(name_arg)?
342 else {
343 return Err(error_invalid!(
344 "`{}` expects a string literal as its first argument",
345 model::COMPAT_META_JSON
346 ));
347 };
348
349 let table::Term::Literal(model::Literal::Str(json_str)) =
350 self.get_term(json_arg)?
351 else {
352 return Err(error_invalid!(
353 "`{}` expects a string literal as its second argument",
354 model::COMPAT_CONST_JSON
355 ));
356 };
357
358 let json_value: NodeMetadata = serde_json::from_str(json_str).map_err(|_| {
359 error_invalid!(
360 "failed to parse JSON string for `{}` metadata",
361 model::COMPAT_CONST_JSON
362 )
363 })?;
364 Some((name.to_owned(), json_value))
365 } else {
366 None
367 },
368 )
369 }
370
371 fn record_links(&mut self, node: Node, direction: Direction, links: &'a [table::LinkIndex]) {
373 let optype = self.hugr.get_optype(node);
374 debug_assert!(links.len() <= optype.port_count(direction));
376
377 for (link, port) in links.iter().zip(self.hugr.node_ports(node, direction)) {
378 self.link_ports
379 .entry((self.region_scope, *link))
380 .or_default()
381 .push((node, port));
382 }
383 }
384
385 fn link_ports(&mut self) -> Result<(), ImportErrorInner> {
388 let mut inputs = Vec::new();
391 let mut outputs = Vec::new();
392
393 for (link_id, link_ports) in std::mem::take(&mut self.link_ports) {
394 if link_ports.is_empty() {
396 continue;
397 }
398
399 for (node, port) in link_ports {
400 match port.as_directed() {
401 Either::Left(input) => inputs.push((node, input)),
402 Either::Right(output) => outputs.push((node, output)),
403 }
404 }
405
406 match (inputs.as_slice(), outputs.as_slice()) {
407 ([], []) => {
408 unreachable!();
409 }
410 (_, [output]) => {
411 for (node, port) in &inputs {
412 self.hugr.connect(output.0, output.1, *node, *port);
413 }
414 }
415 ([input], _) => {
416 for (node, port) in &outputs {
417 self.hugr.connect(*node, *port, input.0, input.1);
418 }
419 }
420 _ => {
421 return Err(error_unsupported!(
422 "link {:?} would require hyperedge",
423 link_id
424 ));
425 }
426 }
427
428 inputs.clear();
429 outputs.clear();
430 }
431
432 Ok(())
433 }
434
435 fn link_static_ports(&mut self) -> Result<(), ImportErrorInner> {
436 for (src_id, dst_id) in std::mem::take(&mut self.static_edges) {
437 let src = self.nodes[&src_id];
439 let dst = self.nodes[&dst_id];
440 let src_port = self.hugr.get_optype(src).static_output_port().unwrap();
441 let dst_port = self.hugr.get_optype(dst).static_input_port().unwrap();
442 self.hugr.connect(src, src_port, dst, dst_port);
443 }
444
445 Ok(())
446 }
447
448 fn get_symbol_name(&self, node_id: table::NodeId) -> Result<&'a str, ImportErrorInner> {
449 let node_data = self.get_node(node_id)?;
450 let name = node_data
451 .operation
452 .symbol()
453 .ok_or_else(|| error_invalid!("node {} is expected to be a symbol", node_id))?;
454 Ok(name)
455 }
456
457 fn get_func_signature(
458 &mut self,
459 func_node: table::NodeId,
460 ) -> Result<PolyFuncType, ImportErrorInner> {
461 let symbol = match self.get_node(func_node)?.operation {
462 table::Operation::DefineFunc(symbol) => symbol,
463 table::Operation::DeclareFunc(symbol) => symbol,
464 _ => {
465 return Err(error_invalid!(
466 "node {} is expected to be a function declaration or definition",
467 func_node
468 ));
469 }
470 };
471
472 self.import_poly_func_type(func_node, *symbol, |_, signature| Ok(signature))
473 }
474
475 fn import_root(&mut self) -> Result<(), ImportErrorInner> {
477 self.region_scope = self.module.root;
478 let region_data = self.get_region(self.module.root)?;
479
480 for node in region_data.children {
481 self.import_node(*node, self.hugr.module_root())?;
482 }
483
484 for meta_item in region_data.meta {
485 self.import_node_metadata(self.hugr.module_root(), *meta_item)?;
486 }
487
488 Ok(())
489 }
490
491 fn import_node(
492 &mut self,
493 node_id: table::NodeId,
494 parent: Node,
495 ) -> Result<Option<Node>, ImportErrorInner> {
496 let node_data = self.get_node(node_id)?;
497
498 let result = match node_data.operation {
499 table::Operation::Invalid => {
500 return Err(error_invalid!("tried to import an `invalid` operation"));
501 }
502
503 table::Operation::Dfg => Some(
504 self.import_node_dfg(node_id, parent, node_data)
505 .map_err(|err| error_context!(err, "`dfg` node with id {}", node_id))?,
506 ),
507
508 table::Operation::Cfg => Some(
509 self.import_node_cfg(node_id, parent, node_data)
510 .map_err(|err| error_context!(err, "`cfg` node with id {}", node_id))?,
511 ),
512
513 table::Operation::Block => Some(
514 self.import_node_block(node_id, parent)
515 .map_err(|err| error_context!(err, "`block` node with id {}", node_id))?,
516 ),
517
518 table::Operation::DefineFunc(symbol) => Some(
519 self.import_node_define_func(node_id, symbol, node_data, parent)
520 .map_err(|err| error_context!(err, "`define-func` node with id {}", node_id))?,
521 ),
522
523 table::Operation::DeclareFunc(symbol) => Some(
524 self.import_node_declare_func(node_id, symbol, parent)
525 .map_err(|err| {
526 error_context!(err, "`declare-func` node with id {}", node_id)
527 })?,
528 ),
529
530 table::Operation::TailLoop => Some(
531 self.import_tail_loop(node_id, parent)
532 .map_err(|err| error_context!(err, "`tail-loop` node with id {}", node_id))?,
533 ),
534
535 table::Operation::Conditional => Some(
536 self.import_conditional(node_id, parent)
537 .map_err(|err| error_context!(err, "`cond` node with id {}", node_id))?,
538 ),
539
540 table::Operation::Custom(operation) => Some(
541 self.import_node_custom(node_id, operation, node_data, parent)
542 .map_err(|err| error_context!(err, "custom node with id {}", node_id))?,
543 ),
544
545 table::Operation::DefineAlias(symbol, value) => Some(
546 self.import_node_define_alias(node_id, symbol, value, parent)
547 .map_err(|err| {
548 error_context!(err, "`define-alias` node with id {}", node_id)
549 })?,
550 ),
551
552 table::Operation::DeclareAlias(symbol) => Some(
553 self.import_node_declare_alias(node_id, symbol, parent)
554 .map_err(|err| {
555 error_context!(err, "`declare-alias` node with id {}", node_id)
556 })?,
557 ),
558
559 table::Operation::Import { .. } => None,
560
561 table::Operation::DeclareConstructor { .. } => None,
562 table::Operation::DeclareOperation { .. } => None,
563 };
564
565 Ok(result)
566 }
567
568 fn import_node_dfg(
569 &mut self,
570 node_id: table::NodeId,
571 parent: Node,
572 node_data: &'a table::Node<'a>,
573 ) -> Result<Node, ImportErrorInner> {
574 let signature = self
575 .get_node_signature(node_id)
576 .map_err(|err| error_context!(err, "node signature"))?;
577
578 let optype = OpType::DFG(DFG { signature });
579 let node = self.make_node(node_id, optype, parent)?;
580
581 let [region] = node_data.regions else {
582 return Err(error_invalid!("dfg region expects a single region"));
583 };
584
585 self.import_dfg_region(*region, node)?;
586 Ok(node)
587 }
588
589 fn import_node_cfg(
590 &mut self,
591 node_id: table::NodeId,
592 parent: Node,
593 node_data: &'a table::Node<'a>,
594 ) -> Result<Node, ImportErrorInner> {
595 let signature = self
596 .get_node_signature(node_id)
597 .map_err(|err| error_context!(err, "node signature"))?;
598
599 let optype = OpType::CFG(CFG { signature });
600 let node = self.make_node(node_id, optype, parent)?;
601
602 let [region] = node_data.regions else {
603 return Err(error_invalid!("cfg nodes expect a single region"));
604 };
605
606 self.import_cfg_region(*region, node)?;
607 Ok(node)
608 }
609
610 fn import_dfg_region(
611 &mut self,
612 region: table::RegionId,
613 node: Node,
614 ) -> Result<(), ImportErrorInner> {
615 let region_data = self.get_region(region)?;
616
617 let prev_region = self.region_scope;
618 if region_data.scope.is_some() {
619 self.region_scope = region;
620 }
621
622 if region_data.kind != model::RegionKind::DataFlow {
623 return Err(error_invalid!("expected dfg region"));
624 }
625
626 let signature = self
627 .import_func_type(
628 region_data
629 .signature
630 .ok_or_else(|| error_uninferred!("region signature"))?,
631 )
632 .map_err(|err| error_context!(err, "signature of dfg region with id {}", region))?;
633
634 let input = self.hugr.add_node_with_parent(
636 node,
637 OpType::Input(Input {
638 types: signature.input,
639 }),
640 );
641 let output = self.hugr.add_node_with_parent(
642 node,
643 OpType::Output(Output {
644 types: signature.output,
645 }),
646 );
647
648 self.record_links(input, Direction::Outgoing, region_data.sources);
650 self.record_links(output, Direction::Incoming, region_data.targets);
651
652 for child in region_data.children {
653 self.import_node(*child, node)?;
654 }
655
656 self.create_order_edges(region, input, output)?;
657
658 for meta_item in region_data.meta {
659 self.import_node_metadata(node, *meta_item)?;
660 }
661
662 self.region_scope = prev_region;
663
664 Ok(())
665 }
666
667 fn create_order_edges(
671 &mut self,
672 region_id: table::RegionId,
673 input: Node,
674 output: Node,
675 ) -> Result<(), ImportErrorInner> {
676 let region_data = self.get_region(region_id)?;
677 debug_assert_eq!(region_data.kind, model::RegionKind::DataFlow);
678
679 let mut order_keys = FxHashMap::<u64, Node>::default();
682
683 for child_id in region_data.children {
684 let child_data = self.get_node(*child_id)?;
685
686 for meta_id in child_data.meta {
687 let Some([key]) = self.match_symbol(*meta_id, model::ORDER_HINT_KEY)? else {
688 continue;
689 };
690
691 let table::Term::Literal(model::Literal::Nat(key)) = self.get_term(key)? else {
692 continue;
693 };
694
695 let child_node = self.nodes[child_id];
698 let child_optype = self.hugr.get_optype(child_node);
699
700 if child_optype.other_output_port().is_none() {
703 return Err(OrderHintError::NoOrderPort(*child_id).into());
704 }
705
706 if order_keys.insert(*key, child_node).is_some() {
707 return Err(OrderHintError::DuplicateKey(region_id, *key).into());
708 }
709 }
710 }
711
712 for meta_id in region_data.meta {
714 if let Some([key]) = self.match_symbol(*meta_id, model::ORDER_HINT_INPUT_KEY)? {
715 let table::Term::Literal(model::Literal::Nat(key)) = self.get_term(key)? else {
716 continue;
717 };
718
719 if order_keys.insert(*key, input).is_some() {
720 return Err(OrderHintError::DuplicateKey(region_id, *key).into());
721 }
722 }
723
724 if let Some([key]) = self.match_symbol(*meta_id, model::ORDER_HINT_OUTPUT_KEY)? {
725 let table::Term::Literal(model::Literal::Nat(key)) = self.get_term(key)? else {
726 continue;
727 };
728
729 if order_keys.insert(*key, output).is_some() {
730 return Err(OrderHintError::DuplicateKey(region_id, *key).into());
731 }
732 }
733 }
734
735 for meta_id in region_data.meta {
737 let Some([a, b]) = self.match_symbol(*meta_id, model::ORDER_HINT_ORDER)? else {
738 continue;
739 };
740
741 let table::Term::Literal(model::Literal::Nat(a)) = self.get_term(a)? else {
742 continue;
743 };
744
745 let table::Term::Literal(model::Literal::Nat(b)) = self.get_term(b)? else {
746 continue;
747 };
748
749 let a = order_keys.get(a).ok_or(OrderHintError::UnknownKey(*a))?;
750 let b = order_keys.get(b).ok_or(OrderHintError::UnknownKey(*b))?;
751
752 let a_port = self.hugr.get_optype(*a).other_output_port().unwrap();
756 let b_port = self.hugr.get_optype(*b).other_input_port().unwrap();
757
758 self.hugr.connect(*a, a_port, *b, b_port);
759 }
760
761 Ok(())
762 }
763
764 fn import_adt_and_rest(
765 &mut self,
766 list: table::TermId,
767 ) -> Result<(Vec<TypeRow>, TypeRow), ImportErrorInner> {
768 let items = self.import_closed_list(list)?;
769
770 let Some((first, rest)) = items.split_first() else {
771 return Err(error_invalid!("expected list to have at least one element"));
772 };
773
774 let sum_rows: Vec<_> = {
775 let [variants] = self.expect_symbol(*first, model::CORE_ADT)?;
776 self.import_type_rows(variants)?
777 };
778
779 let rest = rest
780 .iter()
781 .map(|term| self.import_type(*term))
782 .collect::<Result<Vec<_>, _>>()?
783 .into();
784
785 Ok((sum_rows, rest))
786 }
787
788 fn import_tail_loop(
789 &mut self,
790 node_id: table::NodeId,
791 parent: Node,
792 ) -> Result<Node, ImportErrorInner> {
793 let node_data = self.get_node(node_id)?;
794 debug_assert_eq!(node_data.operation, table::Operation::TailLoop);
795
796 let [region] = node_data.regions else {
797 return Err(error_invalid!(
798 "loop node {} expects a single region",
799 node_id
800 ));
801 };
802
803 let region_data = self.get_region(*region)?;
804
805 let (just_inputs, just_outputs, rest) = (|| {
806 let [_, region_outputs] = self.get_func_type(
807 region_data
808 .signature
809 .ok_or_else(|| error_uninferred!("region signature"))?,
810 )?;
811 let (sum_rows, rest) = self.import_adt_and_rest(region_outputs)?;
812
813 if sum_rows.len() != 2 {
814 return Err(error_invalid!(
815 "loop nodes expect their first target to be an ADT with two variants"
816 ));
817 }
818
819 let mut sum_rows = sum_rows.into_iter();
820 let just_inputs = sum_rows.next().unwrap();
821 let just_outputs = sum_rows.next().unwrap();
822
823 Ok((just_inputs, just_outputs, rest))
824 })()
825 .map_err(|err| error_context!(err, "region signature"))?;
826
827 let optype = OpType::TailLoop(TailLoop {
828 just_inputs,
829 just_outputs,
830 rest,
831 });
832
833 let node = self.make_node(node_id, optype, parent)?;
834
835 self.import_dfg_region(*region, node)?;
836 Ok(node)
837 }
838
839 fn import_conditional(
840 &mut self,
841 node_id: table::NodeId,
842 parent: Node,
843 ) -> Result<Node, ImportErrorInner> {
844 let node_data = self.get_node(node_id)?;
845 debug_assert_eq!(node_data.operation, table::Operation::Conditional);
846
847 let (sum_rows, other_inputs, outputs) = (|| {
848 let [inputs, outputs] = self.get_func_type(
849 node_data
850 .signature
851 .ok_or_else(|| error_uninferred!("node signature"))?,
852 )?;
853 let (sum_rows, other_inputs) = self.import_adt_and_rest(inputs)?;
854 let outputs = self.import_type_row(outputs)?;
855
856 Ok((sum_rows, other_inputs, outputs))
857 })()
858 .map_err(|err| error_context!(err, "node signature"))?;
859
860 let optype = OpType::Conditional(Conditional {
861 sum_rows,
862 other_inputs,
863 outputs,
864 });
865
866 let node = self.make_node(node_id, optype, parent)?;
867
868 for region in node_data.regions {
869 let region_data = self.get_region(*region)?;
870 let signature = self.import_func_type(
871 region_data
872 .signature
873 .ok_or_else(|| error_uninferred!("region signature"))?,
874 )?;
875
876 let case_node = self
877 .hugr
878 .add_node_with_parent(node, OpType::Case(Case { signature }));
879
880 self.import_dfg_region(*region, case_node)?;
881 }
882
883 Ok(node)
884 }
885
886 fn import_cfg_region(
887 &mut self,
888 region: table::RegionId,
889 node: Node,
890 ) -> Result<(), ImportErrorInner> {
891 let region_data = self.get_region(region)?;
892
893 if region_data.kind != model::RegionKind::ControlFlow {
894 return Err(error_invalid!("expected cfg region"));
895 }
896
897 let prev_region = self.region_scope;
898 if region_data.scope.is_some() {
899 self.region_scope = region;
900 }
901
902 let region_target_types = (|| {
903 let [_, region_targets] = self.get_ctrl_type(
904 region_data
905 .signature
906 .ok_or_else(|| error_uninferred!("region signature"))?,
907 )?;
908
909 self.import_closed_list(region_targets)
910 })()
911 .map_err(|err| error_context!(err, "signature of cfg region with id {}", region))?;
912
913 let entry_node = 'find_entry: {
916 let [entry_link] = region_data.sources else {
917 return Err(error_invalid!("cfg region expects a single source"));
918 };
919
920 for child in region_data.children {
921 let child_data = self.get_node(*child)?;
922 let is_entry = child_data.inputs.iter().any(|link| link == entry_link);
923
924 if is_entry {
925 break 'find_entry *child;
926 }
927 }
928
929 return Err(error_invalid!("cfg region without entry node"));
934 };
935
936 self.import_node(entry_node, node)?;
939
940 {
943 let cfg_outputs = {
944 let [target_types] = region_target_types.as_slice() else {
945 return Err(error_invalid!("cfg region expects a single target"));
946 };
947
948 self.import_type_row(*target_types)?
949 };
950
951 let exit = self
952 .hugr
953 .add_node_with_parent(node, OpType::ExitBlock(ExitBlock { cfg_outputs }));
954 self.record_links(exit, Direction::Incoming, region_data.targets);
955 }
956
957 for child in region_data.children {
959 if *child != entry_node {
960 self.import_node(*child, node)?;
961 }
962 }
963
964 for meta_item in region_data.meta {
965 self.import_node_metadata(node, *meta_item)
966 .map_err(|err| error_context!(err, "node metadata"))?;
967 }
968
969 self.region_scope = prev_region;
970
971 Ok(())
972 }
973
974 fn import_node_block(
975 &mut self,
976 node_id: table::NodeId,
977 parent: Node,
978 ) -> Result<Node, ImportErrorInner> {
979 let node_data = self.get_node(node_id)?;
980 debug_assert_eq!(node_data.operation, table::Operation::Block);
981
982 let [region] = node_data.regions else {
983 return Err(error_invalid!("basic block expects a single region"));
984 };
985 let region_data = self.get_region(*region)?;
986 let [inputs, outputs] = self.get_func_type(
987 region_data
988 .signature
989 .ok_or_else(|| error_uninferred!("region signature"))?,
990 )?;
991 let inputs = self.import_type_row(inputs)?;
992 let (sum_rows, other_outputs) = self.import_adt_and_rest(outputs)?;
993
994 let optype = OpType::DataflowBlock(DataflowBlock {
995 inputs,
996 other_outputs,
997 sum_rows,
998 });
999 let node = self.make_node(node_id, optype, parent)?;
1000
1001 self.import_dfg_region(*region, node).map_err(|err| {
1002 error_context!(err, "block body defined by region with id {}", *region)
1003 })?;
1004 Ok(node)
1005 }
1006
1007 fn import_node_define_func(
1008 &mut self,
1009 node_id: table::NodeId,
1010 symbol: &'a table::Symbol<'a>,
1011 node_data: &'a table::Node<'a>,
1012 parent: Node,
1013 ) -> Result<Node, ImportErrorInner> {
1014 let visibility = symbol.visibility.clone().ok_or(ImportErrorInner::Invalid(
1015 "No visibility for FuncDefn".to_string(),
1016 ))?;
1017 self.import_poly_func_type(node_id, *symbol, |ctx, signature| {
1018 let func_name = ctx.import_title_metadata(node_id)?.unwrap_or(symbol.name);
1019
1020 let optype =
1021 OpType::FuncDefn(FuncDefn::new_vis(func_name, signature, visibility.into()));
1022
1023 let node = ctx.make_node(node_id, optype, parent)?;
1024
1025 let [region] = node_data.regions else {
1026 return Err(error_invalid!(
1027 "function definition nodes expect a single region"
1028 ));
1029 };
1030
1031 ctx.import_dfg_region(*region, node).map_err(|err| {
1032 error_context!(err, "function body defined by region with id {}", *region)
1033 })?;
1034
1035 Ok(node)
1036 })
1037 }
1038
1039 fn import_node_declare_func(
1040 &mut self,
1041 node_id: table::NodeId,
1042 symbol: &'a table::Symbol<'a>,
1043 parent: Node,
1044 ) -> Result<Node, ImportErrorInner> {
1045 let visibility = symbol.visibility.clone().ok_or(ImportErrorInner::Invalid(
1046 "No visibility for FuncDecl".to_string(),
1047 ))?;
1048 self.import_poly_func_type(node_id, *symbol, |ctx, signature| {
1049 let func_name = ctx.import_title_metadata(node_id)?.unwrap_or(symbol.name);
1050
1051 let optype =
1052 OpType::FuncDecl(FuncDecl::new_vis(func_name, signature, visibility.into()));
1053 let node = ctx.make_node(node_id, optype, parent)?;
1054 Ok(node)
1055 })
1056 }
1057
1058 fn import_node_custom(
1059 &mut self,
1060 node_id: table::NodeId,
1061 operation: table::TermId,
1062 node_data: &'a table::Node<'a>,
1063 parent: Node,
1064 ) -> Result<Node, ImportErrorInner> {
1065 if let Some([inputs, outputs]) = self.match_symbol(operation, model::CORE_CALL_INDIRECT)? {
1066 let inputs = self.import_type_row(inputs)?;
1067 let outputs = self.import_type_row(outputs)?;
1068 let signature = Signature::new(inputs, outputs);
1069 let optype = OpType::CallIndirect(CallIndirect { signature });
1070 let node = self.make_node(node_id, optype, parent)?;
1071 return Ok(node);
1072 }
1073
1074 if let Some([_, _, func]) = self.match_symbol(operation, model::CORE_CALL)? {
1075 let table::Term::Apply(symbol, args) = self.get_term(func)? else {
1076 return Err(error_invalid!(
1077 "expected a symbol application to be passed to `{}`",
1078 model::CORE_CALL
1079 ));
1080 };
1081
1082 let func_sig = self.get_func_signature(*symbol)?;
1083
1084 let type_args = args
1085 .iter()
1086 .map(|term| self.import_term(*term))
1087 .collect::<Result<Vec<TypeArg>, _>>()?;
1088
1089 self.static_edges.push((*symbol, node_id));
1090 let optype = OpType::Call(
1091 Call::try_new(func_sig, type_args).map_err(ImportErrorInner::Signature)?,
1092 );
1093
1094 let node = self.make_node(node_id, optype, parent)?;
1095 return Ok(node);
1096 }
1097
1098 if let Some([_, value]) = self.match_symbol(operation, model::CORE_LOAD_CONST)? {
1099 if let table::Term::Apply(symbol, args) = self.get_term(value)? {
1101 let func_node_data = self.get_node(*symbol)?;
1102
1103 if let table::Operation::DefineFunc(_) | table::Operation::DeclareFunc(_) =
1104 func_node_data.operation
1105 {
1106 let func_sig = self.get_func_signature(*symbol)?;
1107 let type_args = args
1108 .iter()
1109 .map(|term| self.import_term(*term))
1110 .collect::<Result<Vec<TypeArg>, _>>()?;
1111
1112 self.static_edges.push((*symbol, node_id));
1113
1114 let optype = OpType::LoadFunction(
1115 LoadFunction::try_new(func_sig, type_args)
1116 .map_err(ImportErrorInner::Signature)?,
1117 );
1118
1119 let node = self.make_node(node_id, optype, parent)?;
1120 return Ok(node);
1121 }
1122 }
1123
1124 let signature = node_data
1126 .signature
1127 .ok_or_else(|| error_uninferred!("node signature"))?;
1128 let [_, outputs] = self.get_func_type(signature)?;
1129 let outputs = self.import_closed_list(outputs)?;
1130 let output = outputs.first().ok_or_else(|| {
1131 error_invalid!("`{}` expects a single output", model::CORE_LOAD_CONST)
1132 })?;
1133 let datatype = self.import_type(*output)?;
1134
1135 let imported_value = self.import_value(value, *output)?;
1136
1137 let load_const_node = self.make_node(
1138 node_id,
1139 OpType::LoadConstant(LoadConstant {
1140 datatype: datatype.clone(),
1141 }),
1142 parent,
1143 )?;
1144
1145 let const_node = self
1146 .hugr
1147 .add_node_with_parent(parent, OpType::Const(Const::new(imported_value)));
1148
1149 self.hugr.connect(const_node, 0, load_const_node, 0);
1150
1151 return Ok(load_const_node);
1152 }
1153
1154 if let Some([_, _, tag]) = self.match_symbol(operation, model::CORE_MAKE_ADT)? {
1155 let table::Term::Literal(model::Literal::Nat(tag)) = self.get_term(tag)? else {
1156 return Err(error_invalid!(
1157 "`{}` expects a nat literal tag",
1158 model::CORE_MAKE_ADT
1159 ));
1160 };
1161
1162 let signature = node_data
1163 .signature
1164 .ok_or_else(|| error_uninferred!("node signature"))?;
1165 let [_, outputs] = self.get_func_type(signature)?;
1166 let (variants, _) = self.import_adt_and_rest(outputs)?;
1167 let node = self.make_node(
1168 node_id,
1169 OpType::Tag(Tag {
1170 variants,
1171 tag: *tag as usize,
1172 }),
1173 parent,
1174 )?;
1175 return Ok(node);
1176 }
1177
1178 let table::Term::Apply(node, params) = self.get_term(operation)? else {
1179 return Err(error_invalid!(
1180 "custom operations expect a symbol application referencing an operation"
1181 ));
1182 };
1183 let name = self.get_symbol_name(*node)?;
1184 let args = params
1185 .iter()
1186 .map(|param| self.import_term(*param))
1187 .collect::<Result<Vec<_>, _>>()?;
1188 let (extension, name) = self.import_custom_name(name)?;
1189 let signature = self.get_node_signature(node_id)?;
1190
1191 let optype = OpType::OpaqueOp(OpaqueOp::new(extension, name, args, signature));
1197 self.make_node(node_id, optype, parent)
1198 }
1199
1200 fn import_node_define_alias(
1201 &mut self,
1202 node_id: table::NodeId,
1203 symbol: &'a table::Symbol<'a>,
1204 value: table::TermId,
1205 parent: Node,
1206 ) -> Result<Node, ImportErrorInner> {
1207 if !symbol.params.is_empty() {
1208 return Err(error_unsupported!(
1209 "parameters or constraints in alias definition"
1210 ));
1211 }
1212
1213 let optype = OpType::AliasDefn(AliasDefn {
1214 name: symbol.name.to_smolstr(),
1215 definition: self.import_type(value)?,
1216 });
1217
1218 let node = self.make_node(node_id, optype, parent)?;
1219 Ok(node)
1220 }
1221
1222 fn import_node_declare_alias(
1223 &mut self,
1224 node_id: table::NodeId,
1225 symbol: &'a table::Symbol<'a>,
1226 parent: Node,
1227 ) -> Result<Node, ImportErrorInner> {
1228 if !symbol.params.is_empty() {
1229 return Err(error_unsupported!(
1230 "parameters or constraints in alias declaration"
1231 ));
1232 }
1233
1234 let optype = OpType::AliasDecl(AliasDecl {
1235 name: symbol.name.to_smolstr(),
1236 bound: TypeBound::Copyable,
1237 });
1238
1239 let node = self.make_node(node_id, optype, parent)?;
1240 Ok(node)
1241 }
1242
1243 fn import_poly_func_type<RV: MaybeRV, T>(
1244 &mut self,
1245 node: table::NodeId,
1246 symbol: table::Symbol<'a>,
1247 in_scope: impl FnOnce(&mut Self, PolyFuncTypeBase<RV>) -> Result<T, ImportErrorInner>,
1248 ) -> Result<T, ImportErrorInner> {
1249 (|| {
1250 let mut imported_params = Vec::with_capacity(symbol.params.len());
1251
1252 for (index, param) in symbol.params.iter().enumerate() {
1253 self.local_vars
1254 .insert(table::VarId(node, index as _), LocalVar::new(param.r#type));
1255 }
1256
1257 for constraint in symbol.constraints {
1258 if let Some([term]) = self.match_symbol(*constraint, model::CORE_NON_LINEAR)? {
1259 let table::Term::Var(var) = self.get_term(term)? else {
1260 return Err(error_unsupported!(
1261 "constraint on term that is not a variable"
1262 ));
1263 };
1264
1265 self.local_vars
1266 .get_mut(var)
1267 .ok_or_else(|| error_invalid!("unknown variable {}", var))?
1268 .bound = TypeBound::Copyable;
1269 } else {
1270 return Err(error_unsupported!("constraint other than copy or discard"));
1271 }
1272 }
1273
1274 for (index, param) in symbol.params.iter().enumerate() {
1275 let bound = self.local_vars[&table::VarId(node, index as _)].bound;
1277 imported_params.push(
1278 self.import_term_with_bound(param.r#type, bound)
1279 .map_err(|err| error_context!(err, "type of parameter `{}`", param.name))?,
1280 );
1281 }
1282
1283 let body = self.import_func_type::<RV>(symbol.signature)?;
1284 in_scope(self, PolyFuncTypeBase::new(imported_params, body))
1285 })()
1286 .map_err(|err| error_context!(err, "symbol `{}` defined by node {}", symbol.name, node))
1287 }
1288
1289 fn import_term(&mut self, term_id: table::TermId) -> Result<Term, ImportErrorInner> {
1291 self.import_term_with_bound(term_id, TypeBound::Linear)
1292 }
1293
1294 fn import_term_with_bound(
1295 &mut self,
1296 term_id: table::TermId,
1297 bound: TypeBound,
1298 ) -> Result<Term, ImportErrorInner> {
1299 (|| {
1300 if let Some([]) = self.match_symbol(term_id, model::CORE_STR_TYPE)? {
1301 return Ok(Term::StringType);
1302 }
1303
1304 if let Some([]) = self.match_symbol(term_id, model::CORE_NAT_TYPE)? {
1305 return Ok(Term::max_nat_type());
1306 }
1307
1308 if let Some([]) = self.match_symbol(term_id, model::CORE_BYTES_TYPE)? {
1309 return Ok(Term::BytesType);
1310 }
1311
1312 if let Some([]) = self.match_symbol(term_id, model::CORE_FLOAT_TYPE)? {
1313 return Ok(Term::FloatType);
1314 }
1315
1316 if let Some([]) = self.match_symbol(term_id, model::CORE_TYPE)? {
1317 return Ok(TypeParam::RuntimeType(bound));
1318 }
1319
1320 if let Some([]) = self.match_symbol(term_id, model::CORE_CONSTRAINT)? {
1321 return Err(error_unsupported!("`{}`", model::CORE_CONSTRAINT));
1322 }
1323
1324 if let Some([]) = self.match_symbol(term_id, model::CORE_STATIC)? {
1325 return Ok(Term::StaticType);
1326 }
1327
1328 if let Some([]) = self.match_symbol(term_id, model::CORE_CONST)? {
1329 return Err(error_unsupported!("`{}`", model::CORE_CONST));
1330 }
1331
1332 if let Some([item_type]) = self.match_symbol(term_id, model::CORE_LIST_TYPE)? {
1333 let item_type = self
1336 .import_term(item_type)
1337 .map_err(|err| error_context!(err, "item type of list type"))?;
1338 return Ok(TypeParam::new_list_type(item_type));
1339 }
1340
1341 if let Some([item_types]) = self.match_symbol(term_id, model::CORE_TUPLE_TYPE)? {
1342 let item_types = self
1345 .import_term(item_types)
1346 .map_err(|err| error_context!(err, "item types of tuple type"))?;
1347 return Ok(TypeParam::new_tuple_type(item_types));
1348 }
1349
1350 match self.get_term(term_id)? {
1351 table::Term::Wildcard => Err(error_uninferred!("wildcard")),
1352
1353 table::Term::Var(var) => {
1354 let var_info = self
1355 .local_vars
1356 .get(var)
1357 .ok_or_else(|| error_invalid!("unknown variable {}", var))?;
1358 let decl = self.import_term_with_bound(var_info.r#type, var_info.bound)?;
1359 Ok(Term::new_var_use(var.1 as _, decl))
1360 }
1361
1362 table::Term::List(parts) => {
1363 let parts: Vec<_> = parts
1365 .iter()
1366 .map(|part| self.import_seq_part(part))
1367 .collect::<Result<_, _>>()
1368 .map_err(|err| error_context!(err, "list parts"))?;
1369 Ok(TypeArg::new_list_from_parts(parts))
1370 }
1371
1372 table::Term::Tuple(parts) => {
1373 let parts: Vec<_> = parts
1375 .iter()
1376 .map(|part| self.import_seq_part(part))
1377 .try_collect()
1378 .map_err(|err| error_context!(err, "tuple parts"))?;
1379 Ok(TypeArg::new_tuple_from_parts(parts))
1380 }
1381
1382 table::Term::Literal(model::Literal::Str(value)) => {
1383 Ok(Term::String(value.to_string()))
1384 }
1385
1386 table::Term::Literal(model::Literal::Nat(value)) => Ok(Term::BoundedNat(*value)),
1387
1388 table::Term::Literal(model::Literal::Bytes(value)) => {
1389 Ok(Term::Bytes(value.clone()))
1390 }
1391 table::Term::Literal(model::Literal::Float(value)) => Ok(Term::Float(*value)),
1392 table::Term::Func { .. } => Err(error_unsupported!("function constant")),
1393
1394 table::Term::Apply { .. } => {
1395 let ty: Type = self.import_type(term_id)?;
1396 Ok(ty.into())
1397 }
1398 }
1399 })()
1400 .map_err(|err| error_context!(err, "term {}", term_id))
1401 }
1402
1403 fn import_seq_part(
1404 &mut self,
1405 seq_part: &'a table::SeqPart,
1406 ) -> Result<SeqPart<TypeArg>, ImportErrorInner> {
1407 Ok(match seq_part {
1408 table::SeqPart::Item(term_id) => SeqPart::Item(self.import_term(*term_id)?),
1409 table::SeqPart::Splice(term_id) => SeqPart::Splice(self.import_term(*term_id)?),
1410 })
1411 }
1412
1413 fn import_type<RV: MaybeRV>(
1415 &mut self,
1416 term_id: table::TermId,
1417 ) -> Result<TypeBase<RV>, ImportErrorInner> {
1418 (|| {
1419 if let Some([_, _]) = self.match_symbol(term_id, model::CORE_FN)? {
1420 let func_type = self.import_func_type::<RowVariable>(term_id)?;
1421 return Ok(TypeBase::new_function(func_type));
1422 }
1423
1424 if let Some([variants]) = self.match_symbol(term_id, model::CORE_ADT)? {
1425 let variants = (|| {
1426 self.import_closed_list(variants)?
1427 .iter()
1428 .map(|variant| self.import_type_row::<RowVariable>(*variant))
1429 .collect::<Result<Vec<_>, _>>()
1430 })()
1431 .map_err(|err| error_context!(err, "adt variants"))?;
1432
1433 return Ok(TypeBase::new_sum(variants));
1434 }
1435
1436 match self.get_term(term_id)? {
1437 table::Term::Wildcard => Err(error_uninferred!("wildcard")),
1438
1439 table::Term::Apply(symbol, args) => {
1440 let name = self.get_symbol_name(*symbol)?;
1441
1442 let args = args
1443 .iter()
1444 .map(|arg| self.import_term(*arg))
1445 .collect::<Result<Vec<_>, _>>()
1446 .map_err(|err| {
1447 error_context!(err, "type argument of custom type `{}`", name)
1448 })?;
1449
1450 let (extension, id) = self.import_custom_name(name)?;
1451
1452 let extension_ref =
1453 self.extensions
1454 .get(&extension)
1455 .ok_or_else(|| ExtensionError::Missing {
1456 missing_ext: extension.clone(),
1457 available: self.extensions.ids().cloned().collect(),
1458 })?;
1459
1460 let ext_type =
1461 extension_ref
1462 .get_type(&id)
1463 .ok_or_else(|| ExtensionError::MissingType {
1464 ext: extension.clone(),
1465 name: id.clone(),
1466 })?;
1467
1468 let bound = ext_type.bound(&args);
1469
1470 Ok(TypeBase::new_extension(CustomType::new(
1471 id,
1472 args,
1473 extension,
1474 bound,
1475 &Arc::downgrade(extension_ref),
1476 )))
1477 }
1478
1479 table::Term::Var(var @ table::VarId(_, index)) => {
1480 let local_var = self
1481 .local_vars
1482 .get(var)
1483 .ok_or(error_invalid!("unknown var {}", var))?;
1484 Ok(TypeBase::new_var_use(*index as _, local_var.bound))
1485 }
1486
1487 table::Term::List { .. }
1490 | table::Term::Tuple { .. }
1491 | table::Term::Literal(_)
1492 | table::Term::Func { .. } => Err(error_invalid!("expected a runtime type")),
1493 }
1494 })()
1495 .map_err(|err| error_context!(err, "term {} as `Type`", term_id))
1496 }
1497
1498 fn get_func_type(
1499 &mut self,
1500 term_id: table::TermId,
1501 ) -> Result<[table::TermId; 2], ImportErrorInner> {
1502 self.match_symbol(term_id, model::CORE_FN)?
1503 .ok_or(error_invalid!("expected a function type"))
1504 }
1505
1506 fn get_ctrl_type(
1507 &mut self,
1508 term_id: table::TermId,
1509 ) -> Result<[table::TermId; 2], ImportErrorInner> {
1510 self.match_symbol(term_id, model::CORE_CTRL)?
1511 .ok_or(error_invalid!("expected a control type"))
1512 }
1513
1514 fn import_func_type<RV: MaybeRV>(
1515 &mut self,
1516 term_id: table::TermId,
1517 ) -> Result<FuncTypeBase<RV>, ImportErrorInner> {
1518 (|| {
1519 let [inputs, outputs] = self.get_func_type(term_id)?;
1520 let inputs = self
1521 .import_type_row(inputs)
1522 .map_err(|err| error_context!(err, "function inputs"))?;
1523 let outputs = self
1524 .import_type_row(outputs)
1525 .map_err(|err| error_context!(err, "function outputs"))?;
1526 Ok(FuncTypeBase::new(inputs, outputs))
1527 })()
1528 .map_err(|err| error_context!(err, "function type"))
1529 }
1530
1531 fn import_closed_list(
1532 &mut self,
1533 term_id: table::TermId,
1534 ) -> Result<Vec<table::TermId>, ImportErrorInner> {
1535 fn import_into(
1536 ctx: &mut Context,
1537 term_id: table::TermId,
1538 types: &mut Vec<table::TermId>,
1539 ) -> Result<(), ImportErrorInner> {
1540 match ctx.get_term(term_id)? {
1541 table::Term::List(parts) => {
1542 types.reserve(parts.len());
1543
1544 for part in *parts {
1545 match part {
1546 table::SeqPart::Item(term_id) => {
1547 types.push(*term_id);
1548 }
1549 table::SeqPart::Splice(term_id) => {
1550 import_into(ctx, *term_id, types)?;
1551 }
1552 }
1553 }
1554 }
1555 _ => return Err(error_invalid!("expected a closed list")),
1556 }
1557
1558 Ok(())
1559 }
1560
1561 let mut types = Vec::new();
1562 import_into(self, term_id, &mut types)?;
1563 Ok(types)
1564 }
1565
1566 fn import_closed_tuple(
1567 &mut self,
1568 term_id: table::TermId,
1569 ) -> Result<Vec<table::TermId>, ImportErrorInner> {
1570 fn import_into(
1571 ctx: &mut Context,
1572 term_id: table::TermId,
1573 types: &mut Vec<table::TermId>,
1574 ) -> Result<(), ImportErrorInner> {
1575 match ctx.get_term(term_id)? {
1576 table::Term::Tuple(parts) => {
1577 types.reserve(parts.len());
1578
1579 for part in *parts {
1580 match part {
1581 table::SeqPart::Item(term_id) => {
1582 types.push(*term_id);
1583 }
1584 table::SeqPart::Splice(term_id) => {
1585 import_into(ctx, *term_id, types)?;
1586 }
1587 }
1588 }
1589 }
1590 _ => return Err(error_invalid!("expected a closed tuple")),
1591 }
1592
1593 Ok(())
1594 }
1595
1596 let mut types = Vec::new();
1597 import_into(self, term_id, &mut types)?;
1598 Ok(types)
1599 }
1600
1601 fn import_type_rows<RV: MaybeRV>(
1602 &mut self,
1603 term_id: table::TermId,
1604 ) -> Result<Vec<TypeRowBase<RV>>, ImportErrorInner> {
1605 self.import_closed_list(term_id)?
1606 .into_iter()
1607 .map(|term_id| self.import_type_row::<RV>(term_id))
1608 .collect()
1609 }
1610
1611 fn import_type_row<RV: MaybeRV>(
1612 &mut self,
1613 term_id: table::TermId,
1614 ) -> Result<TypeRowBase<RV>, ImportErrorInner> {
1615 fn import_into<RV: MaybeRV>(
1616 ctx: &mut Context,
1617 term_id: table::TermId,
1618 types: &mut Vec<TypeBase<RV>>,
1619 ) -> Result<(), ImportErrorInner> {
1620 match ctx.get_term(term_id)? {
1621 table::Term::List(parts) => {
1622 types.reserve(parts.len());
1623
1624 for item in *parts {
1625 match item {
1626 table::SeqPart::Item(term_id) => {
1627 types.push(ctx.import_type::<RV>(*term_id)?);
1628 }
1629 table::SeqPart::Splice(term_id) => {
1630 import_into(ctx, *term_id, types)?;
1631 }
1632 }
1633 }
1634 }
1635 table::Term::Var(table::VarId(_, index)) => {
1636 let var = RV::try_from_rv(RowVariable(*index as _, TypeBound::Linear))
1637 .map_err(|_| error_invalid!("expected a closed list"))?;
1638 types.push(TypeBase::new(TypeEnum::RowVar(var)));
1639 }
1640 _ => return Err(error_invalid!("expected a list")),
1641 }
1642
1643 Ok(())
1644 }
1645
1646 let mut types = Vec::new();
1647 import_into(self, term_id, &mut types)?;
1648 Ok(types.into())
1649 }
1650
1651 fn import_custom_name(
1652 &mut self,
1653 symbol: &'a str,
1654 ) -> Result<(ExtensionId, SmolStr), ImportErrorInner> {
1655 use std::collections::hash_map::Entry;
1656 match self.custom_name_cache.entry(symbol) {
1657 Entry::Occupied(occupied_entry) => Ok(occupied_entry.get().clone()),
1658 Entry::Vacant(vacant_entry) => {
1659 let qualified_name = ExtensionId::new(symbol)
1660 .map_err(|_| error_invalid!("`{}` is not a valid symbol name", symbol))?;
1661
1662 let (extension, id) = qualified_name
1663 .split_last()
1664 .ok_or_else(|| error_invalid!("`{}` is not a valid symbol name", symbol))?;
1665
1666 vacant_entry.insert((extension.clone(), id.clone()));
1667 Ok((extension, id))
1668 }
1669 }
1670 }
1671
1672 fn import_value(
1673 &mut self,
1674 term_id: table::TermId,
1675 type_id: table::TermId,
1676 ) -> Result<Value, ImportErrorInner> {
1677 let term_data = self.get_term(term_id)?;
1678
1679 if let Some([runtime_type, json]) = self.match_symbol(term_id, model::COMPAT_CONST_JSON)? {
1683 let table::Term::Literal(model::Literal::Str(json)) = self.get_term(json)? else {
1684 return Err(error_invalid!(
1685 "`{}` expects a string literal",
1686 model::COMPAT_CONST_JSON
1687 ));
1688 };
1689
1690 let value: Option<Box<dyn CustomConst>> = serde_json::from_str(json).ok();
1694
1695 if let Some(value) = value {
1696 let opaque_value = OpaqueValue::from(value);
1697 return Ok(Value::Extension { e: opaque_value });
1698 } else {
1699 let runtime_type = self.import_type(runtime_type)?;
1700 let value: serde_json::Value = serde_json::from_str(json).map_err(|_| {
1701 error_invalid!(
1702 "unable to parse JSON string for `{}`",
1703 model::COMPAT_CONST_JSON
1704 )
1705 })?;
1706 let custom_const = CustomSerialized::new(runtime_type, value);
1707 let opaque_value = OpaqueValue::new(custom_const);
1708 return Ok(Value::Extension { e: opaque_value });
1709 }
1710 }
1711
1712 if let Some([_, element_type_term, contents]) =
1713 self.match_symbol(term_id, ArrayValue::CTR_NAME)?
1714 {
1715 let element_type = self.import_type(element_type_term)?;
1716 let contents = self.import_closed_list(contents)?;
1717 let contents = contents
1718 .iter()
1719 .map(|item| self.import_value(*item, element_type_term))
1720 .collect::<Result<Vec<_>, _>>()?;
1721 return Ok(ArrayValue::new(element_type, contents).into());
1722 }
1723
1724 if let Some([bitwidth, value]) = self.match_symbol(term_id, ConstInt::CTR_NAME)? {
1725 let bitwidth = {
1726 let table::Term::Literal(model::Literal::Nat(bitwidth)) =
1727 self.get_term(bitwidth)?
1728 else {
1729 return Err(error_invalid!(
1730 "`{}` expects a nat literal in its `bitwidth` argument",
1731 ConstInt::CTR_NAME
1732 ));
1733 };
1734 if *bitwidth > 6 {
1735 return Err(error_invalid!(
1736 "`{}` expects the bitwidth to be at most 6, got {}",
1737 ConstInt::CTR_NAME,
1738 bitwidth
1739 ));
1740 }
1741 *bitwidth as u8
1742 };
1743
1744 let value = {
1745 let table::Term::Literal(model::Literal::Nat(value)) = self.get_term(value)? else {
1746 return Err(error_invalid!(
1747 "`{}` expects a nat literal value",
1748 ConstInt::CTR_NAME
1749 ));
1750 };
1751 *value
1752 };
1753
1754 return Ok(ConstInt::new_u(bitwidth, value)
1755 .map_err(|_| error_invalid!("failed to create int constant"))?
1756 .into());
1757 }
1758
1759 if let Some([value]) = self.match_symbol(term_id, ConstF64::CTR_NAME)? {
1760 let table::Term::Literal(model::Literal::Float(value)) = self.get_term(value)? else {
1761 return Err(error_invalid!(
1762 "`{}` expects a float literal value",
1763 ConstF64::CTR_NAME
1764 ));
1765 };
1766
1767 return Ok(ConstF64::new(value.into_inner()).into());
1768 }
1769
1770 if let Some([_, _, tag, values]) = self.match_symbol(term_id, model::CORE_CONST_ADT)? {
1771 let [variants] = self.expect_symbol(type_id, model::CORE_ADT)?;
1772 let values = self.import_closed_tuple(values)?;
1773 let variants = self.import_closed_list(variants)?;
1774
1775 let table::Term::Literal(model::Literal::Nat(tag)) = self.get_term(tag)? else {
1776 return Err(error_invalid!(
1777 "`{}` expects a nat literal tag",
1778 model::CORE_ADT
1779 ));
1780 };
1781
1782 let variant = variants.get(*tag as usize).ok_or(error_invalid!(
1783 "the tag of a `{}` must be a valid index into the list of variants",
1784 model::CORE_CONST_ADT
1785 ))?;
1786
1787 let variant = self.import_closed_list(*variant)?;
1788
1789 let items = values
1790 .iter()
1791 .zip(variant.iter())
1792 .map(|(value, typ)| self.import_value(*value, *typ))
1793 .collect::<Result<Vec<_>, _>>()?;
1794
1795 let typ = {
1796 let typ: Type = self.import_type(type_id)?;
1798 match typ.as_type_enum() {
1799 TypeEnum::Sum(sum) => sum.clone(),
1800 _ => unreachable!(),
1801 }
1802 };
1803
1804 return Ok(Value::sum(*tag as _, items, typ).unwrap());
1805 }
1806
1807 match term_data {
1808 table::Term::Wildcard => Err(error_uninferred!("wildcard")),
1809 table::Term::Var(_) => Err(error_unsupported!("constant value containing a variable")),
1810
1811 table::Term::Apply(symbol, _) => {
1812 let symbol_name = self.get_symbol_name(*symbol)?;
1813 Err(error_unsupported!(
1814 "unknown custom constant value `{}`",
1815 symbol_name
1816 ))
1817 }
1821
1822 table::Term::List { .. } | table::Term::Tuple(_) | table::Term::Literal(_) => {
1823 Err(error_invalid!("expected constant"))
1824 }
1825
1826 table::Term::Func { .. } => Err(error_unsupported!("constant function value")),
1827 }
1828 }
1829
1830 fn match_symbol<const N: usize>(
1831 &self,
1832 term_id: table::TermId,
1833 name: &str,
1834 ) -> Result<Option<[table::TermId; N]>, ImportErrorInner> {
1835 let term = self.get_term(term_id)?;
1836
1837 let table::Term::Apply(symbol, args) = term else {
1840 return Ok(None);
1841 };
1842
1843 if name != self.get_symbol_name(*symbol)? {
1844 return Ok(None);
1845 }
1846
1847 if args.len() > N {
1851 return Ok(None);
1852 }
1853
1854 let result = std::array::from_fn(|i| {
1855 (i + args.len())
1856 .checked_sub(N)
1857 .map(|i| args[i])
1858 .unwrap_or_default()
1859 });
1860
1861 Ok(Some(result))
1862 }
1863
1864 fn expect_symbol<const N: usize>(
1865 &self,
1866 term_id: table::TermId,
1867 name: &str,
1868 ) -> Result<[table::TermId; N], ImportErrorInner> {
1869 self.match_symbol(term_id, name)?.ok_or(error_invalid!(
1870 "expected symbol `{}` with arity {}",
1871 name,
1872 N
1873 ))
1874 }
1875
1876 fn import_title_metadata(
1878 &self,
1879 node_id: table::NodeId,
1880 ) -> Result<Option<&'a str>, ImportErrorInner> {
1881 let node_data = self.get_node(node_id)?;
1882 for meta in node_data.meta {
1883 let Some([name]) = self.match_symbol(*meta, model::CORE_TITLE)? else {
1884 continue;
1885 };
1886
1887 let table::Term::Literal(model::Literal::Str(name)) = self.get_term(name)? else {
1888 return Err(error_invalid!(
1889 "`{}` metadata expected a string literal as argument",
1890 model::CORE_TITLE
1891 ));
1892 };
1893
1894 return Ok(Some(name.as_str()));
1895 }
1896
1897 Ok(None)
1898 }
1899}
1900
1901#[derive(Debug, Clone, Copy)]
1903struct LocalVar {
1904 r#type: table::TermId,
1906 bound: TypeBound,
1908}
1909
1910impl LocalVar {
1911 pub fn new(r#type: table::TermId) -> Self {
1912 Self {
1913 r#type,
1914 bound: TypeBound::Linear,
1915 }
1916 }
1917}