///|
fn Program::compact_patterns(self : Program, rename_top_level : Bool) -> Unit {
  guard !self.has_dynamic_scope else { return }
  let calls = InternalCalls::new(self, rename_top_level)
  let bindings = ValueBindings::new(self, rename_top_level)
  bindings.inline_aliases(rename_top_level)
  bindings.inline_data_values(rename_top_level)
  // Parser arena order places statements after their children. New expression
  // nodes are already simplified when created and need no second traversal.
  let parsed_nodes = self.nodes.length()
  for id in 0.. calls.collect_node(id, node)
      Call => {
        calls.collect_node(id, node)
        bindings.inline_trivial_call(id, rename_top_level)
      }
      FunctionDeclaration => {
        calls.collect_node(id, node)
        self.remove_final_unit_return(node.a)
      }
      FunctionExpression | ArrowExpression =>
        self.remove_final_unit_return(node.a)
      Method => self.remove_final_unit_return(node.b)
      Return | Binary | Unary | Conditional => self.compact_expression(id)
      Parenthesized => self.compact_parenthesized(id)
      Block => {
        let live = self.live_statements(node.a, node.b)
        let slots = self.compact_result_slots(node.a, live, bindings)
        let count = self.compact_statement_list(node.a, slots, bindings)
        self.nodes[id] = { ..node, b: count, }
      }
      SwitchCase => {
        let live = self.live_statements(node.b, node.c)
        let slots = self.compact_result_slots(node.b, live, bindings)
        let count = self.compact_statement_list(node.b, slots, bindings)
        self.nodes[id] = { ..node, c: count, }
      }
      If => {
        self.compact_branch(id)
        self.compact_unit_branch(id)
      }
      Labeled => self.compact_label_exit(id)
      Switch => self.compact_switch_returns(id)
      While => {
        self.compact_expression(id)
        self.remove_tail_continue(node.b)
        self.compact_loop_guard(id)
      }
      For => {
        self.remove_tail_continue(node.d)
        self.compact_loop_guard(id)
      }
      _ => ()
    }
  }
  calls.finish()
  bindings.inline_function_values()
}

///|
fn Program::identifier_symbol(self : Program, id : Int32) -> Int32 {
  guard id >= 0 else { return -1 }
  let node = self.nodes[self.unwrapped_expression(id)]
  guard node.kind is (Identifier | PatternIdentifier) else { return -1 }
  self.references[node.a].symbol
}

///|
fn Program::result_slot(self : Program, declaration : Int32) -> Int32 {
  let node = self.nodes[declaration]
  guard node.kind is VariableDeclaration &&
    node.a == DECLARATION_LET &&
    node.c == 1 else {
    return -1
  }
  let binding = self.nodes[self.edges[node.b]]
  guard binding.b < 0 else { return -1 }
  let symbol = self.identifier_symbol(binding.a)
  guard symbol >= 0 else { return -1 }
  let entry = self.symbols[symbol]
  // Top-level bindings may be externally observed in --top mode. Linked or
  // preserved bindings also have identities beyond this local result slot.
  guard entry.scope > 0 && entry.link < 0 && entry.flags == 0 else { return -1 }
  symbol
}

///|
fn Program::single_statement(self : Program, id : Int32) -> Int32 {
  for current = id {
    let node = self.nodes[current]
    guard node.kind is Block && node.b == 1 else { break current }
    continue self.edges[node.a]
  }
}

///|
fn Program::slot_assignment(
  self : Program,
  statement : Int32,
  symbol : Int32,
) -> Int32 {
  let node = self.nodes[self.single_statement(statement)]
  if node.kind is Block && node.b > 0 {
    for index in 0..<(node.b - 1) {
      guard self.nodes[self.edges[node.a + index]].kind is ExpressionStatement else {
        return -1
      }
    }
    return self.slot_assignment(self.edges[node.a + node.b - 1], symbol)
  }
  guard node.kind is ExpressionStatement else { return -1 }
  let expression = self.nodes[self.sequence_tail(node.a)]
  guard expression.kind is Assignment &&
    self.operators[expression.a] is Operator::Assignment &&
    self.identifier_symbol(expression.b) == symbol else {
    return -1
  }
  let value = self.nodes[self.unwrapped_expression(expression.c)]
  // Function names may change during compression. Keep inferred class names.
  match value.kind {
    ClassExpression if self.classes[value.a].name < 0 => -1
    _ => expression.c
  }
}

