///| Compiler-facts validation is structural only. It validates references,

///| canonical forms, attachment shapes, masks, ranges, and contradictions that

///|
/// are mechanically obvious. It never proves that advisory assertions are true.
fn[T : Validate] validate_compiler_fact_option(
  value : T?,
  env : Env,
) -> Result[Unit, String] {
  match value {
    Some(value) => Validate::validate(value, env)
    None => Ok(())
  }
}

///|
fn validate_compiler_fact_function_index(
  index : UInt32,
  env : Env,
) -> Result[Unit, String] {
  if env.has_func(FuncIdx::new(index)) {
    Ok(())
  } else {
    Err(
      "compiler facts function index out of range: function=\{index} total_functions=\{env.funcs.length()}",
    )
  }
}

///|
fn validate_compiler_fact_type_index(
  index : UInt32,
  env : Env,
) -> Result[Unit, String] {
  if index < env.global_types.length().reinterpret_as_uint() {
    Ok(())
  } else {
    Err(
      "compiler facts type index out of range: type=\{index} total_types=\{env.global_types.length()}",
    )
  }
}

///|
fn validate_compiler_fact_global_index(
  index : UInt32,
  env : Env,
) -> Result[Unit, String] {
  if env.get_global_type(GlobalIdx::new(index)) is Some(_) {
    Ok(())
  } else {
    Err(
      "compiler facts global index out of range: global=\{index} total_globals=\{env.globals.length()}",
    )
  }
}

///|
fn validate_compiler_fact_memory_index(
  index : UInt32,
  env : Env,
) -> Result[Unit, String] {
  if env.get_memtype(MemIdx::new(index)) is Some(_) {
    Ok(())
  } else {
    Err(
      "compiler facts memory index out of range: memory=\{index} total_memories=\{env.mems.length()}",
    )
  }
}

///|
fn validate_compiler_fact_table_index(
  index : UInt32,
  env : Env,
) -> Result[Unit, String] {
  if env.get_table_type(TableIdx::new(index)) is Some(_) {
    Ok(())
  } else {
    Err(
      "compiler facts table index out of range: table=\{index} total_tables=\{env.tables.length()}",
    )
  }
}

///|
fn validate_compiler_fact_closed_uint_set(
  values : Array[UInt32],
  validate_index : (UInt32) -> Result[Unit, String],
  label : String,
) -> Result[Unit, String] {
  let mut previous : UInt32? = None
  for value in values {
    match previous {
      Some(prev) if value <= prev =>
        return Err("compiler facts \{label} must be sorted and duplicate-free")
      _ => ()
    }
    if validate_index(value) is Err(err) {
      return Err(err)
    }
    previous = Some(value)
  }
  Ok(())
}

///|
fn compiler_fact_unsigned_width_max(width : IntWidth) -> UInt64 {
  match width {
    Int8 => 0xFFUL
    Int16 => 0xFFFFUL
    Int32 => 0xFFFF_FFFFUL
    Int64 => UInt64::lnot(0UL)
  }
}

///|
fn compiler_fact_signed_width_bounds(width : IntWidth) -> (Int64, Int64) {
  match width {
    Int8 => (-128L, 127L)
    Int16 => (-32768L, 32767L)
    Int32 => (-2147483648L, 2147483647L)
    Int64 => (-9223372036854775807L - 1L, 9223372036854775807L)
  }
}

///|
fn compiler_fact_value_is_integer_zero(value : FactValue) -> Bool {
  match value {
    IntegerValue(constant) => constant.bits == 0UL
    _ => false
  }
}

///|
fn compiler_fact_value_family(value : FactValue) -> Int32 {
  match value {
    IntegerValue(_) => 0
    F32Bits(_) | F64Bits(_) => 1
    V128Value(_) => 2
    RefNullValue(_) | RefFuncValue(_) => 3
    ImmutableGlobalValue(_) => 4
  }
}

///|
fn compiler_fact_value_fits_type(value : FactValue, type_ : ValType) -> Bool {
  match (value, type_) {
    (IntegerValue(c), NumTypeValType(I32NumType)) => c.width != IntWidth::i64()
    (IntegerValue(c), NumTypeValType(I64NumType)) => c.width == IntWidth::i64()
    (F32Bits(_), NumTypeValType(F32NumType)) => true
    (F64Bits(_), NumTypeValType(F64NumType)) => true
    (V128Value(_), VecTypeValType) => true
    (RefNullValue(_), RefTypeValType(_))
    | (RefFuncValue(_), RefTypeValType(_)) => true
    (ImmutableGlobalValue(_), _) => true
    _ => false
  }
}

///|
fn compiler_fact_closed_values_fit_type(
  possible_values : ClosedValueSet?,
  type_ : ValType,
) -> Bool {
  match possible_values {
    Some(@representation.ClosedValueSet(values)) => {
      for value in values {
        if !compiler_fact_value_fits_type(value, type_) {
          return false
        }
      }
      true
    }
    None => true
  }
}

///|
fn compiler_fact_value_facts_fit_type(
  value : ValueFacts,
  type_ : ValType,
) -> Bool {
  let domain_fits = match type_ {
    NumTypeValType(I32NumType) => {
      let integer_fits = match value.integer {
        Some(facts) => facts.width != IntWidth::i64()
        None => true
      }
      value.float_ is None &&
      value.vector is None &&
      value.reference is None &&
      integer_fits
    }
    NumTypeValType(I64NumType) => {
      let integer_fits = match value.integer {
        Some(facts) => facts.width == IntWidth::i64()
        None => true
      }
      value.float_ is None &&
      value.vector is None &&
      value.reference is None &&
      integer_fits
    }
    NumTypeValType(F32NumType) => {
      let float_fits = match value.float_ {
        Some(facts) => facts.width == FloatWidth::f32()
        None => true
      }
      value.integer is None &&
      value.vector is None &&
      value.reference is None &&
      float_fits
    }
    NumTypeValType(F64NumType) => {
      let float_fits = match value.float_ {
        Some(facts) => facts.width == FloatWidth::f64()
        None => true
      }
      value.integer is None &&
      value.vector is None &&
      value.reference is None &&
      float_fits
    }
    VecTypeValType =>
      value.integer is None && value.float_ is None && value.reference is None
    RefTypeValType(_) =>
      value.integer is None && value.float_ is None && value.vector is None
    BotValType => true
  }
  domain_fits &&
  compiler_fact_closed_values_fit_type(value.possible_values, type_)
}

///|
pub impl Validate for CompilerFactCustomSection with fn validate(value, env) {
  if value.version != 1U {
    return Err(
      "unsupported compiler facts version: version=\{value.version} supported=1",
    )
  }
  Validate::validate(value.facts, env)
}

///|
pub impl Validate for ProducerInfo with fn validate(_, _) {
  Ok(())
}

///|
pub impl Validate for WorldMode with fn validate(_, _) {
  Ok(())
}

///|
pub impl Validate for WorldFacts with fn validate(value, env) {
  Validate::validate(value.mode, env)
}

