diff --git a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java index b009d059c93..bd644180533 100644 --- a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java @@ -12476,6 +12476,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/functions/localFunctions"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); } + @TestMetadata("callBetweenLocalFunctions.kt") + public void testCallBetweenLocalFunctions() throws Exception { + runTest("compiler/testData/codegen/box/functions/localFunctions/callBetweenLocalFunctions.kt"); + } + @TestMetadata("callInlineLocalInLambda.kt") public void testCallInlineLocalInLambda() throws Exception { runTest("compiler/testData/codegen/box/functions/localFunctions/callInlineLocalInLambda.kt"); diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt index 248257db85b..c7ea08a09de 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.ir.builders.declarations.buildValueParameter import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol import org.jetbrains.kotlin.ir.symbols.IrValueSymbol @@ -29,10 +30,7 @@ import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.types.impl.IrTypeAbbreviationImpl import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection -import org.jetbrains.kotlin.ir.util.constructedClass -import org.jetbrains.kotlin.ir.util.file -import org.jetbrains.kotlin.ir.util.parentAsClass -import org.jetbrains.kotlin.ir.util.render +import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid @@ -127,6 +125,8 @@ class LocalDeclarationsLowering( private abstract class LocalContext { val capturedTypeParameterToTypeParameter: MutableMap = mutableMapOf() + // By the time typeRemapper is used, the map will be already filled + val typeRemapper = IrTypeParameterRemapper(capturedTypeParameterToTypeParameter) /** * @return the expression to get the value for given declaration, or `null` if [IrGetValue] should be used. @@ -252,10 +252,14 @@ class LocalDeclarationsLowering( localFunctions.values.forEach { it.transformedDeclaration.apply { val original = it.declaration + val typeRemapper = TypeParameterAdjustmentTypeRemapper(it.capturedTypeParameterToTypeParameter) + this.body = original.body + this.body?.remapTypes(typeRemapper) original.valueParameters.filter { v -> v.defaultValue != null }.forEach { argument -> val body = argument.defaultValue!! + body.remapTypes(typeRemapper) oldParameterToNew[argument]!!.defaultValue = body } acceptChildren(SetDeclarationsParentVisitor, this) @@ -511,7 +515,7 @@ class LocalDeclarationsLowering( private fun createNewCall(oldCall: IrCall, newCallee: IrSimpleFunction) = IrCallImpl( oldCall.startOffset, oldCall.endOffset, - newCallee.returnType, + oldCall.type, newCallee.symbol, typeArgumentsCount = newCallee.typeParameters.size, valueArgumentsCount = newCallee.valueParameters.size, @@ -525,7 +529,7 @@ class LocalDeclarationsLowering( private fun createNewCall(oldCall: IrConstructorCall, newCallee: IrConstructor) = IrConstructorCallImpl.fromSymbolOwner( oldCall.startOffset, oldCall.endOffset, - newCallee.returnType, + oldCall.type, newCallee.symbol, newCallee.parentAsClass.typeParameters.size, oldCall.origin @@ -608,6 +612,9 @@ class LocalDeclarationsLowering( capturedTypeParameters.zip(newTypeParameters) ) newDeclaration.copyTypeParametersFrom(oldDeclaration, parameterMap = localFunctionContext.capturedTypeParameterToTypeParameter) + localFunctionContext.capturedTypeParameterToTypeParameter.putAll( + oldDeclaration.typeParameters.zip(newDeclaration.typeParameters.drop(newTypeParameters.size)) + ) // Type parameters of oldDeclaration may depend on captured type parameters, so deal with that after copying. newDeclaration.typeParameters.drop(newTypeParameters.size).forEach { tp -> tp.superTypes = tp.superTypes.map { localFunctionContext.remapType(it) } @@ -923,5 +930,45 @@ class LocalDeclarationsLowering( } } +class TypeParameterAdjustmentTypeRemapper(val typeParameterMap: Map) : TypeRemapper { + override fun enterScope(irTypeParametersContainer: IrTypeParametersContainer) {} + override fun leaveScope() {} + + override fun remapType(type: IrType): IrType = + if (type !is IrSimpleType) + type + else + IrSimpleTypeImpl( + null, + type.classifier.remap(), + type.hasQuestionMark, + type.arguments.map { it.remap() }, + type.annotations, + type.abbreviation?.remap() + ).apply { + annotations.forEach { it.remapTypes(this@TypeParameterAdjustmentTypeRemapper) } + } + + private fun IrClassifierSymbol.remap() = + (owner as? IrTypeParameter)?.let { typeParameterMap[it]?.symbol } + ?: this + + private fun IrTypeArgument.remap() = + if (this is IrTypeProjection) + makeTypeProjection(remapType(type), variance) + else + this + + private fun IrTypeAbbreviation.remap() = + IrTypeAbbreviationImpl( + typeAlias, + hasQuestionMark, + arguments.map { it.remap() }, + annotations + ).apply { + annotations.forEach { it.remapTypes(this@TypeParameterAdjustmentTypeRemapper) } + } +} + // Local inner classes capture anything through outer internal fun IrClass.isLocalNotInner(): Boolean = visibility == DescriptorVisibilities.LOCAL && !isInner diff --git a/compiler/testData/codegen/box/functions/localFunctions/callBetweenLocalFunctions.kt b/compiler/testData/codegen/box/functions/localFunctions/callBetweenLocalFunctions.kt new file mode 100644 index 00000000000..fde3eb76c91 --- /dev/null +++ b/compiler/testData/codegen/box/functions/localFunctions/callBetweenLocalFunctions.kt @@ -0,0 +1,19 @@ + +fun box(): String { + var a = 0 + fun local(xx: T): T { + class A { + val b = 0 + fun id(x: T): T { + a = b + return x + } + + } + fun local2() : T { + return A().id(xx) + } + return local2() + } + return local("OK") +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 11e9373c9f7..649b2a045fc 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -13871,6 +13871,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/functions/localFunctions"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); } + @TestMetadata("callBetweenLocalFunctions.kt") + public void testCallBetweenLocalFunctions() throws Exception { + runTest("compiler/testData/codegen/box/functions/localFunctions/callBetweenLocalFunctions.kt"); + } + @TestMetadata("callInlineLocalInLambda.kt") public void testCallInlineLocalInLambda() throws Exception { runTest("compiler/testData/codegen/box/functions/localFunctions/callInlineLocalInLambda.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 113a626c5b4..a4549e776ea 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -13871,6 +13871,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/functions/localFunctions"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); } + @TestMetadata("callBetweenLocalFunctions.kt") + public void testCallBetweenLocalFunctions() throws Exception { + runTest("compiler/testData/codegen/box/functions/localFunctions/callBetweenLocalFunctions.kt"); + } + @TestMetadata("callInlineLocalInLambda.kt") public void testCallInlineLocalInLambda() throws Exception { runTest("compiler/testData/codegen/box/functions/localFunctions/callInlineLocalInLambda.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index dc6096e2dc8..5d80362aec6 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -12476,6 +12476,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/functions/localFunctions"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); } + @TestMetadata("callBetweenLocalFunctions.kt") + public void testCallBetweenLocalFunctions() throws Exception { + runTest("compiler/testData/codegen/box/functions/localFunctions/callBetweenLocalFunctions.kt"); + } + @TestMetadata("callInlineLocalInLambda.kt") public void testCallInlineLocalInLambda() throws Exception { runTest("compiler/testData/codegen/box/functions/localFunctions/callInlineLocalInLambda.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java index d75de2fb3db..0243f81a7d8 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java @@ -10731,6 +10731,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/functions/localFunctions"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR_ES6, true); } + @TestMetadata("callBetweenLocalFunctions.kt") + public void testCallBetweenLocalFunctions() throws Exception { + runTest("compiler/testData/codegen/box/functions/localFunctions/callBetweenLocalFunctions.kt"); + } + @TestMetadata("callInlineLocalInLambda.kt") public void testCallInlineLocalInLambda() throws Exception { runTest("compiler/testData/codegen/box/functions/localFunctions/callInlineLocalInLambda.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index 018288b72c7..c1a492956c1 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -10731,6 +10731,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/functions/localFunctions"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true); } + @TestMetadata("callBetweenLocalFunctions.kt") + public void testCallBetweenLocalFunctions() throws Exception { + runTest("compiler/testData/codegen/box/functions/localFunctions/callBetweenLocalFunctions.kt"); + } + @TestMetadata("callInlineLocalInLambda.kt") public void testCallInlineLocalInLambda() throws Exception { runTest("compiler/testData/codegen/box/functions/localFunctions/callInlineLocalInLambda.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 3f0791e7d79..6a2ba46fbaf 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -10731,6 +10731,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/functions/localFunctions"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true); } + @TestMetadata("callBetweenLocalFunctions.kt") + public void testCallBetweenLocalFunctions() throws Exception { + runTest("compiler/testData/codegen/box/functions/localFunctions/callBetweenLocalFunctions.kt"); + } + @TestMetadata("callInlineLocalInLambda.kt") public void testCallInlineLocalInLambda() throws Exception { runTest("compiler/testData/codegen/box/functions/localFunctions/callInlineLocalInLambda.kt");