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.
This commit is contained in:
Steven Schäfer
2020-01-13 16:11:14 +01:00
committed by Alexander Udalov
parent 5321a6af33
commit 8e07482862
12 changed files with 217 additions and 3 deletions
@@ -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 {
@@ -0,0 +1,15 @@
enum class Enum(val x: Int = 0) {
A,
B(0) { override fun f() {} };
open fun f() {}
}
// @Enum.class:
// 0 <init>\(\)V
// 1 private <init>\(Ljava/lang/String;II\)V
// 1 synthetic <init>\(Ljava/lang/String;IIILkotlin/jvm/internal/DefaultConstructorMarker;\)V
// 1 public synthetic <init>\(Ljava/lang/String;IILkotlin/jvm/internal/DefaultConstructorMarker;\)V
// @Enum$B.class:
// 0 <init>\(\)V
// 1 <init>\(Ljava/lang/String;I\)V
@@ -0,0 +1,13 @@
inline class A(val x: Int)
class B(val a: A = A(0))
// @B.class:
// 1 private <init>\(I\)V
// 1 public synthetic <init>\(IILkotlin/jvm/internal/DefaultConstructorMarker;\)V
// 1 public synthetic <init>\(ILkotlin/jvm/internal/DefaultConstructorMarker;\)V
// JVM_TEMPLATES
// 1 private <init>\(\)V
// JVM_IR_TEMPLATES
// 1 public <init>\(\)V
@@ -0,0 +1,13 @@
inline class A(val x: Int = 0)
// 0 <init>\(\)V
// 1 private synthetic <init>\(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
@@ -0,0 +1,12 @@
class A(val s: String) {
inner class B(val x: Int = 0)
}
// @A.class
// 1 public <init>\(Ljava/lang/String;\)V
// @A$B.class
// 0 <init>\(\)V
// 0 <init>\(LA;\)V
// 1 public <init>\(LA;I\)V
// 1 public synthetic <init>\(LA;IILkotlin/jvm/internal/DefaultConstructorMarker;\)V
@@ -0,0 +1,5 @@
class A internal constructor(val x: Int = 0)
// 1 public <init>\(\)V
// 1 public <init>\(I\)V
// 1 public synthetic <init>\(IILkotlin/jvm/internal/DefaultConstructorMarker;\)V
@@ -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 <init>\(Ljava/lang/String;\)V
// @A$f$B.class:
// 0 <init>\(\)V
// 1 public <init>\(I\)V
// 1 public synthetic <init>\(IILkotlin/jvm/internal/DefaultConstructorMarker;\)V
@@ -0,0 +1,5 @@
class A(val x: Int = 0)
// 1 public <init>\(\)V
// 1 public <init>\(I\)V
// 1 public synthetic <init>\(IILkotlin/jvm/internal/DefaultConstructorMarker;\)V
@@ -0,0 +1,5 @@
class A private constructor(val x: Int = 0)
// 0 <init>\(\)V
// 1 private <init>\(I\)V
// 1 synthetic <init>\(IILkotlin/jvm/internal/DefaultConstructorMarker;\)V
@@ -0,0 +1,5 @@
class A protected constructor(val x: Int = 0)
// 1 protected <init>\(\)V
// 1 protected <init>\(I\)V
// 1 synthetic <init>\(IILkotlin/jvm/internal/DefaultConstructorMarker;\)V
@@ -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)
@@ -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)