Skip to content

Runtime Module Contracts

Every target maps one source module to the logical RUNTIME module. That module supplies reserved declarations used to implement language operations; application modules cannot redeclare them. Application source normally uses ASSERT, PANIC, NEW, DELETE, global objects, and VAR PER_THREAD while the compiler connects those features to the contracts on this page.

Diagnostics

Runtime assertion and panic handling use fixed named-parameter signatures:

::ASSERT_FAIL FUNCTION(
  @expr STRING_CONSTANT,
  @file SZ,
  @line SZ,
  @column SZ,
  @tag CONST->STRING_CONSTANT
)
{
  UNIMPLEMENTED;
}

::PANIC FUNCTION(
  @message STRING_CONSTANT,
  @file SZ,
  @line SZ,
  @column SZ
)
{
  UNIMPLEMENTED;
}

ASSERT_FAIL receives the source expression text, source-file index, line, column, and optional assertion tag. PANIC receives the message and source position. These functions must not return to the failed source path.

On Windows native targets, ::CHECK_STACK is an architecture-specific ASM_PROCEDURE used for stack probing. A Windows LLVM output that needs the probe requires that declaration in RUNTIME.

Exception handling

The runtime supplies EXCEPTION_PTR, CURRENT_EXCEPTION(), and THROW_EXCEPTION_PTR(@exception handle), together with exception-object ownership and native propagation. The reserved handle type has a postfix ?? presence test, IS_OUT_OF_MEMORY() sentinel detection, and identity equality; it exposes no reference-count query.

Native targets use the Quxlang unwinder. Exception storage allocation failure selects the permanent UNWIND_OUT_OF_MEMORY sentinel. Compiler-provided POLYMORPHIC_BASE supplies the universal polymorphic view independently of the runtime module's declarations. Application semantics and current target limits are specified in Exception Handling.

Default allocator

NEW and DELETE resolve typed single-object storage through ::DEFAULT_ALLOCATOR:

::DEFAULT_ALLOCATOR STRUCT
{
  ::allocate TEMPLATE(@T TYPE AUTO)
    FUNCTION(): ->TYPED_STORAGE(T)
  {
    UNIMPLEMENTED;
  }

  ::dealloc TEMPLATE(@T TYPE AUTO)
    FUNCTION(@ptr ->TYPED_STORAGE(T))
  {
    UNIMPLEMENTED;
  }
}

The allocator returns storage rather than a live T; the language operation performs construction after allocation and destruction before deallocation. A runtime can select among compile-time, native, and managed implementations with RUNTIME CONSTEXPR, RUNTIME NATIVE, and target predicates. Native allocators provide native storage, while the JVM path uses managed object-storage builtins.

A runtime library may also expose allocate_multiple and dealloc_multiple members returning and accepting =>>TYPED_STORAGE(T), plus explicit @size/@align overloads returning ALIGNED_STORAGE(size, align). Those are ordinary runtime-library APIs; the typed allocate#T and dealloc#T members above are the reserved single-object language integration points.

Global initialization guards

The runtime module alone may name INITGUARD. It supplies three operations for thread-safe initialization of nontrivial global objects:

::INITGUARD_TRY_ACQUIRE FUNCTION(@guard MUT& INITGUARD): BOOL
{
  UNIMPLEMENTED;
}

::INITGUARD_COMPLETE FUNCTION(@guard MUT& INITGUARD)
{
  UNIMPLEMENTED;
}

::INITGUARD_ABORT FUNCTION(@guard MUT& INITGUARD)
{
  UNIMPLEMENTED;
}

INITGUARD_TRY_ACQUIRE returns TRUE to the caller responsible for initialization and FALSE once the object is already initialized. Contending callers wait for that decision. The successful path completes the guard after construction; a failed or unwinding path aborts it so a later attempt can retry.

These declarations are runtime hooks. Application source declares the global object and does not manipulate its guard directly.

Per-thread initialization and destruction

Nontrivial VAR PER_THREAD objects use the corresponding per-thread contract:

::thread_destructor_node STRUCT
{
  .next VAR MUT->thread_destructor_node;
  .deinitializer VAR CONST->PROCEDURE();
  .guard VAR MUT->INITGUARD;
}

::THREAD_INITGUARD_TRY_ACQUIRE
  FUNCTION(@guard MUT& INITGUARD): BOOL
{
  UNIMPLEMENTED;
}

::THREAD_DESTRUCTOR_REGISTER FUNCTION(
  @node MUT& thread_destructor_node,
  @guard MUT& INITGUARD,
  @deinitializer CONST->PROCEDURE()
)
{
  UNIMPLEMENTED;
}

The per-thread acquire operation distinguishes uninitialized, initialized, recursively initializing, and destroyed states. After successful construction, registration adds the object's deinitializer to the current thread. The runtime drains registered destructors in reverse completion order and marks their guards destroyed.

The current runtime also reserves ::THREAD_RUNTIME_START and ::THREAD_FINISH as the entry and exit boundary for a Quxlang-managed thread. They expose operations that establish thread identity and drain per-thread lifetime before native thread teardown; a platform integration may call that boundary when it creates or finishes a managed thread.

::THREAD_RUNTIME_START FUNCTION(@identity MUT->thread_identity)
{
  UNIMPLEMENTED;
}

::THREAD_FINISH FUNCTION()
{
  UNIMPLEMENTED;
}

Startup and test entrypoints

::PROGRAM_START, ::POST_DETECT, and ::UNIT_TEST_MAIN are also reserved to the runtime module. Their signatures, stepping tables, and dispatch order are documented on Program startup and runtime hooks. Runtime CPU detector declarations use the reserved ::DETECT_<capability> names described on CPU capabilities and steppings.

See NEW and DELETE, Thread-Local Variables, and Diagnostics and explicit failure.

Test and arithmetic failures

The runtime owns the TEST_FAILED and ARITHMETIC_OVERFLOW exception types. TEST_EXPECT calls THROW_TEST_FAILED(@EXPR text, @TAG tag) with STRING_CONSTANT arguments. Checked arithmetic calls THROW_ARITHMETIC_OVERFLOW(); failed assumed-in-range arithmetic with checks enabled calls PANIC_ARITHMETIC_OVERFLOW(). These functions terminate the failed path by throwing or panicking as indicated. The exception types and hooks are reserved runtime declarations.