///|
/// One non-mutating uniform-value query owns this state. No caller may retain
/// it across a callback, commit, or candidate-discovery return. Only syntax is
/// cached; candidate values and visited-context decisions remain uncached.
priv struct DaeUniformSyntaxQuery {
  source : @lib.Module
  type_indices : Array[@lib.TypeIdx]
  boundary_facts : DaeCallBoundaryFacts
  indexed : Bool
  syntax_visits : Array[Int32]?
  // Untimed probe: constructions, caller misses, caller hits, stored calls.
  syntax_stats : Array[Int32]?
  targets : @hashmap.HashMap[Int32, Bool]?
  callers : @hashmap.HashMap[Int32, DaeUniformSyntaxCaller]
}

///|
#valtype
priv struct DaeUniformSyntaxCall {
  scope : Array[@lib.Instruction]
  call_idx : Int32
  entry_stack_values : Int32
  preceding_tag : @lib.TagIdx?
}

///|
priv struct DaeUniformSyntaxCaller {
  body : Array[@lib.Instruction]
  entry_stack_values : Int32
  calls : @hashmap.HashMap[Int32, Array[DaeUniformSyntaxCall]]
  initial_tag_entries : Array[Int32]?
  mut first_tag : @lib.TagIdx?
  mut complete : Bool
}

///|
fn DaeUniformSyntaxQuery::new(
  source : @lib.Module,
  type_indices : Array[@lib.TypeIdx],
  boundary_facts : DaeCallBoundaryFacts,
  indexed : Bool,
  syntax_visits~ : Array[Int32]? = None,
  syntax_stats~ : Array[Int32]? = None,
  targets~ : @hashmap.HashMap[Int32, Bool]? = None,
) -> DaeUniformSyntaxQuery {
  match syntax_stats {
    Some(stats) if stats.length() == 4 => stats[0] += 1
    _ => ()
  }
  {
    source,
    type_indices,
    boundary_facts,
    indexed,
    syntax_visits,
    syntax_stats,
    targets,
    callers: @hashmap.new(),
  }
}

///|
fn dae_uniform_syntax_query_matches(
  query : DaeUniformSyntaxQuery,
  source : @lib.Module,
  type_indices : Array[@lib.TypeIdx],
  boundary_facts : DaeCallBoundaryFacts,
) -> Bool {
  query.indexed &&
  physical_equal(query.source, source) &&
  physical_equal(query.type_indices, type_indices) &&
  physical_equal(query.boundary_facts, boundary_facts)
}

///|
/// Match the retained visitor's depth-first order and control admission. A
/// failed child stops construction at its exact position. Earlier calls remain
/// in the index and must run before the trailing failure is returned.
fn dae_uniform_syntax_collect_calls(
  query : DaeUniformSyntaxQuery,
  instrs : Array[@lib.Instruction],
  entry_stack_values : Int32,
  caller : DaeUniformSyntaxCaller,
  tag_entries : Array[Int32],
) -> Bool {
  for idx in 0.. visits[0] += 1
      None => ()
    }
    match instrs[idx] {
      @lib.Call(func_idx) | @lib.ReturnCall(func_idx) => {
        let callee_abs = dae_func_idx_slot(func_idx)
        if !dae_uniform_syntax_target_admitted(query, callee_abs) {
          continue
        }
        let entries = match caller.calls.get(callee_abs) {
          Some(entries) => entries
          None => {
            let entries = []
            caller.calls.set(callee_abs, entries)
            entries
          }
        }
        match query.syntax_stats {
          Some(stats) if stats.length() == 4 => stats[3] += 1
          _ => ()
        }
        entries.push({
          scope: instrs,
          call_idx: idx,
          entry_stack_values,
          preceding_tag: caller.first_tag,
        })
      }
      @lib.Try(block_type, @lib.Expr(body), catches, _) => {
        let entry = match
          dae_control_entry_stack_values(block_type, query.source) {
          Some(entry) => entry
          None => return false
        }
        if !dae_uniform_syntax_collect_calls(
            query, body, entry, caller, tag_entries,
          ) {
          return false
        }
        for catch_ in catches {
          match catch_ {
            @lib.LegacyCatch(tag, _) if caller.first_tag is None =>
              caller.first_tag = Some(tag)
            _ => ()
          }
          let entry = match
            dae_legacy_catch_entry_stack_values(
              catch_,
              query.source,
              tag_entries,
            ) {
            Some(entry) => entry
            None => return false
          }
          let @lib.Expr(body) = match catch_ {
            @lib.LegacyCatch(_, body) | @lib.LegacyCatchAll(body) => body
          }
          if !dae_uniform_syntax_collect_calls(
              query, body, entry, caller, tag_entries,
            ) {
            return false
          }
        }
      }
      @lib.Block(_, @lib.Expr(_))
      | @lib.Loop(_, @lib.Expr(_))
      | @lib.TryTable(_, _, @lib.Expr(_))
      | @lib.If(_, _, _) => {
        let children = match
          dae_nested_control_children_evidence(instrs[idx], query.source) {
          Some(children) => children
          None => return false
        }
        if !dae_uniform_syntax_collect_calls(
            query,
            children.primary_body,
            children.entry_stack_values,
            caller,
            tag_entries,
          ) {
          return false
        }
        match children.secondary_body {
          Some(body) =>
            if !dae_uniform_syntax_collect_calls(
                query,
                body,
                children.entry_stack_values,
                caller,
                tag_entries,
              ) {
              return false
            }
          None => ()
        }
      }
      _ => ()
    }
  }
  true
}

