Compiler, platform and library linking

← Architecture

Platform declarations and value representations

Built-in Guest Kotlin declarations are authored in guest-platform as separately versioned modules. Its build produces only the canonical base platform bundle consumed by both compilation and IDE analysis. Optional integrations own and build their Guest declarations as addon bundles against that base; addons/create, addons/sable and addons/propulsion own independent integration bundles. The public ru.lazyhat.compukters.addon Gradle plugin resolves an isolated builder, base bundle, Minecraft-independent host API, and thin target adapter from one independently versioned SDK release by Maven coordinate. The SDK version changes with its public compatibility boundary rather than every Compukters release. The plugin derives the wire schema and bindings from Guest Kotlin declarations, validates a checked-in selector lock, generates the typed JVM host contract, and packages the resulting .cagb in the independent addon JAR. platform-bundle owns the base bundle model, codec, module graph, and default imports; platform-k2 exposes that metadata to K2 without making the K2 implementation part of the platform format.

Default arguments and loops

Constant Int and qualified enum-entry defaults cross this bundle explicitly; compiler lowering materializes an omitted platform argument without a JVM-style mask dispatcher. The bundle also represents explicit null defaults and array-receiver-size defaults. Project function defaults use admitted Guest expressions lowered at the call site. Receivers and explicit arguments are evaluated and saved before temporarily binding callee and default-owner parameters. Missing arguments then evaluate in parameter order, with bindings and generic substitutions restored after nested calls. Inherited defaults use their declaring symbols while the final call retains virtual dispatch. Default-only generic helper calls participate in specialization discovery. Recursively expanding the same source default produces a bounded target diagnostic instead of unbounded compiler recursion. None of these source expressions are encoded into the platform bundle or require a new VM instruction.

Default-only function-reference adaptations compile the checked K2 adapter body with the ordinary function compiler. A bound leading adapter parameter maps to a closure capture and is excluded from invocation arguments; its source expression evaluates when the closure is constructed. Adapter calls therefore share scoped default evaluation and dispatch with direct calls, without mask dispatchers or a new artifact representation. Unsupported signature conversions retain target diagnostics.

Before capture discovery, GuestLocalSpecialization materializes concrete generic local declarations and generic top-level lexical owners, including their generic callers. Anonymous callbacks and callable-reference adapters trigger the same owner materialization as named local declarations; otherwise their signatures and captures could retain unsubstituted owner type parameters. It shares typed IR deep-copying with inline specialization: each owner copy remaps its lexical symbols and substitutes only that owner’s type parameters, leaving nested generic parameters for their own specialization. Recursive calls reuse cached concrete variants; calls that change type arguments discover distinct variants. This gives closures and mutable cells concrete per-owner layouts before managed types are assigned. The lexical pass shares the 256 function-variant budget with ordinary emitter specialization and bounds expansion depth to 64. testKotlinGenericLocalFunctionsVmConformance exercises these paths without a runtime or ABI change.

prepareGuestFunctionBodies returns the concrete function/class instances and executable lexical roots before capture discovery. Function/class dependencies and copied local declarations iterate to a fixed point. A generic member instance retains its source declaration for dispatch/signature identity and a separate symbol-remapped concrete declaration for executable bodies. The shared IR copier substitutes owner parameters while preserving nested local parameters until lexical specialization. Copies retain a generic-equality origin so Float/Double specializations keep boxed NaN and signed-zero semantics; the collection value conformance also exercises generic class comparisons and floating map keys. Member bodies are cached per declaration and concrete class instance; recursive calls and member references retain the source dispatch symbol while returns target their concrete executable copy. Generic class discovery defers unresolved lexical types rather than treating them as concrete instances. testKotlinGenericClassCapturesVmConformance exercises owner retention, all primitive signatures, shared cells, dispatch and small-heap collection without a VM/ABI change.

