Kotlin
A concise multiplatform language developed by JetBrains
Kotlin 1.3.60 Released
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
inline
classes. - 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.
You can find the complete list of changes in the change log. As always, we’re really grateful to our external contributors.
Let’s dive into the details!
Language changes
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 {#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.
build.gradle.kts
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.
Debugging improvements
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!
Kotlin/Multiplatform
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 expect
” quick-fixes.
Kotlin/Native
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:
- watchOS
watchos_x86
watchos_arm64
watchos_arm32
- tvOS
tvos_x64
tvos_arm64
- Android (native)
android_x86
android_x64
- watchOS
- 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.
- Support for
suspend
callable references. - Functions with “big arity” (on par with the JVM limit)
- The ability to associate a work queue with any context/thread, not just the ones created ad-hoc through
Worker.start
.
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.
We appreciate feedback from early adopters! You can check out how it’s used in some samples (tetris game, CSV parser, video player), and even internally.
Performance
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!
Fixes
-
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. -
Passing
null
to variadic arguments: Whenever
null
was passed as an argument to a function withvararg
arguments, 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 asCOpaquePointer
: the untyped/void pointer, equivalent tovoid *
in C. -
Unboxing negative values on iOS/macOS
: Sometimes, handling negative bytes would result in a nasty crash; it turned out to be an LLVM bug, for which we have suggested a manual workaround.
: Starting from this release, the workaround will be applied automatically by the compiler.
Kotlin/JS
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.
Source maps
With Kotlin 1.3.60, source maps are generated automatically for your code that targets JavaScript through the 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. log
, warn
, 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 --tests
flag:
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 {#upcoming-in-14}
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.
NPE assertions
By also setting the apiVersion
to 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 when
{#break-and-continue}
One of the language changes in Kotlin 1.4 is allowing break
and continue
inside when
. Currently, break
and 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 break
and continue
get their expected meaning inside outer loops:
The fall-through behavior inside when
is subject to further design.
Changes for tail-recursive functions {#tail-recursive}
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.)
Prohibiting open
tailrec
functions
In Kotlin 1.3, combining open
and 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:
The 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:
Now the 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 B
subclass.
Because such behavior is confusing, Kotlin 1.4 prohibits using open
and 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.60
as 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.
If you run into any problems with the new release, you’re welcome to ask for help on the forums on Slack (get an invite here), or to report issues in the issue tracker.
Let’s Kotlin!
External Contributions
We want to especially thank Steven Schäfer for implementing the equality comparison optimization for inline
classes.
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
- pyos
- Jake Wharton
- Yanis Batura
- Kristoffer Andersen
- Sebastian Schuberth
- Kevin Bierhoff
- scache
- keijumt
- Louis CAD
- Matthew Gharrity
- Jim Sproch
- Jim S
- Dereck Bridie
- Sascha Peilicke
- SatoShun
- Dat Trieu
- Burak Eregar
- Ty Smith
- Vladimir Krivosheev
- Alex Chmyr