Clarify lifetimes

Fixes #869
pull/881/head
Wilfred Hughes 2025-09-26 06:50:30 +07:00
parent f4a37c9732
commit 8402832c1c
2 changed files with 65 additions and 62 deletions

@ -17,23 +17,23 @@ use crate::{
pub(crate) struct ExceededGraphLimit {} pub(crate) struct ExceededGraphLimit {}
/// Return the shortest route from `start` to the end vertex. /// Return the shortest route from `start` to the end vertex.
fn shortest_vertex_path<'s, 'b>( fn shortest_vertex_path<'s, 'v>(
start: &'b Vertex<'s, 'b>, start: &'v Vertex<'s, 'v>,
vertex_arena: &'b Bump, vertex_arena: &'v Bump,
size_hint: usize, size_hint: usize,
graph_limit: usize, graph_limit: usize,
) -> Result<Vec<&'b Vertex<'s, 'b>>, ExceededGraphLimit> { ) -> Result<Vec<&'v Vertex<'s, 'v>>, ExceededGraphLimit> {
// We want to visit nodes with the shortest distance first, but // We want to visit nodes with the shortest distance first, but
// RadixHeapMap is a max-heap. Ensure nodes are wrapped with // RadixHeapMap is a max-heap. Ensure nodes are wrapped with
// Reverse to flip comparisons. // Reverse to flip comparisons.
let mut heap: RadixHeapMap<Reverse<_>, &'b Vertex<'s, 'b>> = RadixHeapMap::new(); let mut heap: RadixHeapMap<Reverse<_>, &'v Vertex<'s, 'v>> = RadixHeapMap::new();
heap.push(Reverse(0), start); heap.push(Reverse(0), start);
let mut seen = DftHashMap::default(); let mut seen = DftHashMap::default();
seen.reserve(size_hint); seen.reserve(size_hint);
let end: &'b Vertex<'s, 'b> = loop { let end: &'v Vertex<'s, 'v> = loop {
match heap.pop() { match heap.pop() {
Some((Reverse(distance), current)) => { Some((Reverse(distance), current)) => {
if current.is_end() { if current.is_end() {
@ -77,7 +77,7 @@ fn shortest_vertex_path<'s, 'b>(
); );
let mut current = Some((0, end)); 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 { while let Some((_, node)) = current {
vertex_route.push(node); vertex_route.push(node);
current = node.predecessor.get(); current = node.predecessor.get();
@ -87,9 +87,9 @@ fn shortest_vertex_path<'s, 'b>(
Ok(vertex_route) Ok(vertex_route)
} }
fn shortest_path_with_edges<'s, 'b>( fn shortest_path_with_edges<'s, 'v>(
route: &[&'b Vertex<'s, 'b>], route: &[&'v Vertex<'s, 'v>],
) -> Vec<(Edge, &'b Vertex<'s, 'b>)> { ) -> Vec<(Edge, &'v Vertex<'s, 'v>)> {
let mut prev = route.first().expect("Expected non-empty route"); let mut prev = route.first().expect("Expected non-empty route");
let mut cost = 0; 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 /// The vec returned does not return the very last vertex. This is
/// necessary because a route of N vertices only has N-1 edges. /// necessary because a route of N vertices only has N-1 edges.
fn shortest_path<'s, 'b>( fn shortest_path<'s, 'v>(
start: Vertex<'s, 'b>, start: Vertex<'s, 'v>,
vertex_arena: &'b Bump, vertex_arena: &'v Bump,
size_hint: usize, size_hint: usize,
graph_limit: usize, graph_limit: usize,
) -> Result<Vec<(Edge, &'b Vertex<'s, 'b>)>, ExceededGraphLimit> { ) -> Result<Vec<(Edge, &'v Vertex<'s, 'v>)>, ExceededGraphLimit> {
let start: &'b Vertex<'s, 'b> = vertex_arena.alloc(start); let start: &'v Vertex<'s, 'v> = vertex_arena.alloc(start);
let vertex_path = shortest_vertex_path(start, vertex_arena, size_hint, graph_limit)?; let vertex_path = shortest_vertex_path(start, vertex_arena, size_hint, graph_limit)?;
Ok(shortest_path_with_edges(&vertex_path)) 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); assert_ne!(before, after);
let mut shortest_edge: Option<Edge> = None; let mut shortest_edge: Option<Edge> = None;

@ -50,15 +50,18 @@ use crate::{
/// LHS: X A RHS: A /// LHS: X A RHS: A
/// ^ ^ /// ^ ^
/// ``` /// ```
///
/// Vertices are arena allocated (the 'v lifetime) and have references
/// to syntax nodes (the 's lifetime).
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct Vertex<'s, 'b> { pub(crate) struct Vertex<'s, 'v> {
pub(crate) neighbours: RefCell<Option<&'b [(Edge, &'b Vertex<'s, 'b>)]>>, pub(crate) neighbours: RefCell<Option<&'v [(Edge, &'v Vertex<'s, 'v>)]>>,
pub(crate) predecessor: Cell<Option<(u32, &'b Vertex<'s, 'b>)>>, pub(crate) predecessor: Cell<Option<(u32, &'v Vertex<'s, 'v>)>>,
// TODO: experiment with storing SyntaxId only, and have a HashMap // TODO: experiment with storing SyntaxId only, and have a HashMap
// from SyntaxId to &Syntax. // from SyntaxId to &Syntax.
pub(crate) lhs_syntax: Option<&'s Syntax<'s>>, pub(crate) lhs_syntax: Option<&'s Syntax<'s>>,
pub(crate) rhs_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<SyntaxId>, lhs_parent_id: Option<SyntaxId>,
rhs_parent_id: Option<SyntaxId>, rhs_parent_id: Option<SyntaxId>,
} }
@ -116,12 +119,12 @@ impl Hash for Vertex<'_, '_> {
/// Tracks entering syntax List nodes. /// Tracks entering syntax List nodes.
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
enum EnteredDelimiter<'s, 'b> { enum EnteredDelimiter<'s, 'v> {
/// If we've entered the LHS or RHS separately, we can pop either /// If we've entered the LHS or RHS separately, we can pop either
/// side independently. /// side independently.
/// ///
/// Assumes that at least one stack is non-empty. /// 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 /// 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. /// 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>( fn push_both_delimiters<'s, 'v>(
entered: &Stack<'b, EnteredDelimiter<'s, 'b>>, entered: &Stack<'v, EnteredDelimiter<'s, 'v>>,
lhs_delim: &'s Syntax<'s>, lhs_delim: &'s Syntax<'s>,
rhs_delim: &'s Syntax<'s>, rhs_delim: &'s Syntax<'s>,
alloc: &'b Bump, alloc: &'v Bump,
) -> Stack<'b, EnteredDelimiter<'s, 'b>> { ) -> Stack<'v, EnteredDelimiter<'s, 'v>> {
entered.push(EnteredDelimiter::PopBoth((lhs_delim, rhs_delim)), alloc) entered.push(EnteredDelimiter::PopBoth((lhs_delim, rhs_delim)), alloc)
} }
@ -161,12 +164,12 @@ fn can_pop_either_parent(entered: &Stack<EnteredDelimiter>) -> bool {
matches!(entered.peek(), Some(EnteredDelimiter::PopEither(_))) matches!(entered.peek(), Some(EnteredDelimiter::PopEither(_)))
} }
fn try_pop_both<'s, 'b>( fn try_pop_both<'s, 'v>(
entered: &Stack<'b, EnteredDelimiter<'s, 'b>>, entered: &Stack<'v, EnteredDelimiter<'s, 'v>>,
) -> Option<( ) -> Option<(
&'s Syntax<'s>, &'s Syntax<'s>,
&'s Syntax<'s>, &'s Syntax<'s>,
Stack<'b, EnteredDelimiter<'s, 'b>>, Stack<'v, EnteredDelimiter<'s, 'v>>,
)> { )> {
match entered.peek() { match entered.peek() {
Some(EnteredDelimiter::PopBoth((lhs_delim, rhs_delim))) => { Some(EnteredDelimiter::PopBoth((lhs_delim, rhs_delim))) => {
@ -176,10 +179,10 @@ fn try_pop_both<'s, 'b>(
} }
} }
fn try_pop_lhs<'s, 'b>( fn try_pop_lhs<'s, 'v>(
entered: &Stack<'b, EnteredDelimiter<'s, 'b>>, entered: &Stack<'v, EnteredDelimiter<'s, 'v>>,
alloc: &'b Bump, alloc: &'v Bump,
) -> Option<(&'s Syntax<'s>, Stack<'b, EnteredDelimiter<'s, 'b>>)> { ) -> Option<(&'s Syntax<'s>, Stack<'v, EnteredDelimiter<'s, 'v>>)> {
match entered.peek() { match entered.peek() {
Some(EnteredDelimiter::PopEither((lhs_delims, rhs_delims))) => match lhs_delims.peek() { Some(EnteredDelimiter::PopEither((lhs_delims, rhs_delims))) => match lhs_delims.peek() {
Some(lhs_delim) => { Some(lhs_delim) => {
@ -201,10 +204,10 @@ fn try_pop_lhs<'s, 'b>(
} }
} }
fn try_pop_rhs<'s, 'b>( fn try_pop_rhs<'s, 'v>(
entered: &Stack<'b, EnteredDelimiter<'s, 'b>>, entered: &Stack<'v, EnteredDelimiter<'s, 'v>>,
alloc: &'b Bump, alloc: &'v Bump,
) -> Option<(&'s Syntax<'s>, Stack<'b, EnteredDelimiter<'s, 'b>>)> { ) -> Option<(&'s Syntax<'s>, Stack<'v, EnteredDelimiter<'s, 'v>>)> {
match entered.peek() { match entered.peek() {
Some(EnteredDelimiter::PopEither((lhs_delims, rhs_delims))) => match rhs_delims.peek() { Some(EnteredDelimiter::PopEither((lhs_delims, rhs_delims))) => match rhs_delims.peek() {
Some(rhs_delim) => { Some(rhs_delim) => {
@ -226,11 +229,11 @@ fn try_pop_rhs<'s, 'b>(
} }
} }
fn push_lhs_delimiter<'s, 'b>( fn push_lhs_delimiter<'s, 'v>(
entered: &Stack<'b, EnteredDelimiter<'s, 'b>>, entered: &Stack<'v, EnteredDelimiter<'s, 'v>>,
delimiter: &'s Syntax<'s>, delimiter: &'s Syntax<'s>,
alloc: &'b Bump, alloc: &'v Bump,
) -> Stack<'b, EnteredDelimiter<'s, 'b>> { ) -> Stack<'v, EnteredDelimiter<'s, 'v>> {
match entered.peek() { match entered.peek() {
Some(EnteredDelimiter::PopEither((lhs_delims, rhs_delims))) => entered.pop().unwrap().push( Some(EnteredDelimiter::PopEither((lhs_delims, rhs_delims))) => entered.pop().unwrap().push(
EnteredDelimiter::PopEither((lhs_delims.push(delimiter, alloc), rhs_delims.clone())), 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>( fn push_rhs_delimiter<'s, 'v>(
entered: &Stack<'b, EnteredDelimiter<'s, 'b>>, entered: &Stack<'v, EnteredDelimiter<'s, 'v>>,
delimiter: &'s Syntax<'s>, delimiter: &'s Syntax<'s>,
alloc: &'b Bump, alloc: &'v Bump,
) -> Stack<'b, EnteredDelimiter<'s, 'b>> { ) -> Stack<'v, EnteredDelimiter<'s, 'v>> {
match entered.peek() { match entered.peek() {
Some(EnteredDelimiter::PopEither((lhs_delims, rhs_delims))) => entered.pop().unwrap().push( Some(EnteredDelimiter::PopEither((lhs_delims, rhs_delims))) => entered.pop().unwrap().push(
EnteredDelimiter::PopEither((lhs_delims.clone(), rhs_delims.push(delimiter, alloc))), 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 { pub(crate) fn is_end(&self) -> bool {
self.lhs_syntax.is_none() && self.rhs_syntax.is_none() && self.parents.is_empty() 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>( fn allocate_if_new<'s, 'v>(
v: Vertex<'s, 'b>, v: Vertex<'s, 'v>,
alloc: &'b Bump, alloc: &'v Bump,
seen: &mut DftHashMap<&Vertex<'s, 'b>, SmallVec<[&'b Vertex<'s, 'b>; 2]>>, seen: &mut DftHashMap<&Vertex<'s, 'v>, SmallVec<[&'v Vertex<'s, 'v>; 2]>>,
) -> &'b Vertex<'s, 'b> { ) -> &'v Vertex<'s, 'v> {
// We use the entry API so that we only need to do a single lookup // We use the entry API so that we only need to do a single lookup
// for access and insert. // for access and insert.
match seen.raw_entry_mut().from_key(&v) { 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 // We still use a vec to enable experiments with the value
// of how many possible parenthesis nestings to explore. // 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); vacant.insert(allocated, existing);
allocated allocated
@ -425,19 +428,19 @@ fn looks_like_punctuation(node: &Syntax) -> bool {
/// Pop as many parents of `lhs_node` and `rhs_node` as /// Pop as many parents of `lhs_node` and `rhs_node` as
/// possible. Return the new syntax nodes and parents. /// 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>>, lhs_node: Option<&'s Syntax<'s>>,
rhs_node: Option<&'s Syntax<'s>>, rhs_node: Option<&'s Syntax<'s>>,
lhs_parent_id: Option<SyntaxId>, lhs_parent_id: Option<SyntaxId>,
rhs_parent_id: Option<SyntaxId>, rhs_parent_id: Option<SyntaxId>,
parents: &Stack<'b, EnteredDelimiter<'s, 'b>>, parents: &Stack<'v, EnteredDelimiter<'s, 'v>>,
alloc: &'b Bump, alloc: &'v Bump,
) -> ( ) -> (
Option<&'s Syntax<'s>>, Option<&'s Syntax<'s>>,
Option<&'s Syntax<'s>>, Option<&'s Syntax<'s>>,
Option<SyntaxId>, Option<SyntaxId>,
Option<SyntaxId>, Option<SyntaxId>,
Stack<'b, EnteredDelimiter<'s, 'b>>, Stack<'v, EnteredDelimiter<'s, 'v>>,
) { ) {
let mut lhs_node = lhs_node; let mut lhs_node = lhs_node;
let mut rhs_node = rhs_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, /// Compute the neighbours of `v` if we haven't previously done so,
/// and write them to the .neighbours cell inside `v`. /// and write them to the .neighbours cell inside `v`.
pub(crate) fn set_neighbours<'s, 'b>( pub(crate) fn set_neighbours<'s, 'v>(
v: &Vertex<'s, 'b>, v: &Vertex<'s, 'v>,
alloc: &'b Bump, alloc: &'v Bump,
seen: &mut DftHashMap<&Vertex<'s, 'b>, SmallVec<[&'b Vertex<'s, 'b>; 2]>>, seen: &mut DftHashMap<&Vertex<'s, 'v>, SmallVec<[&'v Vertex<'s, 'v>; 2]>>,
) { ) {
if v.neighbours.borrow().is_some() { if v.neighbours.borrow().is_some() {
return; return;
@ -796,8 +799,8 @@ pub(crate) fn set_neighbours<'s, 'b>(
.replace(Some(alloc.alloc_slice_copy(neighbours.as_slice()))); .replace(Some(alloc.alloc_slice_copy(neighbours.as_slice())));
} }
pub(crate) fn populate_change_map<'s, 'b>( pub(crate) fn populate_change_map<'s, 'v>(
route: &[(Edge, &'b Vertex<'s, 'b>)], route: &[(Edge, &'v Vertex<'s, 'v>)],
change_map: &mut ChangeMap<'s>, change_map: &mut ChangeMap<'s>,
) { ) {
for (e, v) in route { for (e, v) in route {