///|
fn dae_uniform_syntax_initial_tags_match(
  initial : Array[Int32]?,
  actual : Array[Int32],
) -> Bool {
  match initial {
    Some(entries) => entries == actual
    None => actual.is_empty()
  }
}

///|
/// Typed-catch admission initializes the shared cache only on its first empty
/// read. Replay that write before the first matching call after the catch, or
/// after all earlier calls if control admission is the failure barrier.
fn dae_uniform_syntax_replay_tag_initialization(
  mod_ : @lib.Module,
  preceding_tag : @lib.TagIdx?,
  entries : Array[Int32],
) -> Unit {
  match preceding_tag {
    Some(tag) if entries.is_empty() => {
      let _ = dae_legacy_tag_payload_count(mod_, tag, entries)
    }
    _ => ()
  }
}

///|
fn dae_collect_uniform_const_actuals_indexed(
  query : DaeUniformSyntaxQuery,
  instrs : Array[@lib.Instruction],
  callee_abs : Int32,
  param_types : Array[@lib.ValType],
  entry_stack_values : Int32,
  caller_abs : Int32,
  mod_ : @lib.Module,
  func_type_indices : Array[@lib.TypeIdx],
  boundary_facts : DaeCallBoundaryFacts,
  visited : Array[Bool],
  seen : Array[Bool],
  invalid : Array[Bool],
  candidates : Array[@lib.Instruction?],
  active_params : Array[Bool],
  allow_immutable_global_get : Bool,
  uniform_resolution_cache : DaeUniformResolutionCache?,
  tag_entries : Array[Int32],
) -> Bool {
  let caller = match query.callers.get(caller_abs) {
    Some(caller) if physical_equal(caller.body, instrs) &&
      caller.entry_stack_values == entry_stack_values &&
      dae_uniform_syntax_initial_tags_match(
        caller.initial_tag_entries,
        tag_entries,
      ) => {
      match query.syntax_stats {
        Some(stats) if stats.length() == 4 => stats[2] += 1
        _ => ()
      }
      caller
    }
    _ => {
      match query.syntax_stats {
        Some(stats) if stats.length() == 4 => stats[1] += 1
        _ => ()
      }
      let caller : DaeUniformSyntaxCaller = {
        body: instrs,
        entry_stack_values,
        calls: @hashmap.new(),
        initial_tag_entries: if tag_entries.is_empty() {
          None
        } else {
          Some(tag_entries.copy())
        },
        first_tag: None,
        complete: false,
      }
      // Indexing may inspect a later catch before an earlier operand proof
      // fails. The caller's observable cache must not be written eagerly.
      let private_tags = tag_entries.copy()
      caller.complete = dae_uniform_syntax_collect_calls(
        query, instrs, entry_stack_values, caller, private_tags,
      )
      query.callers.set(caller_abs, caller)
      caller
    }
  }
  match caller.calls.get(callee_abs) {
    Some(calls) =>
      for call in calls {
        dae_uniform_syntax_replay_tag_initialization(
          mod_,
          call.preceding_tag,
          tag_entries,
        )
        // Keep the original source scope and offset for stack recovery and
        // local carriers. This executes the retained semantic visitor once.
        if !dae_collect_uniform_const_actuals_instrs(
            call.scope,
            callee_abs,
            param_types,
            call.entry_stack_values,
            caller_abs,
            mod_,
            func_type_indices,
            boundary_facts,
            visited,
            seen,
            invalid,
            candidates,
            allow_immutable_global_get~,
            collect_params=Some(active_params),
            uniform_resolution_cache~,
            syntax_query=Some(query),
            legacy_tag_entries=Some(tag_entries),
            single_call_idx=Some(call.call_idx),
          ) {
          return false
        }
      }
    None => ()
  }
  dae_uniform_syntax_replay_tag_initialization(
    mod_,
    caller.first_tag,
    tag_entries,
  )
  caller.complete
}

///|
fn dae_uniform_syntax_query_values(
  query : DaeUniformSyntaxQuery,
  graph : DaeCurrentBoundaryGraph,
  callee_abs : Int32,
  param_count : Int32,
  allow_immutable_global_get~ : Bool = false,
  collect_params~ : Array[Bool]? = None,
) -> Array[@lib.Instruction?]? {
  dae_collect_uniform_const_actuals_from_boundary_facts(
    graph.mod_,
    callee_abs,
    param_count,
    graph.snapshot.type_indices,
    graph.snapshot.boundary_facts,
    allow_immutable_global_get~,
    collect_params~,
    syntax_query=Some(query),
  )
}