FIR2IR: generate synthetic members for data class only if needed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
acced52384
commit
07add635eb
@@ -9,10 +9,7 @@ import com.intellij.psi.PsiCompiledElement
|
|||||||
import org.jetbrains.kotlin.KtNodeTypes
|
import org.jetbrains.kotlin.KtNodeTypes
|
||||||
import org.jetbrains.kotlin.fir.FirElement
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirConstructor
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirVariable
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty
|
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
|
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirConstKind
|
import org.jetbrains.kotlin.fir.expressions.FirConstKind
|
||||||
@@ -207,17 +204,57 @@ private fun FirConstKind<*>.toIrConstKind(): IrConstKind<*> = when (this) {
|
|||||||
FirConstKind.IntegerLiteral, FirConstKind.UnsignedIntegerLiteral -> throw IllegalArgumentException()
|
FirConstKind.IntegerLiteral, FirConstKind.UnsignedIntegerLiteral -> throw IllegalArgumentException()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun FirClass<*>.collectCallableNamesFromSupertypes(session: FirSession, result: MutableList<Name> = mutableListOf()): List<Name> {
|
private val simpleDeclarationCollector: (FirDeclaration, MutableMap<Name, FirDeclaration>) -> Unit = { declaration, map ->
|
||||||
|
when (declaration) {
|
||||||
|
is FirSimpleFunction ->
|
||||||
|
map.putIfAbsent(declaration.name, declaration)
|
||||||
|
is FirVariable<*> ->
|
||||||
|
map.putIfAbsent(declaration.name, declaration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun FirClass<*>.collectCallableNamesFromSupertypes(
|
||||||
|
session: FirSession,
|
||||||
|
result: MutableMap<Name, FirDeclaration> = mutableMapOf(),
|
||||||
|
record: (FirDeclaration, MutableMap<Name, FirDeclaration>) -> Unit = simpleDeclarationCollector
|
||||||
|
): Set<Name> {
|
||||||
for (superTypeRef in superTypeRefs) {
|
for (superTypeRef in superTypeRefs) {
|
||||||
superTypeRef.collectCallableNamesFromThisAndSupertypes(session, result)
|
superTypeRef.collectDeclarationsFromThisAndSupertypes(session, result, record)
|
||||||
|
}
|
||||||
|
return result.keys
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun FirClass<*>.collectContributedFunctionsFromSupertypes(
|
||||||
|
session: FirSession,
|
||||||
|
result: MutableMap<Name, FirDeclaration> = mutableMapOf(),
|
||||||
|
record: (FirDeclaration, MutableMap<Name, FirDeclaration>) -> Unit = { declaration, map ->
|
||||||
|
if (declaration is FirSimpleFunction && declaration.body != null) {
|
||||||
|
map.putIfAbsent(declaration.name, declaration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
): Map<Name, FirDeclaration> {
|
||||||
|
for (superTypeRef in superTypeRefs) {
|
||||||
|
superTypeRef.collectDeclarationsFromThisAndSupertypes(session, result, record)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun FirTypeRef.collectCallableNamesFromThisAndSupertypes(
|
fun FirClass<*>.collectDeclarationsFromSupertypes(
|
||||||
session: FirSession,
|
session: FirSession,
|
||||||
result: MutableList<Name> = mutableListOf()
|
result: MutableMap<Name, FirDeclaration> = mutableMapOf(),
|
||||||
): List<Name> {
|
record: (FirDeclaration, MutableMap<Name, FirDeclaration>) -> Unit = simpleDeclarationCollector
|
||||||
|
): Map<Name, FirDeclaration> {
|
||||||
|
for (superTypeRef in superTypeRefs) {
|
||||||
|
superTypeRef.collectDeclarationsFromThisAndSupertypes(session, result, record)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun FirTypeRef.collectDeclarationsFromThisAndSupertypes(
|
||||||
|
session: FirSession,
|
||||||
|
result: MutableMap<Name, FirDeclaration> = mutableMapOf(),
|
||||||
|
record: (FirDeclaration, MutableMap<Name, FirDeclaration>) -> Unit = simpleDeclarationCollector
|
||||||
|
): Map<Name, FirDeclaration> {
|
||||||
if (this is FirResolvedTypeRef) {
|
if (this is FirResolvedTypeRef) {
|
||||||
val superType = type
|
val superType = type
|
||||||
if (superType is ConeClassLikeType) {
|
if (superType is ConeClassLikeType) {
|
||||||
@@ -225,16 +262,13 @@ private fun FirTypeRef.collectCallableNamesFromThisAndSupertypes(
|
|||||||
is FirClassSymbol -> {
|
is FirClassSymbol -> {
|
||||||
val superClass = superSymbol.fir as FirClass<*>
|
val superClass = superSymbol.fir as FirClass<*>
|
||||||
for (declaration in superClass.declarations) {
|
for (declaration in superClass.declarations) {
|
||||||
when (declaration) {
|
record(declaration, result)
|
||||||
is FirSimpleFunction -> result += declaration.name
|
|
||||||
is FirVariable<*> -> result += declaration.name
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
superClass.collectCallableNamesFromSupertypes(session, result)
|
superClass.collectDeclarationsFromSupertypes(session, result, record)
|
||||||
}
|
}
|
||||||
is FirTypeAliasSymbol -> {
|
is FirTypeAliasSymbol -> {
|
||||||
val superAlias = superSymbol.fir
|
val superAlias = superSymbol.fir
|
||||||
superAlias.expandedTypeRef.collectCallableNamesFromThisAndSupertypes(session, result)
|
superAlias.expandedTypeRef.collectDeclarationsFromThisAndSupertypes(session, result, record)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,3 +132,11 @@ class Fir2IrTypeConverter(
|
|||||||
return classIdToSymbolMap[classId] ?: getArrayClassSymbol(classId)
|
return classIdToSymbolMap[classId] ?: getArrayClassSymbol(classId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun FirTypeRef.toIrType(
|
||||||
|
typeConverter: Fir2IrTypeConverter,
|
||||||
|
typeContext: ConversionTypeContext = ConversionTypeContext.DEFAULT
|
||||||
|
): IrType =
|
||||||
|
with(typeConverter) {
|
||||||
|
toIrType(typeContext)
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -65,7 +65,7 @@ internal class ClassMemberGenerator(
|
|||||||
// Add synthetic members *before* fake override generations.
|
// Add synthetic members *before* fake override generations.
|
||||||
// Otherwise, redundant members, e.g., synthetic toString _and_ fake override toString, will be added.
|
// Otherwise, redundant members, e.g., synthetic toString _and_ fake override toString, will be added.
|
||||||
if (irClass.isData && klass.getPrimaryConstructorIfAny() != null) {
|
if (irClass.isData && klass.getPrimaryConstructorIfAny() != null) {
|
||||||
processedCallableNames += DataClassMembersGenerator(components).generateDataClassMembers(irClass)
|
processedCallableNames += DataClassMembersGenerator(components).generateDataClassMembers(klass, irClass)
|
||||||
}
|
}
|
||||||
with(fakeOverrideGenerator) { irClass.addFakeOverrides(klass, processedCallableNames) }
|
with(fakeOverrideGenerator) { irClass.addFakeOverrides(klass, processedCallableNames) }
|
||||||
klass.declarations.forEach {
|
klass.declarations.forEach {
|
||||||
|
|||||||
+91
-29
@@ -7,7 +7,13 @@ package org.jetbrains.kotlin.fir.backend.generators
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.fir.backend.Fir2IrComponents
|
import org.jetbrains.kotlin.fir.backend.Fir2IrComponents
|
||||||
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
|
import org.jetbrains.kotlin.fir.backend.*
|
||||||
import org.jetbrains.kotlin.fir.backend.declareThisReceiverParameter
|
import org.jetbrains.kotlin.fir.backend.declareThisReceiverParameter
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||||
import org.jetbrains.kotlin.ir.builders.IrGeneratorContextBase
|
import org.jetbrains.kotlin.ir.builders.IrGeneratorContextBase
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
@@ -25,19 +31,19 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) {
|
|||||||
|
|
||||||
// TODO: generateInlineClassMembers
|
// TODO: generateInlineClassMembers
|
||||||
|
|
||||||
fun generateDataClassMembers(irClass: IrClass): List<Name> =
|
fun generateDataClassMembers(klass: FirClass<*>, irClass: IrClass): List<Name> =
|
||||||
MyDataClassMethodsGenerator(irClass, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER).generate()
|
MyDataClassMethodsGenerator(klass, irClass, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER).generate()
|
||||||
|
|
||||||
fun generateDataClassComponentBody(irFunction: IrFunction) =
|
fun generateDataClassComponentBody(irFunction: IrFunction) =
|
||||||
MyDataClassMethodsGenerator(irFunction.parentAsClass, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER)
|
MyDataClassMethodsGenerator(null, irFunction.parentAsClass, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER)
|
||||||
.generateComponentBody(irFunction)
|
.generateComponentBody(irFunction)
|
||||||
|
|
||||||
fun generateDataClassCopyBody(irFunction: IrFunction) =
|
fun generateDataClassCopyBody(irFunction: IrFunction) =
|
||||||
MyDataClassMethodsGenerator(irFunction.parentAsClass, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER)
|
MyDataClassMethodsGenerator(null, irFunction.parentAsClass, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER)
|
||||||
.generateCopyBody(irFunction)
|
.generateCopyBody(irFunction)
|
||||||
|
|
||||||
|
|
||||||
private inner class MyDataClassMethodsGenerator(
|
private inner class MyDataClassMethodsGenerator(
|
||||||
|
val klass: FirClass<*>?,
|
||||||
val irClass: IrClass,
|
val irClass: IrClass,
|
||||||
val origin: IrDeclarationOrigin
|
val origin: IrDeclarationOrigin
|
||||||
) {
|
) {
|
||||||
@@ -85,38 +91,91 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) {
|
|||||||
valueParameterDescriptor.bind(this)
|
valueParameterDescriptor.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private val FirSimpleFunction.matchesEqualsSignature: Boolean
|
||||||
|
get() = valueParameters.size == 1 &&
|
||||||
|
valueParameters[0].returnTypeRef.toIrType(components.typeConverter) == components.irBuiltIns.anyNType &&
|
||||||
|
returnTypeRef.toIrType(components.typeConverter) == components.irBuiltIns.booleanType
|
||||||
|
|
||||||
|
private val FirSimpleFunction.matchesHashCodeSignature: Boolean
|
||||||
|
get() = valueParameters.isEmpty() &&
|
||||||
|
returnTypeRef.toIrType(components.typeConverter) == components.irBuiltIns.intType
|
||||||
|
|
||||||
|
private val FirSimpleFunction.matchesToStringSignature: Boolean
|
||||||
|
get() = valueParameters.isEmpty() &&
|
||||||
|
returnTypeRef.toIrType(components.typeConverter) == components.irBuiltIns.stringType
|
||||||
|
|
||||||
|
private val FirSimpleFunction.matchesDataClassSyntheticMemberSignatures: Boolean
|
||||||
|
get() = (this.name == equalsName && matchesEqualsSignature) ||
|
||||||
|
(this.name == hashCodeName && matchesHashCodeSignature) ||
|
||||||
|
(this.name == toStringName && matchesToStringSignature)
|
||||||
|
|
||||||
fun generate(): List<Name> {
|
fun generate(): List<Name> {
|
||||||
if (properties.isEmpty()) {
|
if (properties.isEmpty() || klass == null) {
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: generate equals, hashCode, and toString only if needed
|
val result = mutableListOf<Name>()
|
||||||
val equalsFunction = createSyntheticIrFunction(
|
|
||||||
Name.identifier("equals"),
|
val contributedFunctionsInThisType = klass.declarations.mapNotNull {
|
||||||
components.irBuiltIns.booleanType
|
if (it is FirSimpleFunction && it.matchesDataClassSyntheticMemberSignatures) {
|
||||||
).apply {
|
it.name
|
||||||
valueParameters = listOf(
|
} else
|
||||||
createSyntheticIrParameter(this, Name.identifier("other"), components.irBuiltIns.anyNType)
|
null
|
||||||
)
|
|
||||||
}
|
}
|
||||||
irDataClassMembersGenerator.generateEqualsMethod(equalsFunction, properties)
|
val nonOverridableContributedFunctionsInSupertypes =
|
||||||
irClass.declarations.add(equalsFunction)
|
klass.collectContributedFunctionsFromSupertypes(components.session) { declaration, map ->
|
||||||
|
if (declaration is FirSimpleFunction &&
|
||||||
|
declaration.body != null &&
|
||||||
|
!Visibilities.isPrivate(declaration.visibility) &&
|
||||||
|
declaration.modality == Modality.FINAL &&
|
||||||
|
declaration.matchesDataClassSyntheticMemberSignatures
|
||||||
|
) {
|
||||||
|
map.putIfAbsent(declaration.name, declaration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val hashCodeFunction = createSyntheticIrFunction(
|
if (!contributedFunctionsInThisType.contains(equalsName) &&
|
||||||
Name.identifier("hashCode"),
|
!nonOverridableContributedFunctionsInSupertypes.containsKey(equalsName)
|
||||||
components.irBuiltIns.intType
|
) {
|
||||||
)
|
result.add(equalsName)
|
||||||
irDataClassMembersGenerator.generateHashCodeMethod(hashCodeFunction, properties)
|
val equalsFunction = createSyntheticIrFunction(
|
||||||
irClass.declarations.add(hashCodeFunction)
|
equalsName,
|
||||||
|
components.irBuiltIns.booleanType,
|
||||||
|
).apply {
|
||||||
|
valueParameters = listOf(
|
||||||
|
createSyntheticIrParameter(this, Name.identifier("other"), components.irBuiltIns.anyNType)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
irDataClassMembersGenerator.generateEqualsMethod(equalsFunction, properties)
|
||||||
|
irClass.declarations.add(equalsFunction)
|
||||||
|
}
|
||||||
|
|
||||||
val toStringFunction = createSyntheticIrFunction(
|
if (!contributedFunctionsInThisType.contains(hashCodeName) &&
|
||||||
Name.identifier("toString"),
|
!nonOverridableContributedFunctionsInSupertypes.containsKey(hashCodeName)
|
||||||
components.irBuiltIns.stringType
|
) {
|
||||||
)
|
result.add(hashCodeName)
|
||||||
irDataClassMembersGenerator.generateToStringMethod(toStringFunction, properties)
|
val hashCodeFunction = createSyntheticIrFunction(
|
||||||
irClass.declarations.add(toStringFunction)
|
hashCodeName,
|
||||||
|
components.irBuiltIns.intType,
|
||||||
|
)
|
||||||
|
irDataClassMembersGenerator.generateHashCodeMethod(hashCodeFunction, properties)
|
||||||
|
irClass.declarations.add(hashCodeFunction)
|
||||||
|
}
|
||||||
|
|
||||||
return listOf(equalsFunction.name, hashCodeFunction.name, toStringFunction.name)
|
if (!contributedFunctionsInThisType.contains(toStringName) &&
|
||||||
|
!nonOverridableContributedFunctionsInSupertypes.containsKey(toStringName)
|
||||||
|
) {
|
||||||
|
result.add(toStringName)
|
||||||
|
val toStringFunction = createSyntheticIrFunction(
|
||||||
|
toStringName,
|
||||||
|
components.irBuiltIns.stringType,
|
||||||
|
)
|
||||||
|
irDataClassMembersGenerator.generateToStringMethod(toStringFunction, properties)
|
||||||
|
irClass.declarations.add(toStringFunction)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateComponentBody(irFunction: IrFunction) {
|
fun generateComponentBody(irFunction: IrFunction) {
|
||||||
@@ -190,6 +249,9 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val copyName = Name.identifier("copy")
|
private val copyName = Name.identifier("copy")
|
||||||
|
private val equalsName = Name.identifier("equals")
|
||||||
|
private val hashCodeName = Name.identifier("hashCode")
|
||||||
|
private val toStringName = Name.identifier("toString")
|
||||||
|
|
||||||
fun isCopy(irFunction: IrFunction): Boolean =
|
fun isCopy(irFunction: IrFunction): Boolean =
|
||||||
irFunction.name == copyName
|
irFunction.name == copyName
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
|
||||||
data class A(val x: Int) {
|
data class A(val x: Int) {
|
||||||
override fun equals(other: Any?): Boolean = false
|
override fun equals(other: Any?): Boolean = false
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
|
||||||
// TARGET_BACKEND: JVM
|
// TARGET_BACKEND: JVM
|
||||||
|
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
|
||||||
data class A(val x: Int) {
|
data class A(val x: Int) {
|
||||||
override fun hashCode(): Int = -3
|
override fun hashCode(): Int = -3
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
|
||||||
abstract class Base {
|
abstract class Base {
|
||||||
final override fun toString() = "OK"
|
final override fun toString() = "OK"
|
||||||
final override fun hashCode() = 42
|
final override fun hashCode() = 42
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
|
||||||
data class A(val x: Int) {
|
data class A(val x: Int) {
|
||||||
override fun toString(): String = "!"
|
override fun toString(): String = "!"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user