///|
fn Program::slot_assignment_value(
  self : Program,
  statement : Int32,
  symbol : Int32,
) -> Int32 {
  let node = self.nodes[self.single_statement(statement)]
  guard node.kind is Block else {
    let value = self.slot_assignment(statement, symbol)
    let tail = self.sequence_tail(node.a)
    guard tail != self.unwrapped_expression(node.a) else { return value }
    self.nodes[tail] = self.nodes[value]
    return node.a
  }
  let value = self.slot_assignment_value(
    self.edges[node.a + node.b - 1],
    symbol,
  )
  for index = node.b - 2, result = value {
    guard index >= 0 else { break result }
    let expression = self.nodes[self.edges[node.a + index]].a
    continue index - 1, self.sequence_pair(expression, result)
  }
}

///|
fn Program::sequence_tail(self : Program, id : Int32) -> Int32 {
  for expression = self.unwrapped_expression(id) {
    let node = self.nodes[expression]
    guard node.kind is Sequence && node.b > 0 else { break expression }
    continue self.unwrapped_expression(self.edges[node.a + node.b - 1])
  }
}

///|
fn Program::slot_branch_writes(
  self : Program,
  statement : Int32,
  symbol : Int32,
) -> Int32 {
  let id = self.single_statement(statement)
  let node = self.nodes[id]
  guard node.kind is If && node.c >= 0 else {
    guard self.slot_assignment(id, symbol) >= 0 else { return -1 }
    return 1
  }
  let consequent = self.slot_branch_writes(node.b, symbol)
  guard consequent >= 0 else { return -1 }
  let alternative = self.slot_branch_writes(node.c, symbol)
  guard alternative >= 0 else { return -1 }
  consequent + alternative
}

///|
fn Program::compact_result_slots(
  self : Program,
  offset : Int32,
  count : Int32,
  bindings : ValueBindings,
) -> Int32 {
  for read = 0, kept = 0 {
    guard read < count else { break kept }
    let declaration = self.edges[offset + read]
    self.edges[offset + kept] = declaration
    let slot = self.result_slot(declaration)
    guard slot >= 0 && read + 2 < count else { continue read + 1, kept + 1 }
    let producer = self.edges[offset + read + 1]
    let consumer = self.edges[offset + read + 2]
    guard self.fold_result_slot(producer, consumer, slot, bindings) else {
      continue read + 1, kept + 1
    }
    let entry = self.symbols[slot]
    self.symbols[slot] = { ..entry, flags: entry.flags | SYMBOL_ELIDED, }
    self.edges[offset + kept] = {
      guard self.nodes[producer].kind is Switch else { consumer }
      producer
    }
    continue read + 3, kept + 1
  }
}

///|
fn Program::fold_result_slot(
  self : Program,
  producer : Int32,
  consumer : Int32,
  slot : Int32,
  bindings : ValueBindings,
) -> Bool {
  let node = self.nodes[producer]
  guard node.kind is If else {
    guard node.kind is Switch &&
      self.nodes[consumer].kind is Return &&
      self.identifier_symbol(self.nodes[consumer].a) == slot else {
      return false
    }
    return self.fold_switch_slot(producer, slot)
  }
  let expression = self.first_statement_expression(consumer)
  let movement = ValueMovement::new(bindings, slot, producer, -1)
  let reference = self.leading_value_read(expression, movement)
  guard reference >= 0 else { return false }
  let writes = self.slot_branch_writes(producer, slot)
  // Account for declaration, all branch writes and this single consumer.
  // Any self-read, closure capture, extra use or extra declaration rejects it.
  guard writes > 0 && self.symbols[slot].use_count == writes + 2 else {
    return false
  }
  let value = self.slot_branch_value(producer, slot)
  self.nodes[reference] = if reference == expression &&
    self.nodes[consumer].kind is (Return | Throw | If | Switch) {
    self.nodes[value]
  } else {
    Node::new(Parenthesized, value, 0, 0, 0)
  }
  true
}

