JVM_IR: permit @NoArg if super constructor is already zero-argument
#KT-46537 Fixed
This commit is contained in:
+5
-1
@@ -35,7 +35,8 @@ class InitializersLowering(context: CommonBackendContext) : InitializersLowering
|
|||||||
|
|
||||||
val irClass = container.constructedClass
|
val irClass = container.constructedClass
|
||||||
val instanceInitializerStatements = extractInitializers(irClass) {
|
val instanceInitializerStatements = extractInitializers(irClass) {
|
||||||
(it is IrField && !it.isStatic) || (it is IrAnonymousInitializer && !it.isStatic)
|
(it is IrField && !it.isStatic && (container.isPrimary || !it.primaryConstructorParameter)) ||
|
||||||
|
(it is IrAnonymousInitializer && !it.isStatic)
|
||||||
}
|
}
|
||||||
val block = IrBlockImpl(irClass.startOffset, irClass.endOffset, context.irBuiltIns.unitType, null, instanceInitializerStatements)
|
val block = IrBlockImpl(irClass.startOffset, irClass.endOffset, context.irBuiltIns.unitType, null, instanceInitializerStatements)
|
||||||
// Check that the initializers contain no local classes. Deep-copying them is a disaster for code size, and liable to break randomly.
|
// Check that the initializers contain no local classes. Deep-copying them is a disaster for code size, and liable to break randomly.
|
||||||
@@ -54,6 +55,9 @@ class InitializersLowering(context: CommonBackendContext) : InitializersLowering
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val IrField.primaryConstructorParameter: Boolean
|
||||||
|
get() = (initializer?.expression as? IrGetValue)?.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER
|
||||||
|
|
||||||
abstract class InitializersLoweringBase(open val context: CommonBackendContext) {
|
abstract class InitializersLoweringBase(open val context: CommonBackendContext) {
|
||||||
protected fun extractInitializers(irClass: IrClass, filter: (IrDeclaration) -> Boolean) =
|
protected fun extractInitializers(irClass: IrClass, filter: (IrDeclaration) -> Boolean) =
|
||||||
// TODO What about fields that were added by lowerings? e.g. captured outer class or locals?
|
// TODO What about fields that were added by lowerings? e.g. captured outer class or locals?
|
||||||
|
|||||||
@@ -7,29 +7,19 @@ package org.jetbrains.kotlin.noarg
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||||
import org.jetbrains.kotlin.backend.common.lower.SYNTHESIZED_INIT_BLOCK
|
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.extensions.AnnotationBasedExtension
|
import org.jetbrains.kotlin.extensions.AnnotationBasedExtension
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.IrStatement
|
|
||||||
import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor
|
import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
|
||||||
import org.jetbrains.kotlin.ir.descriptors.toIrBasedDescriptor
|
import org.jetbrains.kotlin.ir.descriptors.toIrBasedDescriptor
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrBlock
|
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrGetValue
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
|
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
import org.jetbrains.kotlin.ir.types.getClass
|
import org.jetbrains.kotlin.ir.types.getClass
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||||
import org.jetbrains.kotlin.psi.KtModifierListOwner
|
import org.jetbrains.kotlin.psi.KtModifierListOwner
|
||||||
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_OVERLOADS_FQ_NAME
|
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_OVERLOADS_FQ_NAME
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
|
||||||
|
|
||||||
internal class NoArgIrGenerationExtension(
|
internal class NoArgIrGenerationExtension(
|
||||||
private val annotations: List<String>,
|
private val annotations: List<String>,
|
||||||
@@ -56,7 +46,7 @@ private class NoArgIrTransformer(
|
|||||||
|
|
||||||
if (declaration.kind == ClassKind.CLASS &&
|
if (declaration.kind == ClassKind.CLASS &&
|
||||||
declaration.isAnnotatedWithNoarg() &&
|
declaration.isAnnotatedWithNoarg() &&
|
||||||
declaration.constructors.none(::isZeroParameterConstructor)
|
declaration.constructors.none { it.isZeroParameterConstructor() }
|
||||||
) {
|
) {
|
||||||
declaration.declarations.add(getOrGenerateNoArgConstructor(declaration))
|
declaration.declarations.add(getOrGenerateNoArgConstructor(declaration))
|
||||||
}
|
}
|
||||||
@@ -72,7 +62,7 @@ private class NoArgIrTransformer(
|
|||||||
val superConstructor =
|
val superConstructor =
|
||||||
if (superClass.isAnnotatedWithNoarg())
|
if (superClass.isAnnotatedWithNoarg())
|
||||||
getOrGenerateNoArgConstructor(superClass)
|
getOrGenerateNoArgConstructor(superClass)
|
||||||
else superClass.constructors.singleOrNull { it.valueParameters.isEmpty() }
|
else superClass.constructors.singleOrNull { it.isZeroParameterConstructor() }
|
||||||
?: error("No noarg super constructor for ${klass.render()}:\n" + superClass.constructors.joinToString("\n") { it.render() })
|
?: error("No noarg super constructor for ${klass.render()}:\n" + superClass.constructors.joinToString("\n") { it.render() })
|
||||||
|
|
||||||
context.irFactory.buildConstructor {
|
context.irFactory.buildConstructor {
|
||||||
@@ -86,9 +76,9 @@ private class NoArgIrTransformer(
|
|||||||
ctor.startOffset, ctor.endOffset, context.irBuiltIns.unitType,
|
ctor.startOffset, ctor.endOffset, context.irBuiltIns.unitType,
|
||||||
superConstructor.symbol, 0, superConstructor.valueParameters.size
|
superConstructor.symbol, 0, superConstructor.valueParameters.size
|
||||||
),
|
),
|
||||||
if (invokeInitializers)
|
IrInstanceInitializerCallImpl(
|
||||||
NoArgInitializersLowering(context.irBuiltIns).createInitializersBlock(ctor)
|
ctor.startOffset, ctor.endOffset, klass.symbol, context.irBuiltIns.unitType
|
||||||
else null
|
).takeIf { invokeInitializers }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -97,50 +87,7 @@ private class NoArgIrTransformer(
|
|||||||
private fun IrClass.isAnnotatedWithNoarg(): Boolean =
|
private fun IrClass.isAnnotatedWithNoarg(): Boolean =
|
||||||
toIrBasedDescriptor().hasSpecialAnnotation(null)
|
toIrBasedDescriptor().hasSpecialAnnotation(null)
|
||||||
|
|
||||||
private fun isZeroParameterConstructor(constructor: IrConstructor): Boolean {
|
// Returns true if this constructor is callable with no arguments by JVM rules, i.e. will have descriptor `()V`.
|
||||||
val parameters = constructor.valueParameters
|
private fun IrConstructor.isZeroParameterConstructor(): Boolean =
|
||||||
return parameters.isEmpty() ||
|
valueParameters.all { it.defaultValue != null } && (valueParameters.isEmpty() || isPrimary || hasAnnotation(JVM_OVERLOADS_FQ_NAME))
|
||||||
(parameters.all { it.defaultValue != null } && (constructor.isPrimary || constructor.hasAnnotation(JVM_OVERLOADS_FQ_NAME)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Main parts copied from [org.jetbrains.kotlin.backend.common.lower.InitializersLowering]. */
|
|
||||||
private class NoArgInitializersLowering(private val builtIns: IrBuiltIns) {
|
|
||||||
fun createInitializersBlock(ctor: IrConstructor): IrBlock {
|
|
||||||
val irClass = ctor.constructedClass
|
|
||||||
return IrBlockImpl(irClass.startOffset, irClass.endOffset, builtIns.unitType, null, extractInitializers(irClass))
|
|
||||||
.deepCopyWithSymbols(ctor)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun extractInitializers(irClass: IrClass): List<IrStatement> {
|
|
||||||
val result = mutableListOf<IrStatement>()
|
|
||||||
for (declaration in irClass.declarations) {
|
|
||||||
when (declaration) {
|
|
||||||
is IrAnonymousInitializer -> if (!declaration.isStatic) {
|
|
||||||
result.add(with(declaration) {
|
|
||||||
IrBlockImpl(startOffset, endOffset, builtIns.unitType, SYNTHESIZED_INIT_BLOCK, body.statements)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
is IrProperty -> declaration.backingField.let { field ->
|
|
||||||
if (field != null && !field.isStatic) {
|
|
||||||
val initializer = field.initializer
|
|
||||||
// Take all field initializers except those for properties in the primary constructor, for which we have no values
|
|
||||||
// in the noarg constructor (and thus those properties will be uninitialized).
|
|
||||||
if (initializer != null && initializer.expression.safeAs<IrGetValue>()?.origin !=
|
|
||||||
IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER
|
|
||||||
) {
|
|
||||||
result.add(with(initializer) {
|
|
||||||
IrSetFieldImpl(
|
|
||||||
startOffset, endOffset, field.symbol,
|
|
||||||
IrGetValueImpl(startOffset, endOffset, irClass.thisReceiver!!.type, irClass.thisReceiver!!.symbol),
|
|
||||||
expression, builtIns.unitType, IrStatementOrigin.INITIALIZE_FIELD
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+5
@@ -69,4 +69,9 @@ public class BlackBoxCodegenTestForNoArgGenerated extends AbstractBlackBoxCodege
|
|||||||
public void testSimple() throws Exception {
|
public void testSimple() throws Exception {
|
||||||
runTest("plugins/noarg/noarg-cli/testData/box/simple.kt");
|
runTest("plugins/noarg/noarg-cli/testData/box/simple.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("subclass.kt")
|
||||||
|
public void testSubclass() throws Exception {
|
||||||
|
runTest("plugins/noarg/noarg-cli/testData/box/subclass.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+5
@@ -69,4 +69,9 @@ public class IrBlackBoxCodegenTestForNoArgGenerated extends AbstractIrBlackBoxCo
|
|||||||
public void testSimple() throws Exception {
|
public void testSimple() throws Exception {
|
||||||
runTest("plugins/noarg/noarg-cli/testData/box/simple.kt");
|
runTest("plugins/noarg/noarg-cli/testData/box/simple.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("subclass.kt")
|
||||||
|
public void testSubclass() throws Exception {
|
||||||
|
runTest("plugins/noarg/noarg-cli/testData/box/subclass.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
annotation class NoArg
|
||||||
|
|
||||||
|
open class Base1
|
||||||
|
open class Base2(val a: Int = 1)
|
||||||
|
open class Base3(val a: Int) {
|
||||||
|
@JvmOverloads constructor(a: Long = 1L) : this(a.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
@NoArg class Test1(val b: String) : Base1()
|
||||||
|
@NoArg class Test2(val b: String) : Base2()
|
||||||
|
@NoArg class Test3(val b: String) : Base3()
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val test1 = Test1::class.java.newInstance()
|
||||||
|
val test2 = Test2::class.java.newInstance()
|
||||||
|
if (test2.a != 1) return "fail@test2: ${test2.a}"
|
||||||
|
val test3 = Test3::class.java.newInstance()
|
||||||
|
if (test3.a != 1) return "fail@test3: ${test3.a}"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user