@@ -7,7 +7,7 @@ use std::sync::Arc;
77
88use crate :: {
99 extension:: { ExtensionId , ExtensionRegistry , ExtensionSet , SignatureError } ,
10- hugr:: HugrMut ,
10+ hugr:: { HugrMut , NodeMetadata } ,
1111 ops:: {
1212 constant:: { CustomConst , CustomSerialized , OpaqueValue } ,
1313 AliasDecl , AliasDefn , Call , CallIndirect , Case , Conditional , Const , DataflowBlock ,
@@ -68,6 +68,23 @@ pub enum ImportError {
6868 /// The model is not well-formed.
6969 #[ error( "validate error: {0}" ) ]
7070 Model ( #[ from] table:: ModelError ) ,
71+ /// Incorrect order hints.
72+ #[ error( "incorrect order hint: {0}" ) ]
73+ OrderHint ( #[ from] OrderHintError ) ,
74+ }
75+
76+ /// Import error caused by incorrect order hints.
77+ #[ derive( Debug , Clone , Error ) ]
78+ pub enum OrderHintError {
79+ /// Duplicate order hint key in the same region.
80+ #[ error( "duplicate order hint key {0}" ) ]
81+ DuplicateKey ( table:: NodeId , u64 ) ,
82+ /// Order hint including a key not defined in the region.
83+ #[ error( "order hint with unknown key {0}" ) ]
84+ UnknownKey ( u64 ) ,
85+ /// Order hint involving a node with no order port.
86+ #[ error( "order hint on node with no order port: {0}" ) ]
87+ NoOrderPort ( table:: NodeId ) ,
7188}
7289
7390/// Helper macro to create an `ImportError::Unsupported` error with a formatted message.
@@ -197,11 +214,27 @@ impl<'a> Context<'a> {
197214 self . record_links ( node, Direction :: Incoming , node_data. inputs ) ;
198215 self . record_links ( node, Direction :: Outgoing , node_data. outputs ) ;
199216
217+ // Import the JSON metadata
200218 for meta_item in node_data. meta {
201- // TODO: For now we expect all metadata to be JSON since this is how
202- // it is handled in `hugr-core`.
203- let ( name, value) = self . import_json_meta ( * meta_item) ?;
204- self . hugr . set_metadata ( node, name, value) ;
219+ let Some ( [ name_arg, json_arg] ) =
220+ self . match_symbol ( * meta_item, model:: COMPAT_META_JSON ) ?
221+ else {
222+ continue ;
223+ } ;
224+
225+ let table:: Term :: Literal ( model:: Literal :: Str ( name) ) = self . get_term ( name_arg) ? else {
226+ return Err ( table:: ModelError :: TypeError ( * meta_item) . into ( ) ) ;
227+ } ;
228+
229+ let table:: Term :: Literal ( model:: Literal :: Str ( json_str) ) = self . get_term ( json_arg) ?
230+ else {
231+ return Err ( table:: ModelError :: TypeError ( * meta_item) . into ( ) ) ;
232+ } ;
233+
234+ let json_value: NodeMetadata = serde_json:: from_str ( json_str)
235+ . map_err ( |_| table:: ModelError :: TypeError ( * meta_item) ) ?;
236+
237+ self . hugr . set_metadata ( node, name, json_value) ;
205238 }
206239
207240 Ok ( node)
@@ -617,11 +650,82 @@ impl<'a> Context<'a> {
617650 self . import_node ( * child, node) ?;
618651 }
619652
653+ self . create_order_edges ( region) ?;
654+
620655 self . region_scope = prev_region;
621656
622657 Ok ( ( ) )
623658 }
624659
660+ /// Create order edges between nodes of a dataflow region based on order hint metadata.
661+ ///
662+ /// This method assumes that the nodes for the children of the region have already been imported.
663+ fn create_order_edges ( & mut self , region_id : table:: RegionId ) -> Result < ( ) , ImportError > {
664+ let region_data = self . get_region ( region_id) ?;
665+ debug_assert_eq ! ( region_data. kind, model:: RegionKind :: DataFlow ) ;
666+
667+ // Collect order hint keys
668+ // PERFORMANCE: It might be worthwhile to reuse the map to avoid allocations.
669+ let mut order_keys = FxHashMap :: < u64 , table:: NodeId > :: default ( ) ;
670+
671+ for child_id in region_data. children {
672+ let child_data = self . get_node ( * child_id) ?;
673+
674+ for meta_id in child_data. meta {
675+ let Some ( [ key] ) = self . match_symbol ( * meta_id, model:: ORDER_HINT_KEY ) ? else {
676+ continue ;
677+ } ;
678+
679+ let table:: Term :: Literal ( model:: Literal :: Nat ( key) ) = self . get_term ( key) ? else {
680+ continue ;
681+ } ;
682+
683+ if order_keys. insert ( * key, * child_id) . is_some ( ) {
684+ return Err ( OrderHintError :: DuplicateKey ( * child_id, * key) . into ( ) ) ;
685+ }
686+ }
687+ }
688+
689+ // Insert order edges
690+ for meta_id in region_data. meta {
691+ let Some ( [ a, b] ) = self . match_symbol ( * meta_id, model:: ORDER_HINT_ORDER ) ? else {
692+ continue ;
693+ } ;
694+
695+ let table:: Term :: Literal ( model:: Literal :: Nat ( a) ) = self . get_term ( a) ? else {
696+ continue ;
697+ } ;
698+
699+ let table:: Term :: Literal ( model:: Literal :: Nat ( b) ) = self . get_term ( b) ? else {
700+ continue ;
701+ } ;
702+
703+ let a = order_keys. get ( a) . ok_or ( OrderHintError :: UnknownKey ( * a) ) ?;
704+ let b = order_keys. get ( b) . ok_or ( OrderHintError :: UnknownKey ( * b) ) ?;
705+
706+ // NOTE: The lookups here are expected to succeed since we only
707+ // process the order metadata after we have imported the nodes.
708+ let a_node = self . nodes [ a] ;
709+ let b_node = self . nodes [ b] ;
710+
711+ let a_port = self
712+ . hugr
713+ . get_optype ( a_node)
714+ . other_output_port ( )
715+ . ok_or ( OrderHintError :: NoOrderPort ( * a) ) ?;
716+
717+ let b_port = self
718+ . hugr
719+ . get_optype ( b_node)
720+ . other_input_port ( )
721+ . ok_or ( OrderHintError :: NoOrderPort ( * b) ) ?;
722+
723+ self . hugr . connect ( a_node, a_port, b_node, b_port) ;
724+ }
725+
726+ Ok ( ( ) )
727+ }
728+
625729 fn import_adt_and_rest (
626730 & mut self ,
627731 node_id : table:: NodeId ,
@@ -1358,28 +1462,6 @@ impl<'a> Context<'a> {
13581462 }
13591463 }
13601464
1361- fn import_json_meta (
1362- & mut self ,
1363- term_id : table:: TermId ,
1364- ) -> Result < ( & ' a str , serde_json:: Value ) , ImportError > {
1365- let [ name_arg, json_arg] = self
1366- . match_symbol ( term_id, model:: COMPAT_META_JSON ) ?
1367- . ok_or ( table:: ModelError :: TypeError ( term_id) ) ?;
1368-
1369- let table:: Term :: Literal ( model:: Literal :: Str ( name) ) = self . get_term ( name_arg) ? else {
1370- return Err ( table:: ModelError :: TypeError ( term_id) . into ( ) ) ;
1371- } ;
1372-
1373- let table:: Term :: Literal ( model:: Literal :: Str ( json_str) ) = self . get_term ( json_arg) ? else {
1374- return Err ( table:: ModelError :: TypeError ( term_id) . into ( ) ) ;
1375- } ;
1376-
1377- let json_value =
1378- serde_json:: from_str ( json_str) . map_err ( |_| table:: ModelError :: TypeError ( term_id) ) ?;
1379-
1380- Ok ( ( name, json_value) )
1381- }
1382-
13831465 fn import_value (
13841466 & mut self ,
13851467 term_id : table:: TermId ,
0 commit comments