# What Is a Kotlin Inline Function and When to Use It: A Deep Dive into the Compiler

> Explore Kotlin inline functions and their compiler optimizations. Learn when to use them to eliminate overhead and enable advanced features like reified generics.

- Repository: [JetBrains/kotlin](https://github.com/jetbrains/kotlin)
- Tags: deep-dive
- Published: 2026-02-15

---

**A Kotlin inline function is a compile-time optimization that replaces the function call site with the function's body, eliminating lambda allocation overhead and enabling advanced features like reified generics and non-local returns.**

A Kotlin inline function is not just a performance hint—it is a fundamental mechanism in the JetBrains/kotlin compiler that transforms your source code during compilation. When you mark a function with the `inline` modifier, you instruct the compiler to physically copy the function's bytecode (or IR) into every location where it is invoked, enabling zero-cost abstractions for higher-order functions.

## How Kotlin Inline Functions Work at the Compiler Level

When the Kotlin compiler encounters an `inline` function, it initiates a sophisticated transformation pipeline. According to the JetBrains/kotlin source code, the process begins during frontend analysis and continues through intermediate representation (IR) lowering to final bytecode generation.

The **inlining process** follows these stages:

1. **Resolution** – The `InlineFunctionResolver` (located in [`compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/InlineFunctionResolver.kt`](https://github.com/JetBrains/kotlin/blob/main/compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/InlineFunctionResolver.kt)) determines which call sites are eligible for inlining and selects the proper inline version of the function.

2. **Preprocessing** – The `InlineFunctionBodyPreprocessor` (in [`compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/InlineFunctionBodyPreprocessor.kt`](https://github.com/JetBrains/kotlin/blob/main/compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/InlineFunctionBodyPreprocessor.kt)) prepares the function body by handling captures, parameters, and reified type parameters before splicing the body into the call site.

3. **Validation** – The `InlineDeclarationCheckerLowering` (in [`compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/InlineDeclarationCheckerLowering.kt`](https://github.com/JetBrains/kotlin/blob/main/compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/InlineDeclarationCheckerLowering.kt)) enforces inline-specific constraints, such as verifying `crossinline` usage and ensuring `reified` parameters are handled correctly.

4. **Serialization** – For public inline functions in libraries, `InlineFunctionSerializationPreProcessing` (in [`compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/InlineFunctionSerializationPreProcessing.kt`](https://github.com/JetBrains/kotlin/blob/main/compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/InlineFunctionSerializationPreProcessing.kt)) serializes the function's IR into the module's metadata, enabling **cross-module inlining** so downstream projects can inline the function even without access to the source code.

5. **Bytecode Generation** – Finally, the compiler splices the prepared body into the caller, eliminating the original function call entirely.

## Key Features Enabled by Kotlin Inline Functions

The `inline` modifier unlocks capabilities that are impossible with regular functions due to JVM type erasure and lambda object overhead.

### Lambda Inlining and Zero Allocation

When you pass a lambda to an inline function, the compiler inlines the lambda body directly at the call site. This eliminates the creation of `FunctionN` objects and the virtual call overhead associated with lambda invocation. The `InlineChecker` (in [`compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/InlineChecker.kt`](https://github.com/JetBrains/kotlin/blob/main/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/InlineChecker.kt)) marks these call sites during frontend analysis to ensure they receive this optimization.

### Non-Local Returns

Because the function body is physically present at the call site, a `return` statement inside a lambda passed to an inline function can exit the enclosing function. This is called a **non-local return** and is impossible with regular higher-order functions. The `InlineParameterChecker` (in [`compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineParameterChecker.kt`](https://github.com/JetBrains/kotlin/blob/main/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineParameterChecker.kt)) validates these semantics during type checking.

### Reified Type Parameters

Type erasure on the JVM normally prevents you from accessing generic type arguments at runtime. However, with `inline` functions, you can mark a type parameter as `reified`, allowing the compiler to substitute the actual type at the call site. This enables operations like `T::class.java` or `is T` checks inside the function body.

### Cross-Module Inlining

Public inline functions compiled into libraries contain serialized IR metadata (handled by `InlineFunctionSerializationPreProcessing`). When a downstream module compiles against this library, the compiler deserializes this metadata and performs inlining across module boundaries, preserving performance benefits even when the source is not available.

## When to Use a Kotlin Inline Function

Use the `inline` modifier in these specific scenarios to maximize performance and enable language features:

| Situation | Why `inline` Helps |
|-----------|-------------------|
| **Small higher-order functions** (e.g., `run`, `apply`, `let`) | Eliminates lambda allocation and virtual dispatch overhead. |
| **Functions with `reified` type parameters** | Allows type checks or reflection on generic types at runtime by preserving the actual type at the call site. |
| **APIs requiring non-local returns** (e.g., custom control-flow helpers) | Enables `return` inside a passed lambda to exit the caller function. |
| **Performance-critical hot paths** | The compiler can fully unroll the logic, enabling further JIT optimizations by removing call overhead. |
| **Public library functions** | Emits metadata allowing downstream modules to inline the function, preserving performance across library boundaries. |

## When to Avoid Kotlin Inline Functions

Avoid `inline` when the function body is large or captures many objects. Because the compiler copies the entire function body to every call site, excessive inlining causes **code bloat**, increasing the size of your compiled binary and potentially hurting instruction cache locality. The `InlineUtil` class (in [`compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineUtil.java`](https://github.com/JetBrains/kotlin/blob/main/compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineUtil.java)) provides utilities that help the compiler determine inlining eligibility, but the final decision rests with the developer's judgment about code size versus performance.

## Code Examples

### Simple Inline Function with Lambda

```kotlin
inline fun <T> measureTime(block: () -> T): T {
    val start = System.nanoTime()
    val result = block()
    println("Elapsed: ${System.nanoTime() - start} ns")
    return result
}

```

Usage:

```kotlin
val sum = measureTime { (1..1_000_000).sum() }

```

The lambda passed to `measureTime` is inlined, so no `Function1` object is created at runtime.

### Inline with Reified Type Parameter

```kotlin
inline fun <reified T> Gson.fromJson(json: String): T =
    this.fromJson(json, T::class.java)

```

Usage:

```kotlin
val person: Person = Gson().fromJson(jsonString)

```

Because the function is inline, `T::class.java` resolves to the actual `Person` class at the call site.

### Non-Local Return

```kotlin
inline fun <T> lock(lock: java.util.concurrent.locks.Lock, action: () -> T): T {
    lock.lock()
    try {
        return action()
    } finally {
        lock.unlock()
    }
}

```

Usage:

```kotlin
fun findPositive(nums: List<Int>) = lock(myLock) {
    for (n in nums) {
        if (n > 0) return n
    }
    -1
}

```

The `return n` statement exits `findPositive`, not just the lambda, because the lambda body is inlined into the caller.

## Key Compiler Files for Kotlin Inline Functions

The implementation of inline functions in the JetBrains/kotlin repository spans the frontend, IR, and runtime modules:

| File (relative to repo root) | Description |
|------------------------------|-------------|
| [`compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/InlineFunctionResolver.kt`](https://github.com/JetBrains/kotlin/blob/main/compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/InlineFunctionResolver.kt) | Resolves which call sites are eligible for inlining and selects the appropriate inline version. |
| [`compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/InlineFunctionBodyPreprocessor.kt`](https://github.com/JetBrains/kotlin/blob/main/compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/InlineFunctionBodyPreprocessor.kt) | Prepares the function body, handling captures, parameters, and reified types before splicing. |
| [`compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/InlineDeclarationCheckerLowering.kt`](https://github.com/JetBrains/kotlin/blob/main/compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/InlineDeclarationCheckerLowering.kt) | Validates inline-specific constraints such as `crossinline` usage and `reified` parameter correctness. |
| [`compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/InlineFunctionSerializationPreProcessing.kt`](https://github.com/JetBrains/kotlin/blob/main/compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/InlineFunctionSerializationPreProcessing.kt) | Serializes the inline function's IR into module metadata to enable cross-module inlining. |
| [`libraries/stdlib/jvm/runtime/kotlin/jvm/internal/InlineMarker.java`](https://github.com/JetBrains/kotlin/blob/main/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/InlineMarker.java) | Runtime marker used by the compiler to emit special inlining instructions. |
| [`compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineUtil.java`](https://github.com/JetBrains/kotlin/blob/main/compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineUtil.java) | Utility methods for inline analysis in the frontend. |
| [`compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineParameterChecker.kt`](https://github.com/JetBrains/kotlin/blob/main/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineParameterChecker.kt) | Verifies correct usage of inline parameters including `noinline` and `crossinline`. |
| [`compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/InlineChecker.kt`](https://github.com/JetBrains/kotlin/blob/main/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/InlineChecker.kt) | Core call-site checker that marks calls as inline candidates during frontend analysis. |

These components form the complete **inline function pipeline** in the Kotlin compiler, transforming high-level `inline` annotations into optimized bytecode that eliminates call overhead and enables advanced language features.

## Summary

- A **kotlin inline function** directs the compiler to copy the function body directly to call sites, eliminating the overhead of function calls and lambda object allocation.
- The feature is implemented across the JetBrains/kotlin compiler pipeline, with key components like `InlineFunctionResolver` and `InlineFunctionBodyPreprocessor` handling the transformation in `compiler/ir/ir.inline`.
- Use `inline` for small higher-order functions, `reified` type parameters, non-local returns, and performance-critical hot paths.
- Avoid inlining large functions to prevent code bloat and instruction cache misses.
- Cross-module inlining is supported through metadata serialization in `InlineFunctionSerializationPreProcessing`, allowing library consumers to benefit from inline optimizations.

## Frequently Asked Questions

### What is the difference between inline and noinline in Kotlin?

The `inline` modifier applies to the function itself, causing the compiler to copy its body to every call site. The `noinline` modifier applies to specific lambda parameters of an inline function, preventing those particular lambdas from being inlined while allowing others to be optimized. This is useful when you need to store a lambda in a variable or pass it to another non-inline function.

### Can inline functions be used across different modules?

Yes, inline functions support **cross-module inlining**. When a public inline function is compiled into a library, the Kotlin compiler emits serialized IR metadata via `InlineFunctionSerializationPreProcessing` (located in `compiler/ir/ir.inline`). Downstream modules use this metadata to inline the function body during their own compilation, preserving the performance benefits even without access to the original source code.

### Why does reified require inline?

The `reified` keyword allows you to access a generic type parameter as a concrete class at runtime (e.g., `T::class.java`). This requires `inline` because type erasure on the JVM removes generic type information during compilation. When a function is inlined, the compiler substitutes the actual type argument at each call site, making the concrete type available for reflection or type checks.

### What are the risks of overusing inline functions?

Overusing `inline` can lead to **code bloat**, where the compiled binary size increases significantly because the function body is duplicated at every call site. This can hurt instruction cache locality and increase memory usage. Additionally, inline functions cannot access private members of the enclosing class from inlined lambdas in certain contexts, and debugging can become more challenging because stack traces show the inlined code at the call site rather than inside the original function.