Direct generic ordinary methods extend function-instance identity with both concrete method arguments and concrete owner arguments. One substitutor and the typed IR copier cover both parameter sets before lexical normalization and dependency discovery; canonical member symbols still own call lookup. Concrete method defaults share the same copied parameter and closure symbols as capture layout. Function discovery bounds method variants with the existing function budget. Artifact ordering groups all variants with their owner’s other methods, preserving contiguous nominal method ranges; concrete method arguments distinguish their names. Generic virtual/interface dispatch and generic callable references retain source diagnostics. testKotlinGenericMethodsVmConformance executes this path through the unchanged artifact and VM contract.

Generic ordinary class initialization uses one typed symbol copier per concrete owner for the primary constructor, class receiver, backing-field initializer expressions and init bodies. Original class, field and constructor identities continue to own layout and lookup; copied lexical symbols own execution and captures. Concrete initialization roots participate in dependency convergence, closure/cell discovery and literal/import collection. Constructor emission walks the original declaration order while reading its corresponding concrete expressions and blocks. Call-site defaults use the copied constructor parameters, so their captures match the concrete caller argument bindings. Local declarations inside initializers remain static local functions even when K2 gives them a class parent; they do not enter member dispatch tables. testKotlinGenericInitializerCapturesVmConformance verifies these paths without a runtime or ABI change.

Named local functions are discovered before closure layout. A fixed-point free-value analysis includes captures required by local callees and referenced helpers. Direct calls prepend hidden typed capture arguments; reassigned variables use the same capture cells as lambdas. Local declarations are static artifact functions, including declarations whose K2 parent is a class initializer. References use the existing managed closure adapters and retain captures across the enclosing return. No new instruction or artifact ABI is introduced. testKotlinLocalFunctionsVmConformance covers recursion, default arguments, all primitive capture families, nested helpers, constructor initialization and independent escaping cells.

Callable signature discovery also visits concrete constructor parameter and backing-field types before class layout. Constructor signatures and fields use the same generated callable interfaces as locals and ordinary function parameters. Field writes preserve the existing exact-signature policy, including default accessors, custom backing-field writes and body initializers. Generic owners substitute their concrete types; managed field references preserve closure/capture reachability without new runtime instructions. Callable invocation lowers arguments using the expected parameter types, including typed null arguments. testKotlinFunctionFieldsVmConformance exercises these paths.

Callable shape identity ignores outer reference nullability. Nullable-only local declarations participate in signature discovery, including recursively nested callable parameter/result types; nullable backing fields and parameters use the same generated interface. Exact signature checks admit typed null and reference nullability changes while still rejecting variance between different signatures. Constructor default evaluation scopes its concrete class type substitution before checking and compiling expressions. Safe invocation reuses normal K2 null branches and existing reference instructions. Safe Unit results use the canonical managed Unit singleton or null, while direct Unit calls retain their zero-slot result. testKotlinNullableCallbacksVmConformance executes callback replacement/reset under a 64 KiB heap, keeping an older alias live across 200 allocations of 12 KiB captured arrays with mutable capture cells. No artifact encoding, instruction or GC protocol change is introduced.

CovariantInterfaceBridges derives canonical concrete read views for supported declaration-site out interfaces during class discovery. The set depends on the specialization and its bounds, rather than consumer demand, preserving identical layouts when a trusted library owner is reused. Interface parent edges express reference and nested result covariance. Concrete implementations receive synthetic result-adapting methods for scalar, Unit and value-class results; managed value-class method tables share these generated methods. Interface results widened to Any receive reference forwarding methods because artifact interfaces cannot have a class parent. These methods reuse the normal function compiler and managed boxing/cast instructions. The original object, fields and direct methods retain their layouts. Parameters and callable result signatures must remain unchanged. Existing trusted collection bridges remain canonical. Depth/combination/method limits bound expansion, and library specialization validation retains one nominal owner. Sequence and generic-library conformance exercise this path through the unchanged artifact/VM contract.