///|
/// Keep an early return outside the consumer: its value does not flow through
/// the expression that consumes the successful branch's assigned value.
fn Program::fold_returning_result_slot(
  self : Program,
  producer : Int32,
  consumer : Int32,
  statements : Int32,
  producer_index : Int32,
  bindings : ValueBindings,
) -> Bool {
  let branch = self.nodes[producer]
  let following = self.nodes[consumer]
  guard branch.c >= 0 && following.kind is Return && following.a >= 0 else {
    return false
  }
  let consequent = self.nodes[self.single_statement(branch.b)]
  let alternative = self.nodes[self.single_statement(branch.c)]
  let returns_on_true = consequent.kind is Return && consequent.a >= 0
  let returns_on_false = alternative.kind is Return && alternative.a >= 0
  guard returns_on_true != returns_on_false else { return false }
  let assignment = if returns_on_true { branch.c } else { branch.b }
  let slot = self.slot_assignment_symbol(assignment)
  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) else {
    return false
  }
  guard self.slot_assignment(assignment, slot) >= 0 else { return false }
  let movement = ValueMovement::new(bindings, slot, producer, -1)
  let reference = self.leading_value_read(following.a, movement)
  guard reference >= 0 &&
    self.references[self.nodes[reference].a].scope == entry.scope else {
    return false
  }
  // Only an earlier declaration in this exact statement list is removable.
  // This also excludes for headers and bindings owned by enclosing blocks.
  let declared_here = for previous = producer_index - 1 {
    guard previous >= 0 else { break false }
    if self.edges[statements + previous] == declaration {
      break true
    }
    continue previous - 1
  }
  guard declared_here else { return false }
  // Extraction can rewrite the assignment's sequence tail. Perform it only
  // after proving that the consumer's preceding reads can cross these effects.
  let assigned_value = self.slot_assignment_value(assignment, slot)
  let initializer = self.nodes[self.unwrapped_expression(assigned_value)]
  self.nodes[reference] = if movement.detach_receiver &&
    initializer.kind is (Identifier | Member | Index) {
    let zero = self.node(ZeroLiteral, 0, 0, 0, 0)
    let value = self.sequence_pair(zero, assigned_value)
    Node::new(Parenthesized, value, 0, 0, 0)
  } else if reference == following.a || self.is_primary_value(assigned_value) {
    self.nodes[assigned_value]
  } else {
    Node::new(Parenthesized, assigned_value, 0, 0, 0)
  }
  let value = if returns_on_true {
    self.compact_choice(branch.a, consequent.a, following.a)
  } else {
    self.compact_choice(branch.a, following.a, alternative.a)
  }
  self.nodes[consumer] = { ..following, a: value, }
  self.nodes[declaration] = Node::new(Elided, 0, 0, 0, 0)
  self.symbols[slot] = { ..entry, flags: entry.flags | SYMBOL_ELIDED, }
  true
}

///|
fn Program::slot_assignment_symbol(self : Program, statement : Int32) -> Int32 {
  let node = self.nodes[self.single_statement(statement)]
  if node.kind is Block && node.b > 0 {
    return self.slot_assignment_symbol(self.edges[node.a + node.b - 1])
  }
  guard node.kind is ExpressionStatement else { return -1 }
  let tail = self.nodes[self.sequence_tail(node.a)]
  guard tail.kind is Assignment &&
    self.operators[tail.a] is Operator::Assignment else {
    return -1
  }
  self.identifier_symbol(tail.b)
}

///|
fn Program::slot_branch_value(
  self : Program,
  statement : Int32,
  slot : Int32,
) -> Int32 {
  let id = self.single_statement(statement)
  let node = self.nodes[id]
  guard node.kind is If else { return self.slot_assignment_value(id, slot) }
  let consequent = self.slot_branch_value(node.b, slot)
  let alternative = self.slot_branch_value(node.c, slot)
  self.compact_choice(node.a, consequent, alternative)
}

///|
fn Program::is_boolean_value(self : Program, id : Int32) -> Bool {
  let node = self.nodes[self.unwrapped_expression(id)]
  match node.kind {
    BooleanLiteral => true
    Unary => self.operators[node.a] is Operator::LogicalNot
    Binary =>
      match self.operators[node.a] {
        Equal
        | NotEqual
        | StrictlyEqual
        | StrictlyNotEqual
        | LessThan
        | LessThanOrEqual
        | GreaterThan
        | GreaterThanOrEqual
        | In
        | InstanceOf => true
        _ => false
      }
    Logical => self.is_boolean_value(node.b) && self.is_boolean_value(node.c)
    Conditional =>
      self.is_boolean_value(node.b) && self.is_boolean_value(node.c)
    _ => false
  }
}

///|
fn Program::boolean_literal(self : Program, id : Int32, value : Bool) -> Bool {
  let node = self.nodes[self.unwrapped_expression(id)]
  node.kind is BooleanLiteral && (node.a == 1) == value
}

