@@ -40,6 +40,7 @@ use std::num::NonZeroU32;
4040
4141use controlled_option:: ControlledOption ;
4242use controlled_option:: Niche ;
43+ use enumset:: EnumSetType ;
4344use smallvec:: SmallVec ;
4445
4546use crate :: arena:: Deque ;
@@ -2069,13 +2070,14 @@ impl PartialPath {
20692070 graph[ self . end_node ] . is_jump_to ( )
20702071 }
20712072
2072- /// Returns whether a partial path is "productive" --- that is, it is not cyclic, or its
2073- /// pre- and postcondition are incompatible.
2074- pub fn is_productive ( & self , graph : & StackGraph , partials : & mut PartialPaths ) -> bool {
2073+ /// Returns whether a partial path is cyclic---that is, it starts and ends at the same node,
2074+ /// and its postcondition is compatible with its precondition. If the path is cyclic, a
2075+ /// tuple is returned indicating whether cycle requires strengthening the pre- or postcondition.
2076+ pub fn is_cyclic ( & self , graph : & StackGraph , partials : & mut PartialPaths ) -> Option < Cyclicity > {
20752077 // StackGraph ensures that there are no nodes with duplicate IDs, so we can do a simple
20762078 // comparison of node handles here.
20772079 if self . start_node != self . end_node {
2078- return true ;
2080+ return None ;
20792081 }
20802082
20812083 let lhs = self ;
@@ -2084,30 +2086,36 @@ impl PartialPath {
20842086
20852087 let join = match Self :: compute_join ( graph, partials, lhs, & rhs) {
20862088 Ok ( join) => join,
2087- Err ( _) => return true ,
2089+ Err ( _) => return None ,
20882090 } ;
20892091
20902092 if lhs
20912093 . symbol_stack_precondition
20922094 . variable
20932095 . into_option ( )
20942096 . map_or ( false , |v| join. symbol_bindings . get ( v) . iter ( ) . len ( ) > 0 )
2097+ || lhs
2098+ . scope_stack_precondition
2099+ . variable
2100+ . into_option ( )
2101+ . map_or ( false , |v| join. scope_bindings . get ( v) . iter ( ) . len ( ) > 0 )
20952102 {
2096- // symbol stack precondition strengthened
2097- return true ;
2098- }
2099-
2100- if lhs
2101- . scope_stack_precondition
2103+ Some ( Cyclicity :: StrengthensPrecondition )
2104+ } else if rhs
2105+ . symbol_stack_postcondition
21022106 . variable
21032107 . into_option ( )
2104- . map_or ( false , |v| join. scope_bindings . get ( v) . iter ( ) . len ( ) > 0 )
2108+ . map_or ( false , |v| join. symbol_bindings . get ( v) . iter ( ) . len ( ) > 0 )
2109+ || rhs
2110+ . scope_stack_postcondition
2111+ . variable
2112+ . into_option ( )
2113+ . map_or ( false , |v| join. scope_bindings . get ( v) . iter ( ) . len ( ) > 0 )
21052114 {
2106- // scope stack precondition strengthened
2107- return true ;
2115+ Some ( Cyclicity :: StrengthensPostcondition )
2116+ } else {
2117+ Some ( Cyclicity :: Free )
21082118 }
2109-
2110- false
21112119 }
21122120
21132121 /// Ensures that the content of this partial path is available in both forwards and backwards
@@ -2222,6 +2230,16 @@ impl PartialPath {
22222230 }
22232231}
22242232
2233+ #[ derive( Debug , EnumSetType ) ]
2234+ pub enum Cyclicity {
2235+ /// The path can be freely concatenated to itself.
2236+ Free ,
2237+ /// Concatenating the path to itself strengthens the precondition---symbols are eliminated from the stack.
2238+ StrengthensPrecondition ,
2239+ /// Concatenating the path to itself strengthens the postcondition---symbols are introduced on the stack.
2240+ StrengthensPostcondition ,
2241+ }
2242+
22252243impl < ' a > DisplayWithPartialPaths for & ' a PartialPath {
22262244 fn prepare ( & mut self , graph : & StackGraph , partials : & mut PartialPaths ) {
22272245 self . symbol_stack_precondition
@@ -2285,6 +2303,45 @@ impl PartialPath {
22852303 . with_offset ( scope_variable_offset) ;
22862304 }
22872305
2306+ /// Replaces stack variables in the precondition with empty stacks.
2307+ pub fn eliminate_precondition_stack_variables ( & mut self , partials : & mut PartialPaths ) {
2308+ let mut symbol_bindings = PartialSymbolStackBindings :: new ( ) ;
2309+ let mut scope_bindings = PartialScopeStackBindings :: new ( ) ;
2310+ if let Some ( symbol_variable) = self . symbol_stack_precondition . variable ( ) {
2311+ symbol_bindings
2312+ . add (
2313+ partials,
2314+ symbol_variable,
2315+ PartialSymbolStack :: empty ( ) ,
2316+ & mut scope_bindings,
2317+ )
2318+ . unwrap ( ) ;
2319+ }
2320+ if let Some ( scope_variable) = self . scope_stack_precondition . variable ( ) {
2321+ scope_bindings
2322+ . add ( partials, scope_variable, PartialScopeStack :: empty ( ) )
2323+ . unwrap ( ) ;
2324+ }
2325+
2326+ self . symbol_stack_precondition = self
2327+ . symbol_stack_precondition
2328+ . apply_partial_bindings ( partials, & symbol_bindings, & scope_bindings)
2329+ . unwrap ( ) ;
2330+ self . scope_stack_precondition = self
2331+ . scope_stack_precondition
2332+ . apply_partial_bindings ( partials, & scope_bindings)
2333+ . unwrap ( ) ;
2334+
2335+ self . symbol_stack_postcondition = self
2336+ . symbol_stack_postcondition
2337+ . apply_partial_bindings ( partials, & symbol_bindings, & scope_bindings)
2338+ . unwrap ( ) ;
2339+ self . scope_stack_postcondition = self
2340+ . scope_stack_postcondition
2341+ . apply_partial_bindings ( partials, & scope_bindings)
2342+ . unwrap ( ) ;
2343+ }
2344+
22882345 /// Attempts to append an edge to the end of a partial path. If the edge is not a valid
22892346 /// extension of this partial path, we return an error describing why.
22902347 pub fn append (
@@ -2315,15 +2372,15 @@ impl PartialPath {
23152372 } ,
23162373 ) ;
23172374
2318- self . resolve ( graph, partials) ?;
2375+ self . resolve_from_postcondition ( graph, partials) ?;
23192376
23202377 Ok ( ( ) )
23212378 }
23222379
2323- /// Attempts to resolve any _jump to scope_ node at the end of a partial path. If the partial
2324- /// path does not end in a _jump to scope_ node, we do nothing. If it does, and we cannot
2325- /// resolve it, then we return an error describing why.
2326- pub fn resolve (
2380+ /// Attempts to resolve any _jump to scope_ node at the end of a partial path from the postcondition
2381+ /// scope stack. If the partial path does not end in a _jump to scope_ node, we do nothing. If it
2382+ /// does, and we cannot resolve it, then we return an error describing why.
2383+ pub fn resolve_from_postcondition (
23272384 & mut self ,
23282385 graph : & StackGraph ,
23292386 partials : & mut PartialPaths ,
@@ -2349,10 +2406,11 @@ impl PartialPath {
23492406 Ok ( ( ) )
23502407 }
23512408
2352- /// Attempts to resolve any _jump to scope_ node at the end of a partial path to the given node.
2353- /// If the partial path does not end in a _jump to scope_ node, we do nothing. If it does, and we
2354- /// cannot resolve it, then we return an error describing why.
2355- pub fn resolve_to (
2409+ /// Resolve any _jump to scope_ node at the end of a partial path to the given node, updating the
2410+ /// precondition to include the given node. If the partial path does not end in a _jump to scope_
2411+ /// node, we do nothing. If it does, and we cannot resolve it, then we return an error describing
2412+ /// why.
2413+ pub fn resolve_to_node (
23562414 & mut self ,
23572415 graph : & StackGraph ,
23582416 partials : & mut PartialPaths ,
@@ -2361,11 +2419,31 @@ impl PartialPath {
23612419 if !graph[ self . end_node ] . is_jump_to ( ) {
23622420 return Ok ( ( ) ) ;
23632421 }
2364- if self . scope_stack_postcondition . contains_scopes ( ) {
2365- return Err ( PathResolutionError :: ScopeStackUnsatisfied ) ; // this path was not properly resolved
2366- }
2367- self . scope_stack_precondition . push_back ( partials, node) ;
2422+
2423+ let scope_variable = match self . scope_stack_postcondition . variable ( ) {
2424+ Some ( scope_variable) => scope_variable,
2425+ None => return Err ( PathResolutionError :: ScopeStackUnsatisfied ) ,
2426+ } ;
2427+ let mut scope_stack = PartialScopeStack :: from_variable ( scope_variable) ;
2428+ scope_stack. push_front ( partials, node) ;
2429+
2430+ let symbol_bindings = PartialSymbolStackBindings :: new ( ) ;
2431+ let mut scope_bindings = PartialScopeStackBindings :: new ( ) ;
2432+ scope_bindings
2433+ . add ( partials, scope_variable, scope_stack)
2434+ . unwrap ( ) ;
2435+
2436+ self . symbol_stack_precondition = self
2437+ . symbol_stack_precondition
2438+ . apply_partial_bindings ( partials, & symbol_bindings, & scope_bindings)
2439+ . unwrap ( ) ;
2440+ self . scope_stack_precondition = self
2441+ . scope_stack_precondition
2442+ . apply_partial_bindings ( partials, & scope_bindings)
2443+ . unwrap ( ) ;
2444+
23682445 self . end_node = node;
2446+
23692447 Ok ( ( ) )
23702448 }
23712449
@@ -2407,13 +2485,7 @@ impl PartialPath {
24072485 copious_debugging ! ( " * invalid extension" ) ;
24082486 continue ;
24092487 }
2410- if new_cycle_detector
2411- . append ( graph, partials, & mut ( ) , edges, extension)
2412- . is_err ( )
2413- {
2414- copious_debugging ! ( " * cycle" ) ;
2415- continue ;
2416- }
2488+ new_cycle_detector. append ( edges, extension) ;
24172489 result. push ( ( new_path, new_cycle_detector) ) ;
24182490 }
24192491 }
@@ -2550,9 +2622,7 @@ impl Node {
25502622 scope_stack : & mut PartialScopeStack ,
25512623 ) {
25522624 match self {
2553- Node :: DropScopes ( _) => {
2554- * scope_stack = PartialScopeStack :: empty ( ) ;
2555- }
2625+ Node :: DropScopes ( _) => { }
25562626 Node :: JumpTo ( _) => { }
25572627 Node :: PopScopedSymbol ( node) => {
25582628 let symbol = symbol_stack
@@ -2676,10 +2746,6 @@ impl PartialPaths {
26762746 /// (a) is minimal, no path can be constructed by stitching other paths in the set, and
26772747 /// (b) covers all complete paths, from references to definitions, when used for path stitching
26782748 ///
2679- /// Callers are advised _not_ to filter this set in the visitor using functions like
2680- /// [`PartialPath::is_productive`][] as that may interfere with implementation changes of this
2681- /// function.
2682- ///
26832749 /// This function will not return until all reachable partial paths have been processed, so
26842750 /// `graph` must already contain a complete stack graph. If you have a very large stack graph
26852751 /// stored in some other storage system, and want more control over lazily loading only the
@@ -2734,6 +2800,12 @@ impl PartialPaths {
27342800 copious_debugging ! ( " * visit" ) ;
27352801 visit ( graph, self , path) ;
27362802 }
2803+ } else if !path_cycle_detector
2804+ . is_cyclic ( graph, self , & mut ( ) , & mut edges)
2805+ . expect ( "cyclic test failed when finding partial paths" )
2806+ . is_empty ( )
2807+ {
2808+ copious_debugging ! ( " * cycle" ) ;
27372809 } else {
27382810 copious_debugging ! ( " * extend" ) ;
27392811 path. extend_from_file (
@@ -2901,7 +2973,7 @@ impl PartialPath {
29012973 }
29022974 lhs. end_node = rhs. end_node ;
29032975
2904- lhs. resolve ( graph, partials) ?;
2976+ lhs. resolve_from_postcondition ( graph, partials) ?;
29052977
29062978 Ok ( ( ) )
29072979 }
0 commit comments