///|
/// Move the sole consumer into a producer's final assignment, retaining every
/// existing lexical block. Prefix declarations and their values stay in place.
fn Program::sink_result_slot(
  self : Program,
  producer : Int32,
  consumer : Int32,
  offset : Int32,
  producer_index : Int32,
  bindings : ValueBindings,
) -> Bool {
  let following = self.nodes[consumer]
  guard following.kind is (Return | ExpressionStatement) && following.a >= 0 else {
    return false
  }
  let leaf = self.result_slot_tail(producer)
  guard leaf >= 0 else { return false }
  let statement = self.nodes[leaf]
  let assignment = self.nodes[self.sequence_tail(statement.a)]
  guard assignment.kind is Assignment &&
    self.operators[assignment.a] is Operator::Assignment else {
    return false
  }
  let target = self.nodes[self.unwrapped_expression(assignment.b)]
  guard target.kind is Identifier else { return false }
  let destination = self.references[target.a].scope
  let slot = self.references[target.a].symbol
  guard slot >= 0 else { return false }
  let declaration = bindings.declarations[slot]
  guard declaration >= 0 && self.result_slot(declaration) == slot else {
    return false
  }
  let entry = self.symbols[slot]
  guard entry.use_count == 3 &&
    self.scopes[entry.scope].kind != SCOPE_SWITCH &&
    !self.preserved_names.contains(entry.name) &&
    self.slot_assignment(leaf, slot) >= 0 else {
    return false
  }
  let movement = ValueMovement::new(bindings, slot, producer, -1)
  let read = self.leading_value_read(following.a, movement)
  guard read >= 0 && self.references[self.nodes[read].a].scope == entry.scope else {
    return false
  }
  let declared_here = for previous = producer_index - 1 {
    guard previous >= 0 else { break false }
    if self.edges[offset + previous] == declaration {
      break true
    }
    continue previous - 1
  }
  guard declared_here else { return false }
  let references : Array[Int32] = []
  guard self.collect_slot_sink_references(
    following.a,
    read,
    destination,
    references,
  ) else {
    return false
  }
  // Update only the consumer's references. The initializer is still in its
  // original scope and is inserted after this relocation has finished.
  for index in references {
    self.references[index] = { ..self.references[index], scope: destination, }
  }
  self.mark_result_slot_tail_literals(producer)
  let value = self.slot_assignment_value(leaf, slot)
  let initializer = self.nodes[self.unwrapped_expression(value)]
  self.nodes[read] = if movement.detach_receiver &&
    initializer.kind is (Identifier | Member | Index) {
    let zero = self.node(ZeroLiteral, 0, 0, 0, 0)
    let detached = self.sequence_pair(zero, value)
    Node::new(Parenthesized, detached, 0, 0, 0)
  } else if read == following.a &&
    following.kind is ExpressionStatement &&
    initializer.kind is StringLiteral {
    Node::new(Parenthesized, value, 0, 0, 0)
  } else if self.is_primary_value(value) ||
    (read == following.a && following.kind is Return) {
    self.nodes[value]
  } else {
    Node::new(Parenthesized, value, 0, 0, 0)
  }
  self.nodes[leaf] = following
  self.nodes[consumer] = Node::new(Elided, 0, 0, 0, 0)
  self.nodes[declaration] = Node::new(Elided, 0, 0, 0, 0)
  self.symbols[slot] = { ..entry, flags: entry.flags | SYMBOL_ELIDED, }
  true
}

///|
/// Only direct block tails and a conditional whose other arm exits can reach
/// this leaf. A loop, switch, label or try needs different completion analysis.
fn Program::result_slot_tail(self : Program, id : Int32) -> Int32 {
  let node = self.nodes[id]
  match node.kind {
    ExpressionStatement => id
    Block if node.b > 0 =>
      self.result_slot_tail(self.edges[node.a + node.b - 1])
    If if node.c >= 0 =>
      if self.result_slot_exits(node.b) {
        self.result_slot_tail(node.c)
      } else if self.result_slot_exits(node.c) {
        self.result_slot_tail(node.b)
      } else {
        -1
      }
    _ => -1
  }
}

///|
fn Program::result_slot_exits(self : Program, id : Int32) -> Bool {
  let node = self.nodes[id]
  match node.kind {
    Return | Throw => true
    Block if node.b > 0 =>
      self.result_slot_exits(self.edges[node.a + node.b - 1])
    If =>
      node.c >= 0 &&
      self.result_slot_exits(node.b) &&
      self.result_slot_exits(node.c)
    _ => false
  }
}

///|
/// A moved consumer may introduce literals into a previously literal-free
/// block. Keep the literal-pool traversal's early-out summary conservative.
fn Program::mark_result_slot_tail_literals(self : Program, id : Int32) -> Unit {
  let node = self.nodes[id]
  match node.kind {
    Block => {
      if node.d == 0 {
        self.nodes[id] = { ..node, d: 1, }
      }
      self.mark_result_slot_tail_literals(self.edges[node.a + node.b - 1])
    }
    If =>
      if self.result_slot_exits(node.b) {
        self.mark_result_slot_tail_literals(node.c)
      } else {
        self.mark_result_slot_tail_literals(node.b)
      }
    _ => ()
  }
}

///|
/// Canonical binding names matter here: an eliminated identifier alias can
/// leave a reference's source spelling different from its target symbol name.
fn Program::collect_slot_sink_references(
  self : Program,
  id : Int32,
  replaced_read : Int32,
  destination : Int32,
  references : Array[Int32],
) -> Bool {
  guard id >= 0 && id != replaced_read else { return true }
  let node = self.nodes[id]
  match node.kind {
    Identifier => {
      let reference = self.references[node.a]
      let target = if reference.symbol >= 0 {
        self.symbol_root(reference.symbol)
      } else {
        -1
      }
      let name = if target >= 0 {
        self.symbols[target].name
      } else {
        reference.name
      }
      let binding = self.find_binding(destination, name)
      let same_binding = if target >= 0 {
        binding >= 0 && self.symbol_root(binding) == target
      } else {
        binding < 0
      }
      guard same_binding else { return false }
      references.push(node.a)
      true
    }
    NumberLiteral
    | ZeroLiteral
    | StringLiteral
    | RegexLiteral
    | BooleanLiteral
    | NullLiteral
    | This
    | Super => true
    Parenthesized | EvaluatedValue | Member | Spread =>
      self.collect_slot_sink_references(
        node.a,
        replaced_read,
        destination,
        references,
      )
    Index =>
      self.collect_slot_sink_references(
        node.a,
        replaced_read,
        destination,
        references,
      ) &&
      self.collect_slot_sink_references(
        node.b,
        replaced_read,
        destination,
        references,
      )
    Unary =>
      self.collect_slot_sink_references(
        node.b,
        replaced_read,
        destination,
        references,
      )
    Update =>
      self.collect_slot_sink_references(
        node.c,
        replaced_read,
        destination,
        references,
      )
    Binary | Logical | Assignment =>
      self.collect_slot_sink_references(
        node.b,
        replaced_read,
        destination,
        references,
      ) &&
      self.collect_slot_sink_references(
        node.c,
        replaced_read,
        destination,
        references,
      )
    Conditional =>
      self.collect_slot_sink_references(
        node.a,
        replaced_read,
        destination,
        references,
      ) &&
      self.collect_slot_sink_references(
        node.b,
        replaced_read,
        destination,
        references,
      ) &&
      self.collect_slot_sink_references(
        node.c,
        replaced_read,
        destination,
        references,
      )
    Call | New =>
      self.collect_slot_sink_references(
        node.a,
        replaced_read,
        destination,
        references,
      ) &&
      (
        node.b < 0 ||
        self.collect_slot_sink_list(
          node.b,
          node.c,
          replaced_read,
          destination,
          references,
        )
      )
    ArrayLiteral | ObjectLiteral | Sequence =>
      self.collect_slot_sink_list(
        node.a,
        node.b,
        replaced_read,
        destination,
        references,
      )
    Property =>
      (
        node.d != PROPERTY_COMPUTED ||
        self.collect_slot_sink_references(
          node.a,
          replaced_read,
          destination,
          references,
        )
      ) &&
      self.collect_slot_sink_references(
        node.b,
        replaced_read,
        destination,
        references,
      )
    // Functions, methods, classes and templates can own additional scopes.
    // Binding patterns and other statement nodes are outside this expression.
    _ => false
  }
}

///|
fn Program::collect_slot_sink_list(
  self : Program,
  offset : Int32,
  count : Int32,
  replaced_read : Int32,
  destination : Int32,
  references : Array[Int32],
) -> Bool {
  for index in 0..