///|
/// The dependency specifications for inference: the SDK, then the distribution's own dependencies, which
/// `@typing.infer` adds itself.
fn inference_deps(
  file : @ir.IRFile,
) -> Map[@ir.PackageName, @ir.PackageSpecification] {
  let numeric_source = match file.distribution {
    Library(_, dependencies, _) => {
      let source_package = @ir.Path::parse("morphir/moonbit-source") catch {
        _ => panic()
      }
      match dependencies.get(source_package) {
        Some(spec) =>
          spec.modules.contains(
            @ir.Path::parse("numeric") catch {
              _ => panic()
            },
          )
        None => false
      }
    }
    _ => false
  }
  Map::from_array([
    (
      @sdk_spec.sdk_package_name(),
      if numeric_source {
        @sdk_spec.numeric_sdk_specification() catch {
          _ => panic()
        }
      } else {
        @sdk_spec.sdk_specification()
      },
    ),
  ])
}

///|
/// Runs inference over `unit`. In Check the unit is unchanged. In Fill the filled file replaces the distribution;
/// with `checkpoint`, through `apply_rewrite` with `fill_lineage`, so that metadata stays on its logical nodes,
/// and with `MetadataPolicy::no_derived`, so that a written type gets no metadata of its own.
pub fn infer_unit(
  unit : @data.IRUnit,
  mode : @typing.Mode,
  checkpoint : Bool,
) -> @data.IRUnit raise {
  let filled = @typing.infer(
    unit.distribution,
    inference_deps(unit.distribution),
    mode,
  )
  guard mode is Fill else { return unit }
  if !checkpoint {
    return { ..unit, distribution: filled, }
  }
  @data.apply_rewrite(
    unit,
    { distribution: filled, lineage: Some(@data.fill_lineage(unit, filled)), },
    "inference",
    "inference",
    @data.MetadataPolicy::no_derived(),
  )
}

///|
/// The mode of a `pipeline.inference` value. `None` is `off`.
fn inference_mode(value : String) -> @typing.Mode? raise {
  match value {
    "off" => None
    "fill" => Some(Fill)
    "check" => Some(Check)
    _ =>
      raise @configuration.Invalid(
        "pipeline.inference must be 'off', 'fill' or 'check', not '" +
        value +
        "'",
      )
  }
}