From dd2c7aff6ee0b0f42216203633907fae91909c69 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Mon, 1 Apr 2019 19:55:18 +0200 Subject: [PATCH] Implement IrType.equals/hashCode via isEqualTo/toHashCode --- .../backend/common/CheckIrElementVisitor.kt | 4 +- .../lower/StringConcatenationLowering.kt | 15 ++++--- .../js/lower/CallableReferenceLowering.kt | 3 +- .../js/lower/calls/CallsLoweringUtils.kt | 18 +------- .../jvm/lower/CallableReferenceLowering.kt | 6 +++ .../ir/symbols/IrClassifierEqualityChecker.kt | 25 ++++++++++- .../org/jetbrains/kotlin/ir/types/IrType.kt | 20 ++++++++- .../jetbrains/kotlin/ir/types/IrTypeUtils.kt | 43 +++---------------- .../kotlin/ir/types/impl/IrSimpleTypeImpl.kt | 21 ++++++++- .../kotlin/ir/types/impl/IrTypeBase.kt | 24 ++++++++--- 10 files changed, 103 insertions(+), 76 deletions(-) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CheckIrElementVisitor.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CheckIrElementVisitor.kt index 831b3f44a1f..7758786278f 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CheckIrElementVisitor.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CheckIrElementVisitor.kt @@ -53,8 +53,8 @@ class CheckIrElementVisitor( if (!config.checkTypes) return - if (!expectedType.isEqualTo(type)) { - reportError(this, "unexpected expression.type: expected $expectedType, got ${type.render()}") + if (type != expectedType) { + reportError(this, "unexpected expression.type: expected ${expectedType.render()}, got ${type.render()}") } } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/StringConcatenationLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/StringConcatenationLowering.kt index 330dd08bae4..39959aa2f2d 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/StringConcatenationLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/StringConcatenationLowering.kt @@ -20,21 +20,24 @@ import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.atMostOne import org.jetbrains.kotlin.ir.IrStatement -import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope +import org.jetbrains.kotlin.ir.builders.createTmpVariable +import org.jetbrains.kotlin.ir.builders.irCall +import org.jetbrains.kotlin.ir.builders.irGet import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.declarations.IrSymbolDeclaration import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrStringConcatenation -import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.isNullableAny import org.jetbrains.kotlin.ir.util.constructors import org.jetbrains.kotlin.ir.util.functions import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.name.Name - /** * This lowering pass replaces [IrStringConcatenation]s with StringBuilder appends. */ @@ -65,19 +68,17 @@ private class StringConcatenationTransformer(val lower: StringConcatenationLower private val toStringFunction = stringBuilder.functions.single { it.valueParameters.size == 0 && it.name == nameToString } + private val defaultAppendFunction = stringBuilder.functions.single { it.name == nameAppend && it.valueParameters.size == 1 && it.valueParameters.single().type.isNullableAny() } - private val appendFunctions: Map = typesWithSpecialAppendFunction.map { type -> type to stringBuilder.functions.toList().atMostOne { - it.name == nameAppend && - it.valueParameters.size == 1 && - it.valueParameters.single().type.isEqualTo(type) + it.name == nameAppend && it.valueParameters.singleOrNull()?.type == type } }.toMap() diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CallableReferenceLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CallableReferenceLowering.kt index b1d703d5ad7..de7ec6d4577 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CallableReferenceLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CallableReferenceLowering.kt @@ -29,7 +29,6 @@ import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrTypeProjection import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl -import org.jetbrains.kotlin.ir.types.isEqualTo import org.jetbrains.kotlin.ir.util.isInlined import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid @@ -546,7 +545,7 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP val value = JsIrBuilder.buildGetValue(unboundParamSymbols[i]) val parameter = callTarget.valueParameters[j] val argument = - if (parameter.varargElementType?.let { closureParam.type.isEqualTo(it) } == true) { + if (parameter.varargElementType == closureParam.type) { // fun foo(x: X, vararg y: Y): Z // val r: (X, Y) -> Z = ::foo val tailValues = unboundParamSymbols.drop(i) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/CallsLoweringUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/CallsLoweringUtils.kt index 4bb5436f5c2..30093c9f833 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/CallsLoweringUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/CallsLoweringUtils.kt @@ -5,12 +5,12 @@ package org.jetbrains.kotlin.ir.backend.js.lower.calls -import org.jetbrains.kotlin.ir.util.irCall import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.util.irCall import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.types.SimpleType @@ -58,21 +58,7 @@ internal fun MemberToTransformer.add(type: IrType, name: Name, v: (IrCall) -> Ir put(SimpleMemberKey(type, name), v) } -internal class SimpleMemberKey(val klass: IrType, val name: Name) { - // TODO drop custom equals and hashCode when IrTypes will have right equals - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as SimpleMemberKey - - if (name != other.name) return false - - return klass.isEqualTo(other.klass) - } - - override fun hashCode() = 31 * klass.toHashCode() + name.hashCode() -} +internal data class SimpleMemberKey(val klass: IrType, val name: Name) enum class PrimitiveType { FLOATING_POINT_NUMBER, diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/CallableReferenceLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/CallableReferenceLowering.kt index 0a995385075..1b8831269d3 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/CallableReferenceLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/CallableReferenceLowering.kt @@ -57,6 +57,12 @@ import org.jetbrains.org.objectweb.asm.Type //Hack implementation to support CR java types in lower class CrIrType(val type: Type) : IrType { override val annotations = emptyList() + + override fun equals(other: Any?): Boolean = + other is CrIrType && type == other.type + + override fun hashCode(): Int = + type.hashCode() } internal val callableReferencePhase = makeIrFilePhase( diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/symbols/IrClassifierEqualityChecker.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/symbols/IrClassifierEqualityChecker.kt index 5137999f391..c9c85534be6 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/symbols/IrClassifierEqualityChecker.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/symbols/IrClassifierEqualityChecker.kt @@ -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 diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrType.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrType.kt index 5b145879168..18853db896c 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrType.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrType.kt @@ -11,6 +11,18 @@ import org.jetbrains.kotlin.types.Variance interface IrType { val annotations: List + + /** + * @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`, + * 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 } -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 -} \ No newline at end of file +} diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeUtils.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeUtils.kt index 0ae6bb69f0f..d0ce513dd3a 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeUtils.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeUtils.kt @@ -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.commonSuperclass(): IrClassifierSymbol { var superClassifiers: MutableSet? = null @@ -91,4 +58,4 @@ fun Collection.commonSuperclass(): IrClassifierSymbol { postfix = "]" ) { it.owner.render() }}" ) -} \ No newline at end of file +} diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/impl/IrSimpleTypeImpl.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/impl/IrSimpleTypeImpl.kt index 798902912c8..896957c71dc 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/impl/IrSimpleTypeImpl.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/impl/IrSimpleTypeImpl.kt @@ -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) - } \ No newline at end of file + } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/impl/IrTypeBase.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/impl/IrTypeBase.kt index b1080de4e65..ae4d90ec22f 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/impl/IrTypeBase.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/impl/IrTypeBase.kt @@ -16,31 +16,45 @@ abstract class IrTypeBase( override val annotations: List, override val variance: Variance ) : IrType, IrTypeProjection { - override val type: IrType get() = this - } class IrErrorTypeImpl( kotlinType: KotlinType?, annotations: List, 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, 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()?.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 = emptyList() + + override fun equals(other: Any?): Boolean = this === other + + override fun hashCode(): Int = System.identityHashCode(this) }