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);
|
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 {
|
||||||
|
|||||||
+23
-23
@@ -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
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+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);
|
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 {
|
||||||
|
|||||||
+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);
|
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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user