FIR2IR: avoid descriptors computing hashCode

When synthesizing the hashCode function for data classes, descriptors
were used, in partcular, memberScope for primitive classes.
IrBasedDescriptors have no member scope, so we compute the hashCode
function based on IR structures.
This commit is contained in:
Georgy Bronnikov
2020-12-07 23:09:44 +03:00
parent b07dccb8d7
commit 076272f7ca
7 changed files with 144 additions and 81 deletions
@@ -31,11 +31,18 @@ import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.IrGeneratorContextBase
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.DataClassMembersGenerator
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound
/**
* A generator that generates synthetic members of data class as well as part of inline class.
@@ -93,8 +100,47 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) {
return components.irBuiltIns.anyType
}
override fun commitSubstituted(irMemberAccessExpression: IrMemberAccessExpression<*>, descriptor: CallableDescriptor) {
// TODO
inner class Fir2IrHashCodeFunctionInfo(override val symbol: IrSimpleFunctionSymbol) : HashCodeFunctionInfo {
override fun commitSubstituted(irMemberAccessExpression: IrMemberAccessExpression<*>) {
// TODO
}
}
private fun getHashCodeFunction(klass: IrClass): IrSimpleFunctionSymbol =
klass.functions.singleOrNull {
it.name.asString() == "hashCode" && it.valueParameters.isEmpty() && it.extensionReceiverParameter == null
}?.symbol
?: context.irBuiltIns.anyClass.functions.single { it.owner.name.asString() == "hashCode" }
val IrTypeParameter.erasedUpperBound: IrClass
get() {
// Pick the (necessarily unique) non-interface upper bound if it exists
for (type in superTypes) {
val irClass = type.classOrNull?.owner ?: continue
if (!irClass.isInterface && !irClass.isAnnotationClass) return irClass
}
// Otherwise, choose either the first IrClass supertype or recurse.
// In the first case, all supertypes are interface types and the choice was arbitrary.
// In the second case, there is only a single supertype.
return when (val firstSuper = superTypes.first().classifierOrNull?.owner) {
is IrClass -> firstSuper
is IrTypeParameter -> firstSuper.erasedUpperBound
else -> error("unknown supertype kind $firstSuper")
}
}
override fun getHashCodeFunctionInfo(type: IrType): HashCodeFunctionInfo {
val classifier = type.classifierOrNull
val symbol = when {
classifier.isArrayOrPrimitiveArray -> context.irBuiltIns.dataClassArrayMemberHashCodeSymbol
classifier is IrClassSymbol -> getHashCodeFunction(classifier.owner)
classifier is IrTypeParameterSymbol -> getHashCodeFunction(classifier.owner.erasedUpperBound)
else -> error("Unknown classifier kind $classifier")
}
return Fir2IrHashCodeFunctionInfo(symbol)
}
}
@@ -131,7 +177,6 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) {
val properties = irClass.declarations
.filterIsInstance<IrProperty>()
.take(propertyParametersCount)
.map { it.descriptor }
if (properties.isEmpty()) {
return emptyList()
}