K2: Rewrite delegate inference

The main idea is getting rid of stub types and using just type variables
See more detailed description at docs/fir/delegated_property_inference.md

The problem with stub types is that they need really special treatment
in many places, and on the other hand, there are no clear contracts on
how they should work (that regularly leads to bugs like KT-59529)

^KT-61060 Fixed
^KT-61075 Fixed
^KT-61077 Fixed
^KT-59529 Fixed
^KT-61633 Related
^KT-61618 Related
^KT-61740 Related
^KT-59107 Related
^KT-61747 Related
^KT-61077 Related
^KT-61781 Related
This commit is contained in:
Denis.Zharkov
2023-08-04 19:52:29 +02:00
committed by Space Team
parent cd5105c133
commit a02cb16fb2
53 changed files with 1183 additions and 381 deletions
@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.types.model.*
fun ConstraintStorage.buildCurrentSubstitutor(
context: TypeSystemInferenceExtensionContext,
additionalBindings: Map<TypeConstructorMarker, StubTypeMarker>
additionalBindings: Map<TypeConstructorMarker, KotlinTypeMarker>
): TypeSubstitutorMarker {
return context.typeSubstitutorByTypeConstructor(fixedTypeVariables.entries.associate { it.key to it.value } + additionalBindings)
}
@@ -21,6 +21,11 @@ abstract class ConstraintSystemCompletionContext : VariableFixationFinder.Contex
abstract override val fixedTypeVariables: Map<TypeConstructorMarker, KotlinTypeMarker>
abstract override val postponedTypeVariables: List<TypeVariableMarker>
/**
* See [org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage.outerSystemVariablesPrefixSize]
*/
abstract val outerSystemVariablesPrefixSize: Int
abstract fun getBuilder(): ConstraintSystemBuilder
// type can be proper if it not contains not fixed type variables
@@ -63,7 +63,7 @@ class ResultTypeResolver(
return c.getDefaultType(direction, variableWithConstraints.constraints, variableWithConstraints.typeVariable)
}
private fun findResultTypeOrNull(
fun findResultTypeOrNull(
c: Context,
variableWithConstraints: VariableWithConstraints,
direction: ResolveDirection
@@ -28,6 +28,17 @@ class VariableFixationFinder(
val postponedTypeVariables: List<TypeVariableMarker>
val constraintsFromAllForkPoints: MutableList<Pair<IncorporationConstraintPosition, ForkPointData>>
/**
* If not null, that property means that we should assume temporary
* `allTypeVariables.keys.minus(typeVariablesThatAreNotCountedAsProperTypes)` as proper types when fixating some variables.
*
* By default, if that property is null, we assume all `allTypeVariables` as not proper.
*
* Currently, that is only used for `provideDelegate` resolution, see
* [org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.fixInnerVariablesForProvideDelegateIfNeeded]
*/
val typeVariablesThatAreNotCountedAsProperTypes: Set<TypeConstructorMarker>?
fun isReified(variable: TypeVariableMarker): Boolean
}
@@ -182,7 +193,13 @@ class VariableFixationFinder(
&& !c.isNullabilityConstraint
private fun Context.isProperType(type: KotlinTypeMarker): Boolean =
isProperTypeForFixation(type) { t -> !t.contains { notFixedTypeVariables.containsKey(it.typeConstructor()) } }
isProperTypeForFixation(type) { t -> !t.contains { isNotFixedRelevantVariable(it) } }
private fun Context.isNotFixedRelevantVariable(it: KotlinTypeMarker): Boolean {
if (!notFixedTypeVariables.containsKey(it.typeConstructor())) return false
if (typeVariablesThatAreNotCountedAsProperTypes == null) return true
return typeVariablesThatAreNotCountedAsProperTypes!!.contains(it.typeConstructor())
}
private fun Context.isReified(variable: TypeConstructorMarker): Boolean =
notFixedTypeVariables[variable]?.typeVariable?.let { isReified(it) } ?: false
@@ -93,6 +93,8 @@ object BuilderInferencePosition : ConstraintPosition() {
override fun toString(): String = "For builder inference call"
}
data object ProvideDelegateFixationPosition : ConstraintPosition()
// TODO: should be used only in SimpleConstraintSystemImpl, KT-59675
object SimpleConstraintSystemConstraintPosition : ConstraintPosition()
@@ -48,6 +48,20 @@ interface ConstraintStorage {
val builtFunctionalTypesForPostponedArgumentsByExpectedTypeVariables: Map<TypeConstructorMarker, KotlinTypeMarker>
val constraintsFromAllForkPoints: List<Pair<IncorporationConstraintPosition, ForkPointData>>
/**
* In case some candidate's CS is built in the context of some outer CS, first [outerSystemVariablesPrefixSize] in the list
* of [allTypeVariables] belong to the outer CS.
*
* That information is very limitedly used in a couple of cases when we need to separate those kinds of variables
* - When completing `provideDelegate` calls, we assume outer variables as proper types
* (see fixInnerVariablesForProvideDelegateIfNeeded).
* - When checking consistency of collected variables for the inner candidate
* (see checkNotFixedTypeVariablesCountConsistency).
*
* Also, see docs/fir/delegated_property_inference.md
*/
val outerSystemVariablesPrefixSize: Int
object Empty : ConstraintStorage {
override val allTypeVariables: Map<TypeConstructorMarker, TypeVariableMarker> get() = emptyMap()
override val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints> get() = emptyMap()
@@ -61,6 +75,8 @@ interface ConstraintStorage {
override val builtFunctionalTypesForPostponedArgumentsByTopLevelTypeVariables: Map<Pair<TypeConstructorMarker, List<Pair<TypeConstructorMarker, Int>>>, KotlinTypeMarker> = emptyMap()
override val builtFunctionalTypesForPostponedArgumentsByExpectedTypeVariables: Map<TypeConstructorMarker, KotlinTypeMarker> = emptyMap()
override val constraintsFromAllForkPoints: List<Pair<IncorporationConstraintPosition, ForkPointData>> = emptyList()
override val outerSystemVariablesPrefixSize: Int get() = 0
}
}
@@ -241,4 +241,6 @@ internal class MutableConstraintStorage : ConstraintStorage {
LinkedHashMap()
override val constraintsFromAllForkPoints: MutableList<Pair<IncorporationConstraintPosition, ForkPointData>> = SmartList()
override var outerSystemVariablesPrefixSize: Int = 0
}
@@ -42,10 +42,35 @@ class NewConstraintSystemImpl(
private val properTypesCache: MutableSet<KotlinTypeMarker> = SmartSet.create()
private val notProperTypesCache: MutableSet<KotlinTypeMarker> = SmartSet.create()
private val intersectionTypesCache: MutableMap<Collection<KotlinTypeMarker>, EmptyIntersectionTypeInfo?> = mutableMapOf()
override var typeVariablesThatAreNotCountedAsProperTypes: Set<TypeConstructorMarker>? = null
private var couldBeResolvedWithUnrestrictedBuilderInference: Boolean = false
override var atCompletionState: Boolean = false
/**
* @see [org.jetbrains.kotlin.resolve.calls.inference.components.VariableFixationFinder.Context.typeVariablesThatAreNotCountedAsProperTypes]
* @see [org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.fixInnerVariablesForProvideDelegateIfNeeded]
*/
fun withTypeVariablesThatAreNotCountedAsProperTypes(typeVariables: Set<TypeConstructorMarker>, block: () -> Unit) {
checkState(State.BUILDING)
// Cleaning cache is necessary because temporarily we change the meaning of what does "proper type" mean
properTypesCache.clear()
notProperTypesCache.clear()
require(typeVariablesThatAreNotCountedAsProperTypes == null) {
"Currently there should be no nested withDisallowingOnlyThisTypeVariablesForProperTypes calls"
}
typeVariablesThatAreNotCountedAsProperTypes = typeVariables
block()
typeVariablesThatAreNotCountedAsProperTypes = null
properTypesCache.clear()
notProperTypesCache.clear()
}
private enum class State {
BUILDING,
TRANSACTION,
@@ -276,6 +301,21 @@ class NewConstraintSystemImpl(
)
}
fun addOuterSystem(outerSystem: ConstraintStorage) {
addOtherSystem(outerSystem)
storage.outerSystemVariablesPrefixSize = outerSystem.allTypeVariables.size
}
fun setBaseSystem(outerSystem: ConstraintStorage) {
addOtherSystem(outerSystem)
storage.outerSystemVariablesPrefixSize = outerSystem.outerSystemVariablesPrefixSize
}
fun prepareForGlobalCompletion() {
// There's no more separation of outer/inner variables once global completion starts
storage.outerSystemVariablesPrefixSize = 0
}
override fun addOtherSystem(otherSystem: ConstraintStorage) {
if (otherSystem.allTypeVariables.isNotEmpty()) {
otherSystem.allTypeVariables.forEach {
@@ -317,6 +357,9 @@ class NewConstraintSystemImpl(
it
if (typeToCheck == null) return@contains false
if (typeVariablesThatAreNotCountedAsProperTypes != null) {
return@contains typeVariablesThatAreNotCountedAsProperTypes!!.contains(typeToCheck.typeConstructor())
}
storage.allTypeVariables.containsKey(typeToCheck.typeConstructor())
}
@@ -369,6 +412,9 @@ class NewConstraintSystemImpl(
return storage.postponedTypeVariables
}
override val outerSystemVariablesPrefixSize: Int
get() = storage.outerSystemVariablesPrefixSize
override val constraintsFromAllForkPoints: MutableList<Pair<IncorporationConstraintPosition, ForkPointData>>
get() {
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)