K2 编译器迁移指南
As the Kotlin language and ecosystem have continued to evolve, so has the Kotlin compiler. The first step was the introduction of the new JVM and JS IR (Intermediate Representation) backends that share logic, simplifying code generation for targets on different platforms. Now, the next stage of its evolution brings a new frontend known as K2.
With the arrival of the K2 compiler, the Kotlin frontend has been completely rewritten and features a new, more efficient architecture. The fundamental change the new compiler brings is the use of one unified data structure that contains more semantic information. This frontend is responsible for performing semantic analysis, call resolution, and type inference.
The new architecture and enriched data structure enables the K2 compiler to provide the following benefits:
- Improved call resolution and type inference. The compiler behaves more consistently and understands your code better.
- Easier introduction of syntactic sugar for new language features. In the future, you'll be able to use more concise, readable code when new features are introduced.
- Faster compilation times. Compilation times can be significantly faster.
Enhanced IDE performance. If you enable K2 Kotlin mode in IntelliJ IDEA, then IntelliJ IDEA will use the K2 compiler frontend to analyze your Kotlin code, bringing stability and performance improvements. For more information, see Support in IntelliJ IDEA.
The K2 Kotlin mode is in Alpha. The performance and stability of code highlighting and code completion have been improved, but not all IDE features are supported yet.
Thanks to the new K2 compiler, we've already made improvements to some language features.
This guide:
- Explains the benefits of the new K2 compiler.
- Highlights changes you might encounter during migration and how to adapt your code accordingly.
- Describes how you can roll back to the previous version.
The new K2 compiler is enabled by default starting with 2.0.0-RC3. For more information on the new features provided in Kotlin 2.0.0, as well as the new K2 compiler, see What's new in Kotlin 2.0.0-RC3.
Language feature improvements
The Kotlin K2 compiler improves language features related to smart-casting and Kotlin Multiplatform.
Smart casts
The Kotlin compiler can automatically cast an object to a type in specific cases, saving you the trouble of having to explicitly specify it yourself. This is called smart-casting. The Kotlin K2 compiler now performs smart casts in even more scenarios than before.
In Kotlin 2.0.0, we've made improvements related to smart casts in the following areas:
- Local variables and further scopes
- Type checks with the logical
or
operator - Inline functions
- Properties with function types
- Exception handling
- Increment and decrement operators
Local variables and further scopes
Previously, if a variable was evaluated as not null
within an if
condition, the variable would be smart-cast.
Information about this variable would then be shared further within the scope of the if
block.
However, if you declared the variable outside the if
condition, no information about the variable would be available
within the if
condition, so it couldn't be smart-cast. This behavior was also seen with when
expressions and while
loops.
From Kotlin 2.0.0, if you declare a variable before using it in your if
, when
, or while
condition, then any
information collected by the compiler about the variable will be accessible in the condition statement and its block for
smart-casting.
This can be useful when you want to do things like extract boolean conditions into variables. Then, you can give the variable a meaningful name, which will improve your code readability and make it possible to reuse the variable later in your code. For example:
class Cat {
fun purr() {
println("Purr purr")
}
}
fun petAnimal(animal: Any) {
val isCat = animal is Cat
if (isCat) {
// In Kotlin 2.0.0, the compiler can access
// information about isCat, so it knows that
// animal was smart-cast to the type Cat.
// Therefore, the purr() function is successfully called.
// In Kotlin 1.9.20, the compiler doesn't know
// about the smart cast, so calling the purr()
// function triggers an error.
animal.purr()
}
}
fun main(){
val kitty = Cat()
petAnimal(kitty)
// Purr purr
}
Type checks with the logical or
operator
In Kotlin 2.0.0, if you combine type checks for objects with an or
operator (||
), a smart cast
is made to their closest common supertype. Before this change, a smart cast was always made to the Any
type.
In this case, you still had to manually check the object type afterward before you could access any of its properties or call its functions. For example:
interface Status {
fun signal() {}
}
interface Ok : Status
interface Postponed : Status
interface Declined : Status
fun signalCheck(signalStatus: Any) {
if (signalStatus is Postponed || signalStatus is Declined) {
// signalStatus is smart-cast to a common supertype Status
signalStatus.signal()
// Prior to Kotlin 2.0.0-RC3, signalStatus is smart cast
// to type Any, so calling the signal() function triggered an
// Unresolved reference error. The signal() function can only
// be called successfully after another type check:
// check(signalStatus is Status)
// signalStatus.signal()
}
}
The common supertype is an approximation of a union type. Union types are not currently supported in Kotlin.
Inline functions
In Kotlin 2.0.0, the K2 compiler treats inline functions differently, allowing it to determine in combination with other compiler analyses whether it's safe to smart-cast.
Specifically, inline functions are now treated as having an implicit callsInPlace
contract. This means that any lambda functions passed to an inline function are called in place. Since lambda functions
are called in place, the compiler knows that a lambda function can't leak references to any variables contained within
its function body.
The compiler uses this knowledge along with other compiler analyses to decide whether it's safe to smart-cast any of the captured variables. For example:
interface Processor {
fun process()
}
inline fun inlineAction(f: () -> Unit) = f()
fun nextProcessor(): Processor? = null
fun runProcessor(): Processor? {
var processor: Processor? = null
inlineAction {
// In Kotlin 2.0.0, the compiler knows that processor
// is a local variable and inlineAction() is an inline function, so
// references to processor can't be leaked. Therefore, it's safe
// to smart-cast processor.
// If processor isn't null, processor is smart-cast
if (processor != null) {
// The compiler knows that processor isn't null, so no safe call
// is needed
processor.process()
// In Kotlin 1.9.20, you have to perform a safe call:
// processor?.process()
}
processor = nextProcessor()
}
return processor
}
Properties with function types
In previous versions of Kotlin, there was a bug that meant that class properties with a function type weren't smart-cast. We fixed this behavior in Kotlin 2.0.0 and the K2 compiler. For example:
class Holder(val provider: (() -> Unit)?) {
fun process() {
// In Kotlin 2.0.0, if provider isn't null,
// it is smart-cast
if (provider != null) {
// The compiler knows that provider isn't null
provider()
// In 1.9.20, the compiler doesn't know that provider isn't
// null, so it triggers an error:
// Reference has a nullable type '(() -> Unit)?', use explicit '?.invoke()' to make a function-like call instead
}
}
}
This change also applies if you overload your invoke
operator. For example:
interface Provider {
operator fun invoke()
}
interface Processor : () -> String
class Holder(val provider: Provider?, val processor: Processor?) {
fun process() {
if (provider != null) {
provider()
// In 1.9.20, the compiler triggers an error:
// Reference has a nullable type 'Provider?', use explicit '?.invoke()' to make a function-like call instead
}
}
}
Exception handling
In Kotlin 2.0.0, we've made improvements to exception handling so that smart cast information can be passed
on to catch
and finally
blocks. This change makes your code safer as the compiler keeps track of whether
your object has a nullable type. For example:
//sampleStart
fun testString() {
var stringInput: String? = null
// stringInput is smart-cast to String type
stringInput = ""
try {
// The compiler knows that stringInput isn't null
println(stringInput.length)
// 0
// The compiler rejects previous smart cast information for
// stringInput. Now stringInput has the String? type.
stringInput = null
// Trigger an exception
if (2 > 1) throw Exception()
stringInput = ""
} catch (exception: Exception) {
// In Kotlin 2.0.0, the compiler knows stringInput
// can be null, so stringInput stays nullable.
println(stringInput?.length)
// null
// In Kotlin 1.9.20, the compiler says that a safe call isn't
// needed, but this is incorrect.
}
}
//sampleEnd
fun main() {
testString()
}
Increment and decrement operators
Prior to Kotlin 2.0.0, the compiler didn't understand that the type of an object can change after using an increment or decrement operator. As the compiler couldn't accurately track the object type, your code could lead to unresolved reference errors. In Kotlin 2.0.0, this has been fixed:
interface Rho {
operator fun inc(): Sigma = TODO()
}
interface Sigma : Rho {
fun sigma() = Unit
}
interface Tau {
fun tau() = Unit
}
fun main(input: Rho) {
var unknownObject: Rho = input
// Check if unknownObject inherits from the Tau interface
if (unknownObject is Tau) {
// Use the overloaded inc() operator from interface Rho,
// which smart-casts the type of unknownObject to Sigma.
++unknownObject
// In Kotlin 2.0.0, the compiler knows unknownObject has type
// Sigma, so the sigma() function is called successfully.
unknownObject.sigma()
// In Kotlin 1.9.20, the compiler thinks unknownObject has type
// Tau, so calling the sigma() function throws an error.
// In Kotlin 2.0.0, the compiler knows unknownObject has type
// Sigma, so calling the tau() function throws an error.
unknownObject.tau()
// Unresolved reference 'tau'
// In Kotlin 1.9.20, the compiler mistakenly thinks that
// unknownObject has type Tau, so the tau() function is
// called successfully.
}
}
Kotlin Multiplatform
There are improvements in the K2 compiler related to Kotlin Multiplatform in the following areas:
- Separation of common and platform sources during compilation
- Different visibility levels of expected and actual declarations
Separation of common and platform sources during compilation
Previously, the design of the Kotlin compiler prevented it from keeping common and platform source sets separate at compile time. As a consequence, common code could access platform code, which resulted in different behavior between platforms. In addition, some compiler settings and dependencies from common code used to leak into platform code.
In Kotlin 2.0.0, our implementation of the new Kotlin K2 compiler included a redesign of the compilation scheme to ensure strict separation between common and platform source sets. This change is most noticeable when you use expected and actual functions. Previously, it was possible for a function call in your common code to resolve to a function in platform code. For example:
Common code | Platform code |
```kotlin fun foo(x: Any) = println("common foo") fun exampleFunction() { foo(42) } ``` | ```kotlin // JVM fun foo(x: Int) = println("platform foo") // JavaScript // There is no foo() function overload on the JavaScript platform ``` |
In this example, the common code has different behavior depending on which platform it is run on:
- On the JVM platform, calling the
foo()
function in the common code results in thefoo()
function from the platform code being called asplatform foo
. - On the JavaScript platform, calling the
foo()
function in the common code results in thefoo()
function from the common code being called ascommon foo
, as there is no such function available in the platform code.
In Kotlin 2.0.0, common code doesn't have access to platform code, so both platforms successfully resolve the foo()
function to the foo()
function in the common code: common foo
.
In addition to the improved consistency of behavior across platforms, we also worked hard to fix cases where there was conflicting behavior between IntelliJ IDEA or Android Studio and the compiler. For instance, when you used expected and actual classes, the following would happen:
Common code | Platform code |
```kotlin expect class Identity { fun confirmIdentity(): String } fun common() { // Before 2.0.0, it triggers an IDE-only error Identity().confirmIdentity() // RESOLUTION_TO_CLASSIFIER : Expected class Identity has no default constructor. } ``` | ```kotlin actual class Identity { actual fun confirmIdentity() = "expect class fun: jvm" } ``` |
In this example, the expected class Identity
has no default constructor, so it can't be called successfully in common code.
Previously, an error was only reported by the IDE, but the code still compiled successfully on the JVM. However, now the
compiler correctly reports an error:
Expected class 'expect class Identity : Any' does not have default constructor
When resolution behavior doesn't change
We're still in the process of migrating to the new compilation scheme, so the resolution behavior is still the same when you call functions that aren't within the same source set. You'll notice this difference mainly when you use overloads from a multiplatform library in your common code.
Suppose you have a library, which has two whichFun()
functions with different signatures:
// Example library
// MODULE: common
fun whichFun(x: Any) = println("common function")
// MODULE: JVM
fun whichFun(x: Int) = println("platform function")
If you call the whichFun()
function in your common code, the function that has the most relevant argument type in the
library will be resolved:
// A project that uses the example library for the JVM target
// MODULE: common
fun main(){
whichFun(2)
// platform function
}
In comparison, if you declare the overloads for whichFun()
within the same source set, the function from the common
code will be resolved because your code doesn't have access to the platform-specific version:
// Example library isn't used
// MODULE: common
fun whichFun(x: Any) = println("common function")
fun main(){
whichFun(2)
// common function
}
// MODULE: JVM
fun whichFun(x: Int) = println("platform function")
Similar to multiplatform libraries, since the commonTest
module is in a separate source set, it also still has access
to platform-specific code. Therefore, the resolution of calls to functions in the commonTest
module exhibits the same
behavior as in the old compilation scheme.
In the future, these remaining cases will be more consistent with the new compilation scheme.
Different visibility levels of expected and actual declarations
Before Kotlin 2.0.0, if you used expected and actual declarations in your Kotlin Multiplatform project, they had to have the same visibility level. Kotlin 2.0.0 supports different visibility levels only if the actual declaration is less strict than the expected declaration. For example:
expect internal class Attribute // Visibility is internal
actual class Attribute // Visibility is public by default, which is less strict
If you are using a type alias in your actual declaration, the visibility of the type must be less
strict. Any visibility modifiers for actual typealias
are ignored. For example:
expect internal class Attribute // Visibility is internal
internal actual typealias Attribute = Expanded // The internal visibility modifier is ignored
class Expanded // Visibility is public by default, which is less strict
How to enable the Kotlin K2 compiler
Starting with Kotlin 2.0.0-RC3, the Kotlin K2 compiler is enabled by default.
To upgrade the Kotlin version, change it to 2.0.0-RC3 in your Gradle and Maven build scripts.
Use Kotlin build reports with Gradle
Kotlin build reports provide information about the time spent in different compilation phases for Kotlin compiler tasks, as well as which compiler and Kotlin version were used, and whether the compilation was incremental. These build reports are useful for assessing your build performance. They offer more insight into the Kotlin compilation pipeline than Gradle build scans do because they give you an overview of the performance of all Gradle tasks.
How to enable build reports
To enable build reports, declare where you'd like to save the build report output in your gradle.properties
file:
kotlin.build.report.output=file
The following values and their combinations are available for the output:
Option | Description |
---|---|
file |
This option saves build reports in a human-readable format to a local file. By default, this is ${project_folder}/build/reports/kotlin-build/${project_name}-timestamp.txt . |
json |
This option saves build reports in JSON format as ${project_name}-date-time.json in the directory specified in kotlin.build.report.json.directory="my/dir/path" . |
single_file |
This option saves build reports in a specified local file in the format of an object. |
build_scan |
This option saves build reports in the custom values section of the build scan. Note that the Gradle Enterprise plugin limits the number of custom values and their length. In big projects, some values could be lost. |
http |
This option posts build reports using HTTP(S). The POST method sends metrics in JSON format. You can see the current version of the sent data in the Kotlin repository. Examples of HTTP endpoints can be found in this blog post. |
For more information on what is possible with build reports, see Build reports.
Try the Kotlin K2 compiler in the Kotlin Playground
The Kotlin Playground supports the 2.0.0-RC3 release. Check it out!
Support in IntelliJ IDEA
IntelliJ IDEA can use the new K2 compiler to analyze your code with its K2 Kotlin mode from IntelliJ IDEA 2024.1.
K2 Kotlin mode is in Alpha. The performance and stability of code highlighting and code completion have been improved, but not all IDE features are supported yet.
How to roll back to the previous compiler
To use the previous compiler in Kotlin 2.0.0-RC3, either:
In your
build.gradle.kts
file, set your language version to1.9
.OR
- Use the following compiler option:
-language-version 1.9
.
Changes
With the introduction of the new frontend, the Kotlin compiler has undergone several changes. Let's start by highlighting the most significant modifications affecting your code, explaining what has changed and detailing best practices going forward. If you'd like to learn more, we've organized these changes into subject areas to facilitate your further reading.
This section highlights the following modifications:
- Immediate initialization of open properties with backing fields
- Deprecated synthetic setters on a projected receiver
- Forbidden function calls with inaccessible types
- Consistent resolution order of Kotlin properties and Java fields with the same name
- Improved null safety for Java primitive arrays
Immediate initialization of open properties with backing fields
What's changed?
In Kotlin 2.0, all open
properties with backing fields must be immediately initialized; otherwise, you'll get a
compilation error. Previously, only open var
properties needed to be initialized right away, but now this extends
to open val
properties with backing fields too:
open class Base {
open val a: Int
open var b: Int
init {
// Error starting with Kotlin 2.0 that earlier compiled successfully
this.a = 1 //Error: open val must have initializer
// Always an error
this.b = 1 // Error: open var must have initializer
}
}
class Derived : Base() {
override val a: Int = 2
override var b = 2
}
This change makes the compiler's behavior more predictable. Consider an example where an open val
property is overridden
by a var
property with a custom setter.
If a custom setter is used, deferred initialization can lead to confusion because it's unclear whether you want to initialize the backing field or to invoke the setter. In the past, if you wanted to invoke the setter, the old compiler couldn't guarantee that the setter would then initialize the backing field.
What's the best practice now?
We encourage you to always initialize open properties with backing fields, as we believe this practice is both more efficient and less error-prone.
However, if you don't want to immediately initialize a property, you can:
- Make the property
final
. - Use a private backing property that allows for deferred initialization.
Deprecated synthetics setter on a projected receiver
What's changed?
If you use the synthetic setter of a Java class to assign a type that conflicts with the class's projected type, an error is triggered.
Suppose you have a Java class named Container
that contains the getFoo()
and setFoo()
methods:
public class Container<E> {
public E getFoo() {
return null;
}
public void setFoo(E foo) {}
}
If you have the following Kotlin code, where instances of the Container
class have projected types, using the setFoo()
method will always generate an error. However, only from Kotlin 2.0.0 will the synthetic foo
property trigger an error:
fun exampleFunction(starProjected: Container<*>, inProjected: Container<in Number>, sampleString: String) {
starProjected.setFoo(sampleString)
// Error since Kotlin 1.0
// Synthetic setter `foo` is resolved to the `setFoo()` method
starProjected.foo = sampleString
// Error since Kotlin 2.0.0
inProjected.setFoo(sampleString)
// Error since Kotlin 1.0
// Synthetic setter `foo` is resolved to the `setFoo()` method
inProjected.foo = sampleString
// Error since Kotlin 2.0.0
}
What's the best practice now?
If you see that this change introduces errors in your code, you might wish to reconsider how you structure your type declarations. It could be that you don't need to use type projections, or perhaps you need to remove any assignments from your code.
For more information, see the corresponding issue in YouTrack.
Forbidden function calls with inaccessible types
What's changed?
Before Kotlin 2.0.0, if you declared or called a function that had lambda parameters with an inaccessible type, your code could compile, but you might encounter compiler crashes later. In Kotlin 2.0.0, it is forbidden to declare or call a function that has an inaccessible type.
For example, let's say that you declared a class in one module:
// Module one
class Some(val x: Int)
If you have another module (module two) that has a dependency configured on module one, your code can access the Some
class and use it as a type in lambda expressions:
// Module two
fun foo(f: (Some, String) -> Unit) {}
fun bar(f: (Some) -> Unit) {}
// Both functions compile successfully
However, if you have a third module (module three) that depends only on module two, the third module won't be able to
access the Some
class in module one. As a result, the dependency won't be transitive between modules. Now, any
functions in module three that have lambda parameters with the Some
type will trigger errors in Kotlin 2.0.0, thus
preventing crashes later in your code:
// Module three
fun test() {
// Triggers an error in Kotlin 2.0.0, as the unused lambda
// parameters (_) resolve to Some, which is inaccessible
foo { _, _ -> }
// Triggers a warning in Kotlin 2.0.0, as using an instance of Some
// isn't possible because Some is inaccessible
foo { some, str -> }
}
Errors are also introduced in Kotlin 2.0.0 for some scenarios involving generic classes. Consider, for example, the same arrangement of three modules but with generic classes:
Module one | Module two |
```kotlin
// Module one
class Some |
```kotlin
// Module two
fun foo(f: (Some |
In the third module, if you use the gen()
function from module two to create an instance of the Generic
class with
the type Some<String>
and then assign it to a variable z
, this operation is successful because the gen()
function
has access to the Some<T>
class in module one. However, if you try to pass the variable z
to a function declared
in module three, an error is triggered because functions declared in module three don't have access to the Some<T>
class
in module one:
// Module three
fun test() {
// Triggers a warning in Kotlin 2.0.0
val z = gen()
// Triggers an error in Kotlin 2.0.0
takeString(z)
}
What's the best practice now?
To avoid these problems, you can configure a direct dependency for module three on module one.
Alternatively, you can adapt your code to make the types accessible within the same module.
For more information, see the corresponding issue in YouTrack.
Consistent resolution order of Kotlin properties and Java fields with the same name
What's changed?
Before Kotlin 2.0.0, if you worked with Java and Kotlin classes that inherited from each other and contained Kotlin properties and Java fields with the same name, the resolution behavior of the duplicated name was inconsistent. There was also conflicting behavior between IntelliJ IDEA and the compiler. When developing the new resolution behavior for Kotlin 2.0.0, we aimed to cause the least impact to users.
For example, suppose there is a Java class Base
:
public class Base {
public String a = "a";
public String b = "b";
}
Let's say there is also a Kotlin class Derived
that inherits from the aforementioned Base
class:
class Derived : Base() {
val a = "aa"
// Declares custom get() function
val b get() = "bb"
}
fun main() {
// Resolves Derived.a
println(a)
// aa
// Resolves Base.b
println(b)
// b
}
Prior to Kotlin 2.0.0, a
resolves to the Kotlin property within the Derived
Kotlin class, whereas b
resolves to
the Java field in the Base
Java class.
In Kotlin 2.0.0, the resolution behavior in the example is consistent, ensuring that the Kotlin property supersedes the
Java field of the same name. Now, b
resolves to: Derived.b
.
Prior to Kotlin 2.0.0, if you used IntelliJ IDEA to go to the declaration or usage of
a
, it would incorrectly navigate to the Java field when it should have navigated to the Kotlin property.From Kotlin 2.0.0, IntelliJ IDEA correctly navigates to the same location as the compiler.
{type ="note"}
The general rule is that the subclass takes precedence. The previous example demonstrates this, as the Kotlin property
a
from the Derived
class is resolved because Derived
is a subclass of the Base
Java class.
In the event that the inheritance is reversed and a Java class inherits from a Kotlin class, the Java field in the subclass takes precedence over the Kotlin property with the same name.
Consider this example:
Kotlin | Java |
```kotlin open class Base { val a = "aa" } ``` | ```java public class Derived extends Base { public String a = "a"; } ``` |
Now in the following code:
fun main() {
// Resolves Derived.a
println(a)
// a
}
What's the best practice now?
If this change affects your code, consider whether you really need to use duplicate names. If you want to have Java or Kotlin classes that each contain a field or property with the same name and that each inherit from one another, keep in mind that the field or property in the subclass will take precedence.
For more information, see the corresponding issue in YouTrack.
Improved null safety for Java primitive arrays
What's changed?
Starting with Kotlin 2.0.0, the compiler correctly infers the nullability of Java primitive arrays imported to Kotlin.
Now, it retains native nullability from the TYPE_USE
annotations used with Java primitive arrays and emits errors when
their values are not used according to annotations.
Usually, when Java types with @Nullable
and @NotNull
annotations are called from Kotlin, they receive the appropriate
native nullability:
interface DataService {
@NotNull ResultContainer<@Nullable String> fetchData();
}
val dataService: DataService = ...
dataService.fetchData() // -> ResultContainer<String?>
Previously, however, when Java primitive arrays were imported to Kotlin, all TYPE_USE
annotations were lost, resulting
in platform nullability and possibly unsafe code:
interface DataProvider {
int @Nullable [] fetchData();
}
val dataService: DataProvider = ...
dataService.fetchData() // -> IntArray .. IntArray?
// No error, even though `dataService.fetchData()` might be `null` according to annotations
// This might result in a NullPointerException
dataService.fetchData()[0]
Note that this issue never affected nullability annotations on the declaration itself, only the TYPE_USE
ones.
What's the best practice now?
In Kotlin 2.0.0, null safety for Java primitive arrays is now standard in Kotlin, so check your code for new warnings and errors if you use them:
- Any code that uses a
@Nullable
Java primitive array without an explicit nullability check or attempts to passnull
to a Java method expecting a non-nullable primitive array will now fail to compile. - Using a
@NotNull
primitive array with a nullability check now emits "Unnecessary safe call" or "Comparison with null always false" warnings.
For more information, see the corresponding issue in YouTrack.
Per subject area
These subject areas list changes that are unlikely to affect your code but provide links to the relevant YouTrack issues for further reading. Changes listed with an asterisk (*) next to the Issue ID are explained at the beginning of the section.
Type inference
Issue ID | Title |
---|---|
KT-64189 | Incorrect type in compiled function signature of property reference if the type is Normal explicitly |
KT-47986 | Forbid implicit inferring a type variable into an upper bound in the builder inference context |
KT-59275 | K2: Require explicit type arguments for generic annotation calls in array literals |
KT-53752 | Missed subtyping check for an intersection type |
KT-59138 | Change Java type parameter based types default representation in Kotlin |
KT-57178 | Change inferred type of prefix increment to return type of getter instead of return type of inc() operator |
KT-57609 | K2: Stop relying on the presence of @UnsafeVariance using for contravariant parameters |
KT-57620 | K2: Forbid resolution to subsumed members for raw types |
KT-64641 | K2: Properly inferred type of callable reference to a callable with extension-function parameter |
KT-57011 | Make real type of a destructuring variable consistent with explicit type when specified |
KT-38895 | K2: Fix inconsistent behavior with integer literals overflow |
KT-54862 | Anonymous type can be exposed from anonymous function from type argument |
KT-22379 | Condition of while-loop with break can produce unsound smartcast |
KT-62507 | K2: Prohibit smart cast in common code for expect/actual top-level property |
KT-65750 | Increment and plus operators that change return type must affect smart casts |
KT-65349 | [LC] K2: specifying variable types explicitly breaks bound smart casts in some cases that worked in K1 |
Generics
Issue ID | Title |
---|---|
KT-54309* | Deprecate use of a synthetic setter on a projected receiver |
KT-57600 | Forbid overriding of Java method with raw-typed parameter with generic typed parameter |
KT-54663 | Forbid passing possibly nullable type parameter to `in` projected DNN parameter |
KT-54066 | Deprecate upper bound violation in typealias constructors |
KT-49404 | Fix type unsoundness for contravariant captured type based on Java class |
KT-61718 | Forbid unsound code with self upper bounds and captured types |
KT-61749 | Forbid unsound bound violation in generic inner class of generic outer class |
KT-62923 | K2: Introduce PROJECTION_IN_IMMEDIATE_ARGUMENT_TO_SUPERTYPE for projections of outer super types of inner class |
KT-63243 | Report MANY_IMPL_MEMBER_NOT_IMPLEMENTED when inheriting from collection of primitives with an extra specialized implementation from another supertype |
KT-60305 | K2: Prohibit constructor call and inheritance on type alias that has variance modifiers in expanded type |
KT-64965 | Fix type hole caused by improper handling of captured types with self-upper bounds |
KT-64966 | Forbid generic delegating constructor calls with wrong type for generic parameter |
KT-65712 | Report missing upper bound violation when upper bound is captured type |
Resolution
Issue ID | Title |
---|---|
KT-55017* | Choose Kotlin property from derived class during overload resolution with Java field from base class |
KT-58260 | Make invoke convention works consistently with expected desugaring |
KT-62866 | K2: Change qualifier resolution behavior when companion object is preferred against static scope |
KT-57750 | Report ambiguity error when resolving types and having the same-named classes star imported |
KT-63558 | K2: migrate resolution around COMPATIBILITY_WARNING |
KT-51194 | False negative CONFLICTING_INHERITED_MEMBERS when dependency class contained in two different versions of the same dependency |
KT-37592 | Property invoke of a functional type with receiver is preferred over extension function invoke |
KT-51666 | Qualified this: introduce/prioritize this qualified with type case |
KT-54166 | Confirm unspecified behavior in case of FQ name conflicts in classpath |
KT-64431 | K2: forbid using typealiases as qualifier in imports |
KT-56520 | K1/K2: incorrect work of resolve tower for type references with ambiguity at lower level |
Visibility
Issue ID | Title |
---|---|
KT-64474* | Declare usages of inaccessible types as unspecified behavior |
KT-55179 | False negative PRIVATE_CLASS_MEMBER_FROM_INLINE on calling private class companion object member from internal inline function |
KT-58042 | Make synthetic property invisible if equivalent getter is invisible even when overridden declaration is visible |
KT-64255 | Forbid accessing internal setter from a derived class in another module |
KT-33917 | Prohibit to expose anonymous types from private inline functions |
KT-54997 | Forbid implicit non-public-API accesses from public-API inline function |
KT-56310 | Smart casts should not affect visibility of protected members |
KT-65494 | Forbid access to overlooked private operator functions from public inline function |
KT-65004 | K1: Setter of var, which overrides protected val, is generates as public |
KT-64972 | Forbid overriding by private members in link-time for Kotlin/Native |
Annotations
Issue ID | Title |
---|---|
KT-58723 | Forbid annotating statements with an annotation if it has no EXPRESSION target |
KT-49930 | Ignore parentheses expression during `REPEATED_ANNOTATION` checking |
KT-57422 | K2: Prohibit use-site 'get' targeted annotations on property getters |
KT-46483 | Prohibit annotation on type parameter in where clause |
KT-64299 | Companion scope is ignored for resolution of annotations on companion object |
KT-64654 | K2: Introduced ambiguity between user and compiler-required annotations |
KT-64527 | Annotations on enum values shouldn't be copied to enum value classes |
KT-63389 | K2: `WRONG_ANNOTATION_TARGET` is reported on incompatible annotations of a type wrapped into `()?` |
KT-63388 | K2: `WRONG_ANNOTATION_TARGET` is reported on catch parameter type's annotations |
Null safety
Issue ID | Title |
---|---|
KT-54521* | Deprecate unsafe usages of array types annotated as Nullable in Java |
KT-41034 | K2: Change evaluation semantics for combination of safe calls and convention operators |
KT-50850 | Order of supertypes defines nullability parameters of inherited functions |
KT-53982 | Keep nullability when approximating local types in public signatures |
KT-62998 | Forbid assignment of a nullable to a not-null Java field as a selector of unsafe assignment |
KT-63209 | Report missing errors for error-level nullable arguments of warning-level Java types |
Java interoperability
Issue ID | Title |
---|---|
KT-53061 | Forbid Java and Kotlin classes with the same FQ name in sources |
KT-49882 | Classes inherited from Java collections have inconsistent behavior depending on order of supertypes |
KT-66324 | K2: unspecified behavior in case of Java class inheritance from a Kotlin private class |
KT-66220 | Passing java vararg method to inline function leads to array of arrays in runtime instead of just an array |
KT-66204 | Allow to override internal members in K-J-K hierarchy |
Properties
Issue ID | Title |
---|---|
KT-57555* | [LC] Forbid deferred initialization of open properties with backing field |
KT-58589 | Deprecate missed MUST_BE_INITIALIZED when no primary constructor is presented or when class is local |
KT-64295 | Forbid recursive resolve in case of potential invoke calls on properties |
KT-57290 | Deprecate smart cast on base class property from invisible derived class if base class is from another module |
KT-62661 | K2: Missed OPT_IN_USAGE_ERROR for data class properties |
Control flow
Issue ID | Title |
---|---|
KT-56408 | Inconsistent rules of CFA in class initialization block between K1 and K2 |
KT-57871 | K1/K2 inconsistency on if-conditional without else-branch in parenthesis |
KT-42995 | False negative "VAL_REASSIGNMENT" in try/catch block with initialization in scope function |
KT-65724 | Propagate data flow information from try block to catch and finally blocks |
Enum classes
Issue ID | Title |
---|---|
KT-57608 | Prohibit access to the companion object of enum class during initialization of enum entry |
KT-34372 | Report missed error for virtual inline method in enum classes |
KT-52802 | Report ambiguity resolving between property/field and enum entry |
KT-47310 | Change qualifier resolution behavior when companion property is preferred against enum entry |
Functional (SAM) interfaces
Issue ID | Title |
---|---|
KT-52628 | Deprecate SAM constructor usages which require OptIn without annotation |
KT-57014 | Prohibit returning values with incorrect nullability from lambda for SAM constructor of JDK function interfaces |
KT-64342 | SAM conversion of parameter types of callable references leads to CCE |
Companion object
Issue ID | Title |
---|---|
KT-54316 | Out-of-call reference to companion object's member has invalid signature |
KT-47313 | Change (V)::foo reference resolution when V has a companion |
Miscellaneous
Issue ID | Title | ||
---|---|---|---|
KT-49015 | Qualified this: change behavior in case of potential label conflicts | ||
KT-56545 | Fix incorrect functions mangling in JVM backend in case of accidental clashing overload in a Java subclass | ||
KT-62019 | [LC issue] Prohibit suspend-marked anonymous function declarations in statement positions | ||
KT-55111 | OptIn: forbid constructor calls with default arguments under marker | ||
KT-61182 | Unit conversion is accidentally allowed to be used for expressions on variables + invoke resolution | ||
KT-55199 | Forbid promoting callable references with adaptations to KFunction | ||
KT-65776 | [LC] K2 breaks `false && ...` and `false \ | \ | ...` |
KT-65682 | [LC] Deprecate `header`/`impl` keywords |
Compatibility with Kotlin releases
The following Kotlin releases have support for the new K2 compiler:
Kotlin release | Stability level |
---|---|
2.0.0 | Stable |
1.9.20–1.9.24 | Beta |
1.9.0–1.9.10 | JVM is Beta |
1.7.0–1.8.22 | Alpha |
Compiler plugins support
Currently, the Kotlin K2 compiler supports the following Kotlin compiler plugins:
all-open
- AtomicFU
jvm-abi-gen
js-plain-objects
- kapt
- Lombok
no-arg
- Parcelize
- SAM with receiver
- Serialization
In addition, the Kotlin K2 compiler supports:
- The Jetpack Compose 1.5.0 compiler plugin and later versions.
- The Kotlin Symbol Processing (KSP) plugin since KSP2.
If you use any additional compiler plugins, check their documentation to see if they are compatible with K2.
Share your feedback on the new K2 compiler
We would appreciate any feedback you may have!
- Provide your feedback directly to K2 developers in the Kotlin Slack workspace – get an invite and join the #k2-early-adopters channel.
- Report any problems you face migrating to the new K2 compiler in our issue tracker.
- Enable the Send usage statistics option to allow JetBrains to collect anonymous data about K2 usage.