NI: intersect DFI types before capturing
^KT-37887 Fixed
This commit is contained in:
+27
-4
@@ -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) =
|
||||
|
||||
+15
-12
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user