From 19467861c97187966cde7f980e001407bc834142 Mon Sep 17 00:00:00 2001 From: Pavel Kirpichenkov Date: Fri, 16 Jul 2021 16:36:49 +0300 Subject: [PATCH] [MPP] Fix false positive expect-actual mismatch Use type refiner to correctly expand actual type alias - expect class chains, produced by the commonizer. In a case when type refiner doesn't exist for module create a one-shot thread local instance. This might happen with modules without source code in project (coming from build system model). ^KT-46691 Fixed --- .../multiplatform/ExpectedActualResolver.kt | 79 ++++++++++++++----- .../kotlin/types/KotlinTypeRefinerImpl.kt | 24 +++++- 2 files changed, 81 insertions(+), 22 deletions(-) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectedActualResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectedActualResolver.kt index c3ed18542e5..265061104c4 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectedActualResolver.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectedActualResolver.kt @@ -5,27 +5,22 @@ package org.jetbrains.kotlin.resolve.multiplatform +import org.jetbrains.kotlin.types.KotlinTypeRefinerImpl import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.descriptorUtil.classId -import org.jetbrains.kotlin.resolve.descriptorUtil.isAnnotationConstructor -import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.resolve.descriptorUtil.* import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Compatible import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.TypeConstructor -import org.jetbrains.kotlin.types.TypeConstructorSubstitution -import org.jetbrains.kotlin.types.TypeSubstitutor -import org.jetbrains.kotlin.types.checker.ClassicTypeCheckerContext -import org.jetbrains.kotlin.types.checker.ClassicTypeSystemContext -import org.jetbrains.kotlin.types.checker.NewKotlinTypeChecker +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.checker.* import org.jetbrains.kotlin.types.model.TypeConstructorMarker +import org.jetbrains.kotlin.types.refinement.TypeRefinement import org.jetbrains.kotlin.types.typeUtil.asTypeProjection import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.kotlin.utils.keysToMap @@ -257,22 +252,64 @@ object ExpectedActualResolver { false } + @OptIn(TypeRefinement::class) private fun areCompatibleTypes(a: KotlinType?, b: KotlinType?, platformModule: ModuleDescriptor): Boolean { if (a == null) return b == null if (b == null) return false - with(NewKotlinTypeChecker.Default) { - val context = object : ClassicTypeSystemContext { - override fun areEqualTypeConstructors(c1: TypeConstructorMarker, c2: TypeConstructorMarker): Boolean { - require(c1 is TypeConstructor) - require(c2 is TypeConstructor) - return isExpectedClassAndActualTypeAlias(c1, c2, platformModule) || - isExpectedClassAndActualTypeAlias(c2, c1, platformModule) || - super.areEqualTypeConstructors(c1, c2) - } + return if (platformModule.isTypeRefinementEnabled()) { + areCompatibleTypesViaTypeRefinement(a, b, platformModule) + } else { + areCompatibleTypesViaTypeContext(a, b, platformModule) + } + } + + @OptIn(TypeRefinement::class) + private fun areCompatibleTypesViaTypeRefinement(a: KotlinType, b: KotlinType, platformModule: ModuleDescriptor): Boolean { + val typeRefinerForPlatformModule = platformModule.getKotlinTypeRefiner().let { moduleRefiner -> + if (moduleRefiner is KotlinTypeRefiner.Default) + KotlinTypeRefinerImpl.createStandaloneInstanceFor(platformModule) + else + moduleRefiner + } + + return areCompatibleTypes( + a, b, + typeSystemContext = SimpleClassicTypeSystemContext, + kotlinTypeRefiner = typeRefinerForPlatformModule, + ) + } + + private fun areCompatibleTypesViaTypeContext(a: KotlinType, b: KotlinType, platformModule: ModuleDescriptor): Boolean { + val typeSystemContext = object : ClassicTypeSystemContext { + override fun areEqualTypeConstructors(c1: TypeConstructorMarker, c2: TypeConstructorMarker): Boolean { + require(c1 is TypeConstructor) + require(c2 is TypeConstructor) + return isExpectedClassAndActualTypeAlias(c1, c2, platformModule) || + isExpectedClassAndActualTypeAlias(c2, c1, platformModule) || + super.areEqualTypeConstructors(c1, c2) } - return ClassicTypeCheckerContext(errorTypeEqualsToAnything = false, typeSystemContext = context) - .equalTypes(a.unwrap(), b.unwrap()) + } + + return areCompatibleTypes( + a, b, + typeSystemContext = typeSystemContext, + kotlinTypeRefiner = KotlinTypeRefiner.Default, + ) + } + + private fun areCompatibleTypes( + a: KotlinType, + b: KotlinType, + typeSystemContext: ClassicTypeSystemContext, + kotlinTypeRefiner: KotlinTypeRefiner, + ): Boolean { + with(NewKotlinTypeCheckerImpl(kotlinTypeRefiner)) { + return ClassicTypeCheckerContext( + errorTypeEqualsToAnything = false, + typeSystemContext = typeSystemContext, + kotlinTypeRefiner = kotlinTypeRefiner, + ).equalTypes(a.unwrap(), b.unwrap()) } } diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/KotlinTypeRefinerImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/types/KotlinTypeRefinerImpl.kt index 8523f7475cc..c848d434300 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/KotlinTypeRefinerImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/KotlinTypeRefinerImpl.kt @@ -10,6 +10,7 @@ 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.LockBasedStorageManager import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.types.checker.KotlinTypeRefiner import org.jetbrains.kotlin.types.checker.NewCapturedTypeConstructor @@ -24,8 +25,20 @@ class KotlinTypeRefinerImpl( private val moduleDescriptor: ModuleDescriptor, storageManager: StorageManager ) : KotlinTypeRefiner() { + private var isStandalone: Boolean = false + + private constructor( + moduleDescriptor: ModuleDescriptor, + storageManager: StorageManager, + isStandalone: Boolean + ) : this(moduleDescriptor, storageManager) { + this.isStandalone = isStandalone + } + init { - moduleDescriptor.getCapability(REFINER_CAPABILITY)?.value = TypeRefinementSupport.Enabled(this) + if (!isStandalone) { + moduleDescriptor.getCapability(REFINER_CAPABILITY)?.value = TypeRefinementSupport.Enabled(this) + } } private val refinedTypeCache = storageManager.createCacheWithNotNullValues() @@ -148,6 +161,15 @@ class KotlinTypeRefinerImpl( return result } + + companion object { + /** + * Create a new *thread unsafe* type refiner instance for the specified module. + * Note, that module's type refiner capability won't be changed. + */ + fun createStandaloneInstanceFor(moduleDescriptor: ModuleDescriptor): KotlinTypeRefinerImpl = + KotlinTypeRefinerImpl(moduleDescriptor, LockBasedStorageManager.NO_LOCKS, isStandalone = true) + } } private val TypeConstructor.allDependentTypeConstructors: Collection