What's new in Kotlin 2.2.20
The Kotlin 2.2.20 release is out, delivering important changes for web development. Kotlin/Wasm is now Beta, with improvements to exception handling in JavaScript interop, npm dependency management, built-in browser debugging support, and a new shared source set for js
and wasmJs
targets.
Additionally, here are some main highlights:
Kotlin Multiplatform: Swift export available by default, stable cross-platform compilation for Kotlin libraries, and a new approach for declaring common dependencies.
Language: Improved overload resolution when passing lambdas to overloads with suspend function types.
Kotlin/Native: Support for stack canaries in binaries and smaller binary size for release binaries.
Kotlin/JS:
Long
values compiled into JavaScriptBigInt
.
IDE support
The Kotlin plugin that supports Kotlin 2.2.20 is bundled in the latest versions of IntelliJ IDEA and Android Studio. To update, all you need to do is change the Kotlin version to 2.2.20 in your build scripts.
See Update to a new release for details.
Language
In Kotlin 2.2.20, you can try out upcoming language features planned for Kotlin 2.3.0, including improved overload resolution when passing lambdas to overloads with suspend
function types and support for return
statements in expression bodies with explicit return types. This release also includes improvements to exhaustiveness checks for when
expressions, reified Throwable
catches, and Kotlin contracts.
Improved overload resolution for lambdas with suspend
function types
Previously, overloading a function with both a regular function type and a suspend
function type caused an ambiguity error when passing a lambda. You could work around this error with an explicit type cast, but the compiler incorrectly reported a No cast needed
warning:
With this change, when you define both a regular and a suspend
function type overload, a lambda without a cast resolves to the regular overload. Use the suspend
keyword to resolve to the suspend overload explicitly:
This behavior will be enabled by default in Kotlin 2.3.0. To test it now, set your language version to 2.3
using the following compiler option:
Or configure it in your build.gradle(.kts)
file:
We would appreciate your feedback in our issue tracker, YouTrack.
Support for return
statements in expression bodies with explicit return types
Previously, using return
in an expression body caused a compiler error because it could cause the function's return type to be inferred as Nothing
.
With this change, you can now use return
in expression bodies as long as the return type is written explicitly:
Similarly, return
statements inside lambdas and nested expressions in functions with expression bodies used to compile unintentionally. Kotlin now supports these cases as long as the return type is specified explicitly. Cases without an explicit return type will be deprecated in Kotlin 2.3.0:
This behavior will be enabled by default in Kotlin 2.3.0. To test it now, set your language version to 2.3
using the following compiler option:
Or configure it in your build.gradle(.kts)
file:
We would appreciate your feedback in our issue tracker, YouTrack.
Data-flow-based exhaustiveness checks for when
expressions
Kotlin 2.2.20 introduces data-flow-based exhaustiveness checks for when
expressions. Previously, the compiler's checks were limited to the when
expression itself, often forcing you to add a redundant else
branch. With this update, the compiler now tracks prior condition checks and early returns, so you can remove redundant else
branches.
For example, the compiler now recognizes that the function returns when the if
condition is met, so the when
expression only needs to handle the remaining cases:
This feature is Experimental. To enable it, add the following compiler option to your build.gradle(.kts)
file:
Support for reified types in catch
clauses
In Kotlin 2.2.20, the compiler now allows using reified generic type parameters in catch
clauses of inline
functions.
Here's an example:
Previously, attempting to catch a reified Throwable
type in an inline
function would result in an error.
This behavior will be enabled by default in Kotlin 2.4.0. To use it now, add the following compiler option to your build.gradle(.kts)
file:
The Kotlin team is grateful to external contributor Iven Krall for their contribution.
Improved Kotlin contracts
Kotlin 2.2.20 introduces several improvements to Kotlin contracts, including:
Support for contracts inside property accessors and specific operator functions.
Support for the
returnsNotNull()
function in contracts as a way to ensure a non-null return value when a condition is met.New
holdsIn
keyword, allowing you to assume conditions are true when passed inside lambdas.
These improvements are Experimental. To opt in, you still need to use the @OptIn(ExperimentalContracts::class)
annotation when declaring contracts. The holdsIn
keyword and the returnsNotNull()
function also need the @OptIn(ExperimentalExtendedContracts::class)
annotation.
To use these improvements, you also need to add the compiler options described in each section below.
We would appreciate your feedback in our issue tracker.
Support for generics in contract type assertions
You can now write contracts that perform type assertions on generic types:
In this example, the contract performs a type assertion on the Result
object, allowing the compiler to safely smart cast it to the asserted generic type.
This feature is Experimental. To opt in, add the following compiler option to your build.gradle(.kts)
file:
Support for contracts inside property accessors and specific operator functions
You can now define contracts inside property accessors and specific operator functions. This lets you use contracts on more types of declarations, making them more flexible.
For example, you can use a contract inside a getter to enable smart casting for a receiver object:
Additionally, you can use contracts in the following operator functions:
invoke
contains
rangeTo
,rangeUntil
componentN
iterator
unaryPlus
,unaryMinus
,not
inc
,dec
Here's an example of using a contract in an operator function to ensure the initialization of a variable inside a lambda:
This feature is Experimental. To opt in, add the following compiler option to your build.gradle(.kts)
file:
Support for the returnsNotNull()
function in contracts
Kotlin 2.2.20 introduces the returnsNotNull()
function for contracts. You can use this function to ensure that a function returns a non-null value when a specific condition is met. This simplifies your code by replacing separate nullable and non-nullable function overloads with a single, concise function:
In this example, the contract in the decode()
function allows the compiler to smart-cast its return value when the input is non-null, removing the need for extra null checks or multiple overloads.
This feature is Experimental. To opt in, add the following compiler option to your build.gradle(.kts)
file:
New holdsIn
keyword
Kotlin 2.2.20 introduces the new holdsIn
keyword for contracts. You can use it to ensure that a boolean condition is assumed to be true
inside a specific lambda. This lets you build DSLs with conditional smart casts using contracts.
Here's an example:
This feature is Experimental. To opt in, add the following compiler option to your build.gradle(.kts)
file:
Kotlin/JVM: Support invokedynamic
with when
expressions
In Kotlin 2.2.20, you can now compile when
expressions with invokedynamic
. Previously, when
expressions with multiple type checks compiled to a long chain of instanceof
checks in the bytecode.
Now you can use invokedynamic
with when
expressions to generate smaller bytecode, similar to the bytecode produced by Java switch
statements, when the following conditions are met:
All conditions except for
else
areis
ornull
checks.The expression doesn't contain guard conditions (
if
).The conditions don't include types that can't be type-checked directly, such as mutable Kotlin collections (
MutableList
) or function types (kotlin.Function1
,kotlin.Function2
, and so on).There are at least two conditions besides
else
.All branches check the same subject of the
when
expression.
For example:
With the new feature enabled, the when
expression in this example compiles to a single invokedynamic
type switch instead of multiple instanceof
checks.
To enable this feature, compile your Kotlin code with JVM target 21 or above and add the following compiler option:
Or add it to the compilerOptions {}
block of your build.gradle(.kts)
file:
This feature is Experimental. We would appreciate your feedback in our issue tracker, YouTrack.
Kotlin Multiplatform
Kotlin 2.2.20 introduces significant changes for Kotlin Multiplatform: Swift export is available by default, there's a new shared source set, and you can try a new approach to managing common dependencies.
Swift export available by default
Kotlin 2.2.20 introduces experimental support for Swift export. It allows you to export Kotlin sources directly and call Kotlin code from Swift idiomatically, eliminating the need for Objective-C headers.
This should significantly improve multiplatform development for Apple targets. For example, if you have a Kotlin module with top-level functions, Swift export enables clean, module-specific imports, removing the confusing Objective-C underscores and mangled names.
The key features are:
Multi-module support. Each Kotlin module is exported as a separate Swift module, simplifying function calls.
Package support. Kotlin packages are explicitly preserved during export, avoiding naming conflicts in the generated Swift code.
Type aliases. Kotlin type aliases are exported and preserved in Swift, improving readability.
Enhanced nullability for primitives. Unlike Objective-C interop, which required boxing types like
Int?
into wrapper classes likeKotlinInt
to preserve nullability, Swift export converts nullability information directly.Overloads. You can call Kotlin's overloaded functions in Swift without ambiguity.
Flattened package structure. You can translate Kotlin packages into Swift enums, removing the package prefix from generated Swift code.
Module name customization. You can customize the resulting Swift module names in the Gradle configuration of your Kotlin project.
How to enable Swift export
The feature is currently Experimental and works only in projects that use direct integration to connect the iOS framework to the Xcode project. This is a standard configuration for multiplatform projects created with the Kotlin Multiplatform plugin in IntelliJ IDEA or through the web wizard.
To try out Swift export, configure your Xcode project:
In Xcode, open the project settings.
On the Build Phases tab, locate the Run Script phase with the
embedAndSignAppleFrameworkForXcode
task.Adjust the script to feature the
embedSwiftExportForXcode
task instead in the run script phase:./gradlew :<Shared module name>:embedSwiftExportForXcodeBuild the project. Swift modules are generated in the build output directory.
The feature is available by default. If you have already enabled it in previous releases, you can now remove kotlin.experimental.swift-export.enabled
from your gradle.properties
file.
For more information about Swift export, see our documentation.
Leave feedback
We're planning to expand and gradually stabilize Swift export support in future Kotlin releases. After Kotlin 2.2.20, we'll focus on improving interoperability between Kotlin and Swift, particularly around coroutines and flows.
Support for Swift export is a significant change for Kotlin Multiplatform. We would appreciate your feedback:
Contact the development team directly in Kotlin Slack – get an invite and join the #swift-export channel.
Report any problems you face with Swift export in YouTrack.
Shared source set for js
and wasmJs
targets
Previously, Kotlin Multiplatform didn't include a shared source set for JavaScript (js
) and WebAssembly (wasmJs
) web targets by default. To share code between js
and wasmJs
, you had to manually configure a custom source set or write code in two places, one version for js
and another for wasmJs
. For example:
Starting with this release, the Kotlin Gradle plugin adds a new shared source set for web (comprising webMain
and webTest
) when you use the default hierarchy template.
With this change, the web
source set becomes a parent of both js
and wasmJs
source sets. The updated source set hierarchy looks like this:
The new source set allows you to write one piece of code for both the js
and wasmJs
targets. You can put your shared code in webMain
and have it automatically work for both:
This update simplifies code sharing between the js
and wasmJs
targets. It is particularly useful in two cases:
If you're a library author, and you want to add support for both the
js
andwasmJs
targets, without duplicating code.If you're developing Compose Multiplatform applications that target the web, enabling cross-compilation for both the
js
andwasmJs
targets for wider browser compatibility. Given this fallback mode, when you create a website, it works on all browsers out of the box, as modern browsers usewasmJs
and older ones usejs
.
To try this feature, use the default hierarchy template in the kotlin {}
block of your build.gradle(.kts)
file:
Before using the default hierarchy, consider carefully any potential conflicts if you have projects with a custom shared source set or if you've renamed the js("web")
target. To resolve these conflicts, rename the conflicting source set or target, or don't use the default hierarchy.
Stable cross-platform compilation for Kotlin libraries
Kotlin 2.2.20 completes an important roadmap item, stabilizing cross-platform compilation for Kotlin libraries.
You can now use any host to produce .klib
artifacts for publishing Kotlin libraries. This significantly streamlines the publishing process, particularly for Apple targets that previously required a Mac machine.
The feature is available by default. If you have already enabled cross-compilation with kotlin.native.enableKlibsCrossCompilation=true
, you can now remove it from your gradle.properties
file.
Unfortunately, a few limitations are still present. You still need to use a Mac machine if:
Your library or any dependent modules have cinterop dependencies.
You have the CocoaPods integration set up in your project.
You need to build or test final binaries for Apple targets.
For more information about the publication of multiplatform libraries, see our documentation.
New approach for declaring common dependencies
To simplify setting up multiplatform projects with Gradle, Kotlin 2.2.20 now lets you declare common dependencies in the kotlin {}
block by using a top-level dependencies {}
block when your project uses Gradle 8.8 or higher. These dependencies behave as if they were declared in the commonMain
source set. This feature works similarly to the dependencies block that you use for Kotlin/JVM and Android-only projects, and it's now Experimental in Kotlin Multiplatform.
Declaring common dependencies at the project level reduces repetitive configuration across source sets and helps streamline your build setup. You can still add platform-specific dependencies in each source set as needed.
To try this feature, opt in by adding the @OptIn(ExperimentalKotlinGradlePluginApi::class)
annotation before the top-level dependencies {}
block. For example:
We would appreciate your feedback on this feature in YouTrack.
New diagnostic for target support in dependencies
Before Kotlin 2.2.20, if a dependency in your build script didn't support all the targets required by the source set, the error messages produced by Gradle made it hard to understand the problem.
Kotlin 2.2.20 introduces a new diagnostic that clearly shows which targets each dependency supports and which it doesn't.
This diagnostic is enabled by default. If, for some reason, you need to disable it, let us know in a comment in this YouTrack issue. You can use the following Gradle properties to disable the diagnostic in your gradle.properties
file:
Property | Description |
---|---|
| Runs the diagnostic only for metadata compilations and imports |
| Disables the diagnostic completely |
Kotlin/Native
Kotlin 2.2.20 brings improvements to interoperability with Objective-C/Swift, debugging, and new binary options.
Support for stack canaries in binaries
Starting with Kotlin 2.2.20, Kotlin adds support for stack canaries in the resulting Kotlin/Native binaries. As part of stack protection, this security feature protects against stack smashing, mitigating some common application vulnerabilities. Already available in Swift and Objective-C, it's now supported in Kotlin as well.
The implementation of stack protection in Kotlin/Native follows the behavior of the stack protector in Clang.
To enable stack canaries, add the following binary option to your gradle.properties
file:
The property enables the feature for all the Kotlin functions that are vulnerable to stack smashing. Alternative modes are:
kotlin.native.binary.stackProtector=strong
, which uses a stronger heuristic for the functions vulnerable to stack smashing.kotlin.native.binary.stackProtector=all
, which enables stack protectors for all functions.
Note that in some cases, stack protection might come with a performance cost.
Smaller binary size for release binaries
Kotlin 2.2.20 introduces the smallBinary
option that can help you decrease the binary size for release binaries. The new option effectively sets -Oz
as the default optimization argument for the compiler during the LLVM compilation phase.
With the smallBinary
option enabled, you can make release binaries smaller and improve build time. However, it might affect runtime performance in some cases.
The new feature is currently Experimental. To try it out in your project, add the following binary option to your gradle.properties
file:
The Kotlin team is grateful to Troels Lund for his help in implementing this feature.
Improved debugger object summaries
Kotlin/Native now generates clearer object summaries for debugger tools like LLDB and GDB. This improves the readability of the produced debug information and streamlines your debugging experience.
Consider the following object, for example:
Previously, the inspection would show you only limited information, including a pointer to the object's memory address:
With Kotlin 2.2.20, the debugger now shows richer details, including the actual values:
The Kotlin team is grateful to Nikita Nazarov for his help in implementing this feature.
For more information on debugging in Kotlin/Native, see the documentation.
Explicit names in block types for Objective-C headers
Kotlin 2.2.20 introduces an option to add explicit parameter names to Kotlin's function types for Objective-C headers exported from Kotlin/Native projects. Parameter names improve autocomplete suggestions in Xcode and help avoid Clang warnings.
Previously, parameter names in block types were omitted in the generated Objective-C headers. In such cases, Xcode's autocompletion would suggest calling such functions without parameter names in the Objective-C block. The generated block would trigger Clang warnings.
For example, for the following Kotlin code:
The generated Objective-C header had no parameter name:
So when calling the greetUserBlock()
function from Objective-C in Xcode, the IDE suggested:
The missing parameter name (NSString *)
in the suggestion caused Clang warnings.
With the new option, Kotlin forwards the parameter names from Kotlin function types to Objective-C block types, so Xcode uses them in suggestions:
To enable explicit parameter names, add the following binary option to your gradle.properties
file:
The Kotlin team is grateful to Yijie Jiang for implementing this feature.
Reduced size of Kotlin/Native distribution
The Kotlin/Native distribution used to contain two JAR files with compiler code:
konan/lib/kotlin-native.jar
konan/lib/kotlin-native-compiler-embeddable.jar
.
Starting with Kotlin 2.2.20, kotlin-native.jar
is no longer published.
The removed JAR file is the legacy version of the embeddable compiler, which is no longer needed. This change significantly reduces the size of the distribution.
As a consequence, the following options are now deprecated and removed:
The
kotlin.native.useEmbeddableCompilerJar=false
Gradle property. Instead, the embeddable compiler JAR file is always used for Kotlin/Native projects.The
KotlinCompilerPluginSupportPlugin.getPluginArtifactForNative()
function. Instead, thegetPluginArtifact()
function is always used.
For more information, see the YouTrack issue.
Exporting KDocs to Objective-C headers by default
KDoc comments are now exported by default when generating Objective-C headers during compilation of Kotlin/Native final binaries.
Previously, you needed to add the -Xexport-kdoc
option manually to your build file. Now, it's automatically passed to compilation tasks.
This option embeds KDoc comments into klibs and extracts comments from klibs when producing Apple frameworks. As a result, comments on classes and methods appear during autocompletion, for example, in Xcode.
You can disable the export of KDoc comments from klibs to the produced Apple frameworks in the binaries {}
block of your build.gradle(.kts)
file:
For more information, see our documentation.
Deprecation of x86_64
Apple targets
Apple stopped producing devices with Intel chips a couple of years ago and recently announced that macOS Tahoe 26 will be the last OS version to support Intel-based architecture.
This makes it increasingly difficult for us to properly test these targets on our build agents, especially in future Kotlin releases where we'll update the supported Xcode version that comes with macOS 26.
Starting with Kotlin 2.2.20, the macosX64
and iosX64
targets are demoted to support tier 2. This means the target is regularly tested on CI to ensure it compiles, but it might not be automatically tested to ensure it runs.
We plan to gradually deprecate all x86_64
Apple targets and eventually remove support for them during the Kotlin 2.2.20−2.4.0 release cycle. This includes the following targets:
macosX64
iosX64
tvosX64
watchosX64
For more information on support tiers, see Kotlin/Native target support.
Kotlin/Wasm
Kotlin/Wasm is now Beta, offering greater stability along with improvements such as separated npm dependencies, refined exception handling for JavaScript interop, built-in browser debugging support, and more.
Separated npm dependencies
Previously, in your Kotlin/Wasm projects, all npm dependencies were installed together in your project folder, including both Kotlin tooling dependencies and your own. They were also recorded together in your project's lock files (package-lock.json
or yarn.lock
).
As a result, whenever Kotlin tooling dependencies were updated, you had to update your lock files even if you didn't add or change anything.
Starting from Kotlin 2.2.20, the Kotlin tooling npm dependencies are installed outside your project. Now, the tooling and your (user) dependencies have separate directories:
Tooling dependencies' directory:
<kotlin-user-home>/kotlin-npm-tooling/<yarn|npm>/hash/node_modules
User dependencies' directory:
build/wasm/node_modules
In addition, the lock files inside the project directory contain only user-defined dependencies.
This improvement keeps your lock files focused only on your own dependencies, helps maintain a cleaner project, and reduces unnecessary changes to your files.
This change is enabled by default for the wasm-js
target. The change is not yet implemented for the js
target. While there are plans to implement it in future releases, the behavior of the npm dependencies remains the same as before for the js
target in Kotlin 2.2.20.
Improved exception handling in Kotlin/Wasm and JavaScript interop
Previously, Kotlin had difficulty understanding exceptions (errors) thrown in JavaScript (JS) and crossing over to Kotlin/Wasm code.
In some cases, the issue also occurred in the reverse direction, when an exception was thrown or passed through the Wasm code to JS and wrapped into WebAssembly.Exception
without any details. These Kotlin exception handling issues made debugging difficult.
Starting from Kotlin 2.2.20, the developer experience with exceptions improves in both directions:
When exceptions are thrown from JS, you can see more information on Kotlin's side. When such an exception propagates through Kotlin back to JS, it's no longer wrapped into WebAssembly.
When exceptions are thrown from Kotlin, they can now be caught on the JS side as JS errors.
The new exception handling works automatically in modern browsers that support the WebAssembly.JSTag
feature:
Chrome 115+
Firefox 129+
Safari 18.4+
In older browsers, the exception handling behavior remains unchanged.
Support for debugging in browsers without configuration
Previously, browsers couldn't automatically access the Kotlin/Wasm project sources required for debugging. To debug Kotlin/Wasm applications in the browser, you had to manually configure your build to serve these sources by adding the following snippet to your build.gradle(.kts)
file:
Starting with Kotlin 2.2.20, debugging your applications in modern browsers works out of the box. When you run Gradle development tasks (*DevRun
), Kotlin automatically serves the source files to the browser, allowing you to set breakpoints, inspect variables, and step through Kotlin code without extra setup.
This change simplifies debugging by removing the need for manual configuration. The required configuration is now included in the Kotlin Gradle plugin. If you previously added this configuration to your build.gradle(.kts)
file, you should remove it to avoid conflicts.
Debugging in browsers is enabled by default for all Gradle *DevRun
tasks. These tasks serve not only the application but also its source files, so use them only for local development and avoid running them in cloud or production environments where the sources would be publicly exposed.
Handle repeated reloads during debugging
Serving sources by default may cause repeated reloads of the application in the browser before Kotlin compilation and bundling are complete. As a workaround, adjust your webpack configuration to ignore Kotlin source files and disable watching for served static files. Add a .js
file with the following content into the webpack.config.d
directory at the root of your project:
Elimination of empty yarn.lock
files
Previously, the Kotlin Gradle plugin (KGP) automatically generated a yarn.lock
file that included information about npm packages required by the Kotlin toolchain, along with any existing npm dependencies from the project or used libraries.
Now, the KGP manages toolchain dependencies separately, and a project-level yarn.lock
file is no longer generated unless the project has npm dependencies.
The KGP automatically creates a yarn.lock
file when npm dependencies are added, and it deletes the yarn.lock
file when npm dependencies are removed.
This change cleans up project structures and makes it easier to track when actual npm dependencies are introduced.
No additional steps are required to configure this behavior. It's applied by default in Kotlin/Wasm projects starting from Kotlin 2.2.20.
New compiler error in fully qualified class names
On Kotlin/Wasm, the compiler doesn't store fully qualified names (FQNs) of classes in the generated binary by default. This approach avoids increasing the application size.
As a result, in previous Kotlin releases, calling the KClass::qualifiedName
property returned an empty string instead of the class's qualified name.
Starting with Kotlin 2.2.20, the compiler reports an error when you use the KClass::qualifiedName
property in Kotlin/Wasm projects, unless you explicitly enable the qualified names feature.
This change prevents unexpected empty strings when calling the qualifiedName
property and improves developer experience by catching issues at compile time.
The diagnostic is enabled by default, and errors are reported automatically. To disable the diagnostics and allow storing FQNs in Kotlin/Wasm, instruct the compiler to store fully qualified names for all classes by adding the following option to your build.gradle(.kts)
file:
Kotlin/JS
Kotlin 2.2.20 supports using the BigInt
type to represent Kotlin's Long
type, enabling Long
in exported declarations. Additionally, this release adds a DSL function to clean up Node.js arguments.
Usage of the BigInt
type to represent Kotlin's Long
type
Before the ES2020 standard, JavaScript (JS) did not support a primitive type for precise integers larger than 53 bits.
For this reason, Kotlin/JS used to represent Long
values (which are 64-bit wide) as JavaScript objects containing two number
properties. This custom implementation made interoperability between Kotlin and JavaScript more complex.
Starting with Kotlin 2.2.20, Kotlin/JS now uses JavaScript's built-in BigInt
type to represent Kotlin's Long
values when compiling to modern JavaScript (ES2020).
This change enables exporting the Long
type to JavaScript, a feature also introduced in Kotlin 2.2.20. As a result, this change simplifies the interoperability between Kotlin and JavaScript.
To enable it, you need to add the following compiler option to your build.gradle(.kts)
file:
This feature is Experimental. We would appreciate your feedback in our issue tracker, YouTrack.
Usage of Long
in exported declarations
Because Kotlin/JS used a custom Long
representation, it was difficult to provide a straightforward way to interact with Kotlin's Long
from JavaScript. As a result, you couldn't export Kotlin code that used the Long
type to JavaScript. This issue affected any code using Long
, such as function parameters, class properties, or constructors.
Now that Kotlin's Long
type can be compiled to JavaScript's BigInt
type, Kotlin/JS supports exporting Long
values to JavaScript, simplifying the interoperability between Kotlin and JavaScript code.
To enable this feature:
Allow exporting
Long
in Kotlin/JS by adding the following compiler option to thefreeCompilerArgs
attribute in yourbuild.gradle(.kts)
file:kotlin { js { ... compilerOptions { freeCompilerArgs.add("-XXLanguage:+JsAllowLongInExportedDeclarations") } } }Enable the
BigInt
type. See how to enable it in Usage of theBigInt
type to represent Kotlin'sLong
type.
New DSL function for cleaner arguments
When running a Kotlin/JS application with Node.js, the arguments passed to your program (args
) used to include:
The path to the executable
Node
.The path to your script.
The actual command-line arguments you provided.
However, the expected behavior for args
was to include only the command-line arguments. To achieve this, you had to manually skip the first two arguments using the drop()
function inside your build.gradle(.kts)
file or in your Kotlin code:
This workaround was repetitive, error-prone, and didn't work well when sharing code between platforms.
To fix this issue, Kotlin 2.2.20 introduces a new DSL function called passCliArgumentsToMainFunction()
.
With this function, only the command-line arguments are included, while the Node
and script paths are excluded:
This change reduces boilerplate code, prevents mistakes caused by manually dropping arguments, and improves cross-platform compatibility.
To enable this feature, add the following DSL function inside your build.gradle(.kts)
file:
Gradle
Kotlin 2.2.20 adds new compiler performance metrics for Kotlin/Native tasks in Gradle build reports and makes quality-of-life improvements in incremental compilation.
New compiler performance metrics in build reports for Kotlin/Native tasks
In Kotlin 1.7.0, we introduced build reports to help track compiler performance. Since then, we've added more metrics to make these reports even more detailed and useful for investigating performance issues.
In Kotlin 2.2.20, build reports now include compiler performance metrics for Kotlin/Native tasks.
To learn more about build reports and how to configure them, see Enabling build reports.
Preview improved incremental compilation for Kotlin/JVM
Kotlin 2.0.0 introduced the new K2 compiler with an optimized frontend. Kotlin 2.2.20 builds on this by using the new frontend to improve performance in certain complex incremental compilation scenarios for Kotlin/JVM.
These improvements are disabled by default while we work on stabilizing the behavior. To enable them, add the following property in your gradle.properties
file:
Currently, the kapt
compiler plugin isn't compatible with this new behavior. We're working on adding support in a future Kotlin release.
We'd appreciate your feedback on this feature in YouTrack.
Incremental compilation detects changes in lambdas of inline functions
Before Kotlin 2.2.20, if you enabled incremental compilation and changed the logic inside a lambda in an inline function, the compiler didn't recompile the call sites of that inline function in other modules. As a result, those call sites used the previous version of the lambda, which could cause unexpected behavior.
In Kotlin 2.2.20, the compiler now detects changes in lambdas of inline functions and automatically recompiles their call sites.
Maven: Support for the Kotlin daemon in the kotlin-maven-plugin
Kotlin 2.2.20 takes the build tools API introduced in Kotlin 2.2.0 one step further by adding support for the Kotlin daemon in the kotlin-maven-plugin
. When using the Kotlin daemon, the Kotlin compiler runs in a separate isolated process, which prevents other Maven plugins from overriding system properties. You can see an example in this YouTrack issue.
Starting with Kotlin 2.2.20, the Kotlin daemon is used by default. If you want to revert to the previous behavior, opt out by setting the following property in your pom.xml
file to false
:
Kotlin 2.2.20 also introduces a new jvmArgs
property, which you can use to customize the default JVM arguments for the Kotlin daemon. For example, to override the -Xmx
and -Xms
options, add the following to your pom.xml
file:
New common schema for Kotlin compiler options
Kotlin 2.2.20 introduces a common schema for all compiler options published under org.jetbrains.kotlin:kotlin-compiler-arguments-description
. This artifact includes both a code representation and a JSON equivalent (for non-JVM consumers) of all compiler options, their descriptions, and metadata such as the version in which each option was introduced or stabilized. You can use this schema to generate a custom view of the options or analyze them as needed.
Kotlin standard library
This release introduces new experimental features in the standard library: reflection support for identifying interface types in Kotlin/JS, update functions for common atomic types, and copyOf()
overloads for array resizing.
Support for identifying interface types through reflection in Kotlin/JS
Kotlin 2.2.20 adds the Experimental KClass.isInterface
property to the Kotlin/JS standard library.
With this property, you can now check whether a class reference represents a Kotlin interface. This brings Kotlin/JS closer to parity with Kotlin/JVM, where you can use KClass.java.isInterface
to check if a class represents an interface.
To opt in, use the @OptIn(ExperimentalStdlibApi::class)
annotation:
We would appreciate your feedback in our issue tracker, YouTrack.
New update functions for common atomic types
Kotlin 2.2.20 introduces new experimental functions for updating common atomic types and elements of their array counterparts. Each function atomically computes a new value using one of these update functions and replaces the current value, with the return value depending on which function you use:
update()
andupdateAt()
set a new value without returning a result.fetchAndUpdate()
andfetchAndUpdateAt()
set a new value and return the previous value before the change.updateAndFetch()
andupdateAndFetchAt()
set a new value and return the updated value after the change.
You can use these functions to implement atomic transformations that aren't supported out of the box, such as multiplication or bitwise operations. Before this change, incrementing a common atomic type and reading the previous value required a loop with the compareAndSet()
function.
Like all APIs for common atomic types, these functions are Experimental. To opt in, use the @OptIn(ExperimentalAtomicApi::class)
annotation.
Here's an example of code that performs different kinds of updates and returns either the previous or updated value:
We would appreciate your feedback in our issue tracker, YouTrack.
Support for copyOf()
overloads for arrays
Kotlin 2.2.20 introduces an experimental overload for the copyOf()
function. It's available for arrays of generic type Array<T>
and all primitive array types.
You can use this function to make an array larger and populate the new elements using values from an initializer lambda. This can help you reduce custom boilerplate code and fixes the common pain point where resizing a generic Array<T>
produced a nullable result (Array<T?>
).
Here's an example:
This API is Experimental. To opt in, use the @OptIn(ExperimentalStdlibApi::class)
annotation.
We would appreciate your feedback in our issue tracker.
Compose compiler
In this release, the Compose compiler brings quality-of-life improvements by adding new warnings and improving the output of build metrics to make them easier to read.
Language version restrictions for default parameters
With this release, the Compose compiler reports an error if the language version specified for compilation is lower than what's required to support default parameters in abstract or open composable functions.
Default parameters are supported in the Compose compiler, starting with Kotlin 2.1.0 for abstract functions and Kotlin 2.2.0 for open functions. When using a newer version of the Compose compiler while targeting older Kotlin language versions, library developers should be aware that default parameters in abstract or open functions may still appear in the public API, even if the language version doesn't support them.
Composable target warnings for the K2 compiler
This release adds warnings about @ComposableTarget
mismatches when using the K2 compiler.
For example:
Fully qualified names in build metrics
Class and function names reported in build metrics are now fully qualified, making it easier to distinguish between declarations with the same name in different packages.
In addition, build metrics no longer include a dump of complex expressions from default parameters, making them easier to read.
Breaking changes and deprecations
This section highlights important breaking changes and deprecations worth noting:
The kapt compiler plugin now uses the K2 compiler by default. As a result, the
kapt.use.k2
property, which controls whether the plugin uses the K2 compiler, is deprecated. If you set this property tofalse
to opt out of using the K2 compiler, Gradle shows a warning.
Documentation updates
The Kotlin documentation has received some notable changes:
Kotlin roadmap – See the updated list of Kotlin's priorities on language and ecosystem evolution.
Properties – Learn about the many ways that you can use properties in Kotlin.
Conditions and loops – Learn how conditions and loops work in Kotlin.
Kotlin/JavaScript – Explore the use cases for Kotlin/JS.
Targeting the web – Learn about the different targets that Gradle offers for web development.
Kotlin daemon – Learn about the Kotlin daemon and how it works with build systems and the Kotlin compiler.
Coroutines overview page – Learn about coroutine concepts and get started on your learning journey.
Kotlin/Native binary options – Learn about the binary options for Kotlin/Native and how to configure them.
Debugging Kotlin/Native – Explore the different ways that you can debug with Kotlin/Native.
Tips for customizing LLVM backend – Learn how Kotlin/Native uses LLVM and adjust optimization passes.
Get started with Exposed's DAO API – Learn how to use Exposed's Data Access Object (DAO) API to store and retrieve data in a relational database.
New pages in Exposed documentation about R2DBC:
HTMX integration – Learn how Ktor provides experimental, first-class support for HTMX.
How to update to Kotlin 2.2.20
The Kotlin plugin is distributed as a bundled plugin in IntelliJ IDEA and Android Studio.
To update to the new Kotlin version, change the Kotlin version to 2.2.20 in your build scripts.