From c14a890e7ebd135715e0385755bf1cc9f54bea98 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 28 Apr 2021 17:40:11 +0200 Subject: [PATCH] IR: fix capturing of type parameters in local functions collectPotentiallyCapturedTypeParameters no longer stops on the first class when going through parents. This is needed because otherwise type parameters of the containing class, and its outer classes, were never considered "captured", and thus not duplicated/remapped later in LocalDeclarationsLowering. This led to the IR where a local function referenced generic type parameters of the outer class. On JVM, local function is generated into a private static function in that container class, and static functions can't use generic type parameters, which crashed some bytecode processing tools. Also, add another explicit call to `seeType` to cover references to generic type parameters in function return types. #KT-45941 Fixed --- .../backend/common/lower/ClosureAnnotator.kt | 22 +++++----- .../localFunctions/genericClass.kt | 8 ++++ .../localFunctions/genericClass.txt | 19 +++++++++ .../localFunctions/genericClass_ir.txt | 9 ++++ .../localFunctions/genericInnerClass.kt | 11 +++++ .../localFunctions/genericInnerClass.txt | 42 +++++++++++++++++++ .../localFunctions/genericInnerClass_ir.txt | 28 +++++++++++++ .../localFunctions/genericLocalClass.kt | 11 +++++ .../localFunctions/genericLocalClass.txt | 31 ++++++++++++++ .../localFunctions/genericLocalClass_ir.txt | 19 +++++++++ .../codegen/BytecodeListingTestGenerated.java | 18 ++++++++ .../IrBytecodeListingTestGenerated.java | 18 ++++++++ 12 files changed, 224 insertions(+), 12 deletions(-) create mode 100644 compiler/testData/codegen/bytecodeListing/localFunctions/genericClass.kt create mode 100644 compiler/testData/codegen/bytecodeListing/localFunctions/genericClass.txt create mode 100644 compiler/testData/codegen/bytecodeListing/localFunctions/genericClass_ir.txt create mode 100644 compiler/testData/codegen/bytecodeListing/localFunctions/genericInnerClass.kt create mode 100644 compiler/testData/codegen/bytecodeListing/localFunctions/genericInnerClass.txt create mode 100644 compiler/testData/codegen/bytecodeListing/localFunctions/genericInnerClass_ir.txt create mode 100644 compiler/testData/codegen/bytecodeListing/localFunctions/genericLocalClass.kt create mode 100644 compiler/testData/codegen/bytecodeListing/localFunctions/genericLocalClass.txt create mode 100644 compiler/testData/codegen/bytecodeListing/localFunctions/genericLocalClass_ir.txt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ClosureAnnotator.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ClosureAnnotator.kt index 970636aebff..a1fe49f3f45 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ClosureAnnotator.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ClosureAnnotator.kt @@ -26,7 +26,6 @@ import org.jetbrains.kotlin.ir.symbols.IrValueSymbol import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrTypeProjection -import org.jetbrains.kotlin.ir.util.constructedClass import org.jetbrains.kotlin.ir.util.isLocal import org.jetbrains.kotlin.ir.visitors.IrElementVisitor @@ -160,6 +159,7 @@ class ClosureAnnotator(irElement: IrElement, declaration: IrDeclaration) { this.valueParameters.forEach { closureBuilder.declareVariable(it) } closureBuilder.declareVariable(this.dispatchReceiverParameter) closureBuilder.declareVariable(this.extensionReceiverParameter) + closureBuilder.seeType(this.returnType) if (this is IrConstructor) { val constructedClass = (this.parent as IrClass) @@ -174,20 +174,18 @@ class ClosureAnnotator(irElement: IrElement, declaration: IrDeclaration) { } private fun collectPotentiallyCapturedTypeParameters(closureBuilder: ClosureBuilder) { + var current = closureBuilder.owner.parentClosureBuilder + while (current != null) { + val container = current.owner - fun ClosureBuilder.doCollect() { - if (owner !is IrClass) { - (owner as? IrTypeParametersContainer)?.let { container -> - for (tp in container.typeParameters) { - closureBuilder.addPotentiallyCapturedTypeParameter(tp) - } + if (container is IrTypeParametersContainer) { + for (typeParameter in container.typeParameters) { + closureBuilder.addPotentiallyCapturedTypeParameter(typeParameter) } - - owner.parentClosureBuilder?.doCollect() } - } - closureBuilder.owner.parentClosureBuilder?.doCollect() + current = container.parentClosureBuilder + } } private val IrDeclaration.parentClosureBuilder: ClosureBuilder? @@ -205,7 +203,7 @@ class ClosureAnnotator(irElement: IrElement, declaration: IrDeclaration) { else -> null } - private inner class ClosureCollectorVisitor() : IrElementVisitor { + private inner class ClosureCollectorVisitor : IrElementVisitor { override fun visitElement(element: IrElement, data: ClosureBuilder?) { element.acceptChildren(this, data) diff --git a/compiler/testData/codegen/bytecodeListing/localFunctions/genericClass.kt b/compiler/testData/codegen/bytecodeListing/localFunctions/genericClass.kt new file mode 100644 index 00000000000..7c0e1ee01e1 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/localFunctions/genericClass.kt @@ -0,0 +1,8 @@ +// WITH_SIGNATURES + +class A(val result: T) { + fun f(): T { + fun g(): T = result + return g() + } +} diff --git a/compiler/testData/codegen/bytecodeListing/localFunctions/genericClass.txt b/compiler/testData/codegen/bytecodeListing/localFunctions/genericClass.txt new file mode 100644 index 00000000000..9d7b409e32c --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/localFunctions/genericClass.txt @@ -0,0 +1,19 @@ +@kotlin.Metadata +final class;> A$f$1 { + // source: 'genericClass.kt' + public final <()TT;> method invoke(): java.lang.Object + method (p0: A): void + enclosing method A.f()Ljava/lang/Object; + synthetic final field this$0: A + inner (anonymous) class A$f$1 +} + +@kotlin.Metadata +public final class<Ljava/lang/Object;> A { + // source: 'genericClass.kt' + public final <()TT;> method f(): java.lang.Object + public final <()TT;> method getResult(): java.lang.Object + public <(TT;)V> method (p0: java.lang.Object): void + private final field result: java.lang.Object + inner (anonymous) class A$f$1 +} diff --git a/compiler/testData/codegen/bytecodeListing/localFunctions/genericClass_ir.txt b/compiler/testData/codegen/bytecodeListing/localFunctions/genericClass_ir.txt new file mode 100644 index 00000000000..2e5c93c383e --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/localFunctions/genericClass_ir.txt @@ -0,0 +1,9 @@ +@kotlin.Metadata +public final class<Ljava/lang/Object;> A { + // source: 'genericClass.kt' + public final <()TT;> method f(): java.lang.Object + public final <()TT;> method getResult(): java.lang.Object + public <(TT;)V> method (p0: java.lang.Object): void + private final static <(LA;)TT;> method f$g(p0: A): java.lang.Object + private final field result: java.lang.Object +} diff --git a/compiler/testData/codegen/bytecodeListing/localFunctions/genericInnerClass.kt b/compiler/testData/codegen/bytecodeListing/localFunctions/genericInnerClass.kt new file mode 100644 index 00000000000..04c41f1146c --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/localFunctions/genericInnerClass.kt @@ -0,0 +1,11 @@ +// WITH_SIGNATURES + +class A(val result: T) { + inner class B { + inner class C { + fun f() { + fun g(t: T) {} + } + } + } +} diff --git a/compiler/testData/codegen/bytecodeListing/localFunctions/genericInnerClass.txt b/compiler/testData/codegen/bytecodeListing/localFunctions/genericInnerClass.txt new file mode 100644 index 00000000000..49ca6af06eb --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/localFunctions/genericInnerClass.txt @@ -0,0 +1,42 @@ +@kotlin.Metadata +final class;> A$B$C$f$1 { + // source: 'genericInnerClass.kt' + public final <(TT;)V> method invoke(p0: java.lang.Object): void + static method (): void + method (): void + public synthetic bridge method invoke(p0: java.lang.Object): java.lang.Object + enclosing method A$B$C.f()V + public final static field INSTANCE: A$B$C$f$1 + inner (anonymous) class A$B$C$f$1 + public final inner class A$B + public final inner class A$B$C +} + +@kotlin.Metadata +public final class A$B$C { + // source: 'genericInnerClass.kt' + public <()V> method (p0: A$B): void + public final method f(): void + synthetic final field this$0: A$B + inner (anonymous) class A$B$C$f$1 + public final inner class A$B + public final inner class A$B$C +} + +@kotlin.Metadata +public final class A$B { + // source: 'genericInnerClass.kt' + public <()V> method (p0: A): void + synthetic final field this$0: A + public final inner class A$B + public final inner class A$B$C +} + +@kotlin.Metadata +public final class<Ljava/lang/Object;> A { + // source: 'genericInnerClass.kt' + public final <()TT;> method getResult(): java.lang.Object + public <(TT;)V> method (p0: java.lang.Object): void + private final field result: java.lang.Object + public final inner class A$B +} diff --git a/compiler/testData/codegen/bytecodeListing/localFunctions/genericInnerClass_ir.txt b/compiler/testData/codegen/bytecodeListing/localFunctions/genericInnerClass_ir.txt new file mode 100644 index 00000000000..9e9879326fc --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/localFunctions/genericInnerClass_ir.txt @@ -0,0 +1,28 @@ +@kotlin.Metadata +public final class A$B$C { + // source: 'genericInnerClass.kt' + public <()V> method (p0: A$B): void + private final static <(TT;)V> method f$g(p0: java.lang.Object): void + public final method f(): void + synthetic final field .B;> this$0: A$B + public final inner class A$B + public final inner class A$B$C +} + +@kotlin.Metadata +public final class A$B { + // source: 'genericInnerClass.kt' + public <()V> method (p0: A): void + synthetic final field ;> this$0: A + public final inner class A$B + public final inner class A$B$C +} + +@kotlin.Metadata +public final class<Ljava/lang/Object;> A { + // source: 'genericInnerClass.kt' + public final <()TT;> method getResult(): java.lang.Object + public <(TT;)V> method (p0: java.lang.Object): void + private final field result: java.lang.Object + public final inner class A$B +} diff --git a/compiler/testData/codegen/bytecodeListing/localFunctions/genericLocalClass.kt b/compiler/testData/codegen/bytecodeListing/localFunctions/genericLocalClass.kt new file mode 100644 index 00000000000..55f576315f9 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/localFunctions/genericLocalClass.kt @@ -0,0 +1,11 @@ +// WITH_SIGNATURES + +class A(val result: T) { + fun b() { + class C { + fun f() { + fun g(t: T): S? = null + } + } + } +} diff --git a/compiler/testData/codegen/bytecodeListing/localFunctions/genericLocalClass.txt b/compiler/testData/codegen/bytecodeListing/localFunctions/genericLocalClass.txt new file mode 100644 index 00000000000..3b701acd253 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/localFunctions/genericLocalClass.txt @@ -0,0 +1,31 @@ +@kotlin.Metadata +final class;> A$b$C$f$1 { + // source: 'genericLocalClass.kt' + public final @org.jetbrains.annotations.Nullable <(TT;)TS;> method invoke(p0: java.lang.Object): java.lang.Object + static method (): void + method (): void + enclosing method A$b$C.f()V + public final static field INSTANCE: A$b$C$f$1 + inner (anonymous) class A$b$C$f$1 + inner (local) class A$b$C C +} + +@kotlin.Metadata +public final class<Ljava/lang/Object;> A$b$C { + // source: 'genericLocalClass.kt' + public method (): void + public final method f(): void + enclosing method A.b()V + inner (anonymous) class A$b$C$f$1 + inner (local) class A$b$C C +} + +@kotlin.Metadata +public final class<Ljava/lang/Object;> A { + // source: 'genericLocalClass.kt' + public final <()TT;> method getResult(): java.lang.Object + public <(TT;)V> method (p0: java.lang.Object): void + public final method b(): void + private final field result: java.lang.Object + inner (local) class A$b$C C +} diff --git a/compiler/testData/codegen/bytecodeListing/localFunctions/genericLocalClass_ir.txt b/compiler/testData/codegen/bytecodeListing/localFunctions/genericLocalClass_ir.txt new file mode 100644 index 00000000000..7c636919729 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/localFunctions/genericLocalClass_ir.txt @@ -0,0 +1,19 @@ +@kotlin.Metadata +public final class<Ljava/lang/Object;> A$b$C { + // source: 'genericLocalClass.kt' + private final static <(TT;)TS;> method f$g(p0: java.lang.Object): java.lang.Object + public method (): void + public final method f(): void + enclosing method A.b()V + inner (local) class A$b$C C +} + +@kotlin.Metadata +public final class<Ljava/lang/Object;> A { + // source: 'genericLocalClass.kt' + public final <()TT;> method getResult(): java.lang.Object + public <(TT;)V> method (p0: java.lang.Object): void + public final method b(): void + private final field result: java.lang.Object + inner (local) class A$b$C C +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeListingTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeListingTestGenerated.java index c73e14d2c1f..85c45654fd1 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeListingTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeListingTestGenerated.java @@ -1748,6 +1748,24 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeListing/localFunctions"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); } + @Test + @TestMetadata("genericClass.kt") + public void testGenericClass() throws Exception { + runTest("compiler/testData/codegen/bytecodeListing/localFunctions/genericClass.kt"); + } + + @Test + @TestMetadata("genericInnerClass.kt") + public void testGenericInnerClass() throws Exception { + runTest("compiler/testData/codegen/bytecodeListing/localFunctions/genericInnerClass.kt"); + } + + @Test + @TestMetadata("genericLocalClass.kt") + public void testGenericLocalClass() throws Exception { + runTest("compiler/testData/codegen/bytecodeListing/localFunctions/genericLocalClass.kt"); + } + @Test @TestMetadata("inInitBlock.kt") public void testInInitBlock() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeListingTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeListingTestGenerated.java index 707485cef97..9c20466bc01 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeListingTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeListingTestGenerated.java @@ -1748,6 +1748,24 @@ public class IrBytecodeListingTestGenerated extends AbstractIrBytecodeListingTes KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeListing/localFunctions"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); } + @Test + @TestMetadata("genericClass.kt") + public void testGenericClass() throws Exception { + runTest("compiler/testData/codegen/bytecodeListing/localFunctions/genericClass.kt"); + } + + @Test + @TestMetadata("genericInnerClass.kt") + public void testGenericInnerClass() throws Exception { + runTest("compiler/testData/codegen/bytecodeListing/localFunctions/genericInnerClass.kt"); + } + + @Test + @TestMetadata("genericLocalClass.kt") + public void testGenericLocalClass() throws Exception { + runTest("compiler/testData/codegen/bytecodeListing/localFunctions/genericLocalClass.kt"); + } + @Test @TestMetadata("inInitBlock.kt") public void testInInitBlock() throws Exception {