JVM_IR: Lower IrGetEnumValue to this whenever possible.
The reference can be lowered to `this` if it is captured in the lexical scope of the corresponding enum entry, and not used by the enum entry's super constructor. Otherwise, it is lowered to `GETFIELD SomeEnum.SomeEntry`.
This commit is contained in:
committed by
max-kammerer
parent
faa6eacb25
commit
0dd09ea7de
@@ -81,6 +81,7 @@ val jvmPhases = namedIrFilePhase(
|
|||||||
|
|
||||||
makePatchParentsPhase(1) then
|
makePatchParentsPhase(1) then
|
||||||
|
|
||||||
|
singletonReferencesPhase then
|
||||||
jvmLocalDeclarationsPhase then
|
jvmLocalDeclarationsPhase then
|
||||||
singleAbstractMethodPhase then
|
singleAbstractMethodPhase then
|
||||||
callableReferencePhase then
|
callableReferencePhase then
|
||||||
@@ -95,7 +96,6 @@ val jvmPhases = namedIrFilePhase(
|
|||||||
enumClassPhase then
|
enumClassPhase then
|
||||||
objectClassPhase then
|
objectClassPhase then
|
||||||
makeInitializersPhase(JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, true) then
|
makeInitializersPhase(JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, true) then
|
||||||
singletonReferencesPhase then
|
|
||||||
syntheticAccessorPhase then
|
syntheticAccessorPhase then
|
||||||
bridgePhase then
|
bridgePhase then
|
||||||
jvmOverloadsAnnotationPhase then
|
jvmOverloadsAnnotationPhase then
|
||||||
|
|||||||
+72
-10
@@ -5,14 +5,17 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.jvm.lower
|
package org.jetbrains.kotlin.backend.jvm.lower
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
|
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
|
||||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||||
|
import org.jetbrains.kotlin.backend.common.pop
|
||||||
|
import org.jetbrains.kotlin.backend.common.push
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrBody
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrGetEnumValue
|
import org.jetbrains.kotlin.ir.declarations.IrSymbolOwner
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrGetObjectValue
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
|
|
||||||
@@ -22,18 +25,77 @@ internal val singletonReferencesPhase = makeIrFilePhase(
|
|||||||
description = "Handle singleton references"
|
description = "Handle singleton references"
|
||||||
)
|
)
|
||||||
|
|
||||||
private class SingletonReferencesLowering(val context: JvmBackendContext) : BodyLoweringPass, IrElementTransformerVoid() {
|
private class SingletonReferencesLowering(val context: JvmBackendContext) : ClassLoweringPass, IrElementTransformerVoid() {
|
||||||
override fun lower(irBody: IrBody) {
|
private lateinit var containingClass: IrClass
|
||||||
irBody.transformChildrenVoid(this)
|
private val constructingEnums = arrayListOf()
|
||||||
|
|
||||||
|
override fun lower(irClass: IrClass) {
|
||||||
|
containingClass = irClass
|
||||||
|
irClass.transformChildrenVoid(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitEnumConstructorCall(expression: IrEnumConstructorCall): IrExpression {
|
||||||
|
constructingEnums.push(expression.symbol.owner.parent)
|
||||||
|
val call = super.visitEnumConstructorCall(expression)
|
||||||
|
constructingEnums.pop()
|
||||||
|
return call
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitGetEnumValue(expression: IrGetEnumValue): IrExpression {
|
override fun visitGetEnumValue(expression: IrGetEnumValue): IrExpression {
|
||||||
val entrySymbol = context.declarationFactory.getFieldForEnumEntry(expression.symbol.owner, expression.type)
|
val candidate = expression.symbol.owner.correspondingClass
|
||||||
return IrGetFieldImpl(expression.startOffset, expression.endOffset, entrySymbol.symbol, expression.type)
|
|
||||||
|
return if (candidate != null && isInScope(candidate) && !isVisitingSuperConstructor(candidate)) {
|
||||||
|
// Replace `SomeEnumClass.SomeEnumEntry` with `this`, if possible.
|
||||||
|
//
|
||||||
|
// SomeEnumEntry is a singleton, which is assigned (SETFIELD) to SomeEnumClass after the construction of the singleton is done.
|
||||||
|
// Therefore, during the construction of SomeEnumEntry, SomeEnumClass.SomeEnumEntry isn't available yet. All references to it
|
||||||
|
// must be replaced with `SomeEnumEntry.this`.
|
||||||
|
IrGetValueImpl(expression.startOffset, expression.endOffset, expression.type, candidate.thisReceiver!!.symbol)
|
||||||
|
} else {
|
||||||
|
val entrySymbol = context.declarationFactory.getFieldForEnumEntry(expression.symbol.owner, expression.type)
|
||||||
|
IrGetFieldImpl(expression.startOffset, expression.endOffset, entrySymbol.symbol, expression.type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression {
|
override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression {
|
||||||
val instanceField = context.declarationFactory.getFieldForObjectInstance(expression.symbol.owner)
|
val instanceField = context.declarationFactory.getFieldForObjectInstance(expression.symbol.owner)
|
||||||
return IrGetFieldImpl(expression.startOffset, expression.endOffset, instanceField.symbol, expression.type)
|
return IrGetFieldImpl(expression.startOffset, expression.endOffset, instanceField.symbol, expression.type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `this` is generally available while the reference is within the lexical scope of the containing enum entry.
|
||||||
|
private fun isInScope(symbol: IrSymbolOwner?): Boolean {
|
||||||
|
var candidate: IrDeclaration? = containingClass
|
||||||
|
|
||||||
|
while (candidate != null && symbol != candidate)
|
||||||
|
candidate = candidate.parent as? IrDeclaration
|
||||||
|
|
||||||
|
return candidate != null
|
||||||
|
}
|
||||||
|
|
||||||
|
// `this` isn't usable before `super.<init>`. Consider the example,
|
||||||
|
//
|
||||||
|
// 1: enum class Test(val x: String, val closure1: () -> String) {
|
||||||
|
// 2: FOO("O", { FOO.x }) {
|
||||||
|
// 3: val y: String = run { FOO.x }
|
||||||
|
// 4: };
|
||||||
|
// 5: }
|
||||||
|
//
|
||||||
|
// The constructing sequence would look like the following, if the reference was lowered to `this`:
|
||||||
|
//
|
||||||
|
// FOO.<init>(this) {
|
||||||
|
// val lambda1 = new lambda_in_line_1_type
|
||||||
|
// lambda_in_line_1_type.<init>(lambda1, this)
|
||||||
|
// Test.<init>(this, "O", lambda1)
|
||||||
|
// ...
|
||||||
|
// val lambda3 = new lambda_in_line_3_type
|
||||||
|
// lambda_in_line_3_type.<init>(lambda3, this)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Before and after `Test.<init>`, the type of `this` is `uninitializedThis` and `Test`, respectively. Therefore, passing `this` to
|
||||||
|
// `lambda_containing_foo_x_type.<init>` results in a type mismatch. Passing `this` to `lambda_containing_foo_y_type.<init>` is fine.
|
||||||
|
//
|
||||||
|
// Assumptions:
|
||||||
|
// 1. An enum entry's declaration parent is always the enum class.
|
||||||
|
// 2. Enums are constructed in <clinit>, so there's no interleaving constructor calls from unrelated enums.
|
||||||
|
private fun isVisitingSuperConstructor(irClass: IrClass) = irClass.parent == constructingEnums.lastOrNull()
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
|
|
||||||
enum class A {
|
enum class A {
|
||||||
X {
|
X {
|
||||||
val x = "OK"
|
val x = "OK"
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
|
|
||||||
enum class A {
|
enum class A {
|
||||||
X {
|
X {
|
||||||
val k = "K"
|
val k = "K"
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
interface IFoo {
|
interface IFoo {
|
||||||
fun foo(): String
|
fun foo(): String
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
interface IFoo {
|
interface IFoo {
|
||||||
fun foo(): String
|
fun foo(): String
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
interface IFoo {
|
interface IFoo {
|
||||||
fun foo(): String
|
fun foo(): String
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
|
|
||||||
enum class A {
|
enum class A {
|
||||||
X {
|
X {
|
||||||
val x = "OK"
|
val x = "OK"
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
|
|
||||||
enum class A {
|
enum class A {
|
||||||
X {
|
X {
|
||||||
val x = "OK"
|
val x = "OK"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
enum class Test(val x: String, val closure1: () -> String) {
|
enum class Test(val x: String, val closure1: () -> String) {
|
||||||
FOO("O", { FOO.x }) {
|
FOO("O", { FOO.x }) {
|
||||||
override val y: String = "K"
|
override val y: String = "K"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
enum class Test(val x: String, val closure1: () -> String) {
|
enum class Test(val x: String, val closure1: () -> String) {
|
||||||
FOO("O", run { { FOO.x } }) {
|
FOO("O", run { { FOO.x } }) {
|
||||||
override val y: String = "K"
|
override val y: String = "K"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
enum class X {
|
enum class X {
|
||||||
B {
|
B {
|
||||||
val value2 = "K"
|
val value2 = "K"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
enum class X {
|
enum class X {
|
||||||
B {
|
B {
|
||||||
val value2 = "K"
|
val value2 = "K"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
enum class X {
|
enum class X {
|
||||||
B {
|
B {
|
||||||
val value2 = "K"
|
val value2 = "K"
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
|
|
||||||
enum class X {
|
enum class X {
|
||||||
B {
|
B {
|
||||||
val k = "K"
|
val k = "K"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
enum class X {
|
enum class X {
|
||||||
B {
|
B {
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
enum class X {
|
enum class X {
|
||||||
B {
|
B {
|
||||||
override val value2 = "K"
|
override val value2 = "K"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
enum class X {
|
enum class X {
|
||||||
B {
|
B {
|
||||||
override val value2 = "K"
|
override val value2 = "K"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
enum class X {
|
enum class X {
|
||||||
B {
|
B {
|
||||||
val value2 = "K"
|
val value2 = "K"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
fun <T, R> T.letNoInline(fn: (T) -> R) =
|
fun <T, R> T.letNoInline(fn: (T) -> R) =
|
||||||
fn(this)
|
fn(this)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user