FIR2IR: generate synthetic members for data class only if needed

This commit is contained in:
Jinseong Jeon
2020-04-14 10:40:16 -07:00
committed by Mikhail Glukhikh
parent acced52384
commit 07add635eb
9 changed files with 150 additions and 51 deletions
@@ -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)
}
@@ -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 {
@@ -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,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,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 = "!"
}