diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintError.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintError.kt new file mode 100644 index 00000000000..b9b325279ac --- /dev/null +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintError.kt @@ -0,0 +1,28 @@ +/* + * 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.resolve.calls.inference.constraintPosition.ConstraintPosition +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor + +open class ConstraintError(val constraintPosition: ConstraintPosition) + +class TypeConstructorMismatch(constraintPosition: ConstraintPosition): ConstraintError(constraintPosition) + +class ErrorInConstrainingType(constraintPosition: ConstraintPosition): ConstraintError(constraintPosition) + +class CannotCapture(constraintPosition: ConstraintPosition, val typeVariable: TypeParameterDescriptor): ConstraintError(constraintPosition) diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemImpl.kt index 5dfef831073..3a475315055 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemImpl.kt @@ -31,7 +31,6 @@ import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure import org.jetbrains.kotlin.types.checker.TypeCheckingProcedureCallbacks import org.jetbrains.kotlin.types.TypeConstructor import java.util.LinkedHashMap -import java.util.HashSet import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.* import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.* import java.util.HashMap @@ -52,9 +51,7 @@ public class ConstraintSystemImpl : ConstraintSystem { } private val typeParameterBounds = LinkedHashMap() - private val errorConstraintPositions = HashSet() - private var hasErrorInConstrainingTypes: Boolean = false - private var cannotCaptureTypesError: Boolean = false + private val errors = ArrayList() private val constraintSystemStatus = object : ConstraintSystemStatus { // for debug ConstraintsUtil.getDebugMessageForStatus might be used @@ -63,51 +60,32 @@ public class ConstraintSystemImpl : ConstraintSystem { override fun hasContradiction() = hasTypeConstructorMismatch() || hasConflictingConstraints() || hasCannotCaptureTypesError() - override fun hasViolatedUpperBound(): Boolean { - if (isSuccessful()) return false - return getSystemWithoutWeakConstraints().getStatus().isSuccessful() - } + override fun hasViolatedUpperBound() = !isSuccessful() && getSystemWithoutWeakConstraints().getStatus().isSuccessful() - override fun hasConflictingConstraints(): Boolean { - for (typeBounds in typeParameterBounds.values()) { - if (typeBounds.getValues().size() > 1) return true - } - return false - } + override fun hasConflictingConstraints() = typeParameterBounds.values().any { it.getValues().size() > 1 } - override fun hasUnknownParameters(): Boolean { - for (typeBounds in typeParameterBounds.values()) { - if (typeBounds.isEmpty()) { - return true - } - } - return false - } + override fun hasUnknownParameters() = typeParameterBounds.values().any { it.isEmpty() } - override fun hasTypeConstructorMismatch() = !errorConstraintPositions.isEmpty() + override fun hasTypeConstructorMismatch() = errors.any { it is TypeConstructorMismatch } override fun hasTypeConstructorMismatchAt(constraintPosition: ConstraintPosition) = - errorConstraintPositions.contains(constraintPosition) + errors.any { it is TypeConstructorMismatch && it.constraintPosition == constraintPosition } override fun hasOnlyErrorsFromPosition(constraintPosition: ConstraintPosition): Boolean { if (isSuccessful()) return false - val systemWithoutConstraintsFromPosition = filterConstraintsOut(constraintPosition) - if (systemWithoutConstraintsFromPosition.getStatus().isSuccessful()) { - return true - } - if (errorConstraintPositions.size() == 1 && errorConstraintPositions.contains(constraintPosition)) { - // e.g. if systemWithoutConstraintsFromPosition has unknown type parameters, it's not successful - return true - } - return false + if (filterConstraintsOut(constraintPosition).getStatus().isSuccessful()) return true + return errors.isNotEmpty() && errors.all { it.constraintPosition == constraintPosition } } - override fun hasErrorInConstrainingTypes() = hasErrorInConstrainingTypes + override fun hasErrorInConstrainingTypes() = errors.any { it is ErrorInConstrainingType } - override fun hasCannotCaptureTypesError() = cannotCaptureTypesError + override fun hasCannotCaptureTypesError() = errors.any { it is CannotCapture } } - private fun getParameterToInferredValueMap(typeParameterBounds: Map, getDefaultTypeProjection: Function1): Map { + private fun getParameterToInferredValueMap( + typeParameterBounds: Map, + getDefaultTypeProjection: (TypeParameterDescriptor) -> TypeProjection + ): Map { val substitutionContext = HashMap() for ((typeParameter, typeBounds) in typeParameterBounds) { val typeProjection: TypeProjection @@ -116,7 +94,7 @@ public class ConstraintSystemImpl : ConstraintSystem { typeProjection = TypeProjectionImpl(value) } else { - typeProjection = getDefaultTypeProjection.invoke(typeParameter) + typeProjection = getDefaultTypeProjection(typeParameter) } substitutionContext.put(typeParameter, typeProjection) } @@ -146,8 +124,9 @@ public class ConstraintSystemImpl : ConstraintSystem { for (declaredUpperBound in typeVariable.getUpperBounds()) { if (KotlinBuiltIns.getInstance().getNullableAnyType() == declaredUpperBound) continue //todo remove this line (?) val substitutedBound = constantSubstitutor?.substitute(declaredUpperBound, Variance.INVARIANT) - if (substitutedBound != null && !isErrorOrSpecialType(substitutedBound)) { - typeBounds.addBound(UPPER_BOUND, substitutedBound, TYPE_BOUND_POSITION.position(typeVariable.getIndex())) + val position = TYPE_BOUND_POSITION.position(typeVariable.getIndex()) + if (substitutedBound != null && !isErrorOrSpecialType(substitutedBound, position)) { + typeBounds.addBound(UPPER_BOUND, substitutedBound, position) } } } @@ -193,10 +172,7 @@ public class ConstraintSystemImpl : ConstraintSystem { val newTypeParameter = substituteTypeVariable(typeParameter) newSystem.typeParameterBounds.put(newTypeParameter!!, replaceTypeBounds(typeBounds)) } - newSystem.errorConstraintPositions.addAll(errorConstraintPositions.filter(filterConstraintPosition)) - //todo if 'filterConstraintPosition' is not trivial, it's incorrect to just copy 'hasErrorInConstrainingTypes' - newSystem.hasErrorInConstrainingTypes = hasErrorInConstrainingTypes - newSystem.cannotCaptureTypesError = cannotCaptureTypesError + newSystem.errors.addAll(errors.filter { filterConstraintPosition(it.constraintPosition) }) return newSystem } @@ -232,9 +208,10 @@ public class ConstraintSystemImpl : ConstraintSystem { } override fun capture(typeVariable: JetType, typeProjection: TypeProjection): Boolean { - if (isMyTypeVariable(typeVariable) && constraintPosition.isCaptureAllowed()) { + val myTypeVariable = getMyTypeVariable(typeVariable) + if (myTypeVariable != null && constraintPosition.isCaptureAllowed()) { if (!isTopLevel) { - cannotCaptureTypesError = true + errors.add(CannotCapture(constraintPosition, myTypeVariable)) } generateTypeParameterCaptureConstraint(typeVariable, typeProjection, constraintPosition) return true @@ -243,20 +220,20 @@ public class ConstraintSystemImpl : ConstraintSystem { } override fun noCorrespondingSupertype(subtype: JetType, supertype: JetType): Boolean { - errorConstraintPositions.add(constraintPosition) + errors.add(TypeConstructorMismatch(constraintPosition)) return true } }) doAddConstraint(constraintKind, subType, superType, constraintPosition, typeCheckingProcedure) } - private fun isErrorOrSpecialType(type: JetType?): Boolean { + private fun isErrorOrSpecialType(type: JetType?, constraintPosition: ConstraintPosition): Boolean { if (TypeUtils.isDontCarePlaceholder(type) || ErrorUtils.isUninferredParameter(type)) { return true } if (type == null || (type.isError() && type != TypeUtils.PLACEHOLDER_FUNCTION_TYPE)) { - hasErrorInConstrainingTypes = true + errors.add(ErrorInConstrainingType(constraintPosition)) return true } return false @@ -269,7 +246,7 @@ public class ConstraintSystemImpl : ConstraintSystem { constraintPosition: ConstraintPosition, typeCheckingProcedure: TypeCheckingProcedure ) { - if (isErrorOrSpecialType(subType) || isErrorOrSpecialType(superType)) return + if (isErrorOrSpecialType(subType, constraintPosition) || isErrorOrSpecialType(superType, constraintPosition)) return if (subType == null || superType == null) return assert(superType != TypeUtils.PLACEHOLDER_FUNCTION_TYPE) { @@ -282,7 +259,7 @@ public class ConstraintSystemImpl : ConstraintSystem { // a constraint binds type parameter and any function type, so there is no new info and no error return } - errorConstraintPositions.add(constraintPosition) + errors.add(TypeConstructorMismatch(constraintPosition)) } return } @@ -379,7 +356,7 @@ public class ConstraintSystemImpl : ConstraintSystem { val typeVariable = getMyTypeVariable(parameterType)!! if (!KotlinBuiltIns.isNullableAny(typeVariable.getUpperBoundsAsType()) && constrainingTypeProjection.getProjectionKind() == Variance.IN_VARIANCE) { - cannotCaptureTypesError = true + errors.add(CannotCapture(constraintPosition, typeVariable)) } val typeBounds = getTypeBounds(typeVariable) val typeProjection = if (parameterType.isMarkedNullable()) {