FIR2IR: convert enums with non-primary default ctor correctly
see added test for example
This commit is contained in:
@@ -27,13 +27,11 @@ import org.jetbrains.kotlin.fir.types.FirTypeRef
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrEnumConstructorCallImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
|
||||
import org.jetbrains.kotlin.ir.util.IdSignature
|
||||
import org.jetbrains.kotlin.ir.util.constructors
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
@@ -528,7 +526,6 @@ class Fir2IrClassifierStorage(
|
||||
startOffset, endOffset, origin, symbol, enumEntry.name
|
||||
).apply {
|
||||
declarationStorage.enterScope(this)
|
||||
val irType = enumEntry.returnTypeRef.toIrType()
|
||||
if (irParent != null) {
|
||||
this.parent = irParent
|
||||
}
|
||||
@@ -541,15 +538,6 @@ class Fir2IrClassifierStorage(
|
||||
}
|
||||
// Otherwise, this is a default-ish enum entry whose initializer would be a delegating constructor call,
|
||||
// which will be translated via visitor later.
|
||||
} else if (irParent != null && origin == IrDeclarationOrigin.DEFINED) {
|
||||
val constructor = irParent.constructors.first()
|
||||
this.initializerExpression = factory.createExpressionBody(
|
||||
IrEnumConstructorCallImpl(
|
||||
startOffset, endOffset, irType, constructor.symbol,
|
||||
valueArgumentsCount = constructor.valueParameters.size,
|
||||
typeArgumentsCount = constructor.typeParameters.size
|
||||
)
|
||||
)
|
||||
}
|
||||
declarationStorage.leaveScope(this)
|
||||
}
|
||||
|
||||
@@ -234,6 +234,7 @@ class Fir2IrConverter(
|
||||
if (irConstructor != null) {
|
||||
irClass.declarations += irConstructor
|
||||
}
|
||||
// At least on enum entry creation we may need a default constructor, so ctors should be converted first
|
||||
for (declaration in syntheticPropertiesLast(allDeclarations)) {
|
||||
val irDeclaration = processMemberDeclaration(declaration, regularClass, irClass) ?: continue
|
||||
irClass.declarations += irDeclaration
|
||||
@@ -294,7 +295,7 @@ class Fir2IrConverter(
|
||||
// Sort declarations so that all non-synthetic declarations are before synthetic ones.
|
||||
// This is needed because converting synthetic fields for implementation delegation needs to know
|
||||
// existing declarations in the class to avoid adding redundant delegated members.
|
||||
private fun syntheticPropertiesLast(declarations: List<FirDeclaration>): Iterable<FirDeclaration> {
|
||||
private fun syntheticPropertiesLast(declarations: Iterable<FirDeclaration>): Iterable<FirDeclaration> {
|
||||
return declarations.sortedBy { it !is FirField && it.isSynthetic }
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.types.impl.IrErrorTypeImpl
|
||||
import org.jetbrains.kotlin.ir.util.constructors
|
||||
import org.jetbrains.kotlin.ir.util.defaultConstructor
|
||||
import org.jetbrains.kotlin.ir.util.defaultType
|
||||
import org.jetbrains.kotlin.ir.util.parentClassOrNull
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
@@ -116,6 +117,8 @@ class Fir2IrVisitor(
|
||||
annotationGenerator.generate(irEnumEntry, enumEntry)
|
||||
val correspondingClass = irEnumEntry.correspondingClass
|
||||
val initializer = enumEntry.initializer
|
||||
val irType = enumEntry.returnTypeRef.toIrType()
|
||||
val irParentEnumClass = irEnumEntry.parent as? IrClass
|
||||
// If the enum entry has its own members, we need to introduce a synthetic class.
|
||||
if (correspondingClass != null) {
|
||||
declarationStorage.enterScope(irEnumEntry)
|
||||
@@ -129,7 +132,7 @@ class Fir2IrVisitor(
|
||||
val constructor = correspondingClass.constructors.first()
|
||||
irEnumEntry.initializerExpression = irFactory.createExpressionBody(
|
||||
IrEnumConstructorCallImpl(
|
||||
startOffset, endOffset, enumEntry.returnTypeRef.toIrType(),
|
||||
startOffset, endOffset, irType,
|
||||
constructor.symbol,
|
||||
typeArgumentsCount = constructor.typeParameters.size,
|
||||
valueArgumentsCount = constructor.valueParameters.size
|
||||
@@ -148,6 +151,20 @@ class Fir2IrVisitor(
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if (irParentEnumClass != null && initializer == null) {
|
||||
// a default-ish enum entry whose initializer would be a delegating constructor call
|
||||
val constructor =
|
||||
irParentEnumClass.defaultConstructor ?: error("Assuming that default constructor should exist and be converted at this point")
|
||||
enumEntry.convertWithOffsets { startOffset, endOffset ->
|
||||
irEnumEntry.initializerExpression = irFactory.createExpressionBody(
|
||||
IrEnumConstructorCallImpl(
|
||||
startOffset, endOffset, irType, constructor.symbol,
|
||||
valueArgumentsCount = constructor.valueParameters.size,
|
||||
typeArgumentsCount = constructor.typeParameters.size
|
||||
)
|
||||
)
|
||||
irEnumEntry
|
||||
}
|
||||
}
|
||||
return irEnumEntry
|
||||
}
|
||||
|
||||
+6
@@ -16956,6 +16956,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/entryClassConstructorWithVarargs.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noPrimaryConstructor.kt")
|
||||
public void testNoPrimaryConstructor() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/noPrimaryConstructor.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("secondaryConstructorWithDefaultArguments.kt")
|
||||
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
||||
|
||||
@@ -213,6 +213,9 @@ val IrClassSymbol.functions: Sequence<IrSimpleFunctionSymbol>
|
||||
val IrClass.constructors: Sequence<IrConstructor>
|
||||
get() = declarations.asSequence().filterIsInstance<IrConstructor>()
|
||||
|
||||
val IrClass.defaultConstructor: IrConstructor?
|
||||
get() = constructors.firstOrNull { ctor -> ctor.valueParameters.all { it.defaultValue != null } }
|
||||
|
||||
val IrClassSymbol.constructors: Sequence<IrConstructorSymbol>
|
||||
get() = owner.constructors.map { it.symbol }
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
enum class Test {
|
||||
A(0),
|
||||
B;
|
||||
|
||||
val n: Int
|
||||
|
||||
constructor(n: Int) { this.n = n }
|
||||
constructor() : this(0)
|
||||
}
|
||||
|
||||
fun box(): String =
|
||||
if (Test.A.n == Test.B.n)
|
||||
"OK"
|
||||
else
|
||||
"Fail"
|
||||
+6
@@ -16674,6 +16674,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/entryClassConstructorWithVarargs.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noPrimaryConstructor.kt")
|
||||
public void testNoPrimaryConstructor() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/noPrimaryConstructor.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("secondaryConstructorWithDefaultArguments.kt")
|
||||
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
||||
|
||||
+6
@@ -16956,6 +16956,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/entryClassConstructorWithVarargs.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noPrimaryConstructor.kt")
|
||||
public void testNoPrimaryConstructor() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/noPrimaryConstructor.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("secondaryConstructorWithDefaultArguments.kt")
|
||||
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
||||
|
||||
+5
@@ -13761,6 +13761,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/entryClassConstructorWithVarargs.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noPrimaryConstructor.kt")
|
||||
public void testNoPrimaryConstructor() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/noPrimaryConstructor.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("secondaryConstructorWithDefaultArguments.kt")
|
||||
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/secondaryConstructorWithDefaultArguments.kt");
|
||||
|
||||
+6
@@ -12860,6 +12860,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/entryClassConstructorWithVarargs.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noPrimaryConstructor.kt")
|
||||
public void testNoPrimaryConstructor() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/noPrimaryConstructor.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("secondaryConstructorWithDefaultArguments.kt")
|
||||
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
||||
|
||||
+6
@@ -12956,6 +12956,12 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/entryClassConstructorWithVarargs.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noPrimaryConstructor.kt")
|
||||
public void testNoPrimaryConstructor() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/noPrimaryConstructor.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("secondaryConstructorWithDefaultArguments.kt")
|
||||
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
||||
|
||||
+5
@@ -11486,6 +11486,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
|
||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/entryClassConstructorWithVarargs.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noPrimaryConstructor.kt")
|
||||
public void testNoPrimaryConstructor() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/noPrimaryConstructor.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("secondaryConstructorWithDefaultArguments.kt")
|
||||
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/secondaryConstructorWithDefaultArguments.kt");
|
||||
|
||||
+6
@@ -13968,6 +13968,12 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest
|
||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/entryClassConstructorWithVarargs.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noPrimaryConstructor.kt")
|
||||
public void testNoPrimaryConstructor() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/noPrimaryConstructor.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("secondaryConstructorWithDefaultArguments.kt")
|
||||
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user