IR: do not create unused capture fields
^KT-48784 Fixed
This commit is contained in:
+6
@@ -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);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("constructorOnly.kt")
|
||||
public void testConstructorOnly() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/constructorOnly.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("extensionLambdaExtensionReceiver.kt")
|
||||
public void testExtensionLambdaExtensionReceiver() throws Exception {
|
||||
|
||||
+23
-23
@@ -168,10 +168,10 @@ class LocalDeclarationsLowering(
|
||||
|
||||
// NOTE: This map is iterated over in `rewriteClassMembers` and we're relying on
|
||||
// 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? {
|
||||
val field = capturedValueToField[valueDeclaration] ?: return null
|
||||
val field = capturedValueToField[valueDeclaration]?.value ?: return null
|
||||
|
||||
val receiver = declaration.thisReceiver!!
|
||||
return IrGetFieldImpl(
|
||||
@@ -183,7 +183,7 @@ class LocalDeclarationsLowering(
|
||||
|
||||
private class LocalClassMemberContext(val member: IrFunction, val classContext: LocalClassContext) : LocalContext() {
|
||||
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
|
||||
?: error("No dispatch receiver parameter for ${member.render()}")
|
||||
@@ -192,7 +192,6 @@ class LocalDeclarationsLowering(
|
||||
receiver = IrGetValueImpl(startOffset, endOffset, receiver.type, receiver.symbol)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun LocalContext.remapType(type: IrType): IrType {
|
||||
@@ -284,15 +283,15 @@ class LocalDeclarationsLowering(
|
||||
override fun visitConstructor(declaration: IrConstructor): IrStatement {
|
||||
// Body is transformed separately. See loop over constructors in rewriteDeclarations().
|
||||
|
||||
val constructorContext = localClassConstructors[declaration]
|
||||
return constructorContext?.transformedDeclaration?.apply {
|
||||
val constructorContext = localClassConstructors[declaration] ?: return super.visitConstructor(declaration)
|
||||
return constructorContext.transformedDeclaration.apply {
|
||||
this.body = declaration.body!!
|
||||
|
||||
declaration.valueParameters.filter { it.defaultValue != null }.forEach { argument ->
|
||||
oldParameterToNew[argument]!!.defaultValue = argument.defaultValue
|
||||
}
|
||||
acceptChildren(SetDeclarationsParentVisitor, this)
|
||||
} ?: super.visitConstructor(declaration)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
@@ -478,10 +477,11 @@ class LocalDeclarationsLowering(
|
||||
|
||||
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] ?: emptyList()) + localClassContext.capturedValueToField.values
|
||||
(context.mapping.capturedFields[irClass] ?: emptyList()) + usedCaptureFields
|
||||
|
||||
for (constructorContext in constructorsCallingSuper) {
|
||||
val blockBody = constructorContext.declaration.body as? IrBlockBody
|
||||
@@ -491,9 +491,10 @@ class LocalDeclarationsLowering(
|
||||
// since `AnonymousObjectTransformer` relies on this ordering.
|
||||
blockBody.statements.addAll(
|
||||
0,
|
||||
localClassContext.capturedValueToField.map { (capturedValue, field) ->
|
||||
localClassContext.capturedValueToField.mapNotNull { (capturedValue, field) ->
|
||||
if (!field.isInitialized()) return@mapNotNull null
|
||||
IrSetFieldImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, field.symbol,
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, field.value.symbol,
|
||||
IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, irClass.thisReceiver!!.symbol),
|
||||
constructorContext.irGet(UNDEFINED_OFFSET, UNDEFINED_OFFSET, capturedValue)!!,
|
||||
context.irBuiltIns.unitType,
|
||||
@@ -769,19 +770,18 @@ class LocalDeclarationsLowering(
|
||||
val classDeclaration = localClassContext.declaration
|
||||
val generatedNames = mutableSetOf<String>()
|
||||
localClassContext.closure.capturedValues.forEach { capturedValue ->
|
||||
|
||||
val owner = capturedValue.owner
|
||||
val irField = createFieldForCapturedValue(
|
||||
classDeclaration.startOffset,
|
||||
classDeclaration.endOffset,
|
||||
suggestNameForCapturedValue(owner, generatedNames),
|
||||
visibilityPolicy.forCapturedField(capturedValue),
|
||||
classDeclaration,
|
||||
owner.type,
|
||||
owner is IrValueParameter && owner.isCrossinline
|
||||
)
|
||||
|
||||
localClassContext.capturedValueToField[owner] = irField
|
||||
localClassContext.capturedValueToField[owner] = lazy(LazyThreadSafetyMode.NONE) {
|
||||
createFieldForCapturedValue(
|
||||
classDeclaration.startOffset,
|
||||
classDeclaration.endOffset,
|
||||
suggestNameForCapturedValue(owner, generatedNames),
|
||||
visibilityPolicy.forCapturedField(capturedValue),
|
||||
classDeclaration,
|
||||
owner.type,
|
||||
owner is IrValueParameter && owner.isCrossinline
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
@@ -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
|
||||
+6
@@ -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);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("constructorOnly.kt")
|
||||
public void testConstructorOnly() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/constructorOnly.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("extensionLambdaExtensionReceiver.kt")
|
||||
public void testExtensionLambdaExtensionReceiver() throws Exception {
|
||||
|
||||
+6
@@ -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);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("constructorOnly.kt")
|
||||
public void testConstructorOnly() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/constructorOnly.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("extensionLambdaExtensionReceiver.kt")
|
||||
public void testExtensionLambdaExtensionReceiver() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user