From 8e074828625f8b131590ab69c1d6349b0b15572c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven=20Sch=C3=A4fer?= Date: Mon, 13 Jan 2020 16:11:14 +0100 Subject: [PATCH] JVM IR: Fix generation of parameterless default constructor The JVM backend does not generate parameterless default constructors for private constructors and constructors of local, inner, or inline classes. --- .../lower/JvmDefaultConstructorLowering.kt | 15 ++++- .../constructors/enumPrimaryDefaults.kt | 15 +++++ .../inlineArgumentPrimaryDefaults.kt | 13 +++++ .../constructors/inlinePrimaryDefaults.kt | 13 +++++ .../constructors/innerPrimaryDefaults.kt | 12 ++++ .../constructors/internalPrimaryDefaults.kt | 5 ++ .../constructors/localPrimaryDefaults.kt | 16 +++++ .../constructors/parameterlessPrimary.kt | 5 ++ .../constructors/privatePrimaryDefaults.kt | 5 ++ .../constructors/protectedPrimaryDefaults.kt | 5 ++ .../codegen/BytecodeTextTestGenerated.java | 58 +++++++++++++++++++ .../ir/IrBytecodeTextTestGenerated.java | 58 +++++++++++++++++++ 12 files changed, 217 insertions(+), 3 deletions(-) create mode 100644 compiler/testData/codegen/bytecodeText/constructors/enumPrimaryDefaults.kt create mode 100644 compiler/testData/codegen/bytecodeText/constructors/inlineArgumentPrimaryDefaults.kt create mode 100644 compiler/testData/codegen/bytecodeText/constructors/inlinePrimaryDefaults.kt create mode 100644 compiler/testData/codegen/bytecodeText/constructors/innerPrimaryDefaults.kt create mode 100644 compiler/testData/codegen/bytecodeText/constructors/internalPrimaryDefaults.kt create mode 100644 compiler/testData/codegen/bytecodeText/constructors/localPrimaryDefaults.kt create mode 100644 compiler/testData/codegen/bytecodeText/constructors/parameterlessPrimary.kt create mode 100644 compiler/testData/codegen/bytecodeText/constructors/privatePrimaryDefaults.kt create mode 100644 compiler/testData/codegen/bytecodeText/constructors/protectedPrimaryDefaults.kt diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultConstructorLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultConstructorLowering.kt index 957bf7d1a35..28ffe338470 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultConstructorLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultConstructorLowering.kt @@ -10,6 +10,8 @@ import org.jetbrains.kotlin.backend.common.ir.passTypeArgumentsFrom import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.ir.builders.declarations.addConstructor import org.jetbrains.kotlin.ir.builders.irBlockBody import org.jetbrains.kotlin.ir.builders.irDelegatingConstructorCall @@ -32,16 +34,23 @@ internal val jvmDefaultConstructorPhase = makeIrFilePhase( private class JvmDefaultConstructorLowering(val context: JvmBackendContext) : ClassLoweringPass { override fun lower(irClass: IrClass) { - val primaryConstructor = irClass.constructors.firstOrNull { it.isPrimary } ?: return + if (irClass.kind != ClassKind.CLASS || irClass.visibility == Visibilities.LOCAL || irClass.isInline || irClass.isInner) + return - if (!primaryConstructor.valueParameters.all { it.hasDefaultValue() }) + val primaryConstructor = irClass.constructors.firstOrNull { it.isPrimary } ?: return + if (Visibilities.isPrivate(primaryConstructor.visibility)) + return + + if (primaryConstructor.valueParameters.isEmpty() || !primaryConstructor.valueParameters.all { it.hasDefaultValue() }) return // Skip if the default constructor is already defined by user. if (irClass.constructors.any { it.valueParameters.isEmpty() }) return - irClass.addConstructor().apply { + irClass.addConstructor { + visibility = primaryConstructor.visibility + }.apply { val irBuilder = context.createIrBuilder(this.symbol, startOffset, endOffset) body = irBuilder.irBlockBody { +irDelegatingConstructorCall(primaryConstructor).apply { diff --git a/compiler/testData/codegen/bytecodeText/constructors/enumPrimaryDefaults.kt b/compiler/testData/codegen/bytecodeText/constructors/enumPrimaryDefaults.kt new file mode 100644 index 00000000000..bc53ade18b8 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/constructors/enumPrimaryDefaults.kt @@ -0,0 +1,15 @@ +enum class Enum(val x: Int = 0) { + A, + B(0) { override fun f() {} }; + open fun f() {} +} + +// @Enum.class: +// 0 \(\)V +// 1 private \(Ljava/lang/String;II\)V +// 1 synthetic \(Ljava/lang/String;IIILkotlin/jvm/internal/DefaultConstructorMarker;\)V +// 1 public synthetic \(Ljava/lang/String;IILkotlin/jvm/internal/DefaultConstructorMarker;\)V + +// @Enum$B.class: +// 0 \(\)V +// 1 \(Ljava/lang/String;I\)V \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/constructors/inlineArgumentPrimaryDefaults.kt b/compiler/testData/codegen/bytecodeText/constructors/inlineArgumentPrimaryDefaults.kt new file mode 100644 index 00000000000..4a16844dc7a --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/constructors/inlineArgumentPrimaryDefaults.kt @@ -0,0 +1,13 @@ +inline class A(val x: Int) +class B(val a: A = A(0)) + +// @B.class: +// 1 private \(I\)V +// 1 public synthetic \(IILkotlin/jvm/internal/DefaultConstructorMarker;\)V +// 1 public synthetic \(ILkotlin/jvm/internal/DefaultConstructorMarker;\)V + +// JVM_TEMPLATES +// 1 private \(\)V + +// JVM_IR_TEMPLATES +// 1 public \(\)V diff --git a/compiler/testData/codegen/bytecodeText/constructors/inlinePrimaryDefaults.kt b/compiler/testData/codegen/bytecodeText/constructors/inlinePrimaryDefaults.kt new file mode 100644 index 00000000000..e77ffe8b07f --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/constructors/inlinePrimaryDefaults.kt @@ -0,0 +1,13 @@ +inline class A(val x: Int = 0) + +// 0 \(\)V +// 1 private synthetic \(I\)V +// 1 public final static synthetic box-impl\(I\)LA; + +// JVM_TEMPLATES +// 1 public static constructor-impl\(I\)I +// 1 public static synthetic constructor-impl\$default\(IILkotlin/jvm/internal/DefaultConstructorMarker;\)I + +// JVM_IR_TEMPLATES +// 1 public final static constructor-impl\(I\)I +// 1 public static synthetic constructor-impl\$default\(IILjava/lang/Object;\)I diff --git a/compiler/testData/codegen/bytecodeText/constructors/innerPrimaryDefaults.kt b/compiler/testData/codegen/bytecodeText/constructors/innerPrimaryDefaults.kt new file mode 100644 index 00000000000..e3f74213ef8 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/constructors/innerPrimaryDefaults.kt @@ -0,0 +1,12 @@ +class A(val s: String) { + inner class B(val x: Int = 0) +} + +// @A.class +// 1 public \(Ljava/lang/String;\)V + +// @A$B.class +// 0 \(\)V +// 0 \(LA;\)V +// 1 public \(LA;I\)V +// 1 public synthetic \(LA;IILkotlin/jvm/internal/DefaultConstructorMarker;\)V diff --git a/compiler/testData/codegen/bytecodeText/constructors/internalPrimaryDefaults.kt b/compiler/testData/codegen/bytecodeText/constructors/internalPrimaryDefaults.kt new file mode 100644 index 00000000000..8bedeb0be25 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/constructors/internalPrimaryDefaults.kt @@ -0,0 +1,5 @@ +class A internal constructor(val x: Int = 0) + +// 1 public \(\)V +// 1 public \(I\)V +// 1 public synthetic \(IILkotlin/jvm/internal/DefaultConstructorMarker;\)V diff --git a/compiler/testData/codegen/bytecodeText/constructors/localPrimaryDefaults.kt b/compiler/testData/codegen/bytecodeText/constructors/localPrimaryDefaults.kt new file mode 100644 index 00000000000..08a4ae25b52 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/constructors/localPrimaryDefaults.kt @@ -0,0 +1,16 @@ +class A(val s: String) { + fun f(): Int { + class B(val x: Int = 0) { + fun f(): Int = x + } + return B().f() + } +} + +// @A.class: +// 1 public \(Ljava/lang/String;\)V + +// @A$f$B.class: +// 0 \(\)V +// 1 public \(I\)V +// 1 public synthetic \(IILkotlin/jvm/internal/DefaultConstructorMarker;\)V diff --git a/compiler/testData/codegen/bytecodeText/constructors/parameterlessPrimary.kt b/compiler/testData/codegen/bytecodeText/constructors/parameterlessPrimary.kt new file mode 100644 index 00000000000..5bf61db665c --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/constructors/parameterlessPrimary.kt @@ -0,0 +1,5 @@ +class A(val x: Int = 0) + +// 1 public \(\)V +// 1 public \(I\)V +// 1 public synthetic \(IILkotlin/jvm/internal/DefaultConstructorMarker;\)V diff --git a/compiler/testData/codegen/bytecodeText/constructors/privatePrimaryDefaults.kt b/compiler/testData/codegen/bytecodeText/constructors/privatePrimaryDefaults.kt new file mode 100644 index 00000000000..faa94f7fe17 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/constructors/privatePrimaryDefaults.kt @@ -0,0 +1,5 @@ +class A private constructor(val x: Int = 0) + +// 0 \(\)V +// 1 private \(I\)V +// 1 synthetic \(IILkotlin/jvm/internal/DefaultConstructorMarker;\)V diff --git a/compiler/testData/codegen/bytecodeText/constructors/protectedPrimaryDefaults.kt b/compiler/testData/codegen/bytecodeText/constructors/protectedPrimaryDefaults.kt new file mode 100644 index 00000000000..06a1250744b --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/constructors/protectedPrimaryDefaults.kt @@ -0,0 +1,5 @@ +class A protected constructor(val x: Int = 0) + +// 1 protected \(\)V +// 1 protected \(I\)V +// 1 synthetic \(IILkotlin/jvm/internal/DefaultConstructorMarker;\)V diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index b4a0cbf9749..f478ee82773 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -1239,6 +1239,64 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { } } + @TestMetadata("compiler/testData/codegen/bytecodeText/constructors") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Constructors extends AbstractBytecodeTextTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); + } + + public void testAllFilesPresentInConstructors() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeText/constructors"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); + } + + @TestMetadata("enumPrimaryDefaults.kt") + public void testEnumPrimaryDefaults() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/constructors/enumPrimaryDefaults.kt"); + } + + @TestMetadata("inlineArgumentPrimaryDefaults.kt") + public void testInlineArgumentPrimaryDefaults() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/constructors/inlineArgumentPrimaryDefaults.kt"); + } + + @TestMetadata("inlinePrimaryDefaults.kt") + public void testInlinePrimaryDefaults() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/constructors/inlinePrimaryDefaults.kt"); + } + + @TestMetadata("innerPrimaryDefaults.kt") + public void testInnerPrimaryDefaults() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/constructors/innerPrimaryDefaults.kt"); + } + + @TestMetadata("internalPrimaryDefaults.kt") + public void testInternalPrimaryDefaults() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/constructors/internalPrimaryDefaults.kt"); + } + + @TestMetadata("localPrimaryDefaults.kt") + public void testLocalPrimaryDefaults() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/constructors/localPrimaryDefaults.kt"); + } + + @TestMetadata("parameterlessPrimary.kt") + public void testParameterlessPrimary() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/constructors/parameterlessPrimary.kt"); + } + + @TestMetadata("privatePrimaryDefaults.kt") + public void testPrivatePrimaryDefaults() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/constructors/privatePrimaryDefaults.kt"); + } + + @TestMetadata("protectedPrimaryDefaults.kt") + public void testProtectedPrimaryDefaults() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/constructors/protectedPrimaryDefaults.kt"); + } + } + @TestMetadata("compiler/testData/codegen/bytecodeText/controlStructures") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java index 055320f0a26..8f7687210fd 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java @@ -1249,6 +1249,64 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { } } + @TestMetadata("compiler/testData/codegen/bytecodeText/constructors") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Constructors extends AbstractIrBytecodeTextTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath); + } + + public void testAllFilesPresentInConstructors() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeText/constructors"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @TestMetadata("enumPrimaryDefaults.kt") + public void testEnumPrimaryDefaults() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/constructors/enumPrimaryDefaults.kt"); + } + + @TestMetadata("inlineArgumentPrimaryDefaults.kt") + public void testInlineArgumentPrimaryDefaults() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/constructors/inlineArgumentPrimaryDefaults.kt"); + } + + @TestMetadata("inlinePrimaryDefaults.kt") + public void testInlinePrimaryDefaults() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/constructors/inlinePrimaryDefaults.kt"); + } + + @TestMetadata("innerPrimaryDefaults.kt") + public void testInnerPrimaryDefaults() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/constructors/innerPrimaryDefaults.kt"); + } + + @TestMetadata("internalPrimaryDefaults.kt") + public void testInternalPrimaryDefaults() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/constructors/internalPrimaryDefaults.kt"); + } + + @TestMetadata("localPrimaryDefaults.kt") + public void testLocalPrimaryDefaults() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/constructors/localPrimaryDefaults.kt"); + } + + @TestMetadata("parameterlessPrimary.kt") + public void testParameterlessPrimary() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/constructors/parameterlessPrimary.kt"); + } + + @TestMetadata("privatePrimaryDefaults.kt") + public void testPrivatePrimaryDefaults() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/constructors/privatePrimaryDefaults.kt"); + } + + @TestMetadata("protectedPrimaryDefaults.kt") + public void testProtectedPrimaryDefaults() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/constructors/protectedPrimaryDefaults.kt"); + } + } + @TestMetadata("compiler/testData/codegen/bytecodeText/controlStructures") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)