///|
pub impl Validate for OptimizationFactsSec with fn validate(value, env) {
  if validate_compiler_fact_option(value.producer, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.world, env) is Err(err) {
    return Err(err)
  }
  let seen_functions = Array::make(env.funcs.length(), false)
  for facts in value.functions {
    let index = facts.function.reinterpret_as_int()
    if index >= 0 && index < seen_functions.length() && seen_functions[index] {
      return Err("duplicate function facts record: function=\{facts.function}")
    }
    if Validate::validate(facts, env) is Err(err) {
      return Err(err)
    }
    if index >= 0 && index < seen_functions.length() {
      seen_functions[index] = true
    }
  }
  let seen_signatures = Array::make(env.global_types.length(), false)
  for facts in value.signatures {
    let index = facts.signature.reinterpret_as_int()
    if index >= 0 && index < seen_signatures.length() && seen_signatures[index] {
      return Err(
        "duplicate signature facts record: signature=\{facts.signature}",
      )
    }
    if Validate::validate(facts, env) is Err(err) {
      return Err(err)
    }
    if index >= 0 && index < seen_signatures.length() {
      seen_signatures[index] = true
    }
  }
  let seen_types = Array::make(env.global_types.length(), false)
  for facts in value.types {
    let index = facts.type_.reinterpret_as_int()
    if index >= 0 && index < seen_types.length() && seen_types[index] {
      return Err("duplicate type facts record: type=\{facts.type_}")
    }
    if Validate::validate(facts, env) is Err(err) {
      return Err(err)
    }
    if index >= 0 && index < seen_types.length() {
      seen_types[index] = true
    }
  }
  let seen_globals = Array::make(env.globals.length(), false)
  for facts in value.globals {
    let index = facts.global.reinterpret_as_int()
    if index >= 0 && index < seen_globals.length() && seen_globals[index] {
      return Err("duplicate global facts record: global=\{facts.global}")
    }
    if Validate::validate(facts, env) is Err(err) {
      return Err(err)
    }
    if index >= 0 && index < seen_globals.length() {
      seen_globals[index] = true
    }
  }
  let seen_tables = Array::make(env.tables.length(), false)
  for facts in value.tables {
    let index = facts.table.reinterpret_as_int()
    if index >= 0 && index < seen_tables.length() && seen_tables[index] {
      return Err("duplicate table facts record: table=\{facts.table}")
    }
    if Validate::validate(facts, env) is Err(err) {
      return Err(err)
    }
    if index >= 0 && index < seen_tables.length() {
      seen_tables[index] = true
    }
  }
  let seen_bodies = Array::make(env.funcs.length(), false)
  for body in value.bodies {
    let index = body.function.reinterpret_as_int()
    if index >= 0 && index < seen_bodies.length() && seen_bodies[index] {
      return Err(
        "duplicate function body facts record: function=\{body.function}",
      )
    }
    if Validate::validate(body, env) is Err(err) {
      return Err(err)
    }
    if index >= 0 && index < seen_bodies.length() {
      seen_bodies[index] = true
    }
  }
  Validate::validate(value.hints, env)
}

///|
pub impl Validate for CodeSite with fn validate(value, env) {
  validate_compiler_fact_function_index(value.function, env)
}

///|
pub impl Validate for ValueSite with fn validate(value, env) {
  Validate::validate(value.site, env)
}

///|
pub impl Validate for SourceProvenance with fn validate(_, _) {
  Ok(())
}

///|
pub impl Validate for IntWidth with fn validate(_, _) {
  Ok(())
}

///|
pub impl Validate for IntConstant with fn validate(value, _) {
  let outside = value.bits &
    UInt64::lnot(compiler_fact_unsigned_width_max(value.width))
  if outside == 0UL {
    Ok(())
  } else {
    Err(
      "compiler facts integer constant has bits outside width: width=\{value.width.bit_count()} bits bits=\{value.bits}",
    )
  }
}

///|
pub impl Validate for V128Bits with fn validate(_, _) {
  Ok(())
}

///|
pub impl Validate for FactHeapType with fn validate(value, env) {
  match value {
    TypeIndex(index) => validate_compiler_fact_type_index(index, env)
    _ => Ok(())
  }
}

///|
pub impl Validate for FactValue with fn validate(value, env) {
  match value {
    IntegerValue(value) => Validate::validate(value, env)
    F32Bits(_) | F64Bits(_) | V128Value(_) => Ok(())
    RefNullValue(heap) => Validate::validate(heap, env)
    RefFuncValue(function) =>
      validate_compiler_fact_function_index(function, env)
    ImmutableGlobalValue(global) =>
      validate_compiler_fact_global_index(global, env)
  }
}

///|
pub impl Validate for ClosedValueSet with fn validate(value, env) {
  let @representation.ClosedValueSet(values) = value
  if ClosedValueSet::new(values) != value {
    return Err(
      "compiler facts closed value set must be sorted and duplicate-free",
    )
  }
  let mut family : Int32? = None
  for fact_value in values {
    if Validate::validate(fact_value, env) is Err(err) {
      return Err(err)
    }
    let next_family = compiler_fact_value_family(fact_value)
    match family {
      Some(previous) if previous != next_family =>
        return Err(
          "compiler facts closed value set mixes incompatible value families",
        )
      _ => ()
    }
    family = Some(next_family)
  }
  Ok(())
}

///|
pub impl Validate for SignedRange with fn validate(value, _) {
  if value.min <= value.max {
    Ok(())
  } else {
    Err(
      "compiler facts signed range has min greater than max: min=\{value.min} max=\{value.max}",
    )
  }
}

///|
pub impl Validate for UnsignedRange with fn validate(value, _) {
  if value.min <= value.max {
    Ok(())
  } else {
    Err(
      "compiler facts unsigned range has min greater than max: min=\{value.min} max=\{value.max}",
    )
  }
}

///|
pub impl Validate for KnownBits with fn validate(value, _) {
  if (value.known_zero & value.known_one) == 0UL {
    Ok(())
  } else {
    Err("compiler facts known bits mark the same bit zero and one")
  }
}

///|
pub impl Validate for IntegerFacts with fn validate(value, env) {
  let width_max = compiler_fact_unsigned_width_max(value.width)
  match value.unsigned_range {
    Some(range) => {
      if Validate::validate(range, env) is Err(err) {
        return Err(err)
      }
      if range.max > width_max {
        return Err("compiler facts unsigned range exceeds integer width")
      }
    }
    None => ()
  }
  match value.signed_range {
    Some(range) => {
      if Validate::validate(range, env) is Err(err) {
        return Err(err)
      }
      let (min, max) = compiler_fact_signed_width_bounds(value.width)
      if range.min < min || range.max > max {
        return Err("compiler facts signed range exceeds integer width")
      }
    }
    None => ()
  }
  match value.known_bits {
    Some(bits) => {
      if Validate::validate(bits, env) is Err(err) {
        return Err(err)
      }
      if ((bits.known_zero | bits.known_one) & UInt64::lnot(width_max)) != 0UL {
        return Err("compiler facts known bits exceed integer width")
      }
      match value.unsigned_range {
        Some(range) if bits.known_one > range.max =>
          return Err(
            "compiler facts known-one bits cannot fit the unsigned range",
          )
        _ => ()
      }
    }
    None => ()
  }
  Ok(())
}

///|
pub impl Validate for FloatWidth with fn validate(_, _) {
  Ok(())
}

