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.UNDEFINED_OFFSET
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
|
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.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.*
|
import org.jetbrains.kotlin.ir.symbols.impl.*
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
|
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
|
||||||
import org.jetbrains.kotlin.ir.util.IdSignature
|
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.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.name.StandardClassIds
|
import org.jetbrains.kotlin.name.StandardClassIds
|
||||||
@@ -528,7 +526,6 @@ class Fir2IrClassifierStorage(
|
|||||||
startOffset, endOffset, origin, symbol, enumEntry.name
|
startOffset, endOffset, origin, symbol, enumEntry.name
|
||||||
).apply {
|
).apply {
|
||||||
declarationStorage.enterScope(this)
|
declarationStorage.enterScope(this)
|
||||||
val irType = enumEntry.returnTypeRef.toIrType()
|
|
||||||
if (irParent != null) {
|
if (irParent != null) {
|
||||||
this.parent = irParent
|
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,
|
// Otherwise, this is a default-ish enum entry whose initializer would be a delegating constructor call,
|
||||||
// which will be translated via visitor later.
|
// 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)
|
declarationStorage.leaveScope(this)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -234,6 +234,7 @@ class Fir2IrConverter(
|
|||||||
if (irConstructor != null) {
|
if (irConstructor != null) {
|
||||||
irClass.declarations += irConstructor
|
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)) {
|
for (declaration in syntheticPropertiesLast(allDeclarations)) {
|
||||||
val irDeclaration = processMemberDeclaration(declaration, regularClass, irClass) ?: continue
|
val irDeclaration = processMemberDeclaration(declaration, regularClass, irClass) ?: continue
|
||||||
irClass.declarations += irDeclaration
|
irClass.declarations += irDeclaration
|
||||||
@@ -294,7 +295,7 @@ class Fir2IrConverter(
|
|||||||
// Sort declarations so that all non-synthetic declarations are before synthetic ones.
|
// 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
|
// This is needed because converting synthetic fields for implementation delegation needs to know
|
||||||
// existing declarations in the class to avoid adding redundant delegated members.
|
// 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 }
|
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.*
|
||||||
import org.jetbrains.kotlin.ir.types.impl.IrErrorTypeImpl
|
import org.jetbrains.kotlin.ir.types.impl.IrErrorTypeImpl
|
||||||
import org.jetbrains.kotlin.ir.util.constructors
|
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.defaultType
|
||||||
import org.jetbrains.kotlin.ir.util.parentClassOrNull
|
import org.jetbrains.kotlin.ir.util.parentClassOrNull
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
@@ -116,6 +117,8 @@ class Fir2IrVisitor(
|
|||||||
annotationGenerator.generate(irEnumEntry, enumEntry)
|
annotationGenerator.generate(irEnumEntry, enumEntry)
|
||||||
val correspondingClass = irEnumEntry.correspondingClass
|
val correspondingClass = irEnumEntry.correspondingClass
|
||||||
val initializer = enumEntry.initializer
|
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 the enum entry has its own members, we need to introduce a synthetic class.
|
||||||
if (correspondingClass != null) {
|
if (correspondingClass != null) {
|
||||||
declarationStorage.enterScope(irEnumEntry)
|
declarationStorage.enterScope(irEnumEntry)
|
||||||
@@ -129,7 +132,7 @@ class Fir2IrVisitor(
|
|||||||
val constructor = correspondingClass.constructors.first()
|
val constructor = correspondingClass.constructors.first()
|
||||||
irEnumEntry.initializerExpression = irFactory.createExpressionBody(
|
irEnumEntry.initializerExpression = irFactory.createExpressionBody(
|
||||||
IrEnumConstructorCallImpl(
|
IrEnumConstructorCallImpl(
|
||||||
startOffset, endOffset, enumEntry.returnTypeRef.toIrType(),
|
startOffset, endOffset, irType,
|
||||||
constructor.symbol,
|
constructor.symbol,
|
||||||
typeArgumentsCount = constructor.typeParameters.size,
|
typeArgumentsCount = constructor.typeParameters.size,
|
||||||
valueArgumentsCount = constructor.valueParameters.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
|
return irEnumEntry
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -16956,6 +16956,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
|||||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/entryClassConstructorWithVarargs.kt");
|
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
|
@Test
|
||||||
@TestMetadata("secondaryConstructorWithDefaultArguments.kt")
|
@TestMetadata("secondaryConstructorWithDefaultArguments.kt")
|
||||||
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
||||||
|
|||||||
@@ -213,6 +213,9 @@ val IrClassSymbol.functions: Sequence<IrSimpleFunctionSymbol>
|
|||||||
val IrClass.constructors: Sequence<IrConstructor>
|
val IrClass.constructors: Sequence<IrConstructor>
|
||||||
get() = declarations.asSequence().filterIsInstance<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>
|
val IrClassSymbol.constructors: Sequence<IrConstructorSymbol>
|
||||||
get() = owner.constructors.map { it.symbol }
|
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");
|
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
|
@Test
|
||||||
@TestMetadata("secondaryConstructorWithDefaultArguments.kt")
|
@TestMetadata("secondaryConstructorWithDefaultArguments.kt")
|
||||||
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
||||||
|
|||||||
+6
@@ -16956,6 +16956,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/entryClassConstructorWithVarargs.kt");
|
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
|
@Test
|
||||||
@TestMetadata("secondaryConstructorWithDefaultArguments.kt")
|
@TestMetadata("secondaryConstructorWithDefaultArguments.kt")
|
||||||
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
||||||
|
|||||||
+5
@@ -13761,6 +13761,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/entryClassConstructorWithVarargs.kt");
|
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")
|
@TestMetadata("secondaryConstructorWithDefaultArguments.kt")
|
||||||
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/secondaryConstructorWithDefaultArguments.kt");
|
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");
|
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
|
@Test
|
||||||
@TestMetadata("secondaryConstructorWithDefaultArguments.kt")
|
@TestMetadata("secondaryConstructorWithDefaultArguments.kt")
|
||||||
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
||||||
|
|||||||
+6
@@ -12956,6 +12956,12 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
|||||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/entryClassConstructorWithVarargs.kt");
|
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
|
@Test
|
||||||
@TestMetadata("secondaryConstructorWithDefaultArguments.kt")
|
@TestMetadata("secondaryConstructorWithDefaultArguments.kt")
|
||||||
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
||||||
|
|||||||
+5
@@ -11486,6 +11486,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
|
|||||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/entryClassConstructorWithVarargs.kt");
|
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")
|
@TestMetadata("secondaryConstructorWithDefaultArguments.kt")
|
||||||
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/enum/defaultCtor/secondaryConstructorWithDefaultArguments.kt");
|
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");
|
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
|
@Test
|
||||||
@TestMetadata("secondaryConstructorWithDefaultArguments.kt")
|
@TestMetadata("secondaryConstructorWithDefaultArguments.kt")
|
||||||
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
public void testSecondaryConstructorWithDefaultArguments() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user