From 238f1b2b06adf247914149ffbb94f02761544f1c Mon Sep 17 00:00:00 2001 From: Roman Artemev Date: Thu, 7 Mar 2019 20:53:08 +0300 Subject: [PATCH] [IR] * Implement `isSubtypeOfClass` and `commonSuperclass` based on pure Ir * Implement type strict equality check * Move type checkers into Ir * Implement strict Fqn-based classifier equality checker --- .../jetbrains/kotlin/ir/util/IrTypeUtils.kt | 20 +---- .../ir/symbols/IrClassifierEqualityChecker.kt | 42 ++++++++++ .../jetbrains/kotlin/ir/types/IrTypeUtils.kt | 76 +++++++++++++++++++ 3 files changed, 122 insertions(+), 16 deletions(-) create mode 100644 compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/symbols/IrClassifierEqualityChecker.kt create mode 100644 compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeUtils.kt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt index 239704b8fa7..f1234fab2fe 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt @@ -11,13 +11,9 @@ import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.IrPackageFragment -import org.jetbrains.kotlin.ir.declarations.IrTypeParameter import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol -import org.jetbrains.kotlin.ir.types.IrDynamicType -import org.jetbrains.kotlin.ir.types.IrSimpleType -import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.classifierOrNull +import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.utils.DFS @@ -42,19 +38,11 @@ fun IrType.isNameInPackage(prefix: String, packageFqName: FqName): Boolean { } - -fun IrType.superTypes(): List { - val classifier = classifierOrNull?.owner ?: return emptyList() - return when(classifier) { - is IrClass -> classifier.superTypes - is IrTypeParameter -> classifier.superTypes - else -> throw IllegalStateException() - } -} +fun IrType.superTypes() = classifierOrNull?.superTypes() ?: emptyList() fun IrType.typeParameterSuperTypes(): List { val classifier = classifierOrNull ?: return emptyList() - return when(classifier) { + return when (classifier) { is IrTypeParameterSymbol -> classifier.owner.superTypes is IrClassSymbol -> emptyList() else -> throw IllegalStateException() @@ -92,4 +80,4 @@ private inline fun IrType.isTypeFromKotlinPackage(namePredicate: (Name) -> Boole } else return false } -fun IrType.isPrimitiveArray() = isTypeFromKotlinPackage { it in FQ_NAMES.primitiveArrayTypeShortNames } \ No newline at end of file +fun IrType.isPrimitiveArray() = isTypeFromKotlinPackage { it in FQ_NAMES.primitiveArrayTypeShortNames } 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 new file mode 100644 index 00000000000..531b4d9a833 --- /dev/null +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/symbols/IrClassifierEqualityChecker.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.ir.symbols + +import org.jetbrains.kotlin.descriptors.ClassifierDescriptor +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.name.FqName + +interface IrClassifierEqualityChecker { + fun areEqual(left: IrClassifierSymbol, right: IrClassifierSymbol): Boolean +} + +object FqNameEqualityChecker : IrClassifierEqualityChecker { + override fun areEqual(left: IrClassifierSymbol, right: IrClassifierSymbol): Boolean { + if (left === right) return true + if (!left.isBound || !right.isBound) checkViaDescriptors(left.descriptor, right.descriptor) + return checkViaDeclarations(left.owner, right.owner) + } + + private val IrDeclarationWithName.fqName + get(): FqName? { + val parentFqName = when (val parent = parent) { + is IrPackageFragment -> parent.fqName + is IrDeclarationWithName -> parent.fqName + else -> return null + } + return parentFqName?.child(name) + } + + private fun checkViaDeclarations(c1: IrSymbolOwner, c2: IrSymbolOwner): Boolean { + if (c1 is IrClass && c2 is IrClass) { + return c1.fqName == c2.fqName + } + + return c1 == c2 + } + + private fun checkViaDescriptors(c1: ClassifierDescriptor, c2: ClassifierDescriptor) = c1.typeConstructor == c2.typeConstructor +} \ 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 new file mode 100644 index 00000000000..e35f0f02d46 --- /dev/null +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeUtils.kt @@ -0,0 +1,76 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.ir.types + +import org.jetbrains.kotlin.ir.symbols.* +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 + else -> emptyList() +} + +fun IrClassifierSymbol.isSubtypeOfClass(superClass: IrClassSymbol): Boolean { + if (FqNameEqualityChecker.areEqual(this, superClass)) return true + return superTypes().any { it.isSubtypeOfClass(superClass) } +} + +fun IrType.isSubtypeOfClass(superClass: IrClassSymbol): Boolean { + if (this !is IrSimpleType) return false + 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 Collection.commonSuperclass(): IrClassifierSymbol { + var superClassifiers: MutableSet? = null + + require(isNotEmpty()) + + val order = fold(emptyList()) { _, classifierSymbol -> + val visited = mutableSetOf() + DFS.topologicalOrder( + listOf(classifierSymbol), { it.superTypes().map { s -> (s as IrSimpleType).classifier } }, + DFS.VisitedWithSet(visited) + ).also { + if (superClassifiers == null) { + superClassifiers = visited + } else { + superClassifiers!!.apply { + retainAll { c -> visited.any { v -> FqNameEqualityChecker.areEqual(c, v) } } + } + } + } + } + + requireNotNull(superClassifiers) + + return order.firstOrNull { o -> superClassifiers!!.any { s -> FqNameEqualityChecker.areEqual(o, s) } } + ?: error( + "No common superType found for non-empty set of classifiers: ${joinToString( + prefix = "[", + postfix = "]" + ) { it.owner.render() }}" + ) +} \ No newline at end of file