From 652676dc71c41c0045defa69ed31633f640730de Mon Sep 17 00:00:00 2001 From: Stanislav Erokhin Date: Thu, 4 May 2017 12:23:00 +0300 Subject: [PATCH] [NI] Introduce transactions for constraint system. This will be used for callable reference resolution. --- .../CheckArgumentsResolutionPart.kt | 1 + .../inference/ConstraintSystemBuilder.kt | 19 +++-- .../resolve/calls/inference/InferenceUtils.kt | 2 + .../model/MutableConstraintStorage.kt | 31 +++---- .../model/NewConstraintSystemImpl.kt | 81 +++++++++++++------ 5 files changed, 82 insertions(+), 52 deletions(-) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CheckArgumentsResolutionPart.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CheckArgumentsResolutionPart.kt index cf517fe2b48..93295ff153b 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CheckArgumentsResolutionPart.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CheckArgumentsResolutionPart.kt @@ -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.* diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilder.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilder.kt index 57a32f4e518..63c44258e8c 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilder.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilder.kt @@ -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 + } \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/InferenceUtils.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/InferenceUtils.kt index 911450ddf8d..35d84b708f4 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/InferenceUtils.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/InferenceUtils.kt @@ -57,3 +57,5 @@ fun CallableDescriptor.substituteAndApproximateCapturedTypes(substitutor: NewTyp return substitute(TypeSubstitutor.create(wrappedSubstitution)) } + +fun MutableList.trimToSize(newSize: Int) = subList(newSize, size).clear() \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt index a7aa0e7d9d4..fb22b62a816 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt @@ -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 = emptyList() ) : VariableWithConstraints { override val constraints: List 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(c: Collection): ArrayList(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 = LinkedHashMap() override val notFixedTypeVariables: MutableMap = LinkedHashMap() override val initialConstraints: MutableList = ArrayList() diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt index 9da40d7cef3..1591097c233 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt @@ -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 = 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 { val fixedVariables = LinkedHashMap() @@ -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 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 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) }