From 2fe3a23183a01feddfc5c042a545d3b3d3b40340 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Mon, 22 Jul 2019 19:44:03 +0300 Subject: [PATCH] [Core API] Introduce KotlinTypeRefiner component --- .../kotlin/config/KotlinTypeRefinerImpl.kt | 151 ++++++++++++++++++ .../descriptors/impl/ModuleDescriptorImpl.kt | 11 +- .../kotlin/resolve/DescriptorUtils.kt | 8 +- .../kotlin/types/checker/KotlinTypeRefiner.kt | 82 ++++++++++ 4 files changed, 249 insertions(+), 3 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/config/KotlinTypeRefinerImpl.kt create mode 100644 core/descriptors/src/org/jetbrains/kotlin/types/checker/KotlinTypeRefiner.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/config/KotlinTypeRefinerImpl.kt b/compiler/frontend/src/org/jetbrains/kotlin/config/KotlinTypeRefinerImpl.kt new file mode 100644 index 00000000000..77a3a4d14b5 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/config/KotlinTypeRefinerImpl.kt @@ -0,0 +1,151 @@ +/* + * 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.config + +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.resolve.descriptorUtil.classId +import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.storage.StorageManager +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeConstructor +import org.jetbrains.kotlin.types.checker.KotlinTypeRefiner +import org.jetbrains.kotlin.types.checker.NewCapturedTypeConstructor +import org.jetbrains.kotlin.types.checker.REFINER_CAPABILITY +import org.jetbrains.kotlin.types.refinement.TypeRefinement +import org.jetbrains.kotlin.types.replace +import org.jetbrains.kotlin.utils.DFS +import org.jetbrains.kotlin.utils.addToStdlib.safeAs + +@UseExperimental(TypeRefinement::class) +class KotlinTypeRefinerImpl( + private val moduleDescriptor: ModuleDescriptor, + storageManager: StorageManager +) : KotlinTypeRefiner() { + init { + moduleDescriptor.getCapability(REFINER_CAPABILITY)?.value = this + } + + private val refinedTypeCache = storageManager.createCacheWithNotNullValues() + private val isRefinementNeededForTypeConstructorCache = storageManager.createCacheWithNotNullValues() + private val scopes = storageManager.createCacheWithNotNullValues() + + /** + * IMPORTANT: that function has not obvious contract: it refines only supertypes, + * and don't refines type arguments, so return type is "partly refined". + * + * It's fine for subtyping, because we refine type arguments inside type checker when it needs to + * It's fine for scopes, because we refine type of every expression: + * + * // common module + * expect interface A + * class Inv(val value: T) + * fun getA(): Inv = ... + * + * // platform module + * + * actual interface A { + * val x: Int + * } + * + * fun foo() { + * getA().value.x + * } + * + * Let's call type of `actual interface A` A' + * + * expression `getA()` has not refined type Inv and same refined type + * expression `getA().value` has not refined type A that refines into type A', so there is a + * field `x` in it's member scope + */ + @TypeRefinement + override fun refineType(type: KotlinType): KotlinType { + return when { + !type.needsRefinement() -> type + type.canBeCached() -> { + val cached = refinedTypeCache.computeIfAbsent(type.constructor) { + type.constructor.declarationDescriptor!!.defaultType.refine(this) + } + cached.replace(type.arguments) + } + else -> type.refine(this) + } + } + + private fun KotlinType.needsRefinement(): Boolean = isRefinementNeededForTypeConstructor(constructor) + + private fun KotlinType.canBeCached(): Boolean = hasNotTrivialRefinementFactory && constructor.declarationDescriptor != null + + @TypeRefinement + override fun refineSupertypes(classDescriptor: ClassDescriptor): Collection { + // Note that we can't omit refinement even if classDescriptor.module == moduleDescriptor, + // because such class may have supertypes which need refinement + return classDescriptor.typeConstructor.supertypes.map { refineType(it) } + } + + @TypeRefinement + override fun refineDescriptor(descriptor: DeclarationDescriptor): ClassifierDescriptor? { + if (descriptor !is ClassifierDescriptorWithTypeParameters) return null + val classId = descriptor.classId ?: return null + return moduleDescriptor.findClassifierAcrossModuleDependencies(classId) + } + + @TypeRefinement + override fun findClassAcrossModuleDependencies(classId: ClassId): ClassDescriptor? { + return moduleDescriptor.findClassAcrossModuleDependencies(classId) + } + + @TypeRefinement + override fun isRefinementNeededForModule(moduleDescriptor: ModuleDescriptor): Boolean { + return this.moduleDescriptor !== moduleDescriptor + } + + @TypeRefinement + override fun isRefinementNeededForTypeConstructor(typeConstructor: TypeConstructor): Boolean { + val owner = typeConstructor.declarationDescriptor ?: return typeConstructor.areThereExpectSupertypes() + return isRefinementNeededForTypeConstructorCache.computeIfAbsent(owner) { typeConstructor.areThereExpectSupertypes() } + } + + @TypeRefinement + override fun getOrPutScopeForClass(classDescriptor: ClassDescriptor, compute: () -> S): S { + @Suppress("UNCHECKED_CAST") + return scopes.computeIfAbsent(classDescriptor, compute) as S + } + + private fun TypeConstructor.areThereExpectSupertypes(): Boolean { + var result = false + DFS.dfs( + listOf(this), + DFS.Neighbors(TypeConstructor::allDependentTypeConstructors), + DFS.VisitedWithSet(), + object : DFS.AbstractNodeHandler() { + override fun beforeChildren(current: TypeConstructor): Boolean { + if (current.isExpectClass() && current.declarationDescriptor?.module != moduleDescriptor) { + result = true + return false + } + return true + } + + override fun result() = Unit + } + ) + + return result + } +} + +private val TypeConstructor.allDependentTypeConstructors: Collection + get() = when (this) { + is NewCapturedTypeConstructor -> { + supertypes.map { it.constructor } + projection.type.constructor + } + else -> supertypes.map { it.constructor } + } + +private fun TypeConstructor.isExpectClass() = + declarationDescriptor?.safeAs()?.isExpect == true diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/ModuleDescriptorImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/ModuleDescriptorImpl.kt index 2c561a014f8..eeb0417c907 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/ModuleDescriptorImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/ModuleDescriptorImpl.kt @@ -26,8 +26,10 @@ import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.storage.StorageManager +import org.jetbrains.kotlin.types.checker.REFINER_CAPABILITY +import org.jetbrains.kotlin.types.checker.Ref +import org.jetbrains.kotlin.types.refinement.TypeRefinement import org.jetbrains.kotlin.utils.sure -import java.lang.IllegalArgumentException class ModuleDescriptorImpl @JvmOverloads constructor( moduleName: Name, @@ -35,13 +37,18 @@ class ModuleDescriptorImpl @JvmOverloads constructor( override val builtIns: KotlinBuiltIns, // May be null in compiler context, should be not-null in IDE context override val platform: TargetPlatform? = null, - private val capabilities: Map, Any?> = emptyMap(), + capabilities: Map, Any?> = emptyMap(), override val stableName: Name? = null ) : DeclarationDescriptorImpl(Annotations.EMPTY, moduleName), ModuleDescriptor { + private val capabilities: Map, Any?> + init { if (!moduleName.isSpecial) { throw IllegalArgumentException("Module name must be special: $moduleName") } + this.capabilities = capabilities.toMutableMap() + @UseExperimental(TypeRefinement::class) + this.capabilities[REFINER_CAPABILITY] = Ref(null) } private var dependencies: ModuleDependencies? = null diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt index 5653101f183..a3b69c50530 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt @@ -17,9 +17,9 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils.getContainingClass -import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.resolve.constants.ConstantValue import org.jetbrains.kotlin.resolve.constants.EnumValue import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter @@ -29,6 +29,9 @@ import org.jetbrains.kotlin.types.TypeProjection import org.jetbrains.kotlin.types.TypeSubstitutor import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.checker.KotlinTypeChecker +import org.jetbrains.kotlin.types.checker.KotlinTypeRefiner +import org.jetbrains.kotlin.types.checker.REFINER_CAPABILITY +import org.jetbrains.kotlin.types.refinement.TypeRefinement import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny import org.jetbrains.kotlin.types.typeUtil.makeNullable import org.jetbrains.kotlin.utils.DFS @@ -441,3 +444,6 @@ fun DeclarationDescriptor.isAnnotationConstructor(): Boolean = fun DeclarationDescriptor.isPrimaryConstructorOfInlineClass(): Boolean = this is ConstructorDescriptor && this.isPrimary && this.constructedClass.isInline + +@TypeRefinement +fun ModuleDescriptor.getKotlinTypeRefiner(): KotlinTypeRefiner = getCapability(REFINER_CAPABILITY)?.value ?: KotlinTypeRefiner.Default \ No newline at end of file diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/KotlinTypeRefiner.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/KotlinTypeRefiner.kt new file mode 100644 index 00000000000..3f4dcb7f811 --- /dev/null +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/KotlinTypeRefiner.kt @@ -0,0 +1,82 @@ +/* + * 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.types.checker + +import org.jetbrains.kotlin.container.DefaultImplementation +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeConstructor +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.refinement.TypeRefinement + +@DefaultImplementation(impl = KotlinTypeRefiner.Default::class) +abstract class KotlinTypeRefiner { + @TypeRefinement + abstract fun refineType(type: KotlinType): KotlinType + + @TypeRefinement + abstract fun refineSupertypes(classDescriptor: ClassDescriptor): Collection + + @TypeRefinement + abstract fun refineDescriptor(descriptor: DeclarationDescriptor): ClassifierDescriptor? + + @TypeRefinement + abstract fun findClassAcrossModuleDependencies(classId: ClassId): ClassDescriptor? + + @TypeRefinement + abstract fun isRefinementNeededForModule(moduleDescriptor: ModuleDescriptor): Boolean + + @TypeRefinement + abstract fun isRefinementNeededForTypeConstructor(typeConstructor: TypeConstructor): Boolean + + @TypeRefinement + abstract fun getOrPutScopeForClass(classDescriptor: ClassDescriptor, compute: () -> S): S + + object Default : KotlinTypeRefiner() { + @TypeRefinement + override fun refineType(type: KotlinType): KotlinType = type + + @TypeRefinement + override fun refineSupertypes(classDescriptor: ClassDescriptor): Collection { + return classDescriptor.typeConstructor.supertypes + } + + @TypeRefinement + override fun refineDescriptor(descriptor: DeclarationDescriptor): ClassDescriptor? { + return null + } + + @TypeRefinement + override fun findClassAcrossModuleDependencies(classId: ClassId): ClassDescriptor? { + return null + } + + @TypeRefinement + override fun isRefinementNeededForModule(moduleDescriptor: ModuleDescriptor): Boolean { + return false + } + + @TypeRefinement + override fun isRefinementNeededForTypeConstructor(typeConstructor: TypeConstructor): Boolean { + return false + } + + @TypeRefinement + override fun getOrPutScopeForClass(classDescriptor: ClassDescriptor, compute: () -> S): S { + return compute() + } + } +} + +@TypeRefinement +fun KotlinTypeRefiner.refineTypes(types: Iterable): List = types.map { refineType(it) } + +class Ref(var value: T?) + +@TypeRefinement +val REFINER_CAPABILITY = ModuleDescriptor.Capability>("KotlinTypeRefiner") \ No newline at end of file