///|
/// Explicit extension admission. No classic SDK or arbitrary host binding fallback.
fn source_string_reference(
  ctx : Lowering,
  name : @ir.FQName,
  expected : Lowered,
  context : String,
) -> String raise {
  let text = Atom(Scalar::Text)
  let int = Atom(Scalar::Integer)
  let int32 = Atom(Scalar::SignedInt32)
  let (inputs, output, function) = match name.to_canonical() {
    "morphir/moonbit-source:string#require-valid" =>
      ([text, int], text, "require_valid")
    "morphir/moonbit-source:string#append-bounded" =>
      ([text, text, int], text, "append_bounded")
    "morphir/moonbit-source:string#length-int32" =>
      ([text, int], int32, "length_int32")
    "morphir/moonbit-source:string#slice-owned" =>
      ([text, int32, int32, int], text, "slice_owned")
    _ =>
      raise Invalid(
        "moonbit.unsupported_source_extension",
        context,
        name.to_canonical(),
      )
  }
  let mut signature = output
  for i = inputs.length() - 1; i >= 0; i = i - 1 {
    signature = Arrow(inputs[i], signature)
  }
  if expected != signature {
    raise Invalid(
      "moonbit.source_extension_signature",
      context,
      name.to_canonical(),
    )
  }
  ctx.imports["source_string"] = true
  ctx.imports["integer"] = true
  if output == int32 || inputs.contains(int32) {
    ctx.imports["int"] = true
    ctx.imports["source_string_index"] = true
  }
  let arguments = inputs.mapi((i, ty) => ("a" + i.to_string(), ty))
  let count = "a" + (inputs.length() - 1).to_string()
  let call = "{ guard @integer.to_int(" +
    count +
    ") is Some(limit) else { raise @sdk.OutOfRange(\"MoonBit source String limit\") }; @source_string." +
    function +
    "(" +
    (if inputs.length() == 2 {
      "a0, limit"
    } else if inputs.length() == 3 {
      "a0, a1, limit"
    } else {
      "a0, a1, a2, limit"
    }) +
    ") }"
  curry_source(ctx, arguments, output, call)
}