JVM_IR: generate instance fields even when reification is needed
The inliner supports that, we just need to generate the reification markers on GETFIELD to instance fields.
This commit is contained in:
+6
@@ -3914,6 +3914,12 @@ public class FirBlackBoxInlineCodegenTestGenerated extends AbstractFirBlackBoxIn
|
||||
runTest("compiler/testData/codegen/boxInline/reified/packages.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("singletonLambda.kt")
|
||||
public void testSingletonLambda() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/reified/singletonLambda.kt");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+2
-1
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.backend.jvm.codegen
|
||||
import org.jetbrains.kotlin.backend.common.psi.PsiSourceManager
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isSyntheticSingleton
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.buildAssertionsDisabledField
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.hasAssertionsDisabledField
|
||||
@@ -312,7 +313,7 @@ class ClassCodegen private constructor(
|
||||
if (field.origin != JvmLoweredDeclarationOrigin.CONTINUATION_CLASS_RESULT_FIELD) {
|
||||
val skipNullabilityAnnotations =
|
||||
flags and (Opcodes.ACC_SYNTHETIC or Opcodes.ACC_ENUM) != 0 ||
|
||||
field.origin == JvmLoweredDeclarationOrigin.FIELD_FOR_STATIC_CALLABLE_REFERENCE_INSTANCE
|
||||
(field.origin == IrDeclarationOrigin.FIELD_FOR_OBJECT_INSTANCE && irClass.isSyntheticSingleton)
|
||||
object : AnnotationCodegen(this@ClassCodegen, context, skipNullabilityAnnotations) {
|
||||
override fun visitAnnotation(descr: String, visible: Boolean): AnnotationVisitor {
|
||||
return fv.visitAnnotation(descr, visible)
|
||||
|
||||
+12
-7
@@ -596,16 +596,10 @@ class ExpressionCodegen(
|
||||
val owner = typeMapper.mapClass(callee.constructedClass)
|
||||
val signature = methodSignatureMapper.mapSignatureSkipGeneric(callee)
|
||||
|
||||
closureReifiedMarkers[expression.symbol.owner.parentAsClass]?.let {
|
||||
if (it.wereUsedReifiedParameters()) {
|
||||
putNeedClassReificationMarker(mv)
|
||||
propagateChildReifiedTypeParametersUsages(it)
|
||||
}
|
||||
}
|
||||
|
||||
// IR constructors have no receiver and return the new instance, but on JVM they are void-returning
|
||||
// instance methods named <init>.
|
||||
markLineNumber(expression)
|
||||
putNeedClassReificationMarker(callee.constructedClass)
|
||||
mv.anew(owner)
|
||||
mv.dup()
|
||||
|
||||
@@ -735,6 +729,9 @@ class ExpressionCodegen(
|
||||
assert(expression.type.isUnit())
|
||||
unitValue
|
||||
} else {
|
||||
if (expression.symbol.owner.origin == IrDeclarationOrigin.FIELD_FOR_OBJECT_INSTANCE) {
|
||||
putNeedClassReificationMarker(expression.symbol.owner.parentAsClass)
|
||||
}
|
||||
mv.visitFieldInsn(if (isStatic) Opcodes.GETSTATIC else Opcodes.GETFIELD, ownerName, fieldName, fieldType.descriptor)
|
||||
MaterialValue(this, fieldType, callee.type)
|
||||
}
|
||||
@@ -914,6 +911,14 @@ class ExpressionCodegen(
|
||||
return unitValue
|
||||
}
|
||||
|
||||
private fun putNeedClassReificationMarker(declaration: IrClass) {
|
||||
val reifiedTypeParameters = closureReifiedMarkers[declaration] ?: return
|
||||
if (reifiedTypeParameters.wereUsedReifiedParameters()) {
|
||||
putNeedClassReificationMarker(mv)
|
||||
propagateChildReifiedTypeParametersUsages(reifiedTypeParameters)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateGlobalReturnFlagIfPossible(expression: IrExpression, label: String) {
|
||||
if (state.isInlineDisabled) {
|
||||
context.psiErrorBuilder.at(expression, irFunction).report(Errors.NON_LOCAL_RETURN_IN_DISABLED_INLINE)
|
||||
|
||||
@@ -413,3 +413,9 @@ inline fun IrElement.hasChild(crossinline block: (IrElement) -> Boolean): Boolea
|
||||
}, null)
|
||||
return result
|
||||
}
|
||||
|
||||
val IrClass.isSyntheticSingleton: Boolean
|
||||
get() = (origin == JvmLoweredDeclarationOrigin.LAMBDA_IMPL
|
||||
|| origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL
|
||||
|| origin == JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE)
|
||||
&& primaryConstructor!!.valueParameters.isEmpty()
|
||||
|
||||
+8
-88
@@ -9,31 +9,18 @@ import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isSyntheticSingleton
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildField
|
||||
import org.jetbrains.kotlin.ir.builders.irCall
|
||||
import org.jetbrains.kotlin.ir.builders.irExprBody
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrField
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
||||
import org.jetbrains.kotlin.ir.util.constructedClass
|
||||
import org.jetbrains.kotlin.ir.util.defaultType
|
||||
import org.jetbrains.kotlin.ir.util.primaryConstructor
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import java.util.*
|
||||
|
||||
internal val staticCallableReferencePhase = makeIrFilePhase(
|
||||
::StaticCallableReferenceLowering,
|
||||
@@ -42,15 +29,13 @@ internal val staticCallableReferencePhase = makeIrFilePhase(
|
||||
)
|
||||
|
||||
class StaticCallableReferenceLowering(val backendContext: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoid() {
|
||||
private val staticInstanceFields = HashMap<IrClass, IrField>()
|
||||
|
||||
override fun lower(irFile: IrFile) = irFile.transformChildrenVoid()
|
||||
|
||||
override fun visitClass(declaration: IrClass): IrStatement {
|
||||
declaration.transformChildrenVoid()
|
||||
if (declaration.isSyntheticSingleton) {
|
||||
declaration.declarations += getFieldForStaticCallableReferenceInstance(declaration).also { field ->
|
||||
field.initializer = backendContext.createIrBuilder(field.symbol).run {
|
||||
declaration.declarations += backendContext.cachedDeclarations.getFieldForObjectInstance(declaration).apply {
|
||||
initializer = backendContext.createIrBuilder(symbol).run {
|
||||
irExprBody(irCall(declaration.primaryConstructor!!))
|
||||
}
|
||||
}
|
||||
@@ -58,77 +43,12 @@ class StaticCallableReferenceLowering(val backendContext: JvmBackendContext) : F
|
||||
return declaration
|
||||
}
|
||||
|
||||
private fun getFieldForStaticCallableReferenceInstance(irClass: IrClass): IrField =
|
||||
staticInstanceFields.getOrPut(irClass) {
|
||||
backendContext.irFactory.buildField {
|
||||
name = Name.identifier(JvmAbi.INSTANCE_FIELD)
|
||||
type = irClass.defaultType
|
||||
origin = JvmLoweredDeclarationOrigin.FIELD_FOR_STATIC_CALLABLE_REFERENCE_INSTANCE
|
||||
isFinal = true
|
||||
isStatic = true
|
||||
visibility = DescriptorVisibilities.PUBLIC
|
||||
}.apply {
|
||||
parent = irClass
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
|
||||
val constructor = expression.symbol.owner
|
||||
if (!constructor.constructedClass.isSyntheticSingleton)
|
||||
val constructedClass = expression.symbol.owner.constructedClass
|
||||
if (!constructedClass.isSyntheticSingleton)
|
||||
return super.visitConstructorCall(expression)
|
||||
|
||||
val instanceField = getFieldForStaticCallableReferenceInstance(constructor.constructedClass)
|
||||
val instanceField = backendContext.cachedDeclarations.getFieldForObjectInstance(constructedClass)
|
||||
return IrGetFieldImpl(expression.startOffset, expression.endOffset, instanceField.symbol, expression.type)
|
||||
}
|
||||
|
||||
// Recognize callable references with no value or type arguments. The only type arguments in Kotlin stem from usages of
|
||||
// reified type parameters, which we unfortunately don't record as parameters so we have to check the body of the class.
|
||||
private val IrClass.isSyntheticSingleton: Boolean
|
||||
get() = (origin == JvmLoweredDeclarationOrigin.LAMBDA_IMPL
|
||||
|| origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL
|
||||
|| origin == JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE)
|
||||
&& primaryConstructor!!.valueParameters.isEmpty()
|
||||
&& !containsReifiedTypeParameters
|
||||
|
||||
// Check whether there is any usage of reified type parameters in the body of the given class. This method does not
|
||||
// distinguish between reified type parameters declared in inline functions inside the class and those coming from the
|
||||
// outside. This is sufficient, because we only apply this function to callable references where this does not matter.
|
||||
private val IrClass.containsReifiedTypeParameters: Boolean
|
||||
get() = containsReifiedTypeParametersCache.getOrPut(this) {
|
||||
var containsReified = false
|
||||
acceptChildrenVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
if (!containsReified)
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitMemberAccess(expression: IrMemberAccessExpression<*>) {
|
||||
for (i in 0 until expression.typeArgumentsCount) {
|
||||
if (expression.getTypeArgument(i)?.isReified == true) {
|
||||
containsReified = true
|
||||
break
|
||||
}
|
||||
}
|
||||
super.visitMemberAccess(expression)
|
||||
}
|
||||
|
||||
override fun visitTypeOperator(expression: IrTypeOperatorCall) {
|
||||
if (expression.typeOperand.isReified)
|
||||
containsReified = true
|
||||
super.visitTypeOperator(expression)
|
||||
}
|
||||
|
||||
override fun visitClassReference(expression: IrClassReference) {
|
||||
if (expression.classType.isReified)
|
||||
containsReified = true
|
||||
super.visitClassReference(expression)
|
||||
}
|
||||
|
||||
private val IrType.isReified: Boolean
|
||||
get() = classifierOrNull?.safeAs<IrTypeParameterSymbol>()?.owner?.isReified == true
|
||||
})
|
||||
return containsReified
|
||||
}
|
||||
|
||||
private val containsReifiedTypeParametersCache = mutableMapOf<IrClass, Boolean>()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// CHECK_BYTECODE_LISTING
|
||||
// FILE: 1.kt
|
||||
package test
|
||||
|
||||
inline fun <reified U> bar() = U::class.simpleName!!
|
||||
|
||||
inline fun <reified T> foo(): String {
|
||||
val x = { bar<Array<T>>() }
|
||||
return x()
|
||||
}
|
||||
|
||||
// FILE: 2.kt
|
||||
import test.*
|
||||
|
||||
fun box(): String {
|
||||
val result = foo<Int>()
|
||||
return if (result == "Array") "OK" else result
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
@kotlin.Metadata
|
||||
public final class _2Kt$box$$inlined$foo$1 {
|
||||
// source: '1.kt'
|
||||
enclosing method _2Kt.box()Ljava/lang/String;
|
||||
public final static field INSTANCE: _2Kt$box$$inlined$foo$1
|
||||
inner (anonymous) class _2Kt$box$$inlined$foo$1
|
||||
static method <clinit>(): void
|
||||
public method <init>(): void
|
||||
public synthetic bridge method invoke(): java.lang.Object
|
||||
public final @org.jetbrains.annotations.NotNull method invoke(): java.lang.String
|
||||
}
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class _2Kt {
|
||||
// source: '2.kt'
|
||||
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
|
||||
}
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class test/_1Kt$foo$x$1 {
|
||||
// source: '1.kt'
|
||||
enclosing method test/_1Kt.foo()Ljava/lang/String;
|
||||
public final static field INSTANCE: test._1Kt$foo$x$1
|
||||
inner (anonymous) class test/_1Kt$foo$x$1
|
||||
static method <clinit>(): void
|
||||
public method <init>(): void
|
||||
public synthetic bridge method invoke(): java.lang.Object
|
||||
public final @org.jetbrains.annotations.NotNull method invoke(): java.lang.String
|
||||
}
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class test/_1Kt {
|
||||
// source: '1.kt'
|
||||
inner (anonymous) class test/_1Kt$foo$x$1
|
||||
public synthetic final static method bar(): java.lang.String
|
||||
public synthetic final static method foo(): java.lang.String
|
||||
}
|
||||
+6
@@ -3902,6 +3902,12 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
|
||||
runTest("compiler/testData/codegen/boxInline/reified/packages.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("singletonLambda.kt")
|
||||
public void testSingletonLambda() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/reified/singletonLambda.kt");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+6
@@ -3902,6 +3902,12 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
|
||||
runTest("compiler/testData/codegen/boxInline/reified/packages.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("singletonLambda.kt")
|
||||
public void testSingletonLambda() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/reified/singletonLambda.kt");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+6
@@ -3914,6 +3914,12 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli
|
||||
runTest("compiler/testData/codegen/boxInline/reified/packages.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("singletonLambda.kt")
|
||||
public void testSingletonLambda() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/reified/singletonLambda.kt");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+6
@@ -3914,6 +3914,12 @@ public class IrCompileKotlinAgainstInlineKotlinTestGenerated extends AbstractIrC
|
||||
runTest("compiler/testData/codegen/boxInline/reified/packages.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("singletonLambda.kt")
|
||||
public void testSingletonLambda() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/reified/singletonLambda.kt");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+6
@@ -3914,6 +3914,12 @@ public class IrSerializeCompileKotlinAgainstInlineKotlinTestGenerated extends Ab
|
||||
runTest("compiler/testData/codegen/boxInline/reified/packages.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("singletonLambda.kt")
|
||||
public void testSingletonLambda() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/reified/singletonLambda.kt");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+6
@@ -3914,6 +3914,12 @@ public class JvmIrAgainstOldBoxInlineTestGenerated extends AbstractJvmIrAgainstO
|
||||
runTest("compiler/testData/codegen/boxInline/reified/packages.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("singletonLambda.kt")
|
||||
public void testSingletonLambda() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/reified/singletonLambda.kt");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+6
@@ -3902,6 +3902,12 @@ public class JvmOldAgainstIrBoxInlineTestGenerated extends AbstractJvmOldAgainst
|
||||
runTest("compiler/testData/codegen/boxInline/reified/packages.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("singletonLambda.kt")
|
||||
public void testSingletonLambda() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/reified/singletonLambda.kt");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+5
@@ -3156,6 +3156,11 @@ public class IrJsCodegenInlineES6TestGenerated extends AbstractIrJsCodegenInline
|
||||
runTest("compiler/testData/codegen/boxInline/reified/nonCapturingObjectInLambda.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("singletonLambda.kt")
|
||||
public void testSingletonLambda() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/reified/singletonLambda.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Generated
+5
@@ -3156,6 +3156,11 @@ public class IrJsCodegenInlineTestGenerated extends AbstractIrJsCodegenInlineTes
|
||||
runTest("compiler/testData/codegen/boxInline/reified/nonCapturingObjectInLambda.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("singletonLambda.kt")
|
||||
public void testSingletonLambda() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/reified/singletonLambda.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Generated
+5
@@ -3156,6 +3156,11 @@ public class JsCodegenInlineTestGenerated extends AbstractJsCodegenInlineTest {
|
||||
runTest("compiler/testData/codegen/boxInline/reified/nonCapturingObjectInLambda.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("singletonLambda.kt")
|
||||
public void testSingletonLambda() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/reified/singletonLambda.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user