diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegatedPropertyResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegatedPropertyResolver.java index eaba5464b66..bcdc03b74d6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegatedPropertyResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegatedPropertyResolver.java @@ -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 = diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt index 70d64c74077..b12e8257832 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt @@ -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 MutableResolvedCall.updateResolutionStatusFromConstraintSystem( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolverUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolverUtil.kt index ad6243ecf8a..59ee7d8c629 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolverUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolverUtil.kt @@ -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 } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt index 0559d8b8a31..8f4023b000b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt @@ -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 = 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 completeTypeInferenceDependentOnFunctionArgumentsForCall(context: CallCandidateResolutionContext) { 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 addConstraintForFunctionLiteral( functionLiteral: KtFunction, valueArgument: ValueArgument, valueParameterDescriptor: ValueParameterDescriptor, - constraintSystem: ConstraintSystem, + constraintSystem: ConstraintSystem.Builder, context: CallCandidateResolutionContext ) { 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 ) { val effectiveExpectedType = getEffectiveExpectedType(valueParameterDescriptor, valueArgument) @@ -275,11 +280,11 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes private fun getExpectedTypeForCallableReference( callableReference: KtCallableReferenceExpression, - constraintSystem: ConstraintSystem, + constraintSystem: ConstraintSystem.Builder, context: CallCandidateResolutionContext, 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 diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystem.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystem.kt index 6a32a310586..2c116058b70 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystem.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystem.kt @@ -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, - mapToOriginal: (TypeParameterDescriptor) -> TypeParameterDescriptor = { it }, - external: Boolean = false - ) - +interface ConstraintSystem { /** * Returns a set of all non-external registered type variables. */ - public fun getTypeVariables(): Set + fun getTypeVariables(): Set - /** - * 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 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 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 - public fun fixVariables() + fun toBuilder(filterConstraintPosition: (ConstraintPosition) -> Boolean = { true }): Builder - public fun getNestedTypeVariables(type: KotlinType, original: Boolean): List + 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, + 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 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 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() } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemCompleter.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemCompleter.java index 896c3c6f488..3dda5207c67 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemCompleter.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemCompleter.java @@ -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 ); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemImpl.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemImpl.kt index d78c0a0b960..d806e83451e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemImpl.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemImpl.kt @@ -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() - private val externalTypeParameters = HashSet() - private val localTypeParameterBounds: Map - get() = if (externalTypeParameters.isEmpty()) allTypeParameterBounds - else allTypeParameterBounds.filter { !externalTypeParameters.contains(it.key) } - - private val cachedTypeForVariable = HashMap() - - private val usedInBounds = HashMap>() - - private val errors = ArrayList() - - private val initialConstraints = ArrayList() + internal val allTypeParameterBounds = LinkedHashMap() + internal val externalTypeParameters = HashSet() + internal val cachedTypeForVariable = HashMap() + internal val usedInBounds = HashMap>() + internal val errors = ArrayList() + internal val initialConstraints = ArrayList() + internal val originalToVariables = LinkedHashMap() + internal val variablesToOriginal = LinkedHashMap() private val originalToVariablesSubstitutor: TypeSubstitutor by lazy { createTypeSubstitutor { originalToVariables[it] } } - private val originalToVariables = LinkedHashMap() - private val variablesToOriginal = LinkedHashMap() - - 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 - get() = errors - } - - private fun getParameterToInferredValueMap( - typeParameterBounds: Map, - getDefaultTypeProjection: (TypeParameterDescriptor) -> TypeProjection, - substituteOriginal: Boolean - ): Map { - val substitutionContext = HashMap() - 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, @@ -172,30 +96,12 @@ public class ConstraintSystemImpl : ConstraintSystem { type -> type.getConstructor().getDeclarationDescriptor() in getAllTypeVariables() } - override fun getNestedTypeVariables(type: KotlinType, original: Boolean): List { + fun getNestedTypeVariables(type: KotlinType, original: Boolean): List { 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()) { 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 = 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())) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemSnapshot.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemSnapshot.kt new file mode 100644 index 00000000000..21c33a0200c --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemSnapshot.kt @@ -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, + private val externalTypeParameters: Set, + private val usedInBounds: Map>, + private val errors: List, + private val initialConstraints: List, + private val originalToVariables: Map, + private val variablesToOriginal: Map +) : ConstraintSystem { + private val localTypeParameterBounds: Map + 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 + get() = errors + } + + private fun getParameterToInferredValueMap( + typeParameterBounds: Map, + getDefaultTypeProjection: (TypeParameterDescriptor) -> TypeProjection, + substituteOriginal: Boolean + ): Map { + val substitutionContext = HashMap() + 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 { + 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()) { 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 + } +} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/TypeIntersector.java b/compiler/frontend/src/org/jetbrains/kotlin/types/TypeIntersector.java index 5b515ac1bf7..c8f6735e090 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/TypeIntersector.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/TypeIntersector.java @@ -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() { @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 result) { diff --git a/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt b/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt index 868cb8b3bf4..b0be726d0e9 100644 --- a/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt @@ -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) diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/FuzzyType.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/FuzzyType.kt index 1c3d4d636fc..c379b2e2860 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/FuzzyType.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/FuzzyType.kt @@ -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