From bca53e6c1cb08be7aeb3b6eafce8b5e05e8a7b33 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Thu, 28 Sep 2017 18:57:53 +0300 Subject: [PATCH] Optimize type checking for common cases - Type equality for simple constructors (no arguments) - Subtyping on final classes --- .../testData/resolve/LocalObjects.resolve | 2 +- compiler/testData/resolve/Objects.resolve | 2 +- .../resolve/ResolveOfInfixExpressions.resolve | 4 +-- .../org/jetbrains/kotlin/types/TypeUtils.java | 2 +- .../types/checker/NewKotlinTypeChecker.kt | 28 ++++++++++++++++++- 5 files changed, 32 insertions(+), 6 deletions(-) diff --git a/compiler/testData/resolve/LocalObjects.resolve b/compiler/testData/resolve/LocalObjects.resolve index 89bdbe03779..92c0ee04154 100644 --- a/compiler/testData/resolve/LocalObjects.resolve +++ b/compiler/testData/resolve/LocalObjects.resolve @@ -3,7 +3,7 @@ package localObjects { ~x~val x : Int } - class Foo { + open class Foo { ~foo()~fun foo() : Int } diff --git a/compiler/testData/resolve/Objects.resolve b/compiler/testData/resolve/Objects.resolve index c543cba55de..9f7d60144c2 100644 --- a/compiler/testData/resolve/Objects.resolve +++ b/compiler/testData/resolve/Objects.resolve @@ -1,6 +1,6 @@ package toplevelObjectDeclarations -class Foo(y : Int) { +open class Foo(y : Int) { ~foo()~open fun foo() : Int = 1 } diff --git a/compiler/testData/resolve/ResolveOfInfixExpressions.resolve b/compiler/testData/resolve/ResolveOfInfixExpressions.resolve index 6b5f0e62ac1..2a2ee83c8d1 100644 --- a/compiler/testData/resolve/ResolveOfInfixExpressions.resolve +++ b/compiler/testData/resolve/ResolveOfInfixExpressions.resolve @@ -40,7 +40,7 @@ fun f_plus(): Int { 1 `contains`!in x } -~Foo~class Foo { +open ~Foo~class Foo { ~set1~fun set(i : Int, val1 : String) {} ~get1~fun get(i : Int) : Int {} ~get2~fun get(i : Int, j : Int) : String {} @@ -92,4 +92,4 @@ fun testUnitIncDec() { var x = UnitIncDec() x`uinc`++ x`udec`-- -} \ No newline at end of file +} diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.java b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.java index 01c743bfe95..66abbb32022 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.java +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.java @@ -346,7 +346,7 @@ public class TypeUtils { } public static boolean equalTypes(@NotNull KotlinType a, @NotNull KotlinType b) { - return KotlinTypeChecker.DEFAULT.isSubtypeOf(a, b) && KotlinTypeChecker.DEFAULT.isSubtypeOf(b, a); + return KotlinTypeChecker.DEFAULT.equalTypes(a, b); } public static boolean dependsOnTypeParameters(@NotNull KotlinType type, @NotNull Collection typeParameters) { diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt index 94333a147b6..49663884f55 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt @@ -18,7 +18,9 @@ package org.jetbrains.kotlin.types.checker import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor +import org.jetbrains.kotlin.descriptors.isFinalClass import org.jetbrains.kotlin.resolve.calls.inference.CapturedType import org.jetbrains.kotlin.resolve.calls.inference.CapturedTypeConstructor import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstructor @@ -29,6 +31,7 @@ import org.jetbrains.kotlin.types.checker.TypeCheckerContext.SupertypesPolicy import org.jetbrains.kotlin.types.typeUtil.asTypeProjection import org.jetbrains.kotlin.types.typeUtil.makeNullable import org.jetbrains.kotlin.utils.SmartList +import org.jetbrains.kotlin.utils.addToStdlib.safeAs object StrictEqualityTypeChecker { /** @@ -91,9 +94,24 @@ object NewKotlinTypeChecker : KotlinTypeChecker { fun TypeCheckerContext.equalTypes(a: UnwrappedType, b: UnwrappedType): Boolean { if (a === b) return true + if (isCommonDenotableType(a) && isCommonDenotableType(b)) { + if (!areEqualTypeConstructors(a.constructor, b.constructor)) return false + if (a.arguments.isEmpty()) { + if (a.hasFlexibleNullability() || b.hasFlexibleNullability()) return true + + return a.isMarkedNullable == b.isMarkedNullable + } + } + return isSubtypeOf(a, b) && isSubtypeOf(b, a) } + private fun KotlinType.hasFlexibleNullability() = + lowerIfFlexible().isMarkedNullable != upperIfFlexible().isMarkedNullable + + private fun isCommonDenotableType(type: KotlinType) = + type.constructor.isDenotable && !type.isDynamic() && type.lowerIfFlexible().constructor == type.upperIfFlexible().constructor + fun TypeCheckerContext.isSubtypeOf(subType: UnwrappedType, superType: UnwrappedType): Boolean { val newSubType = transformToNewType(subType) val newSuperType = transformToNewType(superType) @@ -266,6 +284,12 @@ object NewKotlinTypeChecker : KotlinTypeChecker { baseType: SimpleType, constructor: TypeConstructor ): List { + if (constructor.declarationDescriptor.safeAs()?.isCommonFinalClass == true) { + return if (areEqualTypeConstructors(baseType.constructor, constructor)) + listOf(captureFromArguments(baseType, CaptureStatus.FOR_SUBTYPING) ?: baseType) + else + emptyList() + } var result: MutableList? = null @@ -293,6 +317,9 @@ object NewKotlinTypeChecker : KotlinTypeChecker { return result ?: emptyList() } + private val ClassDescriptor.isCommonFinalClass: Boolean + get() = isFinalClass && kind != ClassKind.ENUM_ENTRY + /** * If we have several paths to some interface, we should prefer pure kotlin path. * Example: @@ -357,7 +384,6 @@ object NewKotlinTypeChecker : KotlinTypeChecker { } - object NullabilityChecker { // this method checks only nullability