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:
+54
-3
@@ -17,15 +17,23 @@
|
||||
package org.jetbrains.kotlin.psi2ir.generators
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.DataClassMethodGenerator
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||
import org.jetbrains.kotlin.ir.util.DataClassMembersGenerator
|
||||
import org.jetbrains.kotlin.ir.util.declareSimpleFunctionWithOverrides
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtParameter
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
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.
|
||||
@@ -72,14 +80,57 @@ class DataClassMembersGenerator(
|
||||
override fun getProperty(parameter: ValueParameterDescriptor?, irValueParameter: IrValueParameter?): IrProperty? =
|
||||
parameter?.let {
|
||||
val property = getOrFail(BindingContext.VALUE_PARAMETER_AS_PROPERTY, parameter)
|
||||
return getProperty(property)
|
||||
return getIrProperty(property)
|
||||
}
|
||||
|
||||
override fun transform(typeParameterDescriptor: TypeParameterDescriptor): IrType =
|
||||
typeParameterDescriptor.defaultType.toIrType()
|
||||
|
||||
override fun commitSubstituted(irMemberAccessExpression: IrMemberAccessExpression<*>, descriptor: CallableDescriptor) {
|
||||
irMemberAccessExpression.commitSubstituted(descriptor)
|
||||
private fun MemberScope.findHashCodeFunctionOrNull() =
|
||||
getContributedFunctions(Name.identifier("hashCode"), NoLookupLocation.FROM_BACKEND)
|
||||
.find { it.valueParameters.isEmpty() && it.extensionReceiverParameter == null }
|
||||
|
||||
private fun getHashCodeFunction(type: KotlinType): FunctionDescriptor =
|
||||
type.memberScope.findHashCodeFunctionOrNull()
|
||||
?: context.builtIns.any.unsubstitutedMemberScope.findHashCodeFunctionOrNull()!!
|
||||
|
||||
private fun getHashCodeFunction(
|
||||
type: KotlinType,
|
||||
symbolResolve: (FunctionDescriptor) -> IrSimpleFunctionSymbol
|
||||
): IrSimpleFunctionSymbol =
|
||||
when (val typeConstructorDescriptor = type.constructor.declarationDescriptor) {
|
||||
is ClassDescriptor ->
|
||||
if (KotlinBuiltIns.isArrayOrPrimitiveArray(typeConstructorDescriptor))
|
||||
context.irBuiltIns.dataClassArrayMemberHashCodeSymbol
|
||||
else
|
||||
symbolResolve(getHashCodeFunction(type))
|
||||
|
||||
is TypeParameterDescriptor ->
|
||||
getHashCodeFunction(typeConstructorDescriptor.representativeUpperBound, symbolResolve)
|
||||
|
||||
else ->
|
||||
throw AssertionError("Unexpected type: $type")
|
||||
}
|
||||
|
||||
|
||||
inner class Psi2IrHashCodeFunctionInfo(
|
||||
override val symbol: IrSimpleFunctionSymbol,
|
||||
val substituted: CallableDescriptor
|
||||
) : HashCodeFunctionInfo {
|
||||
|
||||
override fun commitSubstituted(irMemberAccessExpression: IrMemberAccessExpression<*>) {
|
||||
irMemberAccessExpression.commitSubstituted(substituted)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun getHashCodeFunctionInfo(type: IrType): HashCodeFunctionInfo {
|
||||
var substituted: CallableDescriptor? = null
|
||||
val symbol = getHashCodeFunction(type.toKotlinType()) { hashCodeDescriptor ->
|
||||
substituted = hashCodeDescriptor
|
||||
symbolTable.referenceSimpleFunction(hashCodeDescriptor.original)
|
||||
}
|
||||
return Psi2IrHashCodeFunctionInfo(symbol, substituted ?: symbol.descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,30 +5,26 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.util
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
|
||||
import org.jetbrains.kotlin.ir.descriptors.WrappedVariableDescriptor
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.mapTypeParameters
|
||||
import org.jetbrains.kotlin.ir.expressions.mapValueParameters
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
||||
import org.jetbrains.kotlin.ir.types.isNullable
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import org.jetbrains.kotlin.types.isNullable
|
||||
import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound
|
||||
|
||||
/**
|
||||
* A platform-, frontend-independent logic for generating synthetic members of data class: equals, hashCode, toString, componentN, and copy.
|
||||
@@ -127,7 +123,7 @@ abstract class DataClassMembersGenerator(
|
||||
)
|
||||
}
|
||||
|
||||
fun generateEqualsMethodBody(properties: List<PropertyDescriptor>) {
|
||||
fun generateEqualsMethodBody(properties: List<IrProperty>) {
|
||||
val irType = irClass.defaultType
|
||||
|
||||
if (!irClass.isInline) {
|
||||
@@ -136,9 +132,8 @@ abstract class DataClassMembersGenerator(
|
||||
+irIfThenReturnFalse(irNotIs(irOther(), irType))
|
||||
val otherWithCast = irTemporary(irAs(irOther(), irType), "other_with_cast")
|
||||
for (property in properties) {
|
||||
val irProperty = getProperty(property)
|
||||
val arg1 = irGetProperty(irThis(), irProperty)
|
||||
val arg2 = irGetProperty(irGet(irType, otherWithCast.symbol), irProperty)
|
||||
val arg1 = irGetProperty(irThis(), property)
|
||||
val arg2 = irGetProperty(irGet(irType, otherWithCast.symbol), property)
|
||||
+irIfThenReturnFalse(irNotEquals(arg1, arg2))
|
||||
}
|
||||
+irReturnTrue()
|
||||
@@ -157,7 +152,7 @@ abstract class DataClassMembersGenerator(
|
||||
KotlinTypeChecker.DEFAULT.equalTypes(it.valueParameters[0].type, intType)
|
||||
}.let { symbolTable.referenceSimpleFunction(it) }
|
||||
|
||||
fun generateHashCodeMethodBody(properties: List<PropertyDescriptor>) {
|
||||
fun generateHashCodeMethodBody(properties: List<IrProperty>) {
|
||||
if (properties.isEmpty()) {
|
||||
+irReturn(irInt(0))
|
||||
return
|
||||
@@ -189,27 +184,23 @@ abstract class DataClassMembersGenerator(
|
||||
+irReturn(irGet(irResultVar))
|
||||
}
|
||||
|
||||
private fun getHashCodeOfProperty(property: PropertyDescriptor): IrExpression {
|
||||
val irProperty = getProperty(property)
|
||||
private fun getHashCodeOfProperty(property: IrProperty): IrExpression {
|
||||
return when {
|
||||
property.type.isNullable() ->
|
||||
property.backingField!!.type.isNullable() ->
|
||||
irIfNull(
|
||||
context.irBuiltIns.intType,
|
||||
irGetProperty(irThis(), irProperty),
|
||||
irGetProperty(irThis(), property),
|
||||
irInt(0),
|
||||
getHashCodeOf(property, irGetProperty(irThis(), irProperty))
|
||||
getHashCodeOf(property, irGetProperty(irThis(), property))
|
||||
)
|
||||
else ->
|
||||
getHashCodeOf(property, irGetProperty(irThis(), irProperty))
|
||||
getHashCodeOf(property, irGetProperty(irThis(), property))
|
||||
}
|
||||
}
|
||||
|
||||
private fun getHashCodeOf(property: PropertyDescriptor, irValue: IrExpression): IrExpression {
|
||||
var substituted: FunctionDescriptor? = null
|
||||
val hashCodeFunctionSymbol = getHashCodeFunction(property) {
|
||||
substituted = it
|
||||
symbolTable.referenceSimpleFunction(it.original)
|
||||
}
|
||||
private fun getHashCodeOf(property: IrProperty, irValue: IrExpression): IrExpression {
|
||||
val hashCodeFunctionInfo = getHashCodeFunctionInfo(property.backingField!!.type)
|
||||
val hashCodeFunctionSymbol = hashCodeFunctionInfo.symbol
|
||||
|
||||
val hasDispatchReceiver = hashCodeFunctionSymbol.descriptor.dispatchReceiverParameter != null
|
||||
return irCall(
|
||||
@@ -223,26 +214,24 @@ abstract class DataClassMembersGenerator(
|
||||
} else {
|
||||
putValueArgument(0, irValue)
|
||||
}
|
||||
commitSubstituted(this, substituted ?: hashCodeFunctionSymbol.descriptor)
|
||||
hashCodeFunctionInfo.commitSubstituted(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun generateToStringMethodBody(properties: List<PropertyDescriptor>) {
|
||||
fun generateToStringMethodBody(properties: List<IrProperty>) {
|
||||
val irConcat = irConcat()
|
||||
irConcat.addArgument(irString(irClass.descriptor.name.asString() + "("))
|
||||
irConcat.addArgument(irString(irClass.name.asString() + "("))
|
||||
var first = true
|
||||
for (property in properties) {
|
||||
if (!first) irConcat.addArgument(irString(", "))
|
||||
|
||||
irConcat.addArgument(irString(property.name.asString() + "="))
|
||||
|
||||
val irPropertyValue = irGetProperty(irThis(), getProperty(property))
|
||||
val irPropertyValue = irGetProperty(irThis(), property)
|
||||
|
||||
val typeConstructorDescriptor = property.type.constructor.declarationDescriptor
|
||||
val classifier = property.backingField!!.type.classifierOrNull
|
||||
val irPropertyStringValue =
|
||||
if (typeConstructorDescriptor is ClassDescriptor &&
|
||||
KotlinBuiltIns.isArrayOrPrimitiveArray(typeConstructorDescriptor)
|
||||
)
|
||||
if (classifier.isArrayOrPrimitiveArray)
|
||||
irCall(context.irBuiltIns.dataClassArrayMemberToStringSymbol, context.irBuiltIns.stringType).apply {
|
||||
putValueArgument(0, irPropertyValue)
|
||||
}
|
||||
@@ -257,10 +246,16 @@ abstract class DataClassMembersGenerator(
|
||||
}
|
||||
}
|
||||
|
||||
fun getProperty(property: PropertyDescriptor): IrProperty =
|
||||
fun getIrProperty(property: PropertyDescriptor): IrProperty =
|
||||
irPropertiesByDescriptor[property]
|
||||
?: throw AssertionError("Class: ${irClass.descriptor}: unexpected property descriptor: $property")
|
||||
|
||||
fun getBackingField(property: PropertyDescriptor): IrField =
|
||||
getIrProperty(property).backingField!!
|
||||
|
||||
val IrClassifierSymbol?.isArrayOrPrimitiveArray: Boolean
|
||||
get() = this == context.irBuiltIns.arrayClass || this in context.irBuiltIns.primitiveArrays
|
||||
|
||||
abstract fun declareSimpleFunction(startOffset: Int, endOffset: Int, functionDescriptor: FunctionDescriptor): IrFunction
|
||||
|
||||
abstract fun generateSyntheticFunctionParameterDeclarations(irFunction: IrFunction)
|
||||
@@ -336,60 +331,33 @@ abstract class DataClassMembersGenerator(
|
||||
// Entry for psi2ir
|
||||
fun generateEqualsMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>) {
|
||||
buildMember(function) {
|
||||
generateEqualsMethodBody(properties)
|
||||
generateEqualsMethodBody(properties.map { getIrProperty(it) })
|
||||
}
|
||||
}
|
||||
|
||||
// Entry for fir2ir
|
||||
fun generateEqualsMethod(irFunction: IrFunction, properties: List<PropertyDescriptor>) {
|
||||
fun generateEqualsMethod(irFunction: IrFunction, properties: List<IrProperty>) {
|
||||
buildMember(irFunction) {
|
||||
generateEqualsMethodBody(properties)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MemberScope.findHashCodeFunctionOrNull() =
|
||||
getContributedFunctions(Name.identifier("hashCode"), NoLookupLocation.FROM_BACKEND)
|
||||
.find { it.valueParameters.isEmpty() }
|
||||
interface HashCodeFunctionInfo {
|
||||
val symbol: IrSimpleFunctionSymbol
|
||||
fun commitSubstituted(irMemberAccessExpression: IrMemberAccessExpression<*>)
|
||||
}
|
||||
|
||||
private fun getHashCodeFunction(type: KotlinType): FunctionDescriptor =
|
||||
type.memberScope.findHashCodeFunctionOrNull()
|
||||
?: context.builtIns.any.unsubstitutedMemberScope.findHashCodeFunctionOrNull()!!
|
||||
|
||||
private fun getHashCodeFunction(
|
||||
type: KotlinType,
|
||||
symbolResolve: (FunctionDescriptor) -> IrSimpleFunctionSymbol
|
||||
): IrSimpleFunctionSymbol =
|
||||
when (val typeConstructorDescriptor = type.constructor.declarationDescriptor) {
|
||||
is ClassDescriptor ->
|
||||
if (KotlinBuiltIns.isArrayOrPrimitiveArray(typeConstructorDescriptor))
|
||||
context.irBuiltIns.dataClassArrayMemberHashCodeSymbol
|
||||
else
|
||||
symbolResolve(getHashCodeFunction(type))
|
||||
|
||||
is TypeParameterDescriptor ->
|
||||
getHashCodeFunction(typeConstructorDescriptor.representativeUpperBound, symbolResolve)
|
||||
|
||||
else ->
|
||||
throw AssertionError("Unexpected type: $type")
|
||||
}
|
||||
|
||||
private fun getHashCodeFunction(
|
||||
property: PropertyDescriptor,
|
||||
symbolResolve: (FunctionDescriptor) -> IrSimpleFunctionSymbol
|
||||
): IrSimpleFunctionSymbol =
|
||||
getHashCodeFunction(property.type, symbolResolve)
|
||||
|
||||
abstract fun commitSubstituted(irMemberAccessExpression: IrMemberAccessExpression<*>, descriptor: CallableDescriptor)
|
||||
abstract fun getHashCodeFunctionInfo(type: IrType): HashCodeFunctionInfo
|
||||
|
||||
// Entry for psi2ir
|
||||
fun generateHashCodeMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>) {
|
||||
buildMember(function) {
|
||||
generateHashCodeMethodBody(properties)
|
||||
generateHashCodeMethodBody(properties.map { getIrProperty(it) })
|
||||
}
|
||||
}
|
||||
|
||||
// Entry for fir2ir
|
||||
fun generateHashCodeMethod(irFunction: IrFunction, properties: List<PropertyDescriptor>) {
|
||||
fun generateHashCodeMethod(irFunction: IrFunction, properties: List<IrProperty>) {
|
||||
buildMember(irFunction) {
|
||||
generateHashCodeMethodBody(properties)
|
||||
}
|
||||
@@ -398,12 +366,12 @@ abstract class DataClassMembersGenerator(
|
||||
// Entry for psi2ir
|
||||
fun generateToStringMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>) {
|
||||
buildMember(function) {
|
||||
generateToStringMethodBody(properties)
|
||||
generateToStringMethodBody(properties.map { getIrProperty(it) })
|
||||
}
|
||||
}
|
||||
|
||||
// Entry for fir2ir
|
||||
fun generateToStringMethod(irFunction: IrFunction, properties: List<PropertyDescriptor>) {
|
||||
fun generateToStringMethod(irFunction: IrFunction, properties: List<IrProperty>) {
|
||||
buildMember(irFunction) {
|
||||
generateToStringMethodBody(properties)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user