NI: intersect DFI types before capturing

^KT-37887 Fixed
This commit is contained in:
Victor Petukhov
2020-05-14 13:43:30 +03:00
parent 5a7ceec985
commit 73dec25eb1
15 changed files with 141 additions and 32 deletions
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo
import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.checker.intersectWrappedTypes
import org.jetbrains.kotlin.types.checker.prepareArgumentTypeRegardingCaptureTypes
import org.jetbrains.kotlin.utils.DFS
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -40,15 +41,37 @@ internal fun unexpectedArgument(argument: KotlinCallArgument): Nothing =
// if expression is not stable and has smart casts, then we create this type
internal val ReceiverValueWithSmartCastInfo.unstableType: UnwrappedType?
get() {
if (isStable || possibleTypes.isEmpty()) return null
return intersectWrappedTypes(possibleTypes + receiverValue.type)
if (isStable || !hasTypesFromSmartCasts())
return if (isStable) null else receiverValue.type.unwrap()
val intersectionType = intersectWrappedTypes(allOriginalTypes)
return prepareArgumentTypeRegardingCaptureTypes(intersectionType) ?: intersectionType
}
// with all smart casts if stable
val ReceiverValueWithSmartCastInfo.stableType: UnwrappedType
get() {
if (!isStable || possibleTypes.isEmpty()) return receiverValue.type.unwrap()
return intersectWrappedTypes(possibleTypes + receiverValue.type)
if (!isStable || !hasTypesFromSmartCasts())
return receiverValue.type.unwrap()
/*
* We have to intersect types first as after capturing, subtyping relation may change and some type won't be excluded from intersection type.
*
* Example:
* allOriginalTypes = [Inv<out CharSequence>, Inv<String>]
* intersect(Inv<out CharSequence>, Inv<String>) = Inv<String>
* capture(Inv<String>) = Inv<String>
* But with capturing first:
* capture(Inv<out CharSequence>) = Inv<CapturedType(out CharSequence)>
* capture(Inv<String>) = Inv<String>
* intersect(Inv<CapturedType(out CharSequence)>, Inv<String>) = Inv<CapturedType(out CharSequence)> & Inv<String>
*
* Such redundant type with captured argument may further lead to contradiction in constraint system or less exact solution.
*/
val intersectionType = intersectWrappedTypes(allOriginalTypes)
return prepareArgumentTypeRegardingCaptureTypes(intersectionType) ?: intersectionType
}
internal fun KotlinCallArgument.getExpectedType(parameter: ParameterDescriptor, languageVersionSettings: LanguageVersionSettings) =
@@ -97,7 +97,7 @@ internal class MemberScopeTowerLevel(
val unstableError = if (dispatchReceiver.isStable) null else UnstableSmartCastDiagnostic
val unstableCandidates = if (unstableError != null) ArrayList<CandidateWithBoundDispatchReceiver>(0) else null
for (possibleType in dispatchReceiver.possibleTypes) {
for (possibleType in dispatchReceiver.typesFromSmartCasts) {
possibleType.memberScope.getMembers(possibleType).mapTo(unstableCandidates ?: result) {
createCandidateDescriptor(
it,
@@ -107,7 +107,7 @@ internal class MemberScopeTowerLevel(
}
}
if (dispatchReceiver.possibleTypes.isNotEmpty()) {
if (dispatchReceiver.hasTypesFromSmartCasts()) {
if (unstableCandidates == null) {
result.retainAll(result.selectMostSpecificInEachOverridableGroup { descriptor.approximateCapturedTypes(typeApproximator) })
} else {
@@ -156,7 +156,7 @@ internal class MemberScopeTowerLevel(
if (receiverValue !is ImplicitClassReceiver) return this
val newReceiverValue = CastImplicitClassReceiver(receiverValue.classDescriptor, targetType)
return ReceiverValueWithSmartCastInfo(newReceiverValue, possibleTypes, isStable)
return ReceiverValueWithSmartCastInfo(newReceiverValue, typesFromSmartCasts, isStable)
}
override fun getVariables(
@@ -184,9 +184,8 @@ internal class MemberScopeTowerLevel(
}
override fun recordLookup(name: Name) {
dispatchReceiver.receiverValue.type.memberScope.recordLookup(name, location)
dispatchReceiver.possibleTypes.forEach {
it.memberScope.recordLookup(name, location)
for (type in dispatchReceiver.allOriginalTypes) {
type.memberScope.recordLookup(name, location)
}
}
}
@@ -292,16 +291,13 @@ internal class SyntheticScopeBasedTowerLevel(
scopeTower: ImplicitScopeTower,
private val syntheticScopes: SyntheticScopes
) : AbstractScopeTowerLevel(scopeTower) {
private val ReceiverValueWithSmartCastInfo.allTypes: Set<KotlinType>
get() = possibleTypes + receiverValue.type
override fun getVariables(
name: Name,
extensionReceiver: ReceiverValueWithSmartCastInfo?
): Collection<CandidateWithBoundDispatchReceiver> {
if (extensionReceiver == null) return emptyList()
return syntheticScopes.collectSyntheticExtensionProperties(extensionReceiver.allTypes, name, location).map {
return syntheticScopes.collectSyntheticExtensionProperties(extensionReceiver.allOriginalTypes, name, location).map {
createCandidateDescriptor(it, dispatchReceiver = null)
}
}
@@ -240,8 +240,8 @@ class TowerResolver {
private fun ReceiverValueWithSmartCastInfo.mayFitForName(name: Name): Boolean {
if (receiverValue.type.mayFitForName(name)) return true
if (possibleTypes.isEmpty()) return false
return possibleTypes.any { it.mayFitForName(name) }
if (!hasTypesFromSmartCasts()) return false
return typesFromSmartCasts.any { it.mayFitForName(name) }
}
private fun KotlinType.mayFitForName(name: Name) =
@@ -19,17 +19,26 @@ package org.jetbrains.kotlin.resolve.scopes.receivers
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.checker.prepareArgumentTypeRegardingCaptureTypes
import org.jetbrains.kotlin.utils.newLinkedHashSetWithExpectedSize
import org.jetbrains.kotlin.types.checker.*
// this receiver used only for resolution. see subtypes
interface DetailedReceiver
class ReceiverValueWithSmartCastInfo(
val receiverValue: ReceiverValue,
val possibleTypes: Set<KotlinType>, // doesn't include receiver.type
val isStable: Boolean
/*
* It doesn't include receiver.type and is used only to special marking such types (e.g. for IDE green highlighting)
* but not to construct the resulting type
*/
val typesFromSmartCasts: Set<KotlinType>,
val isStable: Boolean,
originalBaseType: KotlinType = receiverValue.type
) : DetailedReceiver {
// It's used to construct the resulting type
val allOriginalTypes = typesFromSmartCasts + originalBaseType
fun hasTypesFromSmartCasts() = typesFromSmartCasts.isNotEmpty()
override fun toString() = receiverValue.toString()
}
@@ -46,13 +55,7 @@ interface QualifierReceiver : Receiver, DetailedReceiver {
}
fun ReceiverValueWithSmartCastInfo.prepareReceiverRegardingCaptureTypes(): ReceiverValueWithSmartCastInfo {
val preparedBaseType = prepareArgumentTypeRegardingCaptureTypes(receiverValue.type.unwrap())
if (preparedBaseType == null && possibleTypes.isEmpty()) return this
val preparedBaseType = prepareArgumentTypeRegardingCaptureTypes(receiverValue.type.unwrap()) ?: return this
val newPossibleTypes = possibleTypes.mapTo(newLinkedHashSetWithExpectedSize(possibleTypes.size)) {
prepareArgumentTypeRegardingCaptureTypes(it.unwrap()) ?: it
}
val newReceiver = if (preparedBaseType != null) receiverValue.replaceType(preparedBaseType) else receiverValue
return ReceiverValueWithSmartCastInfo(newReceiver, newPossibleTypes, isStable)
return ReceiverValueWithSmartCastInfo(receiverValue.replaceType(preparedBaseType), typesFromSmartCasts, isStable, receiverValue.type)
}