[NI] Introduce transactions for constraint system.

This will be used for callable reference resolution.
This commit is contained in:
Stanislav Erokhin
2017-05-04 12:23:00 +03:00
parent cb494c46d7
commit 652676dc71
5 changed files with 82 additions and 52 deletions
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.builtins.isExtensionFunctionType
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible
import org.jetbrains.kotlin.resolve.calls.inference.model.ArgumentConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.model.LambdaTypeVariable
import org.jetbrains.kotlin.resolve.calls.model.*
@@ -23,21 +23,22 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedKotlinCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedLambdaArgument
import org.jetbrains.kotlin.types.UnwrappedType
interface ConstraintSystemBuilder {
interface ConstraintSystemOperation {
val hasContradiction: Boolean
fun registerVariable(variable: NewTypeVariable)
fun addSubtypeConstraint(lowerType: UnwrappedType, upperType: UnwrappedType, position: ConstraintPosition)
fun addEqualityConstraint(a: UnwrappedType, b: UnwrappedType, position: ConstraintPosition)
fun isProperType(type: UnwrappedType): Boolean
}
interface ConstraintSystemBuilder : ConstraintSystemOperation {
fun addInnerCall(innerCall: ResolvedKotlinCall.OnlyResolvedKotlinCall)
fun addLambdaArgument(resolvedLambdaArgument: ResolvedLambdaArgument)
fun addSubtypeConstraintIfCompatible(lowerType: UnwrappedType, upperType: UnwrappedType, position: ConstraintPosition): Boolean
fun isProperType(type: UnwrappedType): Boolean
// if runOperations return true, then this operation will be applied, and function return true
fun runTransaction(runOperations: ConstraintSystemOperation.() -> Boolean): Boolean
/**
* This function removes variables for which we know exact type.
@@ -45,3 +46,9 @@ interface ConstraintSystemBuilder {
*/
fun simplify(): NewTypeSubstitutor
}
fun ConstraintSystemBuilder.addSubtypeConstraintIfCompatible(lowerType: UnwrappedType, upperType: UnwrappedType, position: ConstraintPosition) =
runTransaction {
if (!hasContradiction) addSubtypeConstraint(lowerType, upperType, position)
!hasContradiction
}
@@ -57,3 +57,5 @@ fun CallableDescriptor.substituteAndApproximateCapturedTypes(substitutor: NewTyp
return substitute(TypeSubstitutor.create(wrappedSubstitution))
}
fun <E> MutableList<E>.trimToSize(newSize: Int) = subList(newSize, size).clear()
@@ -16,12 +16,14 @@
package org.jetbrains.kotlin.resolve.calls.inference.model
import org.jetbrains.kotlin.resolve.calls.inference.trimToSize
import org.jetbrains.kotlin.resolve.calls.model.ResolvedKotlinCall
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic
import org.jetbrains.kotlin.resolve.calls.model.ResolvedLambdaArgument
import org.jetbrains.kotlin.types.TypeConstructor
import org.jetbrains.kotlin.types.UnwrappedType
import java.util.*
import kotlin.collections.ArrayList
class MutableVariableWithConstraints(
@@ -29,7 +31,7 @@ class MutableVariableWithConstraints(
constraints: Collection<Constraint> = emptyList()
) : VariableWithConstraints {
override val constraints: List<Constraint> get() = mutableConstraints
private val mutableConstraints = MyArrayList(constraints)
private val mutableConstraints = ArrayList(constraints)
// return new actual constraint, if this constraint is new
fun addConstraint(constraint: Constraint): Constraint? {
@@ -50,15 +52,15 @@ class MutableVariableWithConstraints(
return actualConstraint
}
fun removeLastConstraints(shouldRemove: (Constraint) -> Boolean) {
mutableConstraints.removeLast(shouldRemove)
// This method should be used only for transaction in constraint system
// shouldRemove should give true only for tail elements
internal fun removeLastConstraints(shouldRemove: (Constraint) -> Boolean) {
mutableConstraints.trimToSize(mutableConstraints.indexOfLast { !shouldRemove(it) } + 1)
}
// todo optimize it!
fun removeConstrains(shouldRemove: (Constraint) -> Boolean) {
val newConstraints = mutableConstraints.filter { !shouldRemove(it) }
mutableConstraints.clear()
mutableConstraints.addAll(newConstraints)
// This method should be used only when constraint system has state COMPLETION
internal fun removeConstrains(shouldRemove: (Constraint) -> Boolean) {
mutableConstraints.removeAll(shouldRemove)
}
private fun newConstraintIsUseless(oldKind: ConstraintKind, newKind: ConstraintKind) =
@@ -68,24 +70,13 @@ class MutableVariableWithConstraints(
ConstraintKind.UPPER -> newKind == ConstraintKind.UPPER
}
private class MyArrayList<E>(c: Collection<E>): ArrayList<E>(c) {
fun removeLast(predicate: (E) -> Boolean) {
val newSize = indexOfLast { !predicate(it) } + 1
if (newSize != size) {
removeRange(newSize, size)
}
}
}
override fun toString(): String {
return "Constraints for $typeVariable"
}
}
class MutableConstraintStorage : ConstraintStorage {
internal class MutableConstraintStorage : ConstraintStorage {
override val allTypeVariables: MutableMap<TypeConstructor, NewTypeVariable> = LinkedHashMap()
override val notFixedTypeVariables: MutableMap<TypeConstructor, MutableVariableWithConstraints> = LinkedHashMap()
override val initialConstraints: MutableList<InitialConstraint> = ArrayList()
@@ -17,9 +17,7 @@
package org.jetbrains.kotlin.resolve.calls.inference.model
import org.jetbrains.kotlin.resolve.calls.components.KotlinCallCompleter
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem
import org.jetbrains.kotlin.resolve.calls.inference.buildCurrentSubstitutor
import org.jetbrains.kotlin.resolve.calls.inference.*
import org.jetbrains.kotlin.resolve.calls.inference.components.*
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic
import org.jetbrains.kotlin.resolve.calls.model.ResolvedKotlinCall
@@ -29,6 +27,7 @@ import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.TypeConstructor
import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.typeUtil.contains
import org.jetbrains.kotlin.utils.SmartList
import java.util.*
class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val resultTypeResolver: ResultTypeResolver):
@@ -39,11 +38,13 @@ class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val re
KotlinCallCompleter.Context,
FixationOrderCalculator.Context
{
val storage = MutableConstraintStorage()
private val storage = MutableConstraintStorage()
private var state = State.BUILDING
private val typeVariablesTransaction: MutableList<NewTypeVariable> = SmartList()
private enum class State {
BUILDING,
TRANSACTION,
FREEZED,
COMPLETION
}
@@ -73,44 +74,72 @@ class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val re
return this
}
// ConstraintSystemBuilder
// ConstraintSystemOperation
override fun registerVariable(variable: NewTypeVariable) {
checkState(State.BUILDING, State.COMPLETION)
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
transactionRegisterVariable(variable)
storage.allTypeVariables[variable.freshTypeConstructor] = variable
storage.notFixedTypeVariables[variable.freshTypeConstructor] = MutableVariableWithConstraints(variable)
}
override fun addSubtypeConstraint(lowerType: UnwrappedType, upperType: UnwrappedType, position: ConstraintPosition) =
constraintInjector.addInitialSubtypeConstraint(apply { checkState(State.BUILDING, State.COMPLETION) }, lowerType, upperType, position)
constraintInjector.addInitialSubtypeConstraint(apply { checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION) }, lowerType, upperType, position)
override fun addEqualityConstraint(a: UnwrappedType, b: UnwrappedType, position: ConstraintPosition) =
constraintInjector.addInitialEqualityConstraint(apply { checkState(State.BUILDING, State.COMPLETION) }, a, b, position)
constraintInjector.addInitialEqualityConstraint(apply { checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION) }, a, b, position)
override fun addLambdaArgument(resolvedLambdaArgument: ResolvedLambdaArgument) {
checkState(State.BUILDING, State.COMPLETION)
storage.lambdaArguments.add(resolvedLambdaArgument)
// ConstraintSystemBuilder
private fun transactionRegisterVariable(variable: NewTypeVariable) {
if (state != State.TRANSACTION) return
typeVariablesTransaction.add(variable)
}
override fun addSubtypeConstraintIfCompatible(lowerType: UnwrappedType, upperType: UnwrappedType, position: ConstraintPosition): Boolean {
private fun closeTransaction(beforeState: State) {
checkState(State.TRANSACTION)
typeVariablesTransaction.clear()
state = beforeState
}
override fun runTransaction(runOperations: ConstraintSystemOperation.() -> Boolean): Boolean {
checkState(State.BUILDING, State.COMPLETION)
val beforeState = state
val beforeInitialConstraintCount = storage.initialConstraints.size
val beforeErrorsCount = storage.errors.size
val beforeMaxTypeDepthFromInitialConstraints = storage.maxTypeDepthFromInitialConstraints
if (hasContradiction) return false
addSubtypeConstraint(lowerType, upperType, position)
if (!hasContradiction) return true
state = State.TRANSACTION
// typeVariablesTransaction is clear
if (runOperations()) {
closeTransaction(beforeState)
return true
}
val shouldRemove = { c: Constraint -> c.position === position ||
(c.position is IncorporationConstraintPosition && c.position.from === position) }
for (addedTypeVariable in typeVariablesTransaction) {
storage.allTypeVariables.remove(addedTypeVariable.freshTypeConstructor)
storage.notFixedTypeVariables.remove(addedTypeVariable.freshTypeConstructor)
}
storage.maxTypeDepthFromInitialConstraints = beforeMaxTypeDepthFromInitialConstraints
storage.errors.trimToSize(beforeErrorsCount)
val addedInitialConstraints = storage.initialConstraints.subList(beforeInitialConstraintCount, storage.initialConstraints.size)
val shouldRemove = { c: Constraint -> addedInitialConstraints.contains(c.position.initialConstraint) }
for (variableWithConstraint in storage.notFixedTypeVariables.values) {
variableWithConstraint.removeLastConstraints(shouldRemove)
}
storage.errors.clear()
storage.initialConstraints.removeAt(storage.initialConstraints.lastIndex)
addedInitialConstraints.clear() // remove constraint from storage.initialConstraints
closeTransaction(beforeState)
return false
}
override fun addLambdaArgument(resolvedLambdaArgument: ResolvedLambdaArgument) {
checkState(State.BUILDING, State.COMPLETION)
storage.lambdaArguments.add(resolvedLambdaArgument)
}
private fun getVariablesForFixation(): Map<NewTypeVariable, UnwrappedType> {
val fixedVariables = LinkedHashMap<NewTypeVariable, UnwrappedType>()
@@ -140,7 +169,7 @@ class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val re
// ConstraintSystemBuilder, KotlinCallCompleter.Context
override val hasContradiction: Boolean
get() = diagnostics.any { !it.candidateApplicability.isSuccess }.apply { checkState(State.BUILDING, State.COMPLETION) }
get() = diagnostics.any { !it.candidateApplicability.isSuccess }.apply { checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION) }
override fun addInnerCall(innerCall: ResolvedKotlinCall.OnlyResolvedKotlinCall) {
checkState(State.BUILDING, State.COMPLETION)
@@ -162,7 +191,7 @@ class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val re
// ResultTypeResolver.Context, ConstraintSystemBuilder
override fun isProperType(type: UnwrappedType): Boolean {
checkState(State.BUILDING, State.COMPLETION)
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
return !type.contains {
storage.allTypeVariables.containsKey(it.constructor)
}
@@ -170,31 +199,31 @@ class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val re
// ConstraintInjector.Context
override val allTypeVariables: Map<TypeConstructor, NewTypeVariable> get() {
checkState(State.BUILDING, State.COMPLETION)
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
return storage.allTypeVariables
}
override var maxTypeDepthFromInitialConstraints: Int
get() = storage.maxTypeDepthFromInitialConstraints
set(value) {
checkState(State.BUILDING, State.COMPLETION)
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
storage.maxTypeDepthFromInitialConstraints = value
}
override fun addInitialConstraint(initialConstraint: InitialConstraint) {
checkState(State.BUILDING, State.COMPLETION)
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
storage.initialConstraints.add(initialConstraint)
}
// ConstraintInjector.Context, FixationOrderCalculator.Context
override val notFixedTypeVariables: MutableMap<TypeConstructor, MutableVariableWithConstraints> get() {
checkState(State.BUILDING, State.COMPLETION)
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
return storage.notFixedTypeVariables
}
// ConstraintInjector.Context, KotlinCallCompleter.Context
override fun addError(error: KotlinCallDiagnostic) {
checkState(State.BUILDING, State.COMPLETION)
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
storage.errors.add(error)
}