IR: do not create unused capture fields

^KT-48784 Fixed
This commit is contained in:
pyos
2021-09-16 12:12:12 +02:00
committed by Alexander Udalov
parent e7f1cef3a1
commit 85c53bc24e
5 changed files with 55 additions and 23 deletions
@@ -2278,6 +2278,12 @@ public class FirBytecodeTextTestGenerated extends AbstractFirBytecodeTextTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
} }
@Test
@TestMetadata("constructorOnly.kt")
public void testConstructorOnly() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/constructorOnly.kt");
}
@Test @Test
@TestMetadata("extensionLambdaExtensionReceiver.kt") @TestMetadata("extensionLambdaExtensionReceiver.kt")
public void testExtensionLambdaExtensionReceiver() throws Exception { public void testExtensionLambdaExtensionReceiver() throws Exception {
@@ -168,10 +168,10 @@ class LocalDeclarationsLowering(
// NOTE: This map is iterated over in `rewriteClassMembers` and we're relying on // NOTE: This map is iterated over in `rewriteClassMembers` and we're relying on
// the deterministic iteration order that `mutableMapOf` provides. // the deterministic iteration order that `mutableMapOf` provides.
val capturedValueToField: MutableMap<IrValueDeclaration, IrField> = mutableMapOf() val capturedValueToField: MutableMap<IrValueDeclaration, Lazy<IrField>> = mutableMapOf()
override fun irGet(startOffset: Int, endOffset: Int, valueDeclaration: IrValueDeclaration): IrExpression? { override fun irGet(startOffset: Int, endOffset: Int, valueDeclaration: IrValueDeclaration): IrExpression? {
val field = capturedValueToField[valueDeclaration] ?: return null val field = capturedValueToField[valueDeclaration]?.value ?: return null
val receiver = declaration.thisReceiver!! val receiver = declaration.thisReceiver!!
return IrGetFieldImpl( return IrGetFieldImpl(
@@ -183,7 +183,7 @@ class LocalDeclarationsLowering(
private class LocalClassMemberContext(val member: IrFunction, val classContext: LocalClassContext) : LocalContext() { private class LocalClassMemberContext(val member: IrFunction, val classContext: LocalClassContext) : LocalContext() {
override fun irGet(startOffset: Int, endOffset: Int, valueDeclaration: IrValueDeclaration): IrExpression? { override fun irGet(startOffset: Int, endOffset: Int, valueDeclaration: IrValueDeclaration): IrExpression? {
val field = classContext.capturedValueToField[valueDeclaration] ?: return null val field = classContext.capturedValueToField[valueDeclaration]?.value ?: return null
val receiver = member.dispatchReceiverParameter val receiver = member.dispatchReceiverParameter
?: error("No dispatch receiver parameter for ${member.render()}") ?: error("No dispatch receiver parameter for ${member.render()}")
@@ -192,7 +192,6 @@ class LocalDeclarationsLowering(
receiver = IrGetValueImpl(startOffset, endOffset, receiver.type, receiver.symbol) receiver = IrGetValueImpl(startOffset, endOffset, receiver.type, receiver.symbol)
) )
} }
} }
private fun LocalContext.remapType(type: IrType): IrType { private fun LocalContext.remapType(type: IrType): IrType {
@@ -284,15 +283,15 @@ class LocalDeclarationsLowering(
override fun visitConstructor(declaration: IrConstructor): IrStatement { override fun visitConstructor(declaration: IrConstructor): IrStatement {
// Body is transformed separately. See loop over constructors in rewriteDeclarations(). // Body is transformed separately. See loop over constructors in rewriteDeclarations().
val constructorContext = localClassConstructors[declaration] val constructorContext = localClassConstructors[declaration] ?: return super.visitConstructor(declaration)
return constructorContext?.transformedDeclaration?.apply { return constructorContext.transformedDeclaration.apply {
this.body = declaration.body!! this.body = declaration.body!!
declaration.valueParameters.filter { it.defaultValue != null }.forEach { argument -> declaration.valueParameters.filter { it.defaultValue != null }.forEach { argument ->
oldParameterToNew[argument]!!.defaultValue = argument.defaultValue oldParameterToNew[argument]!!.defaultValue = argument.defaultValue
} }
acceptChildren(SetDeclarationsParentVisitor, this) acceptChildren(SetDeclarationsParentVisitor, this)
} ?: super.visitConstructor(declaration) }
} }
override fun visitGetValue(expression: IrGetValue): IrExpression { override fun visitGetValue(expression: IrGetValue): IrExpression {
@@ -478,10 +477,11 @@ class LocalDeclarationsLowering(
assert(constructorsCallingSuper.any()) { "Expected at least one constructor calling super; class: $irClass" } assert(constructorsCallingSuper.any()) { "Expected at least one constructor calling super; class: $irClass" }
irClass.declarations += localClassContext.capturedValueToField.values val usedCaptureFields = localClassContext.capturedValueToField.values.mapNotNull { if (it.isInitialized()) it.value else null }
irClass.declarations += usedCaptureFields
context.mapping.capturedFields[irClass] = context.mapping.capturedFields[irClass] =
(context.mapping.capturedFields[irClass] ?: emptyList()) + localClassContext.capturedValueToField.values (context.mapping.capturedFields[irClass] ?: emptyList()) + usedCaptureFields
for (constructorContext in constructorsCallingSuper) { for (constructorContext in constructorsCallingSuper) {
val blockBody = constructorContext.declaration.body as? IrBlockBody val blockBody = constructorContext.declaration.body as? IrBlockBody
@@ -491,9 +491,10 @@ class LocalDeclarationsLowering(
// since `AnonymousObjectTransformer` relies on this ordering. // since `AnonymousObjectTransformer` relies on this ordering.
blockBody.statements.addAll( blockBody.statements.addAll(
0, 0,
localClassContext.capturedValueToField.map { (capturedValue, field) -> localClassContext.capturedValueToField.mapNotNull { (capturedValue, field) ->
if (!field.isInitialized()) return@mapNotNull null
IrSetFieldImpl( IrSetFieldImpl(
UNDEFINED_OFFSET, UNDEFINED_OFFSET, field.symbol, UNDEFINED_OFFSET, UNDEFINED_OFFSET, field.value.symbol,
IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, irClass.thisReceiver!!.symbol), IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, irClass.thisReceiver!!.symbol),
constructorContext.irGet(UNDEFINED_OFFSET, UNDEFINED_OFFSET, capturedValue)!!, constructorContext.irGet(UNDEFINED_OFFSET, UNDEFINED_OFFSET, capturedValue)!!,
context.irBuiltIns.unitType, context.irBuiltIns.unitType,
@@ -769,19 +770,18 @@ class LocalDeclarationsLowering(
val classDeclaration = localClassContext.declaration val classDeclaration = localClassContext.declaration
val generatedNames = mutableSetOf<String>() val generatedNames = mutableSetOf<String>()
localClassContext.closure.capturedValues.forEach { capturedValue -> localClassContext.closure.capturedValues.forEach { capturedValue ->
val owner = capturedValue.owner val owner = capturedValue.owner
val irField = createFieldForCapturedValue( localClassContext.capturedValueToField[owner] = lazy(LazyThreadSafetyMode.NONE) {
classDeclaration.startOffset, createFieldForCapturedValue(
classDeclaration.endOffset, classDeclaration.startOffset,
suggestNameForCapturedValue(owner, generatedNames), classDeclaration.endOffset,
visibilityPolicy.forCapturedField(capturedValue), suggestNameForCapturedValue(owner, generatedNames),
classDeclaration, visibilityPolicy.forCapturedField(capturedValue),
owner.type, classDeclaration,
owner is IrValueParameter && owner.isCrossinline owner.type,
) owner is IrValueParameter && owner.isCrossinline
)
localClassContext.capturedValueToField[owner] = irField }
} }
} }
@@ -0,0 +1,14 @@
open class Base(parameter: String)
fun foo(captured: String) {
object : Base(captured) {
// val x = captured
// init { println(captured) }
}
}
// JVM_TEMPLATES
// 1 final synthetic Ljava/lang/String; \$captured
// JVM_IR_TEMPLATES
// 0 final synthetic Ljava/lang/String; \$captured
@@ -2254,6 +2254,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
} }
@Test
@TestMetadata("constructorOnly.kt")
public void testConstructorOnly() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/constructorOnly.kt");
}
@Test @Test
@TestMetadata("extensionLambdaExtensionReceiver.kt") @TestMetadata("extensionLambdaExtensionReceiver.kt")
public void testExtensionLambdaExtensionReceiver() throws Exception { public void testExtensionLambdaExtensionReceiver() throws Exception {
@@ -2278,6 +2278,12 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
} }
@Test
@TestMetadata("constructorOnly.kt")
public void testConstructorOnly() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/constructorOnly.kt");
}
@Test @Test
@TestMetadata("extensionLambdaExtensionReceiver.kt") @TestMetadata("extensionLambdaExtensionReceiver.kt")
public void testExtensionLambdaExtensionReceiver() throws Exception { public void testExtensionLambdaExtensionReceiver() throws Exception {