///|
pub impl Validate for FloatClassMask with fn validate(value, _) {
  let unknown = value.bits() & UInt32::lnot(0x1FFU)
  if unknown == 0U {
    Ok(())
  } else {
    Err("compiler facts float class mask contains undefined bits: \{unknown}")
  }
}

///|
pub impl Validate for FloatFacts with fn validate(value, env) {
  Validate::validate(value.possible_classes, env)
}

///|
pub impl Validate for NullabilityFacts with fn validate(_, _) {
  Ok(())
}

///|
pub impl Validate for ClosedHeapTypeSet with fn validate(value, env) {
  let @representation.ClosedHeapTypeSet(types) = value
  if ClosedHeapTypeSet::new(types) != value {
    return Err(
      "compiler facts closed heap type set must be sorted and duplicate-free",
    )
  }
  Validate::validate(types, env)
}

///|
pub impl Validate for HeapDomain with fn validate(value, env) {
  match value {
    ExactRuntimeType(type_) | SubtypesOf(type_) =>
      Validate::validate(type_, env)
    ClosedRuntimeTypes(types) => Validate::validate(types, env)
  }
}

///|
pub impl Validate for ReferenceFacts with fn validate(value, env) {
  if validate_compiler_fact_option(value.nullability, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.heap, env) is Err(err) {
    return Err(err)
  }
  validate_compiler_fact_option(value.function_targets, env)
}

///|
pub impl Validate for VectorLaneShape with fn validate(_, _) {
  Ok(())
}

///|
pub impl Validate for KnownV128Bits with fn validate(value, _) {
  if (
      (value.known_zero.low & value.known_one.low) |
      (value.known_zero.high & value.known_one.high)
    ) ==
    0UL {
    Ok(())
  } else {
    Err("compiler facts v128 known bits mark the same bit zero and one")
  }
}

///|
pub impl Validate for VectorFacts with fn validate(value, env) {
  if validate_compiler_fact_option(value.known_bits, env) is Err(err) {
    return Err(err)
  }
  match value.splat {
    Some(IntegerValue(_)) | Some(F32Bits(_)) | Some(F64Bits(_)) => ()
    Some(_) =>
      return Err(
        "compiler facts vector splat must be a scalar numeric fact value",
      )
    None => ()
  }
  if validate_compiler_fact_option(value.splat, env) is Err(err) {
    return Err(err)
  }
  validate_compiler_fact_option(value.lane_shape, env)
}

///|
pub impl Validate for ValueFacts with fn validate(value, env) {
  if validate_compiler_fact_option(value.possible_values, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.integer, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.float_, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.vector, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.reference, env) is Err(err) {
    return Err(err)
  }
  let domain_count = (if value.integer is Some(_) { 1 } else { 0 }) +
    (if value.float_ is Some(_) { 1 } else { 0 }) +
    (if value.vector is Some(_) { 1 } else { 0 }) +
    (if value.reference is Some(_) { 1 } else { 0 })
  if domain_count > 1 {
    return Err("compiler facts value domains are mutually incompatible")
  }
  match (value.possible_values, value.integer) {
    (Some(@representation.ClosedValueSet([only])), Some(integer)) if integer.nonzero &&
      compiler_fact_value_is_integer_zero(only) =>
      Err("compiler facts exact integer zero contradicts nonzero=true")
    _ => Ok(())
  }
}

///|
pub impl Validate for ParameterRef with fn validate(value, env) {
  let FuncType(parameters, _) = match
    env.get_functype_by_funcidx(FuncIdx::new(value.function)) {
    Some(type_) => type_
    None =>
      return Err("compiler facts parameter reference has invalid function")
  }
  if value.parameter < parameters.length().reinterpret_as_uint() {
    Ok(())
  } else {
    Err("compiler facts parameter reference index out of range")
  }
}

///|
pub impl Validate for FunctionResultRef with fn validate(value, env) {
  let FuncType(_, results) = match
    env.get_functype_by_funcidx(FuncIdx::new(value.function)) {
    Some(type_) => type_
    None =>
      return Err(
        "compiler facts function result reference has invalid function",
      )
  }
  if value.result < results.length().reinterpret_as_uint() {
    Ok(())
  } else {
    Err("compiler facts function result reference index out of range")
  }
}

///|
pub impl Validate for ValueRef with fn validate(value, env) {
  match value {
    SiteValue(site) => Validate::validate(site, env)
    ParameterValue(parameter) => Validate::validate(parameter, env)
    FunctionResultValue(result) => Validate::validate(result, env)
    GlobalValue(global) => validate_compiler_fact_global_index(global, env)
  }
}

///|
pub impl Validate for AliasSource with fn validate(value, env) {
  match value {
    ParameterAlias(_) => Ok(())
    GlobalAlias(global) => validate_compiler_fact_global_index(global, env)
    SiteAlias(site) => Validate::validate(site, env)
  }
}

///|
pub impl Validate for ClosedAliasSet with fn validate(value, env) {
  let @representation.ClosedAliasSet(sources) = value
  if ClosedAliasSet::new(sources) != value {
    return Err(
      "compiler facts closed alias set must be sorted and duplicate-free",
    )
  }
  Validate::validate(sources, env)
}

///|
pub impl Validate for ResultIdentityFacts with fn validate(value, env) {
  match value {
    FreshReference => Ok(())
    ClosedAliases(aliases) => Validate::validate(aliases, env)
  }
}

///|
pub impl Validate for ParameterUseMask with fn validate(value, _) {
  let unknown = value.bits() & UInt64::lnot(0x7FUL)
  if unknown == 0UL {
    Ok(())
  } else {
    Err("compiler facts parameter use mask contains undefined bits: \{unknown}")
  }
}

///|
pub impl Validate for EscapeSinkMask with fn validate(value, _) {
  let unknown = value.bits() & UInt64::lnot(0xFFUL)
  if unknown == 0UL {
    Ok(())
  } else {
    Err("compiler facts escape sink mask contains undefined bits: \{unknown}")
  }
}

///|
pub impl Validate for EffectMask with fn validate(value, _) {
  let unknown = value.bits() & UInt64::lnot(0x7F_FFFFUL)
  if unknown == 0UL {
    Ok(())
  } else {
    Err("compiler facts effect mask contains undefined bits: \{unknown}")
  }
}

///|
pub impl Validate for ClosedGlobalSet with fn validate(value, env) {
  let @representation.ClosedGlobalSet(values) = value
  validate_compiler_fact_closed_uint_set(
    values,
    fn(i) { validate_compiler_fact_global_index(i, env) },
    "closed global set",
  )
}

///|
pub impl Validate for ClosedMemorySet with fn validate(value, env) {
  let @representation.ClosedMemorySet(values) = value
  validate_compiler_fact_closed_uint_set(
    values,
    fn(i) { validate_compiler_fact_memory_index(i, env) },
    "closed memory set",
  )
}

///|
pub impl Validate for ClosedTableSet with fn validate(value, env) {
  let @representation.ClosedTableSet(values) = value
  validate_compiler_fact_closed_uint_set(
    values,
    fn(i) { validate_compiler_fact_table_index(i, env) },
    "closed table set",
  )
}