///|
fn Program::compact_choice(
  self : Program,
  condition : Int32,
  consequent : Int32,
  alternative : Int32,
) -> Int32 {
  let consequent_is_true = self.boolean_literal(consequent, true)
  let alternative_is_false = self.boolean_literal(alternative, false)
  let consequent_is_false = self.boolean_literal(consequent, false)
  let alternative_is_true = self.boolean_literal(alternative, true)
  if consequent_is_false && alternative_is_true {
    return self.negated_value(condition)
  }
  if consequent_is_true && alternative_is_false {
    guard !self.is_boolean_value(condition) else { return condition }
    return self.negated_value(self.negated_value(condition))
  }
  if self.is_boolean_value(condition) {
    if alternative_is_false {
      guard !consequent_is_true else { return condition }
      return self.slot_logical(Operator::LogicalAnd, condition, consequent)
    } else if consequent_is_true {
      return self.slot_logical(Operator::LogicalOr, condition, alternative)
    } else if consequent_is_false {
      return self.slot_logical(
        Operator::LogicalAnd,
        self.negated_value(condition),
        alternative,
      )
    } else if alternative_is_true {
      return self.slot_logical(
        Operator::LogicalOr,
        self.negated_value(condition),
        consequent,
      )
    }
  }
  let condition_operand = self.slot_operand(condition, ConditionalTest)
  let yes = self.slot_operand(consequent, ConditionalArm)
  let no = self.slot_operand(alternative, ConditionalArm)
  self.node(Conditional, condition_operand, yes, no, 0)
}

///|
priv enum SlotOperand {
  ConditionalTest
  ConditionalArm
  LogicalOperand(Operator)
}

///|
fn Program::slot_operand(
  self : Program,
  id : Int32,
  context : SlotOperand,
) -> Int32 {
  guard self.slot_needs_parentheses(id, context) else { return id }
  self.node(Parenthesized, id, 0, 0, 0)
}

///|
fn Program::slot_needs_parentheses(
  self : Program,
  id : Int32,
  context : SlotOperand,
) -> Bool {
  let node = self.nodes[id]
  match context {
    ConditionalArm => node.kind is Sequence
    ConditionalTest | LogicalOperand(_) =>
      match node.kind {
        Assignment | Conditional | Sequence | ArrowExpression => true
        Unary => self.operators[node.a] is (Yield | YieldStar)
        Logical => {
          guard context is LogicalOperand(parent) else { false }
          self.operators[node.a] is NullishCoalescing ||
          (parent is LogicalAnd && self.operators[node.a] is LogicalOr)
        }
        _ => false
      }
  }
}

///|
fn Program::slot_logical(
  self : Program,
  operator : Operator,
  left : Int32,
  right : Int32,
) -> Int32 {
  let lhs = self.slot_operand(left, LogicalOperand(operator))
  let rhs = self.slot_operand(right, LogicalOperand(operator))
  self.node(Logical, self.add_operator(operator), lhs, rhs, 0)
}

///|
fn Program::slot_case_statements(self : Program, case_id : Int32) -> EdgeRange {
  let clause = self.nodes[case_id]
  for offset = clause.b, count = clause.c {
    guard count == 1 else { break { offset, count, } }
    let statement = self.nodes[self.edges[offset]]
    guard statement.kind is Block else { break { offset, count, } }
    continue statement.a, statement.b
  }
}

///|
fn Program::fold_switch_slot(self : Program, id : Int32, slot : Int32) -> Bool {
  let node = self.nodes[id]
  guard node.c > 0 && self.symbols[slot].use_count == node.c + 2 else {
    return false
  }
  let mut has_default = false
  for index in 0..= 0 else {
      return false
    }
    let exit = self.nodes[self.edges[statements.offset + 1]]
    guard exit.kind is Break && exit.a < 0 else { return false }
  }
  guard has_default else { return false }
  for index in 0.. Unit {
  let node = self.nodes[id]
  match node.kind {
    Block if node.b > 0 => {
      let last = self.edges[node.a + node.b - 1]
      let tail = self.nodes[last]
      guard tail.kind is Continue && tail.a < 0 else {
        return self.remove_tail_continue(last)
      }
      self.nodes[id] = { ..node, b: node.b - 1, }
    }
    If => {
      self.remove_tail_continue(node.b)
      if node.c >= 0 {
        self.remove_tail_continue(node.c)
      }
    }
    Continue if node.a < 0 => self.nodes[id] = Node::new(Empty, 0, 0, 0, 0)
    // Nested loops, switches, labels and try/finally need their own control-flow
    // reasoning; a terminal continue inside them is intentionally left alone.
    _ => ()
  }
}