[K1, MPP] Implement hack for KTIJ-24195

Force-refine typealiases if their expansion isn't visible in the
refiner's module

^KTIJ-24195
This commit is contained in:
Dmitry Savvinov
2023-09-06 16:04:15 +02:00
committed by Space Team
parent 370a6fb461
commit 5736201e3a
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.utils.DFS
@OptIn(TypeRefinement::class)
class KotlinTypeRefinerImpl(
private val moduleDescriptor: ModuleDescriptor,
storageManager: StorageManager
storageManager: StorageManager,
) : KotlinTypeRefiner() {
private val refinedTypeCache: CacheWithNotNullValues<TypeConstructor, KotlinType> =
storageManager.createCacheWithNotNullValues()
@@ -37,7 +37,7 @@ class KotlinTypeRefinerImpl(
private constructor(
moduleDescriptor: ModuleDescriptor,
storageManager: StorageManager,
isStandalone: Boolean
isStandalone: Boolean,
) : this(moduleDescriptor, storageManager) {
this.isStandalone = isStandalone
}
@@ -81,17 +81,24 @@ class KotlinTypeRefinerImpl(
require(type is KotlinType)
if (type.constructor.declarationDescriptor?.module == moduleDescriptor) return type
return when {
!type.needsRefinement() -> type
// See comment on [needsRefinementHackForKtij24195]
type.needsRefinementHackForKtij24195() -> doRefineType((type as AbbreviatedType).abbreviation)
type.canBeCached() -> {
val cached = refinedTypeCache.computeIfAbsent(type.constructor) {
type.constructor.declarationDescriptor!!.defaultType.refineWithRespectToAbbreviatedTypes(this)
}
type.needsRefinement() -> doRefineType(type)
cached.restoreAdditionalTypeInformation(type)
else -> type
}
}
private fun doRefineType(type: KotlinType): KotlinType {
return if (type.canBeCached()) {
val cached = refinedTypeCache.computeIfAbsent(type.constructor) {
type.constructor.declarationDescriptor!!.defaultType.refineWithRespectToAbbreviatedTypes(this)
}
else -> type.refineWithRespectToAbbreviatedTypes(this)
cached.restoreAdditionalTypeInformation(type)
} else {
type.refineWithRespectToAbbreviatedTypes(this)
}
}
@@ -111,6 +118,40 @@ class KotlinTypeRefinerImpl(
private fun KotlinType.canBeCached(): Boolean = hasNotTrivialRefinementFactory && constructor.declarationDescriptor != null
/**
* This is a hack for https://youtrack.jetbrains.com/issue/KTIJ-24195
*
* The rough idea is that if:
* - we see a typealias pointing to a classifier
* - ideally, we'd consider only `actual typealias`es, but we don't write `actual`-flag in metadata :(
* - and that classifier isn't visible from our module
* - then we should re-refine the abbreviation
*
* Read KTIJ-24195 comments for detailed explanation and reasoning why this hack is sufficient.
*
* Performance note: this hack amounts to running an additional `findClassAcrossModuleDependencies` on all `typealias`
* abbreviations. In most cases, the resolution of abbreviation should've happened before, so this call will just hit the cache.
* It is possible to construct a case where this call will actually have to do some non-trivial work, but:
* a) it's quite hard to write such case even knowing how our caches work, so the probability of a real-life user' code hitting that
* case is minuscule
* b) even if we somehow manage to hit that case in real code, this is just one more `getContributedClassifier` per used
* `typealias`, as all subsequent calls will be cached.
*/
private fun KotlinType.needsRefinementHackForKtij24195(): Boolean {
if (this !is AbbreviatedType) return false
if (abbreviation.constructor.declarationDescriptor !is TypeAliasDescriptor) return false
// Would be nice to have the following line uncommented (nice optimization), but unfortunately, serialized binaries do not
// preserve `isActual`-flags
// if (!abbreviation.constructor.declarationDescriptor.isActual) return false
val expansionDescriptorClassId = expandedType.constructor.declarationDescriptor.classId ?: return false
// Expansion invisible - need refinement hack
// NB: important to use 'findClassifier' and not 'findClass', because normally this call is expected to resolve
// a typealias descriptor, which is a Classifier but not a ClassDescriptor
return moduleDescriptor.findClassifierAcrossModuleDependencies(expansionDescriptorClassId) == null
}
@TypeRefinement
override fun refineSupertypes(classDescriptor: ClassDescriptor): Collection<KotlinType> {
// Note that we can't omit refinement even if classDescriptor.module == moduleDescriptor,