Move mutating methods of ConstraintSystem to Builder

Extract immutable ConstraintSystemSnapshot from ConstraintSystemImpl
This commit is contained in:
Alexander Udalov
2015-10-29 19:42:19 +03:00
parent d320922e08
commit bb918a2742
11 changed files with 323 additions and 238 deletions
@@ -352,7 +352,7 @@ public class DelegatedPropertyResolver {
return new ConstraintSystemCompleter() {
@Override
public void completeConstraintSystem(
@NotNull ConstraintSystem constraintSystem, @NotNull ResolvedCall<?> resolvedCall
@NotNull ConstraintSystem.Builder constraintSystem, @NotNull ResolvedCall<?> resolvedCall
) {
KotlinType returnType = resolvedCall.getCandidateDescriptor().getReturnType();
if (returnType == null) return;
@@ -407,7 +407,7 @@ public class DelegatedPropertyResolver {
results.getResultCode() == OverloadResolutionResults.Code.SINGLE_CANDIDATE_ARGUMENT_MISMATCH);
}
private void addConstraintForThisValue(ConstraintSystem constraintSystem, FunctionDescriptor resultingDescriptor) {
private void addConstraintForThisValue(ConstraintSystem.Builder constraintSystem, FunctionDescriptor resultingDescriptor) {
ReceiverParameterDescriptor extensionReceiver = propertyDescriptor.getExtensionReceiverParameter();
ReceiverParameterDescriptor dispatchReceiver = propertyDescriptor.getDispatchReceiverParameter();
KotlinType typeOfThis =
@@ -139,16 +139,19 @@ public class CallCompleter(
expectedType: KotlinType,
trace: BindingTrace
) {
fun updateSystemIfSuccessful(update: (ConstraintSystem) -> Boolean) {
val copy = constraintSystem!!.copy()
if (update(copy)) {
setConstraintSystem(copy)
fun updateSystemIfNeeded(buildSystemWithAdditionalConstraints: (ConstraintSystem.Builder) -> ConstraintSystem?) {
val system = buildSystemWithAdditionalConstraints(constraintSystem!!.toBuilder())
if (system != null) {
setConstraintSystem(system)
}
}
val returnType = getCandidateDescriptor().getReturnType()
if (returnType != null) {
constraintSystem!!.addSupertypeConstraint(expectedType, returnType, EXPECTED_TYPE_POSITION.position())
updateSystemIfNeeded { builder ->
builder.addSupertypeConstraint(expectedType, returnType, EXPECTED_TYPE_POSITION.position())
builder.build()
}
}
val constraintSystemCompleter = trace[CONSTRAINT_SYSTEM_COMPLETER, getCall().getCalleeExpression()]
@@ -156,24 +159,29 @@ public class CallCompleter(
// todo improve error reporting with errors in constraints from completer
// todo add constraints from completer unconditionally; improve constraints from completer for generic methods
// add the constraints only if they don't lead to errors (except errors from upper bounds to improve diagnostics)
updateSystemIfSuccessful {
system ->
constraintSystemCompleter.completeConstraintSystem(system, this)
!system.filterConstraintsOut(TYPE_BOUND_POSITION).getStatus().hasOnlyErrorsDerivedFrom(FROM_COMPLETER)
updateSystemIfNeeded { builder ->
constraintSystemCompleter.completeConstraintSystem(builder, this)
val system = builder.build()
val status = system.filterConstraintsOut(TYPE_BOUND_POSITION).getStatus()
if (status.hasOnlyErrorsDerivedFrom(FROM_COMPLETER)) null else system
}
}
if (returnType != null && expectedType === TypeUtils.UNIT_EXPECTED_TYPE) {
updateSystemIfSuccessful {
system ->
system.addSupertypeConstraint(builtIns.getUnitType(), returnType, EXPECTED_TYPE_POSITION.position())
system.getStatus().isSuccessful()
updateSystemIfNeeded { builder ->
builder.addSupertypeConstraint(builtIns.getUnitType(), returnType, EXPECTED_TYPE_POSITION.position())
val system = builder.build()
if (system.getStatus().isSuccessful()) system else null
}
}
constraintSystem!!.fixVariables()
setResultingSubstitutor(constraintSystem!!.getResultingSubstitutor())
val builder = constraintSystem!!.toBuilder()
builder.fixVariables()
val system = builder.build()
setConstraintSystem(system)
setResultingSubstitutor(system.getResultingSubstitutor())
}
private fun <D : CallableDescriptor> MutableResolvedCall<D>.updateResolutionStatusFromConstraintSystem(
@@ -70,7 +70,7 @@ private fun getReturnTypeForCallable(type: KotlinType) =
private fun CallableDescriptor.hasReturnTypeDependentOnUninferredParams(constraintSystem: ConstraintSystem): Boolean {
val returnType = returnType ?: return false
val nestedTypeVariables = constraintSystem.getNestedTypeVariables(returnType, original = true)
val nestedTypeVariables = constraintSystem.getNestedTypeVariables(returnType)
return nestedTypeVariables.any { constraintSystem.getTypeBounds(it).value == null }
}
@@ -53,8 +53,7 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes
val candidateCall = context.candidateCall
val candidate = candidateCall.candidateDescriptor
val constraintSystem = ConstraintSystemImpl()
candidateCall.setConstraintSystem(constraintSystem)
val builder = ConstraintSystemImpl()
// If the call is recursive, e.g.
// fun foo<T>(t : T) : T = foo(t)
@@ -65,7 +64,7 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes
val candidateWithFreshVariables = FunctionDescriptorUtil.alphaConvertTypeParameters(candidate)
val conversionToOriginal = candidateWithFreshVariables.typeParameters.zip(candidate.typeParameters).toMap()
constraintSystem.registerTypeVariables(candidateWithFreshVariables.typeParameters, { conversionToOriginal[it]!! })
builder.registerTypeVariables(candidateWithFreshVariables.typeParameters, { conversionToOriginal[it]!! })
val substituteDontCare = makeConstantSubstitutor(candidate.typeParameters, DONT_CARE)
@@ -79,8 +78,9 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes
// Here we type check expecting an error type (DONT_CARE, substitution with substituteDontCare)
// and throw the results away
// We'll type check the arguments later, with the inferred types expected
addConstraintForValueArgument(valueArgument, valueParameterDescriptor, substituteDontCare,
constraintSystem, context, SHAPE_FUNCTION_ARGUMENTS)
addConstraintForValueArgument(
valueArgument, valueParameterDescriptor, substituteDontCare, builder, context, SHAPE_FUNCTION_ARGUMENTS
)
}
}
@@ -97,9 +97,12 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes
if (receiverArgument is ExpressionReceiver) {
receiverType = updateResultTypeForSmartCasts(receiverType, receiverArgument.expression, context)
}
constraintSystem.addSubtypeConstraint(receiverType, receiverParameter.type, RECEIVER_POSITION.position())
builder.addSubtypeConstraint(receiverType, receiverParameter.type, RECEIVER_POSITION.position())
}
val constraintSystem = builder.build()
candidateCall.setConstraintSystem(constraintSystem)
// Solution
val hasContradiction = constraintSystem.getStatus().hasContradiction()
if (!hasContradiction) {
@@ -112,7 +115,7 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes
valueArgument: ValueArgument,
valueParameterDescriptor: ValueParameterDescriptor,
substitutor: TypeSubstitutor,
constraintSystem: ConstraintSystem,
builder: ConstraintSystem.Builder,
context: CallCandidateResolutionContext<*>,
resolveFunctionArgumentBodies: ResolveArgumentsMode
) {
@@ -129,16 +132,16 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes
val constraintPosition = VALUE_PARAMETER_POSITION.position(valueParameterDescriptor.index)
if (addConstraintForNestedCall(argumentExpression, constraintPosition, constraintSystem, newContext, effectiveExpectedType)) return
if (addConstraintForNestedCall(argumentExpression, constraintPosition, builder, newContext, effectiveExpectedType)) return
val type = updateResultTypeForSmartCasts(typeInfoForCall.type, argumentExpression, context.replaceDataFlowInfo(dataFlowInfoForArgument))
constraintSystem.addSubtypeConstraint(type, effectiveExpectedType, constraintPosition)
builder.addSubtypeConstraint(type, effectiveExpectedType, constraintPosition)
}
private fun addConstraintForNestedCall(
argumentExpression: KtExpression?,
constraintPosition: ConstraintPosition,
constraintSystem: ConstraintSystem,
builder: ConstraintSystem.Builder,
context: CallCandidateResolutionContext<*>,
effectiveExpectedType: KotlinType
): Boolean {
@@ -153,7 +156,7 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes
val candidateDescriptor = resultingCall.candidateDescriptor
val returnType = candidateDescriptor.returnType ?: return false
val nestedTypeVariables = argumentConstraintSystem.getNestedTypeVariables(returnType, original = true)
val nestedTypeVariables = argumentConstraintSystem.getNestedTypeVariables(returnType)
// we add an additional type variable only if no information is inferred for it.
// otherwise we add currently inferred return type as before
@@ -163,9 +166,9 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes
val conversion = candidateDescriptor.typeParameters.zip(candidateWithFreshVariables.typeParameters).toMap()
val freshVariables = nestedTypeVariables.map { conversion[it] }.filterNotNull()
constraintSystem.registerTypeVariables(freshVariables, external = true)
builder.registerTypeVariables(freshVariables, external = true)
constraintSystem.addSubtypeConstraint(candidateWithFreshVariables.returnType, effectiveExpectedType, constraintPosition)
builder.addSubtypeConstraint(candidateWithFreshVariables.returnType, effectiveExpectedType, constraintPosition)
return true
}
@@ -188,7 +191,7 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes
fun <D : CallableDescriptor> completeTypeInferenceDependentOnFunctionArgumentsForCall(context: CallCandidateResolutionContext<D>) {
val resolvedCall = context.candidateCall
val constraintSystem = resolvedCall.constraintSystem ?: return
val constraintSystem = resolvedCall.constraintSystem?.toBuilder() ?: return
// constraints for function literals
// Value parameters
@@ -204,20 +207,22 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes
}
}
}
resolvedCall.setResultingSubstitutor(constraintSystem.getResultingSubstitutor())
val resultingSystem = constraintSystem.build()
resolvedCall.setConstraintSystem(resultingSystem)
resolvedCall.setResultingSubstitutor(resultingSystem.getResultingSubstitutor())
}
private fun <D : CallableDescriptor> addConstraintForFunctionLiteral(
functionLiteral: KtFunction,
valueArgument: ValueArgument,
valueParameterDescriptor: ValueParameterDescriptor,
constraintSystem: ConstraintSystem,
constraintSystem: ConstraintSystem.Builder,
context: CallCandidateResolutionContext<D>
) {
val argumentExpression = valueArgument.getArgumentExpression() ?: return
val effectiveExpectedType = getEffectiveExpectedType(valueParameterDescriptor, valueArgument)
var expectedType = constraintSystem.getCurrentSubstitutor().substitute(effectiveExpectedType, Variance.INVARIANT)
var expectedType = constraintSystem.build().getCurrentSubstitutor().substitute(effectiveExpectedType, Variance.INVARIANT)
if (expectedType == null || TypeUtils.isDontCarePlaceholder(expectedType)) {
expectedType = argumentTypeResolver.getShapeTypeOfFunctionLiteral(functionLiteral, context.scope, context.trace, false)
}
@@ -261,7 +266,7 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes
callableReference: KtCallableReferenceExpression,
valueArgument: ValueArgument,
valueParameterDescriptor: ValueParameterDescriptor,
constraintSystem: ConstraintSystem,
constraintSystem: ConstraintSystem.Builder,
context: CallCandidateResolutionContext<D>
) {
val effectiveExpectedType = getEffectiveExpectedType(valueParameterDescriptor, valueArgument)
@@ -275,11 +280,11 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes
private fun <D : CallableDescriptor> getExpectedTypeForCallableReference(
callableReference: KtCallableReferenceExpression,
constraintSystem: ConstraintSystem,
constraintSystem: ConstraintSystem.Builder,
context: CallCandidateResolutionContext<D>,
effectiveExpectedType: KotlinType
): KotlinType? {
val substitutedType = constraintSystem.getCurrentSubstitutor().substitute(effectiveExpectedType, Variance.INVARIANT)
val substitutedType = constraintSystem.build().getCurrentSubstitutor().substitute(effectiveExpectedType, Variance.INVARIANT)
if (substitutedType != null && !TypeUtils.isDontCarePlaceholder(substitutedType))
return substitutedType
@@ -23,47 +23,19 @@ import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.derivedFr
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeSubstitutor
public interface ConstraintSystem {
/**
* Registers variables in a constraint system.
* The type variables for the corresponding function are local, the type variables of inner arguments calls are non-local.
*/
public fun registerTypeVariables(
typeVariables: Collection<TypeParameterDescriptor>,
mapToOriginal: (TypeParameterDescriptor) -> TypeParameterDescriptor = { it },
external: Boolean = false
)
interface ConstraintSystem {
/**
* Returns a set of all non-external registered type variables.
*/
public fun getTypeVariables(): Set<TypeParameterDescriptor>
fun getTypeVariables(): Set<TypeParameterDescriptor>
/**
* Adds a constraint that the constraining type is a subtype of the subject type.
* Asserts that only subject type may contain registered type variables.
*
* For example, for `fun <T> id(t: T) {}` to infer `T` in invocation `id(1)`
* the constraint "Int is a subtype of T" should be generated where T is a subject type, and Int is a constraining type.
*/
public fun addSubtypeConstraint(constrainingType: KotlinType?, subjectType: KotlinType, constraintPosition: ConstraintPosition)
/**
* Adds a constraint that the constraining type is a supertype of the subject type.
* Asserts that only subject type may contain registered type variables.
*
* For example, for `fun <T> create(): T` to infer `T` in invocation `val i: Int = create()`
* the constraint "Int is a supertype of T" should be generated where T is a subject type, and Int is a constraining type.
*/
public fun addSupertypeConstraint(constrainingType: KotlinType?, subjectType: KotlinType, constraintPosition: ConstraintPosition)
public fun getStatus(): ConstraintSystemStatus
fun getStatus(): ConstraintSystemStatus
/**
* Returns the resulting type constraints of solving the constraint system for specific type variable.
* Throws IllegalArgumentException if the type variable was not registered.
*/
public fun getTypeBounds(typeVariable: TypeParameterDescriptor): TypeBounds
fun getTypeBounds(typeVariable: TypeParameterDescriptor): TypeBounds
/**
* Returns the result of solving the constraint system (mapping from the type variable to the resulting type projection).
@@ -74,26 +46,58 @@ public interface ConstraintSystem {
* If the addition of the 'expected type' constraint made the system fail,
* this constraint is not included in the resulting substitution.
*/
public fun getResultingSubstitutor(): TypeSubstitutor
fun getResultingSubstitutor(): TypeSubstitutor
/**
* Returns the current result of solving the constraint system (mapping from the type variable to the resulting type projection).
* If there is no information for type parameter, returns type projection for DONT_CARE type.
*/
public fun getCurrentSubstitutor(): TypeSubstitutor
fun getCurrentSubstitutor(): TypeSubstitutor
/**
* Returns the substitution only for type parameters that have result values, otherwise returns the type parameter itself.
*/
public fun getPartialSubstitutor(): TypeSubstitutor
fun getPartialSubstitutor(): TypeSubstitutor
public fun copy(filterConstraintPosition: (ConstraintPosition) -> Boolean = { true }): ConstraintSystem
fun getNestedTypeVariables(type: KotlinType): List<TypeParameterDescriptor>
public fun fixVariables()
fun toBuilder(filterConstraintPosition: (ConstraintPosition) -> Boolean = { true }): Builder
public fun getNestedTypeVariables(type: KotlinType, original: Boolean): List<TypeParameterDescriptor>
interface Builder {
/**
* Registers variables in a constraint system.
* The type variables for the corresponding function are local, the type variables of inner arguments calls are non-local.
*/
fun registerTypeVariables(
typeVariables: Collection<TypeParameterDescriptor>,
mapToOriginal: (TypeParameterDescriptor) -> TypeParameterDescriptor = { it },
external: Boolean = false
)
/**
* Adds a constraint that the constraining type is a subtype of the subject type.
* Asserts that only subject type may contain registered type variables.
*
* For example, for `fun <T> id(t: T) {}` to infer `T` in invocation `id(1)`
* the constraint "Int is a subtype of T" should be generated where T is a subject type, and Int is a constraining type.
*/
fun addSubtypeConstraint(constrainingType: KotlinType?, subjectType: KotlinType, constraintPosition: ConstraintPosition)
/**
* Adds a constraint that the constraining type is a supertype of the subject type.
* Asserts that only subject type may contain registered type variables.
*
* For example, for `fun <T> create(): T` to infer `T` in invocation `val i: Int = create()`
* the constraint "Int is a supertype of T" should be generated where T is a subject type, and Int is a constraining type.
*/
fun addSupertypeConstraint(constrainingType: KotlinType?, subjectType: KotlinType, constraintPosition: ConstraintPosition)
fun fixVariables()
fun build(): ConstraintSystem
}
}
fun ConstraintSystem.filterConstraintsOut(excludePositionKind: ConstraintPositionKind): ConstraintSystem {
return copy { !it.derivedFrom(excludePositionKind) }
return toBuilder { !it.derivedFrom(excludePositionKind) }.build()
}
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
public interface ConstraintSystemCompleter {
void completeConstraintSystem(
@NotNull ConstraintSystem constraintSystem,
@NotNull ConstraintSystem.Builder constraintSystem,
@NotNull ResolvedCall<?> resolvedCall
);
}
@@ -27,21 +27,21 @@ import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.*
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.TYPE_BOUND_POSITION
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.derivedFrom
import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.resolve.descriptorUtil.hasExactAnnotation
import org.jetbrains.kotlin.resolve.descriptorUtil.hasInternalAnnotationForResolve
import org.jetbrains.kotlin.resolve.descriptorUtil.hasNoInferAnnotation
import org.jetbrains.kotlin.resolve.descriptorUtil.isInternalAnnotationForResolve
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.TypeUtils.DONT_CARE
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure
import org.jetbrains.kotlin.types.checker.TypeCheckingProcedureCallbacks
import org.jetbrains.kotlin.types.typeUtil.builtIns
import org.jetbrains.kotlin.types.typeUtil.getNestedArguments
import org.jetbrains.kotlin.types.typeUtil.isDefaultBound
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import java.util.*
public class ConstraintSystemImpl : ConstraintSystem {
public class ConstraintSystemImpl : ConstraintSystem.Builder {
data class Constraint(val kind: ConstraintKind, val subtype: KotlinType, val superType: KotlinType, val position: ConstraintPosition)
@@ -52,94 +52,18 @@ public class ConstraintSystemImpl : ConstraintSystem {
fun ConstraintKind.toBound() = if (this == SUB_TYPE) UPPER_BOUND else EXACT_BOUND
private val allTypeParameterBounds = LinkedHashMap<TypeParameterDescriptor, TypeBoundsImpl>()
private val externalTypeParameters = HashSet<TypeParameterDescriptor>()
private val localTypeParameterBounds: Map<TypeParameterDescriptor, TypeBoundsImpl>
get() = if (externalTypeParameters.isEmpty()) allTypeParameterBounds
else allTypeParameterBounds.filter { !externalTypeParameters.contains(it.key) }
private val cachedTypeForVariable = HashMap<TypeParameterDescriptor, KotlinType>()
private val usedInBounds = HashMap<TypeParameterDescriptor, MutableList<TypeBounds.Bound>>()
private val errors = ArrayList<ConstraintError>()
private val initialConstraints = ArrayList<Constraint>()
internal val allTypeParameterBounds = LinkedHashMap<TypeParameterDescriptor, TypeBoundsImpl>()
internal val externalTypeParameters = HashSet<TypeParameterDescriptor>()
internal val cachedTypeForVariable = HashMap<TypeParameterDescriptor, KotlinType>()
internal val usedInBounds = HashMap<TypeParameterDescriptor, MutableList<TypeBounds.Bound>>()
internal val errors = ArrayList<ConstraintError>()
internal val initialConstraints = ArrayList<Constraint>()
internal val originalToVariables = LinkedHashMap<TypeParameterDescriptor, TypeParameterDescriptor>()
internal val variablesToOriginal = LinkedHashMap<TypeParameterDescriptor, TypeParameterDescriptor>()
private val originalToVariablesSubstitutor: TypeSubstitutor by lazy {
createTypeSubstitutor { originalToVariables[it] }
}
private val originalToVariables = LinkedHashMap<TypeParameterDescriptor, TypeParameterDescriptor>()
private val variablesToOriginal = LinkedHashMap<TypeParameterDescriptor, TypeParameterDescriptor>()
private val constraintSystemStatus = object : ConstraintSystemStatus {
// for debug ConstraintsUtil.getDebugMessageForStatus might be used
override fun isSuccessful() = !hasContradiction() && !hasUnknownParameters()
override fun hasContradiction() = hasParameterConstraintError() || hasConflictingConstraints()
|| hasCannotCaptureTypesError() || hasTypeInferenceIncorporationError()
override fun hasViolatedUpperBound() = !isSuccessful() && filterConstraintsOut(TYPE_BOUND_POSITION).getStatus().isSuccessful()
override fun hasConflictingConstraints() = localTypeParameterBounds.values().any { it.values.size() > 1 }
override fun hasUnknownParameters() =
localTypeParameterBounds.values().any { it.values.isEmpty() } || hasTypeParameterWithUnsatisfiedOnlyInputTypesError()
override fun hasParameterConstraintError() = errors.any { it is ParameterConstraintError }
override fun hasOnlyErrorsDerivedFrom(kind: ConstraintPositionKind): Boolean {
if (isSuccessful()) return false
if (filterConstraintsOut(kind).getStatus().isSuccessful()) return true
return errors.isNotEmpty() && errors.all { it.constraintPosition.derivedFrom(kind) }
}
override fun hasErrorInConstrainingTypes() = errors.any { it is ErrorInConstrainingType }
override fun hasCannotCaptureTypesError() = errors.any { it is CannotCapture }
override fun hasTypeInferenceIncorporationError() = errors.any { it is TypeInferenceError } || !satisfyInitialConstraints()
override fun hasTypeParameterWithUnsatisfiedOnlyInputTypesError() =
localTypeParameterBounds.values.any { it.typeVariable.hasOnlyInputTypesAnnotation() && it.value == null }
override val constraintErrors: List<ConstraintError>
get() = errors
}
private fun getParameterToInferredValueMap(
typeParameterBounds: Map<TypeParameterDescriptor, TypeBoundsImpl>,
getDefaultTypeProjection: (TypeParameterDescriptor) -> TypeProjection,
substituteOriginal: Boolean
): Map<TypeParameterDescriptor, TypeProjection> {
val substitutionContext = HashMap<TypeParameterDescriptor, TypeProjection>()
for ((variable, typeBounds) in typeParameterBounds) {
val typeProjection: TypeProjection
val value = typeBounds.value
val typeParameter = if (substituteOriginal) variablesToOriginal[variable]!! else variable
if (value != null && !TypeUtils.containsSpecialType(value, DONT_CARE)) {
typeProjection = TypeProjectionImpl(value)
}
else {
typeProjection = getDefaultTypeProjection(typeParameter)
}
substitutionContext.put(typeParameter, typeProjection)
}
return substitutionContext
}
private fun replaceUninferredBy(
getDefaultValue: (TypeParameterDescriptor) -> TypeProjection,
substituteOriginal: Boolean
): TypeSubstitutor {
val parameterToInferredValueMap = getParameterToInferredValueMap(allTypeParameterBounds, getDefaultValue, substituteOriginal)
val substitution = TypeConstructorSubstitution.createByParametersMap(parameterToInferredValueMap)
return SubstitutionFilteringInternalResolveAnnotations(substitution).buildSubstitutor()
}
override fun getStatus(): ConstraintSystemStatus = constraintSystemStatus
override fun registerTypeVariables(
typeVariables: Collection<TypeParameterDescriptor>,
@@ -172,30 +96,12 @@ public class ConstraintSystemImpl : ConstraintSystem {
type -> type.getConstructor().getDeclarationDescriptor() in getAllTypeVariables()
}
override fun getNestedTypeVariables(type: KotlinType, original: Boolean): List<TypeParameterDescriptor> {
fun getNestedTypeVariables(type: KotlinType, original: Boolean): List<TypeParameterDescriptor> {
return type.getNestedArguments().map { typeProjection ->
typeProjection.type.constructor.declarationDescriptor as? TypeParameterDescriptor
}.filterNotNull().filter { if (original) it in originalToVariables.keys else it in getAllTypeVariables() }
}
override fun copy(filterConstraintPosition: (ConstraintPosition) -> Boolean): ConstraintSystem {
val newSystem = ConstraintSystemImpl()
for ((typeParameter, typeBounds) in allTypeParameterBounds) {
newSystem.allTypeParameterBounds.put(typeParameter, typeBounds.filter(filterConstraintPosition))
}
newSystem.usedInBounds.putAll(usedInBounds.map {
val (variable, bounds) = it
variable to bounds.filterTo(arrayListOf<Bound>()) { filterConstraintPosition(it.position )}
}.toMap())
newSystem.externalTypeParameters.addAll(externalTypeParameters )
newSystem.errors.addAll(errors.filter { filterConstraintPosition(it.constraintPosition) })
newSystem.initialConstraints.addAll(initialConstraints.filter { filterConstraintPosition(it.position) })
newSystem.originalToVariables.putAll(originalToVariables)
newSystem.variablesToOriginal.putAll(variablesToOriginal)
return newSystem
}
override fun addSupertypeConstraint(constrainingType: KotlinType?, subjectType: KotlinType, constraintPosition: ConstraintPosition) {
if (constrainingType != null && TypeUtils.noExpectedType(constrainingType)) return
@@ -423,13 +329,11 @@ public class ConstraintSystemImpl : ConstraintSystem {
addBound(typeVariable, capturedType, EXACT_BOUND, constraintContext)
}
override fun getTypeVariables() = originalToVariables.keySet()
fun getAllTypeVariables() = allTypeParameterBounds.keySet()
fun getBoundsUsedIn(typeVariable: TypeParameterDescriptor): List<Bound> = usedInBounds[typeVariable] ?: emptyList()
override fun getTypeBounds(typeVariable: TypeParameterDescriptor): TypeBoundsImpl {
fun getTypeBounds(typeVariable: TypeParameterDescriptor): TypeBoundsImpl {
val variableForOriginal = originalToVariables[typeVariable]
if (variableForOriginal != null && variableForOriginal != typeVariable) {
return getTypeBounds(variableForOriginal)
@@ -449,42 +353,10 @@ public class ConstraintSystemImpl : ConstraintSystem {
return if (typeParameterDescriptor != null && isMyTypeVariable(typeParameterDescriptor)) typeParameterDescriptor else null
}
override fun getResultingSubstitutor() =
getSubstitutor(substituteOriginal = true) { TypeProjectionImpl(ErrorUtils.createUninferredParameterType(it)) }
override fun getCurrentSubstitutor() =
getSubstitutor(substituteOriginal = true) { TypeProjectionImpl(TypeUtils.DONT_CARE) }
override fun getPartialSubstitutor() =
getSubstitutor(substituteOriginal = true) { TypeProjectionImpl(it.correspondingType) }
private fun getSubstitutor(substituteOriginal: Boolean, getDefaultValue: (TypeParameterDescriptor) -> TypeProjection) =
replaceUninferredBy(getDefaultValue, substituteOriginal).setApproximateCapturedTypes()
private fun storeInitialConstraint(constraintKind: ConstraintKind, subType: KotlinType, superType: KotlinType, position: ConstraintPosition) {
initialConstraints.add(Constraint(constraintKind, subType, superType, position))
}
private fun satisfyInitialConstraints(): Boolean {
fun KotlinType.substitute(): KotlinType? {
val substitutor = getSubstitutor(substituteOriginal = false) { TypeProjectionImpl(ErrorUtils.createUninferredParameterType(it)) }
return substitutor.substitute(this, Variance.INVARIANT) ?: return null
}
return initialConstraints.all {
constraint ->
val resultSubType = constraint.subtype.substitute()?.let {
// the call might be done via safe access, so we check for notNullable receiver type;
// 'unsafe call' error is reported otherwise later
if (constraint.position.kind != ConstraintPositionKind.RECEIVER_POSITION) it else it.makeNotNullable()
} ?: return false
val resultSuperType = constraint.superType.substitute() ?: return false
when (constraint.kind) {
SUB_TYPE -> KotlinTypeChecker.DEFAULT.isSubtypeOf(resultSubType, resultSuperType)
EQUAL -> KotlinTypeChecker.DEFAULT.equalTypes(resultSubType, resultSuperType)
}
}
}
fun fixVariable(typeVariable: TypeParameterDescriptor) {
val typeBounds = getTypeBounds(typeVariable)
if (typeBounds.isFixed) return
@@ -505,6 +377,10 @@ public class ConstraintSystemImpl : ConstraintSystem {
functionTypeParameters.forEach { fixVariable(it) }
}
override fun build(): ConstraintSystem {
return ConstraintSystemSnapshot(allTypeParameterBounds, externalTypeParameters, usedInBounds, errors, initialConstraints,
originalToVariables, variablesToOriginal)
}
}
fun createTypeForFunctionPlaceholder(
@@ -532,7 +408,7 @@ fun createTypeForFunctionPlaceholder(
return functionPlaceholder.builtIns.getFunctionType(Annotations.EMPTY, receiverType, newArgumentTypes, DONT_CARE)
}
private fun TypeSubstitutor.setApproximateCapturedTypes(): TypeSubstitutor {
internal fun TypeSubstitutor.setApproximateCapturedTypes(): TypeSubstitutor {
return TypeSubstitutor.create(SubstitutionWithCapturedTypeApproximation(getSubstitution()))
}
@@ -0,0 +1,186 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.resolve.calls.inference
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.EQUAL
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.SUB_TYPE
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.TYPE_BOUND_POSITION
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.derivedFrom
import org.jetbrains.kotlin.resolve.descriptorUtil.hasOnlyInputTypesAnnotation
import org.jetbrains.kotlin.resolve.scopes.KtScope
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.TypeUtils.DONT_CARE
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.typeUtil.getNestedArguments
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import java.util.*
class ConstraintSystemSnapshot(
private val allTypeParameterBounds: Map<TypeParameterDescriptor, TypeBoundsImpl>,
private val externalTypeParameters: Set<TypeParameterDescriptor>,
private val usedInBounds: Map<TypeParameterDescriptor, MutableList<TypeBounds.Bound>>,
private val errors: List<ConstraintError>,
private val initialConstraints: List<ConstraintSystemImpl.Constraint>,
private val originalToVariables: Map<TypeParameterDescriptor, TypeParameterDescriptor>,
private val variablesToOriginal: Map<TypeParameterDescriptor, TypeParameterDescriptor>
) : ConstraintSystem {
private val localTypeParameterBounds: Map<TypeParameterDescriptor, TypeBoundsImpl>
get() = if (externalTypeParameters.isEmpty()) allTypeParameterBounds
else allTypeParameterBounds.filter { !externalTypeParameters.contains(it.key) }
private val constraintSystemStatus = object : ConstraintSystemStatus {
// for debug ConstraintsUtil.getDebugMessageForStatus might be used
override fun isSuccessful() = !hasContradiction() && !hasUnknownParameters()
override fun hasContradiction() = hasParameterConstraintError() || hasConflictingConstraints()
|| hasCannotCaptureTypesError() || hasTypeInferenceIncorporationError()
override fun hasViolatedUpperBound() = !isSuccessful() && filterConstraintsOut(TYPE_BOUND_POSITION).getStatus().isSuccessful()
override fun hasConflictingConstraints() = localTypeParameterBounds.values.any { it.values.size > 1 }
override fun hasUnknownParameters() =
localTypeParameterBounds.values.any { it.values.isEmpty() } || hasTypeParameterWithUnsatisfiedOnlyInputTypesError()
override fun hasParameterConstraintError() = errors.any { it is ParameterConstraintError }
override fun hasOnlyErrorsDerivedFrom(kind: ConstraintPositionKind): Boolean {
if (isSuccessful()) return false
if (filterConstraintsOut(kind).getStatus().isSuccessful()) return true
return errors.isNotEmpty() && errors.all { it.constraintPosition.derivedFrom(kind) }
}
override fun hasErrorInConstrainingTypes() = errors.any { it is ErrorInConstrainingType }
override fun hasCannotCaptureTypesError() = errors.any { it is CannotCapture }
override fun hasTypeInferenceIncorporationError() = errors.any { it is TypeInferenceError } || !satisfyInitialConstraints()
override fun hasTypeParameterWithUnsatisfiedOnlyInputTypesError() =
localTypeParameterBounds.values.any { it.typeVariable.hasOnlyInputTypesAnnotation() && it.value == null }
override val constraintErrors: List<ConstraintError>
get() = errors
}
private fun getParameterToInferredValueMap(
typeParameterBounds: Map<TypeParameterDescriptor, TypeBoundsImpl>,
getDefaultTypeProjection: (TypeParameterDescriptor) -> TypeProjection,
substituteOriginal: Boolean
): Map<TypeParameterDescriptor, TypeProjection> {
val substitutionContext = HashMap<TypeParameterDescriptor, TypeProjection>()
for ((variable, typeBounds) in typeParameterBounds) {
val typeProjection: TypeProjection
val value = typeBounds.value
val typeParameter = if (substituteOriginal) variablesToOriginal[variable]!! else variable
if (value != null && !TypeUtils.containsSpecialType(value, DONT_CARE)) {
typeProjection = TypeProjectionImpl(value)
}
else {
typeProjection = getDefaultTypeProjection(typeParameter)
}
substitutionContext.put(typeParameter, typeProjection)
}
return substitutionContext
}
private fun replaceUninferredBy(
getDefaultValue: (TypeParameterDescriptor) -> TypeProjection,
substituteOriginal: Boolean
): TypeSubstitutor {
val parameterToInferredValueMap = getParameterToInferredValueMap(allTypeParameterBounds, getDefaultValue, substituteOriginal)
val substitution = TypeConstructorSubstitution.createByParametersMap(parameterToInferredValueMap)
return SubstitutionFilteringInternalResolveAnnotations(substitution).buildSubstitutor()
}
override fun getStatus(): ConstraintSystemStatus = constraintSystemStatus
override fun getNestedTypeVariables(type: KotlinType): List<TypeParameterDescriptor> {
return type.getNestedArguments().map { typeProjection ->
typeProjection.type.constructor.declarationDescriptor as? TypeParameterDescriptor
}.filterNotNull().filter { it in getTypeVariables() }
}
override fun getTypeVariables() = originalToVariables.keys
override fun getTypeBounds(typeVariable: TypeParameterDescriptor): TypeBoundsImpl {
val variableForOriginal = originalToVariables[typeVariable]
if (variableForOriginal != null && variableForOriginal != typeVariable) {
return getTypeBounds(variableForOriginal)
}
return allTypeParameterBounds[typeVariable] ?:
throw IllegalArgumentException("TypeParameterDescriptor is not a type variable for constraint system: $typeVariable")
}
override fun getResultingSubstitutor() =
getSubstitutor(substituteOriginal = true) { TypeProjectionImpl(ErrorUtils.createUninferredParameterType(it)) }
override fun getCurrentSubstitutor() =
getSubstitutor(substituteOriginal = true) { TypeProjectionImpl(TypeUtils.DONT_CARE) }
override fun getPartialSubstitutor() =
getSubstitutor(substituteOriginal = true) {
TypeProjectionImpl(KotlinTypeImpl.create(Annotations.EMPTY, it.typeConstructor, false, listOf(), KtScope.Empty))
}
private fun getSubstitutor(substituteOriginal: Boolean, getDefaultValue: (TypeParameterDescriptor) -> TypeProjection) =
replaceUninferredBy(getDefaultValue, substituteOriginal).setApproximateCapturedTypes()
private fun satisfyInitialConstraints(): Boolean {
fun KotlinType.substitute(): KotlinType? {
val substitutor = getSubstitutor(substituteOriginal = false) { TypeProjectionImpl(ErrorUtils.createUninferredParameterType(it)) }
return substitutor.substitute(this, Variance.INVARIANT) ?: return null
}
return initialConstraints.all {
constraint ->
val resultSubType = constraint.subtype.substitute()?.let {
// the call might be done via safe access, so we check for notNullable receiver type;
// 'unsafe call' error is reported otherwise later
if (constraint.position.kind != ConstraintPositionKind.RECEIVER_POSITION) it else it.makeNotNullable()
} ?: return false
val resultSuperType = constraint.superType.substitute() ?: return false
when (constraint.kind) {
SUB_TYPE -> KotlinTypeChecker.DEFAULT.isSubtypeOf(resultSubType, resultSuperType)
EQUAL -> KotlinTypeChecker.DEFAULT.equalTypes(resultSubType, resultSuperType)
}
}
}
override fun toBuilder(filterConstraintPosition: (ConstraintPosition) -> Boolean): ConstraintSystem.Builder {
val result = ConstraintSystemImpl()
for ((typeParameter, typeBounds) in allTypeParameterBounds) {
result.allTypeParameterBounds.put(typeParameter, typeBounds.filter(filterConstraintPosition))
}
result.usedInBounds.putAll(usedInBounds.map {
val (variable, bounds) = it
variable to bounds.filterTo(arrayListOf<TypeBounds.Bound>()) { filterConstraintPosition(it.position )}
}.toMap())
result.externalTypeParameters.addAll(externalTypeParameters )
result.errors.addAll(errors.filter { filterConstraintPosition(it.constraintPosition) })
result.initialConstraints.addAll(initialConstraints.filter { filterConstraintPosition(it.position) })
result.originalToVariables.putAll(originalToVariables)
result.variablesToOriginal.putAll(variablesToOriginal)
return result
}
}
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.descriptors.ClassifierDescriptor;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem;
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl;
import org.jetbrains.kotlin.resolve.scopes.ChainedScope;
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
@@ -184,7 +185,7 @@ public class TypeIntersector {
};
processAllTypeParameters(withParameters, Variance.INVARIANT, processor);
processAllTypeParameters(expected, Variance.INVARIANT, processor);
ConstraintSystemImpl constraintSystem = new ConstraintSystemImpl();
ConstraintSystem.Builder constraintSystem = new ConstraintSystemImpl();
constraintSystem.registerTypeVariables(parameters.keySet(), new Function1<TypeParameterDescriptor, TypeParameterDescriptor>() {
@Override
public TypeParameterDescriptor invoke(TypeParameterDescriptor descriptor) {
@@ -193,7 +194,7 @@ public class TypeIntersector {
}, false);
constraintSystem.addSubtypeConstraint(withParameters, expected, SPECIAL.position());
return constraintSystem.getStatus().isSuccessful();
return constraintSystem.build().getStatus().isSuccessful();
}
private static void processAllTypeParameters(KotlinType type, Variance howThisTypeIsUsed, Function1<TypeParameterUsage, Unit> result) {
@@ -76,12 +76,12 @@ abstract public class AbstractConstraintSystemTest() : KotlinLiteFixture() {
val constraintsFile = File(filePath)
val constraintsFileText = constraintsFile.readLines()
val constraintSystem = ConstraintSystemImpl()
val builder = ConstraintSystemImpl()
val variables = parseVariables(constraintsFileText)
val fixVariables = constraintsFileText.contains("FIX_VARIABLES")
val typeParameterDescriptors = variables.map { testDeclarations.getParameterDescriptor(it) }
constraintSystem.registerTypeVariables(typeParameterDescriptors)
builder.registerTypeVariables(typeParameterDescriptors)
val constraints = parseConstraints(constraintsFileText)
fun KotlinType.assertNotError(): KotlinType {
@@ -93,17 +93,20 @@ abstract public class AbstractConstraintSystemTest() : KotlinLiteFixture() {
val secondType = testDeclarations.getType(constraint.secondType).assertNotError()
val context = ConstraintContext(SPECIAL.position(), initial = true)
when (constraint.kind) {
MyConstraintKind.SUBTYPE -> constraintSystem.addSubtypeConstraint(firstType, secondType, context.position)
MyConstraintKind.SUPERTYPE -> constraintSystem.addSupertypeConstraint(firstType, secondType, context.position)
MyConstraintKind.EQUAL -> constraintSystem.addConstraint(
MyConstraintKind.SUBTYPE -> builder.addSubtypeConstraint(firstType, secondType, context.position)
MyConstraintKind.SUPERTYPE -> builder.addSupertypeConstraint(firstType, secondType, context.position)
MyConstraintKind.EQUAL -> builder.addConstraint(
ConstraintSystemImpl.ConstraintKind.EQUAL, firstType, secondType, context)
}
}
if (fixVariables) constraintSystem.fixVariables()
val resultingStatus = Renderers.RENDER_CONSTRAINT_SYSTEM_SHORT.render(constraintSystem)
if (fixVariables) builder.fixVariables()
val resultingSubstitutor = constraintSystem.getResultingSubstitutor()
val system = builder.build()
val resultingStatus = Renderers.RENDER_CONSTRAINT_SYSTEM_SHORT.render(system)
val resultingSubstitutor = system.getResultingSubstitutor()
val result = typeParameterDescriptors.map {
val parameterType = testDeclarations.getType(it.getName().asString())
val resultType = resultingSubstitutor.substitute(parameterType, Variance.INVARIANT)
@@ -116,16 +116,18 @@ class FuzzyType(
return if (type.checkInheritance(otherType.type)) TypeSubstitutor.EMPTY else null
}
val constraintSystem = ConstraintSystemImpl()
constraintSystem.registerTypeVariables(freeParameters)
constraintSystem.registerTypeVariables(otherType.freeParameters)
val builder = ConstraintSystemImpl()
builder.registerTypeVariables(freeParameters)
builder.registerTypeVariables(otherType.freeParameters)
when (matchKind) {
MatchKind.IS_SUBTYPE -> constraintSystem.addSubtypeConstraint(type, otherType.type, ConstraintPositionKind.RECEIVER_POSITION.position())
MatchKind.IS_SUPERTYPE -> constraintSystem.addSubtypeConstraint(otherType.type, type, ConstraintPositionKind.RECEIVER_POSITION.position())
MatchKind.IS_SUBTYPE -> builder.addSubtypeConstraint(type, otherType.type, ConstraintPositionKind.RECEIVER_POSITION.position())
MatchKind.IS_SUPERTYPE -> builder.addSubtypeConstraint(otherType.type, type, ConstraintPositionKind.RECEIVER_POSITION.position())
}
constraintSystem.fixVariables()
builder.fixVariables()
val constraintSystem = builder.build()
if (constraintSystem.getStatus().hasContradiction()) return null