///|
pub impl Validate for ClosedTypeViewSet with fn validate(value, env) {
  let @representation.ClosedTypeViewSet(values) = value
  validate_compiler_fact_closed_uint_set(
    values,
    fn(i) { validate_compiler_fact_type_index(i, env) },
    "closed type view set",
  )
}

///|
pub impl Validate for ClosedFunctionSet with fn validate(value, env) {
  let @representation.ClosedFunctionSet(values) = value
  validate_compiler_fact_closed_uint_set(
    values,
    fn(i) { validate_compiler_fact_function_index(i, env) },
    "closed function set",
  )
}

///|
pub impl Validate for EffectFacts with fn validate(value, env) {
  if Validate::validate(value.may, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.read_globals, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.written_globals, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.read_memories, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.written_memories, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.read_tables, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.written_tables, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.read_heap_types, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.written_heap_types, env) is Err(err) {
    return Err(err)
  }
  validate_compiler_fact_option(value.direct_callees, env)
}

///|
pub impl Validate for FunctionBoundaryFacts with fn validate(_, _) {
  Ok(())
}

///|
pub impl Validate for FunctionProfile with fn validate(value, env) {
  validate_compiler_fact_option(value.temperature, env)
}

///|
pub impl Validate for CallProfile with fn validate(value, env) {
  validate_compiler_fact_option(value.temperature, env)
}

///|
pub impl Validate for BranchProfile with fn validate(_, _) {
  Ok(())
}

///|
pub impl Validate for LoopProfile with fn validate(_, _) {
  Ok(())
}

///|
pub impl Validate for TemperatureHint with fn validate(_, _) {
  Ok(())
}

///|
pub impl Validate for InlinePolicy with fn validate(_, _) {
  Ok(())
}

///|
pub impl Validate for ParameterFacts with fn validate(value, env) {
  if validate_compiler_fact_option(value.value, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.uses, env) is Err(err) {
    return Err(err)
  }
  validate_compiler_fact_option(value.escape_sinks, env)
}

///|
pub impl Validate for ResultFacts with fn validate(value, env) {
  if validate_compiler_fact_option(value.value, env) is Err(err) {
    return Err(err)
  }
  validate_compiler_fact_option(value.identity, env)
}

///|
pub impl Validate for FunctionFacts with fn validate(value, env) {
  if validate_compiler_fact_function_index(value.function, env) is Err(err) {
    return Err(err)
  }
  let FuncType(parameters, results) = match
    env.get_functype_by_funcidx(FuncIdx::new(value.function)) {
    Some(type_) => type_
    None => return Err("compiler facts function index has no function type")
  }
  if validate_compiler_fact_option(value.effects, env) is Err(err) {
    return Err(err)
  }
  let seen_parameters = Array::make(parameters.length(), false)
  for parameter in value.parameters {
    let index = parameter.parameter.reinterpret_as_int()
    if index < 0 || index >= parameters.length() {
      return Err(
        "compiler facts parameter index out of range: function=\{value.function} parameter=\{parameter.parameter}",
      )
    }
    if seen_parameters[index] {
      return Err(
        "duplicate compiler facts parameter record: function=\{value.function} parameter=\{parameter.parameter}",
      )
    }
    if Validate::validate(parameter, env) is Err(err) {
      return Err(err)
    }
    match parameter.value {
      Some(facts) if !compiler_fact_value_facts_fit_type(
          facts,
          parameters[index],
        ) =>
        return Err(
          "compiler facts parameter value domain is incompatible with parameter type",
        )
      _ => ()
    }
    seen_parameters[index] = true
  }
  let seen_results = Array::make(results.length(), false)
  for result in value.results {
    let index = result.result.reinterpret_as_int()
    if index < 0 || index >= results.length() {
      return Err(
        "compiler facts result index out of range: function=\{value.function} result=\{result.result}",
      )
    }
    if seen_results[index] {
      return Err(
        "duplicate compiler facts result record: function=\{value.function} result=\{result.result}",
      )
    }
    if Validate::validate(result, env) is Err(err) {
      return Err(err)
    }
    match result.value {
      Some(facts) if !compiler_fact_value_facts_fit_type(facts, results[index]) =>
        return Err(
          "compiler facts result value domain is incompatible with result type",
        )
      _ => ()
    }
    match result.identity {
      Some(ClosedAliases(@representation.ClosedAliasSet(sources))) =>
        for source in sources {
          match source {
            ParameterAlias(parameter) if parameter >=
              parameters.length().reinterpret_as_uint() =>
              return Err("compiler facts identity parameter index out of range")
            _ => ()
          }
        }
      _ => ()
    }
    seen_results[index] = true
  }
  if validate_compiler_fact_option(value.boundary, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.profile, env) is Err(err) {
    return Err(err)
  }
  validate_compiler_fact_option(value.inline_policy, env)
}

///|
pub impl Validate for IndexedValueFacts with fn validate(value, env) {
  Validate::validate(value.value, env)
}

///|
pub impl Validate for SignatureFacts with fn validate(value, env) {
  let FuncType(parameters, results) = match
    env.resolve_functype(TypeIdx::new(value.signature)) {
    Some(type_) => type_
    None =>
      return Err(
        "compiler facts signature index must reference a function type",
      )
  }
  let seen_parameters = Array::make(parameters.length(), false)
  for facts in value.parameters {
    let index = facts.index.reinterpret_as_int()
    if index < 0 || index >= parameters.length() {
      return Err("compiler facts signature parameter index out of range")
    }
    if seen_parameters[index] {
      return Err("duplicate compiler facts signature parameter record")
    }
    if Validate::validate(facts, env) is Err(err) {
      return Err(err)
    }
    if !compiler_fact_value_facts_fit_type(facts.value, parameters[index]) {
      return Err("compiler facts signature parameter domain is incompatible")
    }
    seen_parameters[index] = true
  }
  let seen_results = Array::make(results.length(), false)
  for facts in value.results {
    let index = facts.index.reinterpret_as_int()
    if index < 0 || index >= results.length() {
      return Err("compiler facts signature result index out of range")
    }
    if seen_results[index] {
      return Err("duplicate compiler facts signature result record")
    }
    if Validate::validate(facts, env) is Err(err) {
      return Err(err)
    }
    if !compiler_fact_value_facts_fit_type(facts.value, results[index]) {
      return Err("compiler facts signature result domain is incompatible")
    }
    seen_results[index] = true
  }
  Ok(())
}

///|
pub impl Validate for TypeView with fn validate(value, env) {
  match value {
    ExactTypeView(type_) | SubtypeClosureView(type_) =>
      validate_compiler_fact_type_index(type_, env)
  }
}

///|
pub impl Validate for TypePopulation with fn validate(value, env) {
  match value {
    EmptyTypePopulation => Ok(())
    ClosedTypePopulation(types) => Validate::validate(types, env)
  }
}

///|
pub impl Validate for WriteFacts with fn validate(value, env) {
  match value {
    NoWrites | InitializationOnly => Ok(())
    ClosedWrites(sites) => {
      if WriteFacts::closed(sites) != value {
        return Err(
          "compiler facts closed write sites must be sorted and duplicate-free",
        )
      }
      Validate::validate(sites, env)
    }
  }
}

///|
pub impl Validate for FieldUsageFacts with fn validate(_, _) {
  Ok(())
}

///|
fn compiler_fact_type_view_index(view : TypeView) -> UInt32 {
  match view {
    ExactTypeView(type_) | SubtypeClosureView(type_) => type_
  }
}

///|
pub impl Validate for FieldFacts with fn validate(value, env) {
  if Validate::validate(value.view, env) is Err(err) {
    return Err(err)
  }
  let type_ = TypeIdx::new(compiler_fact_type_view_index(value.view))
  let fields = match env.resolve_comptype(type_) {
    Some(StructCompType(fields)) => fields
    Some(_) =>
      return Err("compiler facts field view must reference a struct type")
    None => return Err("compiler facts field view type does not exist")
  }
  let index = value.field.reinterpret_as_int()
  if index < 0 || index >= fields.length() {
    return Err("compiler facts struct field index out of range")
  }
  if validate_compiler_fact_option(value.stored_contents, env) is Err(err) {
    return Err(err)
  }
  match value.stored_contents {
    Some(facts) if !compiler_fact_value_facts_fit_type(
        facts,
        fields[index].unpack(),
      ) =>
      return Err(
        "compiler facts stored field domain is incompatible with storage type",
      )
    _ => ()
  }
  if validate_compiler_fact_option(value.writes, env) is Err(err) {
    return Err(err)
  }
  validate_compiler_fact_option(value.usage, env)
}

///|
pub impl Validate for ArrayElementFacts with fn validate(value, env) {
  if Validate::validate(value.view, env) is Err(err) {
    return Err(err)
  }
  let field = match
    env.resolve_comptype(
      TypeIdx::new(compiler_fact_type_view_index(value.view)),
    ) {
    Some(ArrayCompType(field)) => field
    Some(_) =>
      return Err(
        "compiler facts array element view must reference an array type",
      )
    None => return Err("compiler facts array element view type does not exist")
  }
  if validate_compiler_fact_option(value.stored_contents, env) is Err(err) {
    return Err(err)
  }
  match value.stored_contents {
    Some(facts) if !compiler_fact_value_facts_fit_type(facts, field.unpack()) =>
      return Err(
        "compiler facts array element domain is incompatible with storage type",
      )
    _ => ()
  }
  validate_compiler_fact_option(value.writes, env)
}

///|
pub impl Validate for TypeFacts with fn validate(value, env) {
  if validate_compiler_fact_type_index(value.type_, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.population, env) is Err(err) {
    return Err(err)
  }
  for field in value.fields {
    if compiler_fact_type_view_index(field.view) != value.type_ {
      return Err("compiler facts field view does not match containing type")
    }
    if Validate::validate(field, env) is Err(err) {
      return Err(err)
    }
  }
  match value.array_element {
    Some(array) => {
      if compiler_fact_type_view_index(array.view) != value.type_ {
        return Err("compiler facts array view does not match containing type")
      }
      Validate::validate(array, env)
    }
    None => Ok(())
  }
}

///|
pub impl Validate for MutationPhase with fn validate(_, _) {
  Ok(())
}

///|
pub impl Validate for MutationFacts with fn validate(value, env) {
  if Validate::validate(value.latest_phase, env) is Err(err) {
    return Err(err)
  }
  validate_compiler_fact_option(value.sites, env)
}

///|
pub impl Validate for GlobalFacts with fn validate(value, env) {
  if validate_compiler_fact_global_index(value.global, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.initial_value, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.steady_state_value, env) is Err(err) {
    return Err(err)
  }
  validate_compiler_fact_option(value.mutation, env)
}

///|
pub impl Validate for TableEntryFacts with fn validate(value, env) {
  Validate::validate(value.value, env)
}

///|
pub impl Validate for TableFacts with fn validate(value, env) {
  if validate_compiler_fact_table_index(value.table, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.mutation, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.default_value, env) is Err(err) {
    return Err(err)
  }
  let mut previous : UInt64? = None
  for entry in value.entries {
    match previous {
      Some(index) if entry.index <= index =>
        return Err(
          "compiler facts table entries must be sorted and duplicate-free",
        )
      _ => ()
    }
    if Validate::validate(entry, env) is Err(err) {
      return Err(err)
    }
    previous = Some(entry.index)
  }
  Ok(())
}

///|
fn compiler_fact_allocation_type(value : AllocationKind) -> UInt32 {
  match value {
    StructAllocation(type_)
    | ArrayAllocation(type_)
    | ClosureAllocation(type_)
    | BoxAllocation(type_)
    | OtherGcAllocation(type_) => type_
  }
}

///|
pub impl Validate for AllocationKind with fn validate(value, env) {
  let type_ = compiler_fact_allocation_type(value)
  if validate_compiler_fact_type_index(type_, env) is Err(err) {
    return Err(err)
  }
  match (value, env.resolve_comptype(TypeIdx::new(type_))) {
    (StructAllocation(_), Some(StructCompType(_)))
    | (ArrayAllocation(_), Some(ArrayCompType(_))) => Ok(())
    (StructAllocation(_), _) =>
      Err("compiler facts struct allocation kind must reference a struct type")
    (ArrayAllocation(_), _) =>
      Err("compiler facts array allocation kind must reference an array type")
    _ => Ok(())
  }
}

///|
pub impl Validate for AllocationFieldFacts with fn validate(value, env) {
  validate_compiler_fact_option(value.stored_value, env)
}

///|
pub impl Validate for AllocationFacts with fn validate(value, env) {
  if Validate::validate(value.kind, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.escape_sinks, env) is Err(err) {
    return Err(err)
  }
  match (value.kind, value.length) {
    (ArrayAllocation(_), _) => ()
    (_, Some(_)) =>
      return Err(
        "compiler facts allocation length is only valid for array allocations",
      )
    _ => ()
  }
  if validate_compiler_fact_option(value.length, env) is Err(err) {
    return Err(err)
  }
  let fields = match
    env.resolve_comptype(
      TypeIdx::new(compiler_fact_allocation_type(value.kind)),
    ) {
    Some(StructCompType(fields)) => Some(fields)
    _ => None
  }
  let seen : Array[Bool] = match fields {
    Some(fields) => Array::make(fields.length(), false)
    None => []
  }
  for field in value.fields {
    if Validate::validate(field, env) is Err(err) {
      return Err(err)
    }
    match fields {
      Some(field_types) => {
        let index = field.field.reinterpret_as_int()
        if index < 0 || index >= field_types.length() {
          return Err("compiler facts allocation field index out of range")
        }
        if seen[index] {
          return Err("duplicate compiler facts allocation field record")
        }
        match field.stored_value {
          Some(facts) if !compiler_fact_value_facts_fit_type(
              facts,
              field_types[index].unpack(),
            ) =>
            return Err("compiler facts allocation field domain is incompatible")
          _ => ()
        }
        seen[index] = true
      }
      None =>
        if !value.fields.is_empty() {
          return Err(
            "compiler facts allocation fields require a struct-like allocation",
          )
        }
    }
  }
  Ok(())
}

///|
pub impl Validate for AllocationSiteFacts with fn validate(value, env) {
  if Validate::validate(value.site, env) is Err(err) {
    return Err(err)
  }
  if Validate::validate(value.facts, env) is Err(err) {
    return Err(err)
  }
  validate_compiler_fact_option(value.provenance, env)
}

///|
pub impl Validate for AccessFacts with fn validate(value, _) {
  match value.alignment_log2 {
    Some(alignment) if alignment > 63U =>
      Err("compiler facts access alignment_log2 is out of range")
    _ => Ok(())
  }
}

///|
pub impl Validate for AccessSiteFacts with fn validate(value, env) {
  if Validate::validate(value.site, env) is Err(err) {
    return Err(err)
  }
  if Validate::validate(value.facts, env) is Err(err) {
    return Err(err)
  }
  validate_compiler_fact_option(value.provenance, env)
}

///|
pub impl Validate for NumericOperationFacts with fn validate(_, _) {
  Ok(())
}

///|
pub impl Validate for NumericOperationSiteFacts with fn validate(value, env) {
  if Validate::validate(value.site, env) is Err(err) {
    return Err(err)
  }
  if Validate::validate(value.facts, env) is Err(err) {
    return Err(err)
  }
  validate_compiler_fact_option(value.provenance, env)
}

///|
pub impl Validate for FactOperand with fn validate(value, env) {
  match value {
    ValueOperand(value) => Validate::validate(value, env)
    ConstantOperand(value) => Validate::validate(value, env)
  }
}

///|
pub impl Validate for RelationKind with fn validate(_, _) {
  Ok(())
}

///|
pub impl Validate for ScopedRelationFacts with fn validate(value, env) {
  if Validate::validate(value.at, env) is Err(err) {
    return Err(err)
  }
  if Validate::validate(value.left, env) is Err(err) {
    return Err(err)
  }
  if Validate::validate(value.kind, env) is Err(err) {
    return Err(err)
  }
  Validate::validate(value.right, env)
}

///|
pub impl Validate for ExpressionFact with fn validate(value, env) {
  if Validate::validate(value.value, env) is Err(err) {
    return Err(err)
  }
  if Validate::validate(value.facts, env) is Err(err) {
    return Err(err)
  }
  validate_compiler_fact_option(value.provenance, env)
}

///|
pub impl Validate for CallSiteFacts with fn validate(value, env) {
  if Validate::validate(value.site, env) is Err(err) {
    return Err(err)
  }
  match value.direct_target {
    Some(index) =>
      if validate_compiler_fact_function_index(index, env) is Err(err) {
        return Err(err)
      }
    None => ()
  }
  if validate_compiler_fact_option(value.profile, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.inline_policy, env) is Err(err) {
    return Err(err)
  }
  validate_compiler_fact_option(value.provenance, env)
}

///|
pub impl Validate for BranchFacts with fn validate(value, env) {
  if Validate::validate(value.site, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.profile, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.temperature, env) is Err(err) {
    return Err(err)
  }
  validate_compiler_fact_option(value.provenance, env)
}

///|
pub impl Validate for LoopFacts with fn validate(value, env) {
  if Validate::validate(value.site, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.profile, env) is Err(err) {
    return Err(err)
  }
  if validate_compiler_fact_option(value.temperature, env) is Err(err) {
    return Err(err)
  }
  validate_compiler_fact_option(value.provenance, env)
}

///|
pub impl Validate for FunctionBodyFacts with fn validate(value, env) {
  if validate_compiler_fact_function_index(value.function, env) is Err(err) {
    return Err(err)
  }
  let seen_values : Array[ValueSite] = []
  for site in value.sites {
    if site.value.site.function != value.function {
      return Err("compiler facts expression site containing function mismatch")
    }
    if seen_values.any(fn(existing) { existing == site.value }) {
      return Err("duplicate compiler facts value site record")
    }
    if Validate::validate(site, env) is Err(err) {
      return Err(err)
    }
    seen_values.push(site.value)
  }
  for relation in value.relations {
    if relation.at.function != value.function {
      return Err("compiler facts relation containing function mismatch")
    }
    if Validate::validate(relation, env) is Err(err) {
      return Err(err)
    }
  }
  let seen_calls : Array[CodeSite] = []
  for call in value.calls {
    if call.site.function != value.function {
      return Err("compiler facts call site containing function mismatch")
    }
    if seen_calls.any(fn(site) { site == call.site }) {
      return Err("duplicate compiler facts call site record")
    }
    if Validate::validate(call, env) is Err(err) {
      return Err(err)
    }
    seen_calls.push(call.site)
  }
  for branch in value.branches {
    if branch.site.function != value.function {
      return Err("compiler facts branch site containing function mismatch")
    }
    if Validate::validate(branch, env) is Err(err) {
      return Err(err)
    }
  }
  for loop_ in value.loops {
    if loop_.site.function != value.function {
      return Err("compiler facts loop site containing function mismatch")
    }
    if Validate::validate(loop_, env) is Err(err) {
      return Err(err)
    }
  }
  for allocation in value.allocations {
    if allocation.site.site.function != value.function {
      return Err("compiler facts allocation site containing function mismatch")
    }
    if Validate::validate(allocation, env) is Err(err) {
      return Err(err)
    }
  }
  for access in value.accesses {
    if access.site.function != value.function {
      return Err("compiler facts access site containing function mismatch")
    }
    if Validate::validate(access, env) is Err(err) {
      return Err(err)
    }
  }
  for numeric in value.numeric_operations {
    if numeric.site.site.function != value.function {
      return Err("compiler facts numeric site containing function mismatch")
    }
    if Validate::validate(numeric, env) is Err(err) {
      return Err(err)
    }
  }
  Ok(())
}

///|
pub impl Validate for OptimizationGoal with fn validate(_, _) {
  Ok(())
}

///|
pub impl Validate for HintTarget with fn validate(value, env) {
  match value {
    FunctionHintTarget(function) =>
      validate_compiler_fact_function_index(function, env)
    SiteHintTarget(site) => Validate::validate(site, env)
  }
}

///|
pub impl Validate for OptimizationHint with fn validate(value, env) {
  match value {
    HotnessHint(target, temperature) => {
      if Validate::validate(target, env) is Err(err) {
        return Err(err)
      }
      Validate::validate(temperature, env)
    }
    InlineHint(target, policy) => {
      if Validate::validate(target, env) is Err(err) {
        return Err(err)
      }
      Validate::validate(policy, env)
    }
    GoalHint(target, goal) => {
      if Validate::validate(target, env) is Err(err) {
        return Err(err)
      }
      Validate::validate(goal, env)
    }
    BranchProbabilityHint(site, probability) => {
      if probability > 65535U {
        return Err("compiler facts branch probability exceeds 65535")
      }
      Validate::validate(site, env)
    }
    EstimatedLoopTripsHint(site, _) => Validate::validate(site, env)
    LikelyCallTargetHint(site, function, confidence) => {
      if confidence > 65535U {
        return Err("compiler facts call target confidence exceeds 65535")
      }
      if Validate::validate(site, env) is Err(err) {
        return Err(err)
      }
      validate_compiler_fact_function_index(function, env)
    }
    LikelyHeapTypeHint(site, type_, confidence) => {
      if confidence > 65535U {
        return Err("compiler facts heap type confidence exceeds 65535")
      }
      if Validate::validate(site, env) is Err(err) {
        return Err(err)
      }
      Validate::validate(type_, env)
    }
  }
}

///|
fn module_has_compiler_fact_metadata(module_ : Module) -> Bool {
  if module_.compiler_fact_custom_section is Some(_) {
    return true
  }
  module_.custom_secs.any(fn(custom) {
    let CustomSec(name, _) = custom
    name == Name::new("compiler.facts")
  })
}

///|
priv struct CompilerFactOpcodeIndex {
  imported_functions : Int32
  defined_sites : Array[Array[(UInt32, Instruction)]]
}

///|
fn compiler_fact_opcode_index(
  module_ : Module,
  imported_functions : Int32,
) -> Result[CompilerFactOpcodeIndex, String] {
  match @binary.compiler_fact_module_opcode_sites(module_) {
    Ok(defined_sites) => Ok({ imported_functions, defined_sites, })
    Err(err) =>
      Err(
        "compiler facts could not index encoded function instructions: \{err}",
      )
  }
}

///|
fn compiler_fact_instruction_at(
  index : CompilerFactOpcodeIndex,
  site : CodeSite,
  kind : String,
) -> Result[Instruction, String] {
  let defined = site.function.reinterpret_as_int() - index.imported_functions
  if defined < 0 || defined >= index.defined_sites.length() {
    return Err(
      "compiler facts instruction site has no defined function body: kind=\{kind} function=\{site.function} offset=\{site.offset}",
    )
  }
  for candidate in index.defined_sites[defined] {
    if candidate.0 == site.offset {
      return Ok(candidate.1)
    }
  }
  Err(
    "compiler facts instruction site does not exist: kind=\{kind} function=\{site.function} offset=\{site.offset}",
  )
}

///|
fn compiler_fact_block_result_arity(
  block_type : BlockType,
  env : Env,
) -> UInt32? {
  match env.expand_blocktype(block_type) {
    Ok((_, results)) => Some(results.length().reinterpret_as_uint())
    Err(_) => None
  }
}

///|
fn compiler_fact_instruction_result_arity(
  instr : Instruction,
  env : Env,
) -> UInt32? {
  match instr {
    Call(function) =>
      match env.get_functype_by_funcidx(function) {
        Some(FuncType(_, results)) =>
          Some(results.length().reinterpret_as_uint())
        None => None
      }
    CallIndirect(type_, _) | CallRef(type_) =>
      match env.resolve_functype(type_) {
        Some(FuncType(_, results)) =>
          Some(results.length().reinterpret_as_uint())
        None => None
      }
    Block(type_, _)
    | Loop(type_, _)
    | If(type_, _, _)
    | Try(type_, _, _, _)
    | TryTable(type_, _, _) => compiler_fact_block_result_arity(type_, env)
    Unreachable
    | Nop
    | Throw(_)
    | ThrowRef
    | Rethrow(_)
    | Br(_)
    | BrIf(_)
    | BrTable(_, _)
    | Return
    | ReturnCall(_)
    | ReturnCallIndirect(_, _)
    | ReturnCallRef(_)
    | LocalSet(_)
    | GlobalSet(_)
    | TableSet(_)
    | I32Store(_)
    | I64Store(_)
    | F32Store(_)
    | F64Store(_)
    | I32Store8(_)
    | I32Store16(_)
    | I64Store8(_)
    | I64Store16(_)
    | I64Store32(_)
    | AtomicFence(_)
    | I32AtomicStore(_, _)
    | I64AtomicStore(_, _)
    | I32AtomicStore8(_, _)
    | I32AtomicStore16(_, _)
    | I64AtomicStore8(_, _)
    | I64AtomicStore16(_, _)
    | I64AtomicStore32(_, _)
    | StructSet(_, _)
    | ArraySet(_)
    | ArrayStore(_, _, _)
    | ArrayFill(_)
    | ArrayCopy(_, _)
    | ArrayInitData(_, _)
    | ArrayInitElem(_, _)
    | MemoryInit(_, _)
    | DataDrop(_)
    | MemoryCopy(_, _)
    | MemoryFill(_)
    | TableInit(_, _)
    | ElemDrop(_)
    | TableCopy(_, _)
    | TableFill(_)
    | V128Store(_)
    | V128Store8Lane(_, _)
    | V128Store16Lane(_, _)
    | V128Store32Lane(_, _)
    | V128Store64Lane(_, _)
    | Drop => Some(0U)
    _ => Some(1U)
  }
}

///|
fn compiler_fact_is_call_opcode(instr : Instruction) -> Bool {
  match instr {
    Call(_)
    | CallIndirect(_, _)
    | CallRef(_)
    | ReturnCall(_)
    | ReturnCallIndirect(_, _)
    | ReturnCallRef(_) => true
    _ => false
  }
}

///|
fn compiler_fact_is_allocation_opcode(instr : Instruction) -> Bool {
  match instr {
    StructNew(_)
    | StructNewDefault(_)
    | StructNewDesc(_)
    | StructNewDefaultDesc(_)
    | ArrayNew(_)
    | ArrayNewDefault(_)
    | ArrayNewFixed(_, _)
    | ArrayNewData(_, _)
    | ArrayNewElem(_, _)
    | ContNew(_)
    | RefI31 => true
    _ => false
  }
}

///|
fn compiler_fact_is_access_opcode(instr : Instruction) -> Bool {
  match instr {
    TableGet(_)
    | TableSet(_)
    | I32Load(_)
    | I64Load(_)
    | F32Load(_)
    | F64Load(_)
    | I32Load8S(_)
    | I32Load8U(_)
    | I32Load16S(_)
    | I32Load16U(_)
    | I64Load8S(_)
    | I64Load8U(_)
    | I64Load16S(_)
    | I64Load16U(_)
    | I64Load32S(_)
    | I64Load32U(_)
    | I32Store(_)
    | I64Store(_)
    | F32Store(_)
    | F64Store(_)
    | I32Store8(_)
    | I32Store16(_)
    | I64Store8(_)
    | I64Store16(_)
    | I64Store32(_)
    | StructGet(_, _)
    | StructGetS(_, _)
    | StructGetU(_, _)
    | StructSet(_, _)
    | ArrayGet(_)
    | ArrayGetS(_)
    | ArrayGetU(_)
    | ArraySet(_)
    | ArrayLoad(_, _, _)
    | ArrayStore(_, _, _) => true
    _ => false
  }
}

///|
fn compiler_fact_is_numeric_opcode(instr : Instruction) -> Bool {
  match instr {
    I32Add
    | I32Sub
    | I32Mul
    | I32DivS
    | I32DivU
    | I32RemS
    | I32RemU
    | I64Add
    | I64Sub
    | I64Mul
    | I64DivS
    | I64DivU
    | I64RemS
    | I64RemU => true
    _ => false
  }
}

///|
fn compiler_fact_provenance_suffix(provenance : SourceProvenance?) -> String {
  match provenance {
    Some(value) =>
      " body_id=\{value.body_id} expression_id=\{value.expression_id}"
    None => ""
  }
}

///|
fn validate_compiler_fact_value_site(
  index : CompilerFactOpcodeIndex,
  value : ValueSite,
  env : Env,
  kind : String,
  provenance : SourceProvenance?,
) -> Result[Instruction, String] {
  let instr = match compiler_fact_instruction_at(index, value.site, kind) {
    Ok(instr) => instr
    Err(err) => return Err(err + compiler_fact_provenance_suffix(provenance))
  }
  let arity = match compiler_fact_instruction_result_arity(instr, env) {
    Some(arity) => arity
    None =>
      return Err(
        "compiler facts could not resolve instruction result arity" +
        compiler_fact_provenance_suffix(provenance),
      )
  }
  if value.result >= arity {
    return Err(
      "compiler facts value result index out of range: function=\{value.site.function} offset=\{value.site.offset} result=\{value.result} result_arity=\{arity}" +
      compiler_fact_provenance_suffix(provenance),
    )
  }
  Ok(instr)
}

///|
fn validate_compiler_fact_operand_sites(
  index : CompilerFactOpcodeIndex,
  operand : FactOperand,
  env : Env,
) -> Result[Unit, String] {
  match operand {
    ValueOperand(SiteValue(site)) =>
      match
        validate_compiler_fact_value_site(
          index,
          site,
          env,
          "relation-value",
          None,
        ) {
        Ok(_) => Ok(())
        Err(err) => Err(err)
      }
    _ => Ok(())
  }
}

///|
fn validate_compiler_fact_body_sites(
  facts : OptimizationFactsSec,
  index : CompilerFactOpcodeIndex,
  env : Env,
) -> Result[Unit, String] {
  for body in facts.bodies {
    let defined = body.function.reinterpret_as_int() - index.imported_functions
    if defined < 0 || defined >= index.defined_sites.length() {
      return Err(
        "compiler facts function body record has no defined function body: function=\{body.function}",
      )
    }
    for site in body.sites {
      if validate_compiler_fact_value_site(
          index,
          site.value,
          env,
          "expression",
          site.provenance,
        )
        is Err(err) {
        return Err(err)
      }
    }
    for relation in body.relations {
      if compiler_fact_instruction_at(index, relation.at, "relation-scope")
        is Err(err) {
        return Err(err)
      }
      if validate_compiler_fact_operand_sites(index, relation.left, env)
        is Err(err) {
        return Err(err)
      }
      if validate_compiler_fact_operand_sites(index, relation.right, env)
        is Err(err) {
        return Err(err)
      }
    }
    for call in body.calls {
      let instr = match compiler_fact_instruction_at(index, call.site, "call") {
        Ok(instr) => instr
        Err(err) =>
          return Err(err + compiler_fact_provenance_suffix(call.provenance))
      }
      if !compiler_fact_is_call_opcode(instr) {
        return Err(
          "compiler facts call fact is attached to a non-call opcode" +
          compiler_fact_provenance_suffix(call.provenance),
        )
      }
      match call.direct_target {
        Some(target) => {
          let target_idx = FuncIdx::new(target)
          match instr {
            CallRef(type_) | ReturnCallRef(type_) => {
              let target_type = match
                env.get_functypeidx_by_funcidx(target_idx) {
                Some(type_) => type_
                None =>
                  return Err(
                    "compiler facts exact-call-target has no target function type" +
                    compiler_fact_provenance_suffix(call.provenance),
                  )
              }
              if !Match::matches(
                  ExternType::func(target_type),
                  ExternType::func(type_),
                  env,
                ) {
                return Err(
                  "compiler facts exact-call-target signature is incompatible: function=\{call.site.function} offset=\{call.site.offset} target=\{target}" +
                  compiler_fact_provenance_suffix(call.provenance),
                )
              }
            }
            Call(actual) | ReturnCall(actual) if actual != target_idx =>
              return Err(
                "compiler facts exact-call-target contradicts direct call target" +
                compiler_fact_provenance_suffix(call.provenance),
              )
            _ => ()
          }
        }
        None => ()
      }
    }
    for branch in body.branches {
      if compiler_fact_instruction_at(index, branch.site, "branch") is Err(err) {
        return Err(err)
      }
    }
    for loop_ in body.loops {
      let instr = match
        compiler_fact_instruction_at(index, loop_.site, "loop") {
        Ok(instr) => instr
        Err(err) => return Err(err)
      }
      if !(instr is Loop(_, _)) {
        return Err("compiler facts loop fact is attached to a non-loop opcode")
      }
    }
    for allocation in body.allocations {
      let instr = match
        validate_compiler_fact_value_site(
          index,
          allocation.site,
          env,
          "allocation",
          allocation.provenance,
        ) {
        Ok(instr) => instr
        Err(err) => return Err(err)
      }
      if !compiler_fact_is_allocation_opcode(instr) {
        return Err(
          "compiler facts allocation fact is attached to a non-allocation opcode" +
          compiler_fact_provenance_suffix(allocation.provenance),
        )
      }
    }
    for access in body.accesses {
      let instr = match
        compiler_fact_instruction_at(index, access.site, "access") {
        Ok(instr) => instr
        Err(err) => return Err(err)
      }
      if !compiler_fact_is_access_opcode(instr) {
        return Err(
          "compiler facts access fact is attached to an incompatible opcode",
        )
      }
    }
    for numeric in body.numeric_operations {
      let instr = match
        validate_compiler_fact_value_site(
          index,
          numeric.site,
          env,
          "numeric-operation",
          numeric.provenance,
        ) {
        Ok(instr) => instr
        Err(err) => return Err(err)
      }
      if !compiler_fact_is_numeric_opcode(instr) {
        return Err(
          "compiler facts numeric operation fact is attached to an incompatible opcode",
        )
      }
    }
  }
  for hint in facts.hints {
    match hint {
      HotnessHint(SiteHintTarget(site), _)
      | InlineHint(SiteHintTarget(site), _)
      | GoalHint(SiteHintTarget(site), _)
      | BranchProbabilityHint(site, _)
      | EstimatedLoopTripsHint(site, _)
      | LikelyCallTargetHint(site, _, _) =>
        if compiler_fact_instruction_at(index, site, "hint") is Err(err) {
          return Err(err)
        }
      LikelyHeapTypeHint(site, _, _) =>
        if validate_compiler_fact_value_site(
            index,
            site,
            env,
            "heap-type-hint",
            None,
          )
          is Err(err) {
          return Err(err)
        }
      _ => ()
    }
  }
  Ok(())
}

///|
fn validate_compiler_fact_module_section(
  module_ : Module,
  env : Env,
  imported_functions : Int32,
) -> Result[Unit, String] {
  for custom in module_.custom_secs {
    let CustomSec(name, _) = custom
    if name == Name::new("compiler.facts") {
      return Err(
        "reserved compiler.facts custom section must use Module.compiler_fact_custom_section",
      )
    }
  }
  match module_.compiler_fact_custom_section {
    Some(section) => {
      if Validate::validate(section, env) is Err(err) {
        return Err(err)
      }
      let index = match
        compiler_fact_opcode_index(module_, imported_functions) {
        Ok(index) => index
        Err(err) => return Err(err)
      }
      validate_compiler_fact_body_sites(section.facts, index, env)
    }
    None => Ok(())
  }
}