[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.builtins.isFunctionType
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder 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.ArgumentConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.model.LambdaTypeVariable import org.jetbrains.kotlin.resolve.calls.inference.model.LambdaTypeVariable
import org.jetbrains.kotlin.resolve.calls.model.* 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.resolve.calls.model.ResolvedLambdaArgument
import org.jetbrains.kotlin.types.UnwrappedType import org.jetbrains.kotlin.types.UnwrappedType
interface ConstraintSystemOperation {
interface ConstraintSystemBuilder {
val hasContradiction: Boolean val hasContradiction: Boolean
fun registerVariable(variable: NewTypeVariable) fun registerVariable(variable: NewTypeVariable)
fun addSubtypeConstraint(lowerType: UnwrappedType, upperType: UnwrappedType, position: ConstraintPosition) fun addSubtypeConstraint(lowerType: UnwrappedType, upperType: UnwrappedType, position: ConstraintPosition)
fun addEqualityConstraint(a: UnwrappedType, b: 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 addInnerCall(innerCall: ResolvedKotlinCall.OnlyResolvedKotlinCall)
fun addLambdaArgument(resolvedLambdaArgument: ResolvedLambdaArgument) fun addLambdaArgument(resolvedLambdaArgument: ResolvedLambdaArgument)
fun addSubtypeConstraintIfCompatible(lowerType: UnwrappedType, upperType: UnwrappedType, position: ConstraintPosition): Boolean // if runOperations return true, then this operation will be applied, and function return true
fun runTransaction(runOperations: ConstraintSystemOperation.() -> Boolean): Boolean
fun isProperType(type: UnwrappedType): Boolean
/** /**
* This function removes variables for which we know exact type. * This function removes variables for which we know exact type.
@@ -45,3 +46,9 @@ interface ConstraintSystemBuilder {
*/ */
fun simplify(): NewTypeSubstitutor 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)) 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 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.ResolvedKotlinCall
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic
import org.jetbrains.kotlin.resolve.calls.model.ResolvedLambdaArgument import org.jetbrains.kotlin.resolve.calls.model.ResolvedLambdaArgument
import org.jetbrains.kotlin.types.TypeConstructor import org.jetbrains.kotlin.types.TypeConstructor
import org.jetbrains.kotlin.types.UnwrappedType import org.jetbrains.kotlin.types.UnwrappedType
import java.util.* import java.util.*
import kotlin.collections.ArrayList
class MutableVariableWithConstraints( class MutableVariableWithConstraints(
@@ -29,7 +31,7 @@ class MutableVariableWithConstraints(
constraints: Collection<Constraint> = emptyList() constraints: Collection<Constraint> = emptyList()
) : VariableWithConstraints { ) : VariableWithConstraints {
override val constraints: List<Constraint> get() = mutableConstraints 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 // return new actual constraint, if this constraint is new
fun addConstraint(constraint: Constraint): Constraint? { fun addConstraint(constraint: Constraint): Constraint? {
@@ -50,15 +52,15 @@ class MutableVariableWithConstraints(
return actualConstraint return actualConstraint
} }
fun removeLastConstraints(shouldRemove: (Constraint) -> Boolean) { // This method should be used only for transaction in constraint system
mutableConstraints.removeLast(shouldRemove) // shouldRemove should give true only for tail elements
internal fun removeLastConstraints(shouldRemove: (Constraint) -> Boolean) {
mutableConstraints.trimToSize(mutableConstraints.indexOfLast { !shouldRemove(it) } + 1)
} }
// todo optimize it! // This method should be used only when constraint system has state COMPLETION
fun removeConstrains(shouldRemove: (Constraint) -> Boolean) { internal fun removeConstrains(shouldRemove: (Constraint) -> Boolean) {
val newConstraints = mutableConstraints.filter { !shouldRemove(it) } mutableConstraints.removeAll(shouldRemove)
mutableConstraints.clear()
mutableConstraints.addAll(newConstraints)
} }
private fun newConstraintIsUseless(oldKind: ConstraintKind, newKind: ConstraintKind) = private fun newConstraintIsUseless(oldKind: ConstraintKind, newKind: ConstraintKind) =
@@ -68,24 +70,13 @@ class MutableVariableWithConstraints(
ConstraintKind.UPPER -> newKind == ConstraintKind.UPPER 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 { override fun toString(): String {
return "Constraints for $typeVariable" return "Constraints for $typeVariable"
} }
} }
class MutableConstraintStorage : ConstraintStorage { internal class MutableConstraintStorage : ConstraintStorage {
override val allTypeVariables: MutableMap<TypeConstructor, NewTypeVariable> = LinkedHashMap() override val allTypeVariables: MutableMap<TypeConstructor, NewTypeVariable> = LinkedHashMap()
override val notFixedTypeVariables: MutableMap<TypeConstructor, MutableVariableWithConstraints> = LinkedHashMap() override val notFixedTypeVariables: MutableMap<TypeConstructor, MutableVariableWithConstraints> = LinkedHashMap()
override val initialConstraints: MutableList<InitialConstraint> = ArrayList() override val initialConstraints: MutableList<InitialConstraint> = ArrayList()
@@ -17,9 +17,7 @@
package org.jetbrains.kotlin.resolve.calls.inference.model package org.jetbrains.kotlin.resolve.calls.inference.model
import org.jetbrains.kotlin.resolve.calls.components.KotlinCallCompleter import org.jetbrains.kotlin.resolve.calls.components.KotlinCallCompleter
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder import org.jetbrains.kotlin.resolve.calls.inference.*
import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem
import org.jetbrains.kotlin.resolve.calls.inference.buildCurrentSubstitutor
import org.jetbrains.kotlin.resolve.calls.inference.components.* import org.jetbrains.kotlin.resolve.calls.inference.components.*
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic
import org.jetbrains.kotlin.resolve.calls.model.ResolvedKotlinCall 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.TypeConstructor
import org.jetbrains.kotlin.types.UnwrappedType import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.typeUtil.contains import org.jetbrains.kotlin.types.typeUtil.contains
import org.jetbrains.kotlin.utils.SmartList
import java.util.* import java.util.*
class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val resultTypeResolver: ResultTypeResolver): class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val resultTypeResolver: ResultTypeResolver):
@@ -39,11 +38,13 @@ class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val re
KotlinCallCompleter.Context, KotlinCallCompleter.Context,
FixationOrderCalculator.Context FixationOrderCalculator.Context
{ {
val storage = MutableConstraintStorage() private val storage = MutableConstraintStorage()
private var state = State.BUILDING private var state = State.BUILDING
private val typeVariablesTransaction: MutableList<NewTypeVariable> = SmartList()
private enum class State { private enum class State {
BUILDING, BUILDING,
TRANSACTION,
FREEZED, FREEZED,
COMPLETION COMPLETION
} }
@@ -73,44 +74,72 @@ class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val re
return this return this
} }
// ConstraintSystemBuilder // ConstraintSystemOperation
override fun registerVariable(variable: NewTypeVariable) { 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.allTypeVariables[variable.freshTypeConstructor] = variable
storage.notFixedTypeVariables[variable.freshTypeConstructor] = MutableVariableWithConstraints(variable) storage.notFixedTypeVariables[variable.freshTypeConstructor] = MutableVariableWithConstraints(variable)
} }
override fun addSubtypeConstraint(lowerType: UnwrappedType, upperType: UnwrappedType, position: ConstraintPosition) = 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) = 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) { // ConstraintSystemBuilder
checkState(State.BUILDING, State.COMPLETION) private fun transactionRegisterVariable(variable: NewTypeVariable) {
storage.lambdaArguments.add(resolvedLambdaArgument) 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) 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 state = State.TRANSACTION
addSubtypeConstraint(lowerType, upperType, position) // typeVariablesTransaction is clear
if (!hasContradiction) return true if (runOperations()) {
closeTransaction(beforeState)
return true
}
val shouldRemove = { c: Constraint -> c.position === position || for (addedTypeVariable in typeVariablesTransaction) {
(c.position is IncorporationConstraintPosition && c.position.from === position) } 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) { for (variableWithConstraint in storage.notFixedTypeVariables.values) {
variableWithConstraint.removeLastConstraints(shouldRemove) variableWithConstraint.removeLastConstraints(shouldRemove)
} }
storage.errors.clear()
storage.initialConstraints.removeAt(storage.initialConstraints.lastIndex)
addedInitialConstraints.clear() // remove constraint from storage.initialConstraints
closeTransaction(beforeState)
return false return false
} }
override fun addLambdaArgument(resolvedLambdaArgument: ResolvedLambdaArgument) {
checkState(State.BUILDING, State.COMPLETION)
storage.lambdaArguments.add(resolvedLambdaArgument)
}
private fun getVariablesForFixation(): Map<NewTypeVariable, UnwrappedType> { private fun getVariablesForFixation(): Map<NewTypeVariable, UnwrappedType> {
val fixedVariables = LinkedHashMap<NewTypeVariable, UnwrappedType>() val fixedVariables = LinkedHashMap<NewTypeVariable, UnwrappedType>()
@@ -140,7 +169,7 @@ class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val re
// ConstraintSystemBuilder, KotlinCallCompleter.Context // ConstraintSystemBuilder, KotlinCallCompleter.Context
override val hasContradiction: Boolean 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) { override fun addInnerCall(innerCall: ResolvedKotlinCall.OnlyResolvedKotlinCall) {
checkState(State.BUILDING, State.COMPLETION) checkState(State.BUILDING, State.COMPLETION)
@@ -162,7 +191,7 @@ class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val re
// ResultTypeResolver.Context, ConstraintSystemBuilder // ResultTypeResolver.Context, ConstraintSystemBuilder
override fun isProperType(type: UnwrappedType): Boolean { override fun isProperType(type: UnwrappedType): Boolean {
checkState(State.BUILDING, State.COMPLETION) checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
return !type.contains { return !type.contains {
storage.allTypeVariables.containsKey(it.constructor) storage.allTypeVariables.containsKey(it.constructor)
} }
@@ -170,31 +199,31 @@ class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val re
// ConstraintInjector.Context // ConstraintInjector.Context
override val allTypeVariables: Map<TypeConstructor, NewTypeVariable> get() { override val allTypeVariables: Map<TypeConstructor, NewTypeVariable> get() {
checkState(State.BUILDING, State.COMPLETION) checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
return storage.allTypeVariables return storage.allTypeVariables
} }
override var maxTypeDepthFromInitialConstraints: Int override var maxTypeDepthFromInitialConstraints: Int
get() = storage.maxTypeDepthFromInitialConstraints get() = storage.maxTypeDepthFromInitialConstraints
set(value) { set(value) {
checkState(State.BUILDING, State.COMPLETION) checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
storage.maxTypeDepthFromInitialConstraints = value storage.maxTypeDepthFromInitialConstraints = value
} }
override fun addInitialConstraint(initialConstraint: InitialConstraint) { override fun addInitialConstraint(initialConstraint: InitialConstraint) {
checkState(State.BUILDING, State.COMPLETION) checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
storage.initialConstraints.add(initialConstraint) storage.initialConstraints.add(initialConstraint)
} }
// ConstraintInjector.Context, FixationOrderCalculator.Context // ConstraintInjector.Context, FixationOrderCalculator.Context
override val notFixedTypeVariables: MutableMap<TypeConstructor, MutableVariableWithConstraints> get() { override val notFixedTypeVariables: MutableMap<TypeConstructor, MutableVariableWithConstraints> get() {
checkState(State.BUILDING, State.COMPLETION) checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
return storage.notFixedTypeVariables return storage.notFixedTypeVariables
} }
// ConstraintInjector.Context, KotlinCallCompleter.Context // ConstraintInjector.Context, KotlinCallCompleter.Context
override fun addError(error: KotlinCallDiagnostic) { override fun addError(error: KotlinCallDiagnostic) {
checkState(State.BUILDING, State.COMPLETION) checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
storage.errors.add(error) storage.errors.add(error)
} }