We’re happy to present the new release today, Kotlin 1.3.60. In addition to the quality improvements, this version focuses on:
- Optimizing the comparison of
- Tooling improvements for debugging, J2K converter, and Gradle scripts written in Kotlin.
- Support for more Kotlin/Native platforms/targets.
- Improving the Kotlin/MPP IDE experience.
- For Kotlin/JS, adding support for source maps and improving the platform test runner integration.
- Preview for some already implemented features of Kotlin 1.4.
Let’s dive into the details!
An incremental release doesn’t bring any language changes other than minor improvements (like changing confusing error messages) or updates for experimental features (like
inline classes). To take a sneak peek at what is coming in Kotlin 1.4, read the corresponding section below.
Improvements for inline classes
The equality comparison of two instances of
inline classes caused unnecessary boxing of their underlying values. Starting with v1.3.60, value comparisons are optimized:
In the bytecode, a special static method
equals-impl0 which compares the underlying values is generated for each
inline class. When you use the equality comparison on unboxed instances, it’s called under the hood to avoid extra boxing.
Note that for now, it’s not possible to override
equals/hashCode for inline classes. The generated
equals-impl0 method simply compares the values. In the future versions, when the custom
equals for inline classes is supported, the same
equals-imp0 method is intended to be used under the hood.
Note that for compatibility reasons, this optimization will work for the
kotlin.Result class starting with Kotlin 1.4 only.
Improved error messages
In rare situations, when you read a compiler error message, it’s not obvious why the error occurs. We try to fix this and to improve any error messages that might cause confusion.
Kotlin supports the trailing lambda convention: the lambda can be moved out of the parentheses. Such a lambda can also begin on the following line. This convention causes confusion sometimes when the compiler assumes that the curly braces on the next line should be the lambda argument of the function, but they’re not:
Now the error message for such cases has been improved, and you can automatically apply an easy fix: insert a semicolon at the end of the preceding line. (Yes, a semicolon is sometimes necessary in Kotlin!)
Another case worth highlighting is trying to make a mutable variable lazy. A
lazy variable is by design read-only, but this might cause confusion. Now the error message is improved and an automatic quick-fix is available:
If you have other use-cases in mind that seem confusing or if you’re missing some useful quick-fixes, please log such requests in our issue tracker.
IntelliJ IDEA support
Scratches and Worksheets
We’ve redesigned and improved Scratch files, which let you perform small experiments with your codebase. Now it’s easier to see the results, which are shown in a different window. The multiline output is wrapped, and the output for the given line is highlighted:
Sometimes, however, scratch files don’t play well. There are situations where you would prefer to use a sandbox that’s part of the project, rather than to be defined outside of the project. This can be especially useful for educational purposes, creating demo projects, or during presentations. For all such use-cases, please welcome the brand new Kotlin Worksheets:
Kotlin Worksheets are conceptually and technically very similar to Scratch files: you can play with your codebase and see the results right away. The major difference between the two is that Worksheets are a part of the project, which means they can be stored in a VCS and shared, while Scratches are intended to be used outside of a project.
We’re working on enhancing your user experience with Kotlin Gradle build scripts. We already have completion and highlighting performance improvements, and we will continue working closely with Gradle to improve it further.
You can now set function breakpoints in the Kotlin code. The debugger will then stop execution on entering or exiting the corresponding function. You can also set an additional entry condition if needed:
Completion and imports improvements
Several known bugs have been fixed, like completion for cases when the package name matches the local variable name:
Now if you define a typealias for enum, its members are now correctly shown in completion:
If you use an operator function like
invoke via the concise operator syntax, IntelliJ IDEA will suggest importing it automatically:
New Java-to-Kotlin converter
We’ve done some good work on the new Java-to-Kotlin converter. Many corner-case issues have been fixed, such as conversion for static imports and proper analysis of usages of a collection, should a collection become mutable or read-only after the conversion, even if this collection itself is a generic argument.
Now, when you convert several files at once, they are analyzed together and the usages from the other files affect the final result. For example, if you pass
null as a
String argument to a
foo function in Java, after converting a function and its usage together, the converted Kotlin function will take a nullable
String? as an argument:
Note that the new converter is now used by default.
Eclipse IDE plugin update
We are happy to announce that the kotlin-eclipse plugin now supports experimentally incremental compilation for single modules. To try it, select the “incremental compilation” checkbox in the
Kotlin | Building section of Eclipse properties. It’s still an experimental feature, so we will be happy to hear any feedback you may have!
We’ve devoted a lot of focus to MPP tooling, so if anything didn’t quite work the way you expected in the past, please give it another try!
While future release(s) may bring new superpowers to the multiplatform side of Kotlin, this one comes with a lot of improvements/fixes to the usability within the IDE. In particular, we’ve significantly enhanced some “create
The Kotlin/Native compiler has acquired a few new capabilities:
- Compatibility with the latest tooling bits: XCode 11 and LLVM 8.0.
A plethora of new platforms/targets:
Experimental symbolication of iOS crash reports for release binaries (including LLVM-inlined code, which is one step further than what XCode is able to decode).
Thread-safe tracking of Objective-C weak/shared references to Kotlin objects.
Functions with “big arity” (on par with the JVM limit)
Generic Kotlin/Multiplatform command-line parser
Some of you may have noticed that the
kotlinx.cli project has been dormant for a few months. We’re happy to share that the project’s code has been (mostly) rewritten, and it’s also included in this release of the Kotlin/Native compiler.
Even though the Kotlin/Native compiler is yet to be deeply optimized for performance, this release brings a few improvements that result in some impressive speedups.
The compilation speed, especially for large projects, has been increased by producing native libraries directly from klibs (instead of sources).
The runtime performance has also been improved: interface calls are now up to 5x faster, and type checks up to 50x faster!
Missing debug information for inlined code
- Some inlining optimizations were forgetting to update the mapping between source line numbers and binary code location, which resulted in misbehaving breakpoints that would “overshoot their destination”.
- We’ve taught them to be more diligent, and now breakpoints work as expected.
nullto variadic arguments
nullwas passed as an argument to a function with
varargarguments, such as:
…the compiler wasn’t exactly sure what type to assign to it, and would protest by crashing!
- Heeding this, now we treat those values as
COpaquePointer: the untyped/void pointer, equivalent to
void *in C.
In the world of Kotlin/JS, new changes are landing that focus on your quality of life and simplifying working with the new
org.jetbrains.kotlin.js plugin, most notably support for source maps and improvements for test runners.
org.jetbrains.kotlin.js Gradle plugin. This makes it a lot more comfortable to debug your own code, as it provides readable stack traces when you run into an error, and equips you with the superpowers provided by the developer tools in the case of targeting the browser – with support for breakpoints, code annotations, information about the local scope, and more. They also simplify working with tests for the JS target, as we will see in the next section.
Test runner improvements
When running your tests on the JS platform, the standard output generated by your tests on the regular channels (ie.
error) is included in the generated Gradle reports. This functionality is available for the Node.js and browser targets. Integration with source maps makes the stack traces of your tests readable and easy to relate to your code – with file names and line numbers pointing directly to your Kotlin sources:
Test filtering support for the JS target means that tests can be run selectively instead of having to execute all tasks at once. You can use this functionality via the Gradle command line interface through the
Alternatively, you can use the gutter icons in IntelliJ IDEA to run individual tests or tests from a specific set:
Upcoming changes in Kotlin 1.4
Kotlin 1.4 is planned to be released some time in 2020. However, you can already try some implemented features by specifying the corresponding language version:
Note that so far Kotlin 1.4 is available in the experimental state.
By also setting the
1.4, you can observe the changed behavior with null-check optimizations described earlier. The following code now throws a
NullPointerException instead of an
IllegalStateException with the old message
"JavaCode.getNull() must not be null":
Break & continue inside
One of the language changes in Kotlin 1.4 is allowing
continue expressions without labels are forbidden because these keywords were reserved to possibly be used for fall-through in
when. However, using labels turned out to be rather cumbersome, so
continue get their expected meaning inside outer loops:
The fall-through behavior inside
when is subject to further design.
Changes for tail-recursive functions
We’re going to fix some “corner-case” behavioral peculiarities for tail-recursive functions.
Initialization order of default values
The change is only noticeable if your tail-recursive function defines default values with side effects. In Kotlin 1.3, the initialization order of default values inside the
tailrec function is wrong: the default values are initialized from the last one to the first one, even though they should be initialized vice versa, from the first one to the last one, as it works for regular functions.
The change is apparent with the following example:
The output in Kotlin 1.3 is:
In Kotlin 1.4, the output is:
We expect that such cases should very rarely occur in practice. (But if for some reason you use this complicated combination of language features, please take note of this upcoming change.)
In Kotlin 1.3, combining
tailrec modifiers is a warning, in Kotlin 1.4 it becomes an error. Note that it’s a “breaking change”: code that used to work doesn’t work anymore, but we don’t expect this case to ever be used in practice.
It’s unclear whether an
open tail-recursive function is supposed to behave as a tail-recursive or as an
open function primarily. In Kotlin 1.3, the
open modifier is “ignored” but that leads to some confusing behavior:
The output of this code is:
foo function behaves as expected for a tail-recursive function: it calls itself and this call is optimized under the hood.
If we remove the
tailrec modifier from the
foo function in
A, then the output becomes:
foo function behaves as expected for an
open function: at first,
super.foo(count) calls explicitly a function from the parent
A class. Then
foo(count - 1) is a virtual call that calls a function from the
Because such behavior is confusing, Kotlin 1.4 prohibits using
tailrec at the same time.
How to update
As always, you can try Kotlin online at play.kotl.in.
- In Maven, Gradle, and npm: Use
1.3.60as the version for the compiler and the standard library. See the docs here.
- In IntelliJ IDEA and Android Studio: Update the Kotlin plugin to version 1.3.60. Use Tools | Kotlin | Configure Kotlin Plugin Updates and click the “Check for updates now” button.
- In Eclipse: Install the plugin using the Marketplace.
- The command-line compiler can be downloaded from the Github release page.
We want to especially thank Steven Schäfer for implementing the equality comparison optimization for
We’d like to thank all our external contributors whose pull requests were included in this release:
- Toshiaki Kameyama
- Ivan Gavrilovic
- Mads Ager
- Mark Punzalan
- Jake Wharton
- Yanis Batura
- Kristoffer Andersen
- Sebastian Schuberth
- Kevin Bierhoff
- Louis CAD
- Matthew Gharrity
- Jim Sproch
- Jim S
- Dereck Bridie
- Sascha Peilicke
- Dat Trieu
- Burak Eregar
- Ty Smith
- Vladimir Krivosheev
- Alex Chmyr