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.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||
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.*
|
||||
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty
|
||||
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirConstKind
|
||||
@@ -207,17 +204,57 @@ private fun FirConstKind<*>.toIrConstKind(): IrConstKind<*> = when (this) {
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
||||
private fun FirTypeRef.collectCallableNamesFromThisAndSupertypes(
|
||||
fun FirClass<*>.collectDeclarationsFromSupertypes(
|
||||
session: FirSession,
|
||||
result: MutableList<Name> = mutableListOf()
|
||||
): List<Name> {
|
||||
result: MutableMap<Name, FirDeclaration> = mutableMapOf(),
|
||||
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) {
|
||||
val superType = type
|
||||
if (superType is ConeClassLikeType) {
|
||||
@@ -225,16 +262,13 @@ private fun FirTypeRef.collectCallableNamesFromThisAndSupertypes(
|
||||
is FirClassSymbol -> {
|
||||
val superClass = superSymbol.fir as FirClass<*>
|
||||
for (declaration in superClass.declarations) {
|
||||
when (declaration) {
|
||||
is FirSimpleFunction -> result += declaration.name
|
||||
is FirVariable<*> -> result += declaration.name
|
||||
}
|
||||
record(declaration, result)
|
||||
}
|
||||
superClass.collectCallableNamesFromSupertypes(session, result)
|
||||
superClass.collectDeclarationsFromSupertypes(session, result, record)
|
||||
}
|
||||
is FirTypeAliasSymbol -> {
|
||||
val superAlias = superSymbol.fir
|
||||
superAlias.expandedTypeRef.collectCallableNamesFromThisAndSupertypes(session, result)
|
||||
superAlias.expandedTypeRef.collectDeclarationsFromThisAndSupertypes(session, result, record)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,4 +131,12 @@ class Fir2IrTypeConverter(
|
||||
private fun getBuiltInClassSymbol(classId: ClassId?): IrClassSymbol? {
|
||||
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.
|
||||
// Otherwise, redundant members, e.g., synthetic toString _and_ fake override toString, will be added.
|
||||
if (irClass.isData && klass.getPrimaryConstructorIfAny() != null) {
|
||||
processedCallableNames += DataClassMembersGenerator(components).generateDataClassMembers(irClass)
|
||||
processedCallableNames += DataClassMembersGenerator(components).generateDataClassMembers(klass, irClass)
|
||||
}
|
||||
with(fakeOverrideGenerator) { irClass.addFakeOverrides(klass, processedCallableNames) }
|
||||
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.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.declarations.*
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.IrGeneratorContextBase
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
@@ -25,19 +31,19 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) {
|
||||
|
||||
// TODO: generateInlineClassMembers
|
||||
|
||||
fun generateDataClassMembers(irClass: IrClass): List<Name> =
|
||||
MyDataClassMethodsGenerator(irClass, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER).generate()
|
||||
fun generateDataClassMembers(klass: FirClass<*>, irClass: IrClass): List<Name> =
|
||||
MyDataClassMethodsGenerator(klass, irClass, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER).generate()
|
||||
|
||||
fun generateDataClassComponentBody(irFunction: IrFunction) =
|
||||
MyDataClassMethodsGenerator(irFunction.parentAsClass, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER)
|
||||
MyDataClassMethodsGenerator(null, irFunction.parentAsClass, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER)
|
||||
.generateComponentBody(irFunction)
|
||||
|
||||
fun generateDataClassCopyBody(irFunction: IrFunction) =
|
||||
MyDataClassMethodsGenerator(irFunction.parentAsClass, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER)
|
||||
MyDataClassMethodsGenerator(null, irFunction.parentAsClass, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER)
|
||||
.generateCopyBody(irFunction)
|
||||
|
||||
|
||||
private inner class MyDataClassMethodsGenerator(
|
||||
val klass: FirClass<*>?,
|
||||
val irClass: IrClass,
|
||||
val origin: IrDeclarationOrigin
|
||||
) {
|
||||
@@ -85,38 +91,91 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) {
|
||||
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> {
|
||||
if (properties.isEmpty()) {
|
||||
if (properties.isEmpty() || klass == null) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
// TODO: generate equals, hashCode, and toString only if needed
|
||||
val equalsFunction = createSyntheticIrFunction(
|
||||
Name.identifier("equals"),
|
||||
components.irBuiltIns.booleanType
|
||||
).apply {
|
||||
valueParameters = listOf(
|
||||
createSyntheticIrParameter(this, Name.identifier("other"), components.irBuiltIns.anyNType)
|
||||
)
|
||||
val result = mutableListOf<Name>()
|
||||
|
||||
val contributedFunctionsInThisType = klass.declarations.mapNotNull {
|
||||
if (it is FirSimpleFunction && it.matchesDataClassSyntheticMemberSignatures) {
|
||||
it.name
|
||||
} else
|
||||
null
|
||||
}
|
||||
irDataClassMembersGenerator.generateEqualsMethod(equalsFunction, properties)
|
||||
irClass.declarations.add(equalsFunction)
|
||||
val nonOverridableContributedFunctionsInSupertypes =
|
||||
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(
|
||||
Name.identifier("hashCode"),
|
||||
components.irBuiltIns.intType
|
||||
)
|
||||
irDataClassMembersGenerator.generateHashCodeMethod(hashCodeFunction, properties)
|
||||
irClass.declarations.add(hashCodeFunction)
|
||||
if (!contributedFunctionsInThisType.contains(equalsName) &&
|
||||
!nonOverridableContributedFunctionsInSupertypes.containsKey(equalsName)
|
||||
) {
|
||||
result.add(equalsName)
|
||||
val equalsFunction = createSyntheticIrFunction(
|
||||
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(
|
||||
Name.identifier("toString"),
|
||||
components.irBuiltIns.stringType
|
||||
)
|
||||
irDataClassMembersGenerator.generateToStringMethod(toStringFunction, properties)
|
||||
irClass.declarations.add(toStringFunction)
|
||||
if (!contributedFunctionsInThisType.contains(hashCodeName) &&
|
||||
!nonOverridableContributedFunctionsInSupertypes.containsKey(hashCodeName)
|
||||
) {
|
||||
result.add(hashCodeName)
|
||||
val hashCodeFunction = createSyntheticIrFunction(
|
||||
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) {
|
||||
@@ -190,6 +249,9 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) {
|
||||
|
||||
companion object {
|
||||
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 =
|
||||
irFunction.name == copyName
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
data class A(val x: Int) {
|
||||
override fun equals(other: Any?): Boolean = false
|
||||
}
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// TARGET_BACKEND: JVM
|
||||
|
||||
// WITH_RUNTIME
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
data class A(val x: Int) {
|
||||
override fun hashCode(): Int = -3
|
||||
}
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
abstract class Base {
|
||||
final override fun toString() = "OK"
|
||||
final override fun hashCode() = 42
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
data class A(val x: Int) {
|
||||
override fun toString(): String = "!"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user