Implement IrType.equals/hashCode via isEqualTo/toHashCode

This commit is contained in:
Alexander Udalov
2019-04-01 19:55:18 +02:00
parent 8cce5dc8de
commit dd2c7aff6e
10 changed files with 103 additions and 76 deletions
@@ -5,13 +5,18 @@
package org.jetbrains.kotlin.ir.symbols
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
interface IrClassifierEqualityChecker {
fun areEqual(left: IrClassifierSymbol, right: IrClassifierSymbol): Boolean
fun getHashCode(symbol: IrClassifierSymbol): Int
}
object FqNameEqualityChecker : IrClassifierEqualityChecker {
@@ -23,8 +28,24 @@ object FqNameEqualityChecker : IrClassifierEqualityChecker {
return checkViaDeclarations(left.owner, right.owner)
}
private val IrDeclarationWithName.fqName
get(): FqName? {
override fun getHashCode(symbol: IrClassifierSymbol): Int {
if (symbol.isBound) {
val owner = symbol.owner
if (owner is IrClass && !isLocalClass(owner)) {
return owner.fqName.hashCode()
}
return owner.hashCode()
}
val descriptor = symbol.descriptor
if (descriptor is ClassDescriptor && !DescriptorUtils.isLocal(descriptor)) {
return descriptor.fqNameSafe.hashCode()
}
return descriptor.hashCode()
}
private val IrDeclarationWithName.fqName: FqName?
get() {
val parentFqName = when (val parent = parent) {
is IrPackageFragment -> parent.fqName
is IrDeclarationWithName -> parent.fqName
@@ -11,6 +11,18 @@ import org.jetbrains.kotlin.types.Variance
interface IrType {
val annotations: List<IrCall>
/**
* @return true if this type is equal to [other] symbolically. Note that this is NOT EQUIVALENT to the full type checking algorithm
* used in the compiler frontend. For example, this method will return `false` on the types `List<*>` and `List<Any?>`,
* whereas the real type checker from the compiler frontend would return `true`.
*
* Classes are compared by FQ names, which means that even if two types refer to different symbols of the class with the same FQ name,
* such types will be considered equal. Type annotations do not have any effect on the behavior of this method.
*/
override fun equals(other: Any?): Boolean
override fun hashCode(): Int
}
interface IrErrorType : IrType
@@ -23,11 +35,15 @@ interface IrSimpleType : IrType {
val arguments: List<IrTypeArgument>
}
interface IrTypeArgument
interface IrTypeArgument {
override fun equals(other: Any?): Boolean
override fun hashCode(): Int
}
interface IrStarProjection : IrTypeArgument
interface IrTypeProjection : IrTypeArgument {
val variance: Variance
val type: IrType
}
}
@@ -5,11 +5,13 @@
package org.jetbrains.kotlin.ir.types
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.FqNameEqualityChecker
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.utils.DFS
fun IrClassifierSymbol.superTypes() = when (this) {
is IrClassSymbol -> owner.superTypes
is IrTypeParameterSymbol -> owner.superTypes
@@ -26,41 +28,6 @@ fun IrType.isSubtypeOfClass(superClass: IrClassSymbol): Boolean {
return classifier.isSubtypeOfClass(superClass)
}
fun IrType.isEqualTo(that: IrType): Boolean {
if (this is IrDynamicType && that is IrDynamicType) return true
if (this is IrErrorType || that is IrErrorType) return false
if (this === that) return true
if (this is IrSimpleType && that is IrSimpleType) return FqNameEqualityChecker.areEqual(this.classifier, that.classifier) &&
this.arguments.zip(that.arguments).all { (ths, tht) ->
when (ths) {
is IrStarProjection -> tht is IrStarProjection
is IrTypeProjection -> tht is IrTypeProjection
&& ths.variance == tht.variance
&& ths.type.isEqualTo(tht.type)
else -> error("Unsupported Type Argument")
}
}
return false
}
fun IrTypeArgument.toHashCode(): Int = when (this) {
is IrTypeProjection -> 31 * type.toHashCode() + variance.hashCode()
is IrStarProjection -> hashCode()
else -> 0
}
fun IrType.toHashCode(): Int {
if (this is IrDynamicType) return -1
if (this is IrErrorType) return 0
require(this is IrSimpleType)
var result = classifier.hashCode()
result = 31 * result + arguments.fold(0) { a, t -> 31 * a + t.toHashCode() }
return 31 * result + if (hasQuestionMark) 1 else 0
}
fun Collection<IrClassifierSymbol>.commonSuperclass(): IrClassifierSymbol {
var superClassifiers: MutableSet<IrClassifierSymbol>? = null
@@ -91,4 +58,4 @@ fun Collection<IrClassifierSymbol>.commonSuperclass(): IrClassifierSymbol {
postfix = "]"
) { it.owner.render() }}"
)
}
}
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.ir.types.impl
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.symbols.FqNameEqualityChecker
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.types.KotlinType
@@ -45,12 +46,28 @@ class IrSimpleTypeImpl(
other.classifier, other.hasQuestionMark, other.arguments, other.annotations, variance
)
override fun equals(other: Any?): Boolean =
other is IrSimpleTypeImpl &&
FqNameEqualityChecker.areEqual(classifier, other.classifier) &&
hasQuestionMark == other.hasQuestionMark &&
arguments == other.arguments &&
variance == other.variance
override fun hashCode(): Int =
((FqNameEqualityChecker.getHashCode(classifier) * 31 + hasQuestionMark.hashCode()) * 31 +
arguments.hashCode()) * 31 + variance.hashCode()
}
class IrTypeProjectionImpl internal constructor(
override val type: IrType,
override val variance: Variance
) : IrTypeProjection
) : IrTypeProjection {
override fun equals(other: Any?): Boolean =
other is IrTypeProjectionImpl && type == other.type && variance == other.variance
override fun hashCode(): Int =
type.hashCode() * 31 + variance.hashCode()
}
fun makeTypeProjection(type: IrType, variance: Variance): IrTypeProjection =
when {
@@ -59,4 +76,4 @@ fun makeTypeProjection(type: IrType, variance: Variance): IrTypeProjection =
type is IrDynamicType -> IrDynamicTypeImpl(null, type.annotations, variance)
type is IrErrorType -> IrErrorTypeImpl(null, type.annotations, variance)
else -> IrTypeProjectionImpl(type, variance)
}
}
@@ -16,31 +16,45 @@ abstract class IrTypeBase(
override val annotations: List<IrCall>,
override val variance: Variance
) : IrType, IrTypeProjection {
override val type: IrType get() = this
}
class IrErrorTypeImpl(
kotlinType: KotlinType?,
annotations: List<IrCall>,
variance: Variance
) : IrTypeBase(kotlinType, annotations, variance), IrErrorType
) : IrTypeBase(kotlinType, annotations, variance), IrErrorType {
override fun equals(other: Any?): Boolean = other is IrErrorTypeImpl
override fun hashCode(): Int = IrErrorTypeImpl::class.java.name.hashCode()
}
class IrDynamicTypeImpl(
kotlinType: KotlinType?,
annotations: List<IrCall>,
variance: Variance
) : IrTypeBase(kotlinType, annotations, variance), IrDynamicType, IrTypeProjection
) : IrTypeBase(kotlinType, annotations, variance), IrDynamicType, IrTypeProjection {
override fun equals(other: Any?): Boolean = other is IrDynamicTypeImpl
override fun hashCode(): Int = IrDynamicTypeImpl::class.java.name.hashCode()
}
val IrType.originalKotlinType: KotlinType?
get() = safeAs<IrTypeBase>()?.kotlinType
object IrStarProjectionImpl : IrStarProjection
object IrStarProjectionImpl : IrStarProjection {
override fun equals(other: Any?): Boolean = this === other
override fun hashCode(): Int = System.identityHashCode(this)
}
@Deprecated("Hack to temporary cover late type initialization")
object IrUninitializedType : IrType {
override val annotations: List<IrCall> = emptyList()
override fun equals(other: Any?): Boolean = this === other
override fun hashCode(): Int = System.identityHashCode(this)
}