Canonical IntRange and IntProgression declarations let K2 resolve ascending, descending, and stepped Int loops. The compiler recognizes only their canonical for shape and evaluates start, end, and step once into scalar registers; no range or iterator object enters the VM. It validates a positive step before iteration, calculates the next index in Long to avoid Int overflow, and retains a loop-header quota safepoint. Stored progressions and non-Int range loops use ordinary Guest library classes and iterators; the allocation-free optimization is specific to the recognized direct Int shape.

Text iteration uses the ordinary managed CharSequence.iterator / CharIterator library path, including String, StringBuilder and Guest implementations. It traverses UTF-16 units and allocates an iterator; there is no direct text-loop allocation optimization. The generated canonical runtime owns CharSequence and String’s interface methods; stdlib exports the canonical CharIterator base class once.

Direct primitive and reference-array for loops follow the same canonical-shape rule: the compiler snapshots the array reference, loads its length, and reads each element with existing array instructions at the current index. The loop header retains its quota safepoint; no iterator object is created. A source variable may be reassigned without changing the traversed array, while writes to that array’s elements remain visible to later iterations. Reference-array loops share the indexed-read path for concrete generic element types and nullable/value-class storage; non-null value-class elements unbox before the body. Array.iterator is a structural external declaration consumed by canonical-loop recognition; stored iterator calls remain unsupported. Loop jumps select an active loop by K2 IR identity, so labels also work across different loop forms. The target records its enclosing finally depth and condition or advancement point. Only cleanup scopes crossed by the jump execute; cleanup can replace it with another exit. Optimized for loops emit an advancement block only when the body can fall through or has a continue targeting that loop.

Library compilation exports every enum cache together with its concrete collection read views. Its class specialization budget is 1024, separate from the existing 256-class project budget; the generic function budget remains 256. This keeps whole-platform exports bounded without reducing the immutable list contract or increasing user-program expansion limits.

Value representations

Scalar value classes retain their scalar representation for direct typed calls. Nullable/Any/interface boundaries and reference collection storage use ordinary nominal managed wrapper classes with payload fields matching the value layout. Canonical platform and addon libraries export the wrapper type and payload field through existing type/field links; consumers import these instead of introducing a second nominal identity. Source value classes own their application-local wrappers. Equality checks nominal type and payload, hashing uses the payload, and virtual toString dispatch preserves default or custom rendering. Wrappers carry the value class’s interface parents. The established direct scalar fast path applies to single Int/Boolean/Char layouts; other payload layouts use nominal inline values. Concrete interface implementations get virtual bridges that read the scalar receiver and call its static implementation; an underlying-property getter returns that payload directly. Inherited interface defaults use existing interface dispatch, including concrete generic specializations. Scalar boxing uses ordinary managed classes and the established ABI. Multi-field and other non-scalar value layouts use ABI 1.15’s nominal inline types and compact frame components for direct calls and locals. Reference contexts box flattened payloads through ordinary managed fields; nested source properties retain source grouping for hashing/text. Libraries export the direct inline type separately from its managed class and export every payload field. Consumers import both identities, including concrete generic layouts; linker signature matching resolves nominal inline identities across modules. Bound method references store the boxed receiver in their closure environment and reconstruct its direct layout before static invocation. Constructor references reuse constructor lowering, including argument layout and initializer execution. References to stateless trusted platform object methods use the same receiver-free signature as direct calls, so factories such as Matrix4f::perspective do not capture an absent runtime companion instance. Managed object and companion methods retain their ordinary receiver capture. Compiler and IDE sessions share CompuktersLanguageVersionSettings so native multi-field declarations receive consistent frontend analysis.

Nullable String and supported Guest class source references use the artifact’s existing nullable reference types and Null instruction. K2’s safe-call and Elvis branches lower through ordinary verified control flow, with reference casts when a non-null branch joins a nullable result. All twelve nullable primitives use their distinct nominal managed boxes or null. Safe casts and non-null assertions on value classes preserve their direct layout; see the language contract for the tested boundary.

Generic-array storage specialization

Array discovery runs after concrete generic function/class substitution. Each admitted Array<T> has a nominal application array type; non-null primitive elements select the same GuestPrimitive scalar and storage width as dedicated primitive arrays. This preserves Array<Int> versus IntArray type identity while reusing allocation, indexed access, loops and bulk-copy instructions. Nullable and heterogeneous arrays retain nominal reference element storage. Array-to-Any transitions preserve the array reference; scalar-to-reference transitions use the existing primitive boxes.

Copies retaining compatible storage use ArrayCopy. Resized generic copies return Array<T?>, so dense primitive sources use a charged scalar-load/box/store loop. The same conversion handles copyInto to nullable and Any destinations. Entire source/destination ranges are checked before writes with subtractions only after non-negative endpoint checks, avoiding overflow and preserving empty-range errors. Scalar/reference layouts cannot alias; compatible layouts keep VM overlap handling. Loop headers and allocations use ordinary safepoints and GC roots. Existing artifact encoding and runtime ABI are unchanged.

Reference-array initialization

The canonical Array<T>(size, init) constructor is lowered with the same existing allocation and charged index loop as primitive-array initializers. Size and callback expressions evaluate before allocation; NewArray enforces negative-size errors before the first callback invocation. Function-interface calls produce each value in ascending index order, and value-class results are boxed at the reference-array storage boundary. Concrete generic container specializations retain their callback and element shapes; bare type-parameter array construction retains K2’s reification requirement, and reified inline functions remain outside the Guest subset. Allocation, exceptions, suspension and GC use the ordinary VM mechanisms; Kotlin does not run the initializer on the host.

Compilation, specialization and linking

The trusted server JVM owns compiler scheduling, diagnostics, artifact production, and the server-global content-addressed cache. The Rust machine validates guest paths, captures an immutable source snapshot plus filesystem preconditions, and suspends only the requesting foreground process. The server tick submits and polls bounded compiler work without waiting on the worker or cache. On completion Rust re-verifies the complete artifact and atomically installs it only if the source and output preconditions still match. Native buffers remain caller-owned and no Rust pointer enters Kotlin.

Compiler preparation and caching

Compiler service initialization runs in ServerStartingEvent, after the world filesystem store is opened. Ordinary computer creation and target-profile requests require an already prepared service and cannot lazily unpack worker resources on the server tick. Worker compilation remains asynchronous; this changes the preparation phase, not the compilation scheduler or wire formats.

Compiler worker launch defaults live in compiler-client’s compiler-specific WorkerLaunch: 512 MiB maximum heap and 256 MiB metaspace. Both NeoForge server and IDE callers use these defaults; the separate analysis worker keeps its own bounds. The 30-second compilation deadline is unchanged. A ship project using six addon APIs retained roughly 290 MiB after collection and compiled in 2–4 seconds at 512 MiB; the same project exhausted a 256 MiB heap. This is a host compiler resource bound, with no Guest memory, language, artifact or runtime ABI change.

Each pinned compiler adapter retains prepared library artifacts, symbol links and resolved source-library FIR for its most recent validated module selection, keyed by module names and content hashes. A different selection disposes that one entry; failed preparation is not retained. Module closure and addon payload identities are still checked on every request before reuse. Only admitted platform and addon modules enter this cache; project files and prospective user source libraries require their own source invalidation policy.

The retained entry owns a library-only compiler environment. Each request creates a fresh project/PSI environment and source session over the same admitted metadata symbols, preserving built-in type identity across module boundaries. FIR-to-IR uses K2’s reusable-FIR configuration when source libraries participate: ordinary KLIB conversion clears resolved declaration bodies and cannot safely consume the same FIR twice. IR, lowering/specialization state, source snapshots and request limits remain specific to each compilation. Canonical intrinsic registries index admitted handlers by full callable name and signature once; lookup still filters by source-module ownership or selected modules and rejects ambiguous matches. Each compilation session snapshots platform function links and indexes all overloads by symbol. These indexes contain immutable declaration metadata, not project IR or resolved calls. The worker closes the retained environment on shutdown. K2CompilerAdapterTest compares fresh and reused artifacts while alternating project declarations and generic specializations, including an unresolved reference to a previous project’s function and subsequent recovery.

The prepared selection also owns PreparedLibraryModules: semantic hashes and export indexes keyed by module instance identity for its immutable library artifacts. Specialization lookup preserves duplicate exports and canonical defining-library precedence. Each link supplements this metadata with request-local caches for project, pruned and relocated modules; those caches never grow the retained library entry. Mutable project files and future user libraries cannot reuse library metadata without an explicit invalidation policy. Replacing or closing the prepared selection releases its frontend and linker metadata together. Hash-only encoding checks debug sizes and source coordinates without materializing debug sections. Full artifact encoding still includes those sections. The compiler adapter uses LibraryModuleLinker.linkAndWrite to assemble, validate and serialize the executable once. The write applies the stricter of the ordinary linker ceiling and the request ceiling for every resource, retaining the request’s diagnostic budget. Ordinary link callers still receive a validated model and an exception for invalid linked artifacts; the serialized API returns writer diagnostics.

The packaged tooling payload is validated and published beneath <world>/compukters/compiler-worker; temporary worker state is kept separately beneath <world>/compukters/compiler-temp. Successful server artifacts are stored beneath <world>/compukters/compiler-cache/v1, shared by every computer and dimension in that server world, and reused after restart. Cache keys cover the ordered source snapshot, compiler and payload identity, target, selected trusted platform modules, and compilation limits. Cache publication and cache hits both pass the stateless Rust artifact verifier over FFM; runtime admission quotas are deliberately not part of cache validity.

Specialization and library ownership

KotlinProjectLowering coordinates body preparation, layouts, emission and artifact assembly. GuestPlatformLinking owns library symbol discovery and deterministic import binding. Discovery returns source-symbol links before layout; binding runs after value-class component discovery and allocates type, boxed/inline value, field and function imports in their existing canonical order. Its bound tables serve both artifact assembly and function emission, including imported payload fields. FunctionCompiler receives the library dependencies as GuestFunctionImports; register, block, capture and default-expansion state remains local to each function compiler. This phase split preserves the artifact format and source versus concrete function identity used by specialization and dispatch.

Guest generic functions and supported generic classes are specialized before artifact writing. Final classes, concrete value classes and the admitted abstract/open generic provider hierarchies share the bounded specialization model. Ordinary generic subclasses retain substituted superclass layouts. Final generic method discovery resolves K2 fake overrides to the executable declaring method; discovery, field-accessor lookup and call emission share traversal of the substituted superclass graph. Specialization identity belongs to the original declaration and its instantiated owner, independent of the subclass used at the call site. Function and class specializations are discovered together until no further dependencies appear, including helpers called from generic members whose bodies introduce other generic classes. The VM receives ordinary concrete function and class records, with typed scalar or reference fields and calls. Platform ABI 3 records the symbol and signature of each declaration requiring source compilation: generic implementations, classes with generic supertypes, members of those classes, and inline bodies. One module can retain an ordinary precompiled library fragment alongside those source bodies. The compiler resolves each source library as a separate Kotlin module over one shared metadata session, preserving internal and private boundaries. It includes canonical sources for resolution and specialization but binds ordinary implementations, including private helpers, to their compiled fragment instead of emitting them again in the consumer. Kotlin source visibility still governs access to those helpers. Ordinary function exports use the qualified declaration symbol and canonical signature, so same-name functions in different packages or owners remain distinct even when their lowered signatures coincide. Fragment assembly retains exported implementations, complete exported class/interface method ranges and their dependencies. Final application linking retains methods reached by calls, imports and class initializers, then closes over matching name/parameter-count candidates on reachable types for every retained virtual or interface-owned declaration. This conservative closure preserves implementations, inherited methods, interface defaults and bridges required by native admission, even for virtual declarations reached through direct calls. Newly reached types and method bodies extend the closure until stable. Relocation rebuilds both method start and count while preserving contiguous order. Fields remain conservative; unused methods and their otherwise unreachable dependencies disappear without changing the executable format or ABI. Minimum semantic Runtime ABI is recomputed from the remaining instructions and types, including ABI 1.10 when an array or exception root retains superclass methods; deleted method bodies cannot implicitly supply that requirement.

Tooling normalizes dependency module indexes into symbolic identities before linking so a shared library retains one owner across fragment containers. The final executable restores concrete indexes. Generic binary templates are not part of the platform bundle or VM artifact contract. Bundle format 9 and standalone module format 5 reject older representations. Selected addon modules can provide source templates through the same verified source-library path. Their metadata carrier retains template declaration identities while the separate source carrier supplies the matching files; sources are reattached before template validation. Standalone module decoding still rejects detached templates. Managed object/companion instances use existing class initializers and static fields, including exported singleton fields for independent libraries. Typed callback interfaces use names derived from their complete signatures and participate in trusted specialization ownership, so producer/consumer discovery order cannot change their identity. These changes reuse the existing bundle and artifact representations and do not change the native ABI. Default-argument metadata includes an explicit array-receiver size expression, evaluated from the already computed receiver rather than re-evaluating its source expression, and an explicit null value distinct from an absent default. Private Kotlin metadata format 6 carries the same defaults, including primary-constructor defaults without a receiver slot. Ordinary public library constructors are linked as static functions receiving the already allocated object; direct construction and superclass calls share argument/default evaluation. Their Kotlin bodies are compiled in the owning library, not regenerated or bypassed by the consumer. The standard library has one owner, stdlib:core: core helpers, inline scope functions, repeat, ranges, collections and portable kotlin.math. Its generic and inline source bodies coexist with ordinary precompiled implementations.

Ordinary library implementations may materialize concrete generic classes and structural collection interfaces, including ArrayList<String> and List<String>. These variants have canonical internal type, constructor, method and field exports in the owning library artifact. Linking redirects matching trusted template materializations to that owner and removes the superseded local records; variants absent from dependencies continue to specialize from source. Source provenance, rather than a user class’s spelling, controls eligibility. Ambiguous owners, incompatible layouts and missing members are rejected. Internal linking exports do not change Kotlin source visibility. The representation uses the existing artifact import/export format, without new VM instructions or binary generic templates. Only concrete variants are shared; inline and generic top-level source bodies retain their normal consumer specialization behavior. K2 still materializes a consumer variant transiently for type and layout checking; this linking contract does not yet skip that lowering work. Trusted collection variants include their supported read-view interfaces regardless of consumer usage, so covariance cannot change a specialization’s nominal layout. Collection value semantics add internal read-only nominal protocols after frontend analysis. Generated bridges use the concrete receiver’s typed size, iterator and entry reads, boxing only returned values; trusted hash containers use internal Any-key lookups, while custom reference implementations scan typed elements/entries. Protocol methods have reserved artifact names separate from user declarations. Covariant source views exclude these compiler-only protocols as widening targets. The protocols participate in trusted specialization ownership, so a precompiled library and consumer agree on their type and method identity. Repeated lowering of reused library IR retains the existing synthetic wrappers as executable roots; source declaration collectors omit their undefined offsets. They expose no source API or mutable widening and reuse existing dispatch, boxes and artifact representations. User-defined concrete type arguments retain their qualified identity, with a separate root-package namespace, rather than Kotlin metadata’s short display names. User classes named String therefore cannot reuse a built-in String variant.

Inline source normalization admits top-level functions and methods of named objects and companions. Generic object methods specialize under their original owner, preserving the dispatch receiver; class traversal snapshots tolerate new variants. Unused inline templates are removed recursively from objects, while runtime-referenced methods remain available. Ordinary class/local, suspend and reified inline methods remain outside this subset. The expansion and specialization budgets are unchanged.

Built-in module graph

Math public functions and extension getters are ordinary canonical library bodies; their internal floating primitives resolve through exact trusted signatures and emit ABI 1.16 math instructions. This keeps callable references on the existing precompiled library path. Integer helpers, arbitrary-base logarithms and integer rounding compose ordinary Guest instructions. The owning numerical contract and evidence are in portable mathematics.

The complete built-in graph is kotlin:builtins → stdlib:core → compukter:core. Compukter core owns environment and VM-runtime APIs: compiler, child processes, redstone, sound, text displays, terminal, filesystem, and cooperative tasks. This includes the environment-dependent kotlin.io facade without changing its package or default imports. Module ownership changes the canonical platform content identity, not capability operation schemas or the VM executable ABI. Tooling and addons using removed split-module IDs must rebuild against the consolidated owners.

Collection storage and universal views

The standard library publishes read-only lists and public ArrayList<T> with source bodies for specialization. When consecutive platform libraries reuse dependency IR, collection value bridges retain their original internal read-protocol declarations and methods. Lowering recovers those declarations from the existing bridges and traverses their result types before using plugin-context lookup. This keeps protocol types and interface dispatch available even when the next module cannot reference the internal class by name. Bridge probing skips local types carrying enclosing arguments that do not instantiate their own declaration. listOf and emptyList create fresh ArrayList<T> instances exposed as List<T>, with capacity equal to the number of elements. Factories fill them through the specialized add method in source order. A read-only view can be cast to MutableList<T> or ArrayList<T> and changed; it is not an immutable object. All twelve non-null primitive families use their corresponding primitive arrays, including compact Byte/Short and unsigned storage; reference and nullable storage uses typed reference arrays. Collection interfaces and iterator methods are specialized in the consuming artifact. Covariant widening to List<Any> retains the same list and backing array. The compiler publishes get, iterator, and next bridges with reference result signatures alongside the typed methods. An Int read through Any allocates an ordinary managed kotlin.Int box; a reference read preserves the element reference. The box’s runtime type and i32 payload support checked is Int and as Int, while typed paths remain unboxed. Direct List<Any> factories use a local nominal Array<Any> whose reference slots hold boxed Int and supported objects; each value is converted once before storage. Direct source Array<Any> factories and indexed writes use the same boxing boundary; array reads return the stored reference. Collection<T> owns size, isEmpty, and contains; specialized lists publish an Any argument bridge for covariant contains calls. Iterable<T> search extensions contains, indexOf, and lastIndexOf scan with the iterator, while the List<T> index methods use direct indexed access and publish Any and Any? argument bridges for covariant calls. The Iterable<T> predicate extensions any, all, and none specialize both the element and function value signature before lowering calls to the predicate. List storage and lazy sequence caches use one internal compiler registry to select concrete primitive/reference constructors by GuestPrimitive and nullability. Lists retain their typed array-backed implementations. Sequence filtering uses a separate one-value storage interface: primitive leaves have scalar fields; reference leaves have nullable storage, with occupancy owned by the filtering iterator and consumed references cleared. The internal factory lowers to ordinary object allocation and constructor calls; it adds no VM opcode or host operation.

MutableList<T> is invariant. ArrayList<T> uses a typed storage interface, with the compiler selecting the matching primitive or reference storage at its trusted factory call. Growth, shifting, searches, slot clearing, and mutable iteration are ordinary Guest bodies; all allocations use the VM’s existing metered array instructions. Read-only aliases share the same list. Iterator bridges expose both read-only and mutable result signatures, and a modification counter rejects stale iterator next/remove calls after structural mutations. ArrayList is the sole library list implementation. Its indexed-search read-view helpers live alongside it in ArrayList.kt and operate over List<T> to preserve specialization for typed, nullable and universal reads; read-only and mutable views share the same iterator implementation with mutation checks and removal state. The compiler uses one trusted collection read-view registry for bridge method names and additional interface types. Library lists also publish nullable element read bridges for read-only views such as List<Int?> over List<Int>. These views share storage: scalar reads allocate boxes, and reference reads retain identity. filterNotNull narrows elements into a new typed list, unboxing nullable Int values and retaining non-null references. Generic smart-cast arguments use checked reference conversions against their instantiated non-null parameter types. Generic call arguments are converted against the specialized parameter types, so searching a widened List<Any> boxes an Int argument before comparison. Universal equality over references uses the canonical virtual Any.equals(Any?) method. Nullable == evaluates both operands once, compares null left receivers by identity, and dispatches non-null left receivers. A literal-null comparison remains a reference test; explicit .equals(null) still invokes the receiver. Root Any and arrays use identity, String compares UTF-16 content, and typed boxes compare values with canonical NaN and distinct signed zero for Float. Primitive Float == retains IEEE semantics. Generated data-class equals is an ordinary class method: it checks identity/type and compares primary-constructor properties through the same scalar/reference contract, including nullable and object fields. Array properties compare identity, while their generated hash uses content. Body properties do not participate. Custom/inherited methods and compiled libraries share dispatch; there is no compiler-side enumeration of class layouts at each equality call. Each nullable primitive uses a nullable reference to its nominal managed box. Conversion to the direct scalar checks the box and reads its payload; nullable and Any boundaries preserve existing box references. Virtual hashCode and reference-default string conversion are described in the ABI reference (Runtime ABI 1.11 and 1.10). This equality implementation reuses ABI 1.11 instructions without adding an opcode, C ABI change or persisted-state format. Compiled libraries must rebuild against the updated canonical runtime module.

Nullable reference arrays have distinct nominal types with nullable element descriptors. Array<Int?> and List<Int?> store the same managed Int boxes or null, while List<Int> retains scalar storage. Nullable and non-null lists expose Any? bridges for reads, iteration, and searches.

Reference arrays and register allocation

Concrete Array<GuestClass> uses become distinct local nominal array types whose elements are exact Guest class references. Existing array instructions allocate, load, and store them; the verifier checks element types and GC follows live references in their slots. Execution tracing consults each frame’s safepoint map before resolving a reference register, so a dead register left after GC cannot invalidate a running program.

After reachability pruning and relocation, the linker reuses temporary registers whose lifetimes do not overlap. Allocation uses the same control-flow analysis as GC roots, including loop back edges, exceptional edges and resume blocks. The analysis indexes instruction nodes and read/write sets once per function, propagates liveness with request-local bitsets, and materializes register sets only for the final boundaries. An exception destination is killed only on its exceptional edge; normal continuations and other handlers retain their own live inputs. Only identical semantic types and physical shapes share a register; parameters, caught-exception destinations and operands in unreachable code retain dedicated slots. Inputs and destinations remain distinct through an instruction, including allocation and suspended calls. A Move whose source dies at that instruction may share its destination when no other boundary makes the pair interfere. A coalesced self-copy is removed; an instruction anchoring an inline debug parent remains present. Collapsed debug boundaries retain the last source entry and relocate dense inline-parent IDs. Other instructions retain their order. Root maps, metered block costs, linked module hashes and frame storage requirements are rebuilt through the existing allocation, linking and writing stages. The artifact encoding and runtime layout/verifier contract are unchanged; already compiled artifacts retain their original frame storage.

ServerCompilerService performs bounded asynchronous preparation, persistent-cache lookup, and single-flight deduplication by compilation identity. Minecraft-facing code submits requests and drains completions; worker and cache I/O run outside the server tick thread.

Enum identity storage

The generated Kotlin runtime module owns a non-generic abstract kotlin.Enum class with name/ordinal fields. Guest source Enum<E> and Enum<*> map to this base, while each concrete enum retains its own nominal type and singleton fields. Initializers create entry objects, set exact declaration names and indices, then publish static references. Trusted getters emit ordinary base field reads, including for separately compiled enums. This introduces no host lookup, per-enum dispatch table or VM instruction.