Calling Conventions

RukaLang has two internal call boundaries that matter for compiler work:

  1. MIR-level ownership and representation conventions.
  2. Backend ABI conventions (Rust emission and direct WASM emission).

This page documents the current rules and points to the rustdoc pages where those rules are encoded.

MIR-Level Contract

MIR stores both ownership mode and runtime/storage representation. Together, these define how a value is passed at call boundaries.

Current boundary semantics:

  • View parameters are source-level read-only access; MutBorrow parameters are mutable borrow access; Owned parameters are value transfer parameters.
  • MirParamBinding::source_repr and MirParamBinding::local_repr define source boundary shape vs lowered local shape.
  • MirParamBinding::requires_materialization marks parameters where the lowered local representation differs from the source boundary representation.
  • MirParamBinding::materializes_view_from_owned marks the current materialized view case where a source owned value is projected into a view-local boundary.
  • MirCallArgBinding::requires_deref_read marks call arguments that need a load/read from a place local before value passing.
  • Boundary coercions that require runtime checks currently lower at MIR call sites with explicit CollectionLen comparisons and CallExtern("std::panic") on failure.

Core MIR container docs:

Rust Backend Convention

Rust codegen follows Rust-level references/values directly:

Behavior by argument mode:

  • Borrowed (view call arg mode) passes &T (or &*place when the local is place-shaped).
  • MutableBorrow passes &mut T (or &mut *place for mutable place locals).
  • OwnedMove passes by move; place reads are cloned from dereference.
  • OwnedCopy passes a cloned value; place reads are cloned from dereference.

For slice place reads copied into owned values, Rust emission uses .to_vec() instead of (*place).clone().

Entry points:

WASM Backend Convention

The direct WASM backend uses a strict, explicit ABI with normalized value types and out-slot returns for aggregate return values.

Signature shaping is defined by:

Current value mapping:

  • i64 lowers to i64.
  • Most other runtime values lower to pointer-sized i32 handles (including strings, pointers, arrays, tuples, structs, enums, slices, and references).

Return conventions:

  • Non-aggregate mutable-borrow params use an inout convention: argument value in, updated value returned as an extra WASM result.
  • Scalar-like returns use normal WASM result values.
  • Aggregate returns (currently tuple/struct/slice) use an out-slot pointer parameter inserted at parameter index 0 and no WASM result.
  • Aggregate temporaries are placed on the runtime shadow stack when required.

For the full shadow-stack lifecycle and memory layout, see WASM Shadow Stack.

Call lowering is defined by:

The call-argument strategy is selected from local representation and arg mode:

  • Pass-through by value for normal value locals and mutable-borrow pointer ABI args.
  • Dereference-load for mutable-borrow inout args when the source local is a non-passthrough place.
  • Dereference-load to match callee ABI when reading from place-shaped locals.

Backend entry points:

Runtime WASM ABI Surface

Runtime-call ABI metadata is centralized in ruka_runtime:

The coercion runtime trap path is exposed as std::panic in this descriptor table.

This descriptor table is what the WASM backend linker and call lowering use for symbol resolution and runtime signature checks.