diff --git a/src/diff/dijkstra.rs b/src/diff/dijkstra.rs index 55ffc3f9c..8fe86748c 100644 --- a/src/diff/dijkstra.rs +++ b/src/diff/dijkstra.rs @@ -17,23 +17,23 @@ use crate::{ pub(crate) struct ExceededGraphLimit {} /// Return the shortest route from `start` to the end vertex. -fn shortest_vertex_path<'s, 'b>( - start: &'b Vertex<'s, 'b>, - vertex_arena: &'b Bump, +fn shortest_vertex_path<'s, 'v>( + start: &'v Vertex<'s, 'v>, + vertex_arena: &'v Bump, size_hint: usize, graph_limit: usize, -) -> Result>, ExceededGraphLimit> { +) -> Result>, ExceededGraphLimit> { // We want to visit nodes with the shortest distance first, but // RadixHeapMap is a max-heap. Ensure nodes are wrapped with // Reverse to flip comparisons. - let mut heap: RadixHeapMap, &'b Vertex<'s, 'b>> = RadixHeapMap::new(); + let mut heap: RadixHeapMap, &'v Vertex<'s, 'v>> = RadixHeapMap::new(); heap.push(Reverse(0), start); let mut seen = DftHashMap::default(); seen.reserve(size_hint); - let end: &'b Vertex<'s, 'b> = loop { + let end: &'v Vertex<'s, 'v> = loop { match heap.pop() { Some((Reverse(distance), current)) => { if current.is_end() { @@ -77,7 +77,7 @@ fn shortest_vertex_path<'s, 'b>( ); let mut current = Some((0, end)); - let mut vertex_route: Vec<&'b Vertex<'s, 'b>> = vec![]; + let mut vertex_route: Vec<&'v Vertex<'s, 'v>> = vec![]; while let Some((_, node)) = current { vertex_route.push(node); current = node.predecessor.get(); @@ -87,9 +87,9 @@ fn shortest_vertex_path<'s, 'b>( Ok(vertex_route) } -fn shortest_path_with_edges<'s, 'b>( - route: &[&'b Vertex<'s, 'b>], -) -> Vec<(Edge, &'b Vertex<'s, 'b>)> { +fn shortest_path_with_edges<'s, 'v>( + route: &[&'v Vertex<'s, 'v>], +) -> Vec<(Edge, &'v Vertex<'s, 'v>)> { let mut prev = route.first().expect("Expected non-empty route"); let mut cost = 0; @@ -111,18 +111,18 @@ fn shortest_path_with_edges<'s, 'b>( /// /// The vec returned does not return the very last vertex. This is /// necessary because a route of N vertices only has N-1 edges. -fn shortest_path<'s, 'b>( - start: Vertex<'s, 'b>, - vertex_arena: &'b Bump, +fn shortest_path<'s, 'v>( + start: Vertex<'s, 'v>, + vertex_arena: &'v Bump, size_hint: usize, graph_limit: usize, -) -> Result)>, ExceededGraphLimit> { - let start: &'b Vertex<'s, 'b> = vertex_arena.alloc(start); +) -> Result)>, ExceededGraphLimit> { + let start: &'v Vertex<'s, 'v> = vertex_arena.alloc(start); let vertex_path = shortest_vertex_path(start, vertex_arena, size_hint, graph_limit)?; Ok(shortest_path_with_edges(&vertex_path)) } -fn edge_between<'s, 'b>(before: &Vertex<'s, 'b>, after: &Vertex<'s, 'b>) -> Edge { +fn edge_between<'s, 'v>(before: &Vertex<'s, 'v>, after: &Vertex<'s, 'v>) -> Edge { assert_ne!(before, after); let mut shortest_edge: Option = None; diff --git a/src/diff/graph.rs b/src/diff/graph.rs index 7e943f6b8..60092a159 100644 --- a/src/diff/graph.rs +++ b/src/diff/graph.rs @@ -50,15 +50,18 @@ use crate::{ /// LHS: X A RHS: A /// ^ ^ /// ``` +/// +/// Vertices are arena allocated (the 'v lifetime) and have references +/// to syntax nodes (the 's lifetime). #[derive(Debug, Clone)] -pub(crate) struct Vertex<'s, 'b> { - pub(crate) neighbours: RefCell)]>>, - pub(crate) predecessor: Cell)>>, +pub(crate) struct Vertex<'s, 'v> { + pub(crate) neighbours: RefCell)]>>, + pub(crate) predecessor: Cell)>>, // TODO: experiment with storing SyntaxId only, and have a HashMap // from SyntaxId to &Syntax. pub(crate) lhs_syntax: Option<&'s Syntax<'s>>, pub(crate) rhs_syntax: Option<&'s Syntax<'s>>, - parents: Stack<'b, EnteredDelimiter<'s, 'b>>, + parents: Stack<'v, EnteredDelimiter<'s, 'v>>, lhs_parent_id: Option, rhs_parent_id: Option, } @@ -116,12 +119,12 @@ impl Hash for Vertex<'_, '_> { /// Tracks entering syntax List nodes. #[derive(Clone, PartialEq)] -enum EnteredDelimiter<'s, 'b> { +enum EnteredDelimiter<'s, 'v> { /// If we've entered the LHS or RHS separately, we can pop either /// side independently. /// /// Assumes that at least one stack is non-empty. - PopEither((Stack<'b, &'s Syntax<'s>>, Stack<'b, &'s Syntax<'s>>)), + PopEither((Stack<'v, &'s Syntax<'s>>, Stack<'v, &'s Syntax<'s>>)), /// If we've entered the LHS and RHS together, we must pop both /// sides together too. Otherwise we'd consider the following case to have no changes. /// @@ -148,12 +151,12 @@ impl fmt::Debug for EnteredDelimiter<'_, '_> { } } -fn push_both_delimiters<'s, 'b>( - entered: &Stack<'b, EnteredDelimiter<'s, 'b>>, +fn push_both_delimiters<'s, 'v>( + entered: &Stack<'v, EnteredDelimiter<'s, 'v>>, lhs_delim: &'s Syntax<'s>, rhs_delim: &'s Syntax<'s>, - alloc: &'b Bump, -) -> Stack<'b, EnteredDelimiter<'s, 'b>> { + alloc: &'v Bump, +) -> Stack<'v, EnteredDelimiter<'s, 'v>> { entered.push(EnteredDelimiter::PopBoth((lhs_delim, rhs_delim)), alloc) } @@ -161,12 +164,12 @@ fn can_pop_either_parent(entered: &Stack) -> bool { matches!(entered.peek(), Some(EnteredDelimiter::PopEither(_))) } -fn try_pop_both<'s, 'b>( - entered: &Stack<'b, EnteredDelimiter<'s, 'b>>, +fn try_pop_both<'s, 'v>( + entered: &Stack<'v, EnteredDelimiter<'s, 'v>>, ) -> Option<( &'s Syntax<'s>, &'s Syntax<'s>, - Stack<'b, EnteredDelimiter<'s, 'b>>, + Stack<'v, EnteredDelimiter<'s, 'v>>, )> { match entered.peek() { Some(EnteredDelimiter::PopBoth((lhs_delim, rhs_delim))) => { @@ -176,10 +179,10 @@ fn try_pop_both<'s, 'b>( } } -fn try_pop_lhs<'s, 'b>( - entered: &Stack<'b, EnteredDelimiter<'s, 'b>>, - alloc: &'b Bump, -) -> Option<(&'s Syntax<'s>, Stack<'b, EnteredDelimiter<'s, 'b>>)> { +fn try_pop_lhs<'s, 'v>( + entered: &Stack<'v, EnteredDelimiter<'s, 'v>>, + alloc: &'v Bump, +) -> Option<(&'s Syntax<'s>, Stack<'v, EnteredDelimiter<'s, 'v>>)> { match entered.peek() { Some(EnteredDelimiter::PopEither((lhs_delims, rhs_delims))) => match lhs_delims.peek() { Some(lhs_delim) => { @@ -201,10 +204,10 @@ fn try_pop_lhs<'s, 'b>( } } -fn try_pop_rhs<'s, 'b>( - entered: &Stack<'b, EnteredDelimiter<'s, 'b>>, - alloc: &'b Bump, -) -> Option<(&'s Syntax<'s>, Stack<'b, EnteredDelimiter<'s, 'b>>)> { +fn try_pop_rhs<'s, 'v>( + entered: &Stack<'v, EnteredDelimiter<'s, 'v>>, + alloc: &'v Bump, +) -> Option<(&'s Syntax<'s>, Stack<'v, EnteredDelimiter<'s, 'v>>)> { match entered.peek() { Some(EnteredDelimiter::PopEither((lhs_delims, rhs_delims))) => match rhs_delims.peek() { Some(rhs_delim) => { @@ -226,11 +229,11 @@ fn try_pop_rhs<'s, 'b>( } } -fn push_lhs_delimiter<'s, 'b>( - entered: &Stack<'b, EnteredDelimiter<'s, 'b>>, +fn push_lhs_delimiter<'s, 'v>( + entered: &Stack<'v, EnteredDelimiter<'s, 'v>>, delimiter: &'s Syntax<'s>, - alloc: &'b Bump, -) -> Stack<'b, EnteredDelimiter<'s, 'b>> { + alloc: &'v Bump, +) -> Stack<'v, EnteredDelimiter<'s, 'v>> { match entered.peek() { Some(EnteredDelimiter::PopEither((lhs_delims, rhs_delims))) => entered.pop().unwrap().push( EnteredDelimiter::PopEither((lhs_delims.push(delimiter, alloc), rhs_delims.clone())), @@ -243,11 +246,11 @@ fn push_lhs_delimiter<'s, 'b>( } } -fn push_rhs_delimiter<'s, 'b>( - entered: &Stack<'b, EnteredDelimiter<'s, 'b>>, +fn push_rhs_delimiter<'s, 'v>( + entered: &Stack<'v, EnteredDelimiter<'s, 'v>>, delimiter: &'s Syntax<'s>, - alloc: &'b Bump, -) -> Stack<'b, EnteredDelimiter<'s, 'b>> { + alloc: &'v Bump, +) -> Stack<'v, EnteredDelimiter<'s, 'v>> { match entered.peek() { Some(EnteredDelimiter::PopEither((lhs_delims, rhs_delims))) => entered.pop().unwrap().push( EnteredDelimiter::PopEither((lhs_delims.clone(), rhs_delims.push(delimiter, alloc))), @@ -260,7 +263,7 @@ fn push_rhs_delimiter<'s, 'b>( } } -impl<'s, 'b> Vertex<'s, 'b> { +impl<'s, 'v> Vertex<'s, 'v> { pub(crate) fn is_end(&self) -> bool { self.lhs_syntax.is_none() && self.rhs_syntax.is_none() && self.parents.is_empty() } @@ -363,11 +366,11 @@ impl Edge { } } -fn allocate_if_new<'s, 'b>( - v: Vertex<'s, 'b>, - alloc: &'b Bump, - seen: &mut DftHashMap<&Vertex<'s, 'b>, SmallVec<[&'b Vertex<'s, 'b>; 2]>>, -) -> &'b Vertex<'s, 'b> { +fn allocate_if_new<'s, 'v>( + v: Vertex<'s, 'v>, + alloc: &'v Bump, + seen: &mut DftHashMap<&Vertex<'s, 'v>, SmallVec<[&'v Vertex<'s, 'v>; 2]>>, +) -> &'v Vertex<'s, 'v> { // We use the entry API so that we only need to do a single lookup // for access and insert. match seen.raw_entry_mut().from_key(&v) { @@ -404,7 +407,7 @@ fn allocate_if_new<'s, 'b>( // // We still use a vec to enable experiments with the value // of how many possible parenthesis nestings to explore. - let existing: SmallVec<[&'b Vertex<'s, 'b>; 2]> = smallvec![&*allocated]; + let existing: SmallVec<[&'v Vertex<'s, 'v>; 2]> = smallvec![&*allocated]; vacant.insert(allocated, existing); allocated @@ -425,19 +428,19 @@ fn looks_like_punctuation(node: &Syntax) -> bool { /// Pop as many parents of `lhs_node` and `rhs_node` as /// possible. Return the new syntax nodes and parents. -fn pop_all_parents<'s, 'b>( +fn pop_all_parents<'s, 'v>( lhs_node: Option<&'s Syntax<'s>>, rhs_node: Option<&'s Syntax<'s>>, lhs_parent_id: Option, rhs_parent_id: Option, - parents: &Stack<'b, EnteredDelimiter<'s, 'b>>, - alloc: &'b Bump, + parents: &Stack<'v, EnteredDelimiter<'s, 'v>>, + alloc: &'v Bump, ) -> ( Option<&'s Syntax<'s>>, Option<&'s Syntax<'s>>, Option, Option, - Stack<'b, EnteredDelimiter<'s, 'b>>, + Stack<'v, EnteredDelimiter<'s, 'v>>, ) { let mut lhs_node = lhs_node; let mut rhs_node = rhs_node; @@ -493,10 +496,10 @@ fn pop_all_parents<'s, 'b>( /// Compute the neighbours of `v` if we haven't previously done so, /// and write them to the .neighbours cell inside `v`. -pub(crate) fn set_neighbours<'s, 'b>( - v: &Vertex<'s, 'b>, - alloc: &'b Bump, - seen: &mut DftHashMap<&Vertex<'s, 'b>, SmallVec<[&'b Vertex<'s, 'b>; 2]>>, +pub(crate) fn set_neighbours<'s, 'v>( + v: &Vertex<'s, 'v>, + alloc: &'v Bump, + seen: &mut DftHashMap<&Vertex<'s, 'v>, SmallVec<[&'v Vertex<'s, 'v>; 2]>>, ) { if v.neighbours.borrow().is_some() { return; @@ -796,8 +799,8 @@ pub(crate) fn set_neighbours<'s, 'b>( .replace(Some(alloc.alloc_slice_copy(neighbours.as_slice()))); } -pub(crate) fn populate_change_map<'s, 'b>( - route: &[(Edge, &'b Vertex<'s, 'b>)], +pub(crate) fn populate_change_map<'s, 'v>( + route: &[(Edge, &'v Vertex<'s, 'v>)], change_map: &mut ChangeMap<'s>, ) { for (e, v) in route {