FIR2IR: convert enums with non-primary default ctor correctly

see added test for example
This commit is contained in:
Ilya Chernikov
2022-08-17 15:37:16 +02:00
parent 22d6906b33
commit 4e4511bba2
13 changed files with 85 additions and 14 deletions
@@ -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
}
@@ -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"
@@ -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 {
@@ -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 {
@@ -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");
@@ -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 {
@@ -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 {
@@ -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");
@@ -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 {