org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl.kt Maven / Gradle / Ivy
/*
* 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.types.TypeProjection
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.TypeUtils.DONT_CARE
import org.jetbrains.kotlin.types.TypeProjectionImpl
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.ErrorUtils.FunctionPlaceholderTypeConstructor
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind
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 org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.*
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.*
import java.util.HashMap
import java.util.ArrayList
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.CompoundConstraintPosition
import org.jetbrains.kotlin.types.getCustomTypeVariable
import org.jetbrains.kotlin.types.isFlexible
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.Bound
import org.jetbrains.kotlin.types.TypeSubstitution
import org.jetbrains.kotlin.types.checker.JetTypeChecker
public class ConstraintSystemImpl : ConstraintSystem {
public enum class ConstraintKind {
SUB_TYPE,
EQUAL
}
private val typeParameterBounds = LinkedHashMap()
private val errors = ArrayList()
public val constraintErrors: List
get() = errors
private val constraintSystemStatus = object : ConstraintSystemStatus {
// for debug ConstraintsUtil.getDebugMessageForStatus might be used
override fun isSuccessful() = !hasContradiction() && !hasUnknownParameters()
override fun hasContradiction() = hasTypeConstructorMismatch() || hasConflictingConstraints() || hasCannotCaptureTypesError()
override fun hasViolatedUpperBound() = !isSuccessful() && getSystemWithoutWeakConstraints().getStatus().isSuccessful()
override fun hasConflictingConstraints() = typeParameterBounds.values().any { it.getValues().size() > 1 }
override fun hasUnknownParameters() = typeParameterBounds.values().any { it.isEmpty() }
override fun hasTypeConstructorMismatch() = errors.any { it is TypeConstructorMismatch }
override fun hasOnlyErrorsFromPosition(constraintPosition: ConstraintPosition): Boolean {
if (isSuccessful()) return false
if (filterConstraintsOut(constraintPosition).getStatus().isSuccessful()) return true
return errors.isNotEmpty() && errors.all { it.constraintPosition == constraintPosition }
}
override fun hasErrorInConstrainingTypes() = errors.any { it is ErrorInConstrainingType }
override fun hasCannotCaptureTypesError() = errors.any { it is CannotCapture }
}
private fun getParameterToInferredValueMap(
typeParameterBounds: Map,
getDefaultTypeProjection: (TypeParameterDescriptor) -> TypeProjection
): Map {
val substitutionContext = HashMap()
for ((typeParameter, typeBounds) in typeParameterBounds) {
val typeProjection: TypeProjection
val value = typeBounds.getValue()
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): TypeSubstitutor {
return TypeUtils.makeSubstitutorForTypeParametersMap(getParameterToInferredValueMap(typeParameterBounds, getDefaultValue))
}
private fun replaceUninferredBy(defaultValue: JetType): TypeSubstitutor {
return replaceUninferredBy { TypeProjectionImpl(defaultValue) }
}
private fun replaceUninferredBySpecialErrorType(): TypeSubstitutor {
return replaceUninferredBy { TypeProjectionImpl(ErrorUtils.createUninferredParameterType(it)) }
}
override fun getStatus(): ConstraintSystemStatus = constraintSystemStatus
override fun registerTypeVariables(typeVariables: Map) {
for ((typeVariable, positionVariance) in typeVariables) {
typeParameterBounds.put(typeVariable, TypeBoundsImpl(typeVariable, positionVariance))
}
val constantSubstitutor = TypeUtils.makeConstantSubstitutor(typeParameterBounds.keySet(), DONT_CARE)
for ((typeVariable, typeBounds) in typeParameterBounds) {
for (declaredUpperBound in typeVariable.getUpperBounds()) {
if (KotlinBuiltIns.getInstance().getNullableAnyType() == declaredUpperBound) continue //todo remove this line (?)
val substitutedBound = constantSubstitutor?.substitute(declaredUpperBound, Variance.INVARIANT)
val position = TYPE_BOUND_POSITION.position(typeVariable.getIndex())
if (substitutedBound != null && !isErrorOrSpecialType(substitutedBound, position)) {
typeBounds.addBound(UPPER_BOUND, substitutedBound, position)
}
}
}
}
public fun copy(): ConstraintSystem = createNewConstraintSystemFromThis({ it }, { it.copy() }, { true })
public fun substituteTypeVariables(typeVariablesMap: (TypeParameterDescriptor) -> TypeParameterDescriptor): ConstraintSystem {
// type bounds are proper types and don't contain other variables
return createNewConstraintSystemFromThis(typeVariablesMap, { it }, { true })
}
public fun filterConstraintsOut(vararg excludePositions: ConstraintPosition): ConstraintSystem {
val positions = excludePositions.toSet()
return filterConstraints { !positions.contains(it) }
}
public fun filterConstraints(condition: (ConstraintPosition) -> Boolean): ConstraintSystem {
return createNewConstraintSystemFromThis({ it }, { it.filter(condition) }, condition)
}
public fun getSystemWithoutWeakConstraints(): ConstraintSystem {
return filterConstraints {
constraintPosition ->
// 'isStrong' for compound means 'has some strong constraints'
// but for testing absence of weak constraints we need 'has only strong constraints' here
if (constraintPosition is CompoundConstraintPosition) {
constraintPosition.positions.all { it.isStrong() }
}
else {
constraintPosition.isStrong()
}
}
}
private fun createNewConstraintSystemFromThis(
substituteTypeVariable: (TypeParameterDescriptor) -> TypeParameterDescriptor,
replaceTypeBounds: (TypeBoundsImpl) -> TypeBoundsImpl,
filterConstraintPosition: (ConstraintPosition) -> Boolean
): ConstraintSystem {
val newSystem = ConstraintSystemImpl()
for ((typeParameter, typeBounds) in typeParameterBounds) {
val newTypeParameter = substituteTypeVariable(typeParameter)
newSystem.typeParameterBounds.put(newTypeParameter, replaceTypeBounds(typeBounds))
}
newSystem.errors.addAll(errors.filter { filterConstraintPosition(it.constraintPosition) }.map { it.substituteTypeVariable(substituteTypeVariable) })
return newSystem
}
override fun addSupertypeConstraint(constrainingType: JetType?, subjectType: JetType, constraintPosition: ConstraintPosition) {
if (constrainingType != null && TypeUtils.noExpectedType(constrainingType)) return
addConstraint(SUB_TYPE, subjectType, constrainingType, constraintPosition)
}
override fun addSubtypeConstraint(constrainingType: JetType?, subjectType: JetType, constraintPosition: ConstraintPosition) {
addConstraint(SUB_TYPE, constrainingType, subjectType, constraintPosition)
}
private fun addConstraint(constraintKind: ConstraintKind, subType: JetType?, superType: JetType?, constraintPosition: ConstraintPosition) {
val typeCheckingProcedure = TypeCheckingProcedure(object : TypeCheckingProcedureCallbacks {
private var depth = 0
override fun assertEqualTypes(a: JetType, b: JetType, typeCheckingProcedure: TypeCheckingProcedure): Boolean {
depth++
doAddConstraint(EQUAL, a, b, constraintPosition, typeCheckingProcedure)
depth--
return true
}
override fun assertEqualTypeConstructors(a: TypeConstructor, b: TypeConstructor): Boolean {
return a == b
}
override fun assertSubtype(subtype: JetType, supertype: JetType, typeCheckingProcedure: TypeCheckingProcedure): Boolean {
depth++
doAddConstraint(SUB_TYPE, subtype, supertype, constraintPosition, typeCheckingProcedure)
depth--
return true
}
override fun capture(typeVariable: JetType, typeProjection: TypeProjection): Boolean {
val myTypeVariable = getMyTypeVariable(typeVariable)
if (myTypeVariable != null && constraintPosition.isCaptureAllowed()) {
if (depth > 0) {
errors.add(CannotCapture(constraintPosition, myTypeVariable))
}
generateTypeParameterCaptureConstraint(typeVariable, typeProjection, constraintPosition)
return true
}
return false
}
override fun noCorrespondingSupertype(subtype: JetType, supertype: JetType): Boolean {
errors.add(TypeConstructorMismatch(constraintPosition))
return true
}
})
doAddConstraint(constraintKind, subType, superType, constraintPosition, typeCheckingProcedure)
}
private fun isErrorOrSpecialType(type: JetType?, constraintPosition: ConstraintPosition): Boolean {
if (TypeUtils.isDontCarePlaceholder(type) || ErrorUtils.isUninferredParameter(type)) {
return true
}
if (type == null || (type.isError() && !ErrorUtils.isFunctionPlaceholder(type))) {
errors.add(ErrorInConstrainingType(constraintPosition))
return true
}
return false
}
private fun doAddConstraint(
constraintKind: ConstraintKind,
subType: JetType?,
superType: JetType?,
constraintPosition: ConstraintPosition,
typeCheckingProcedure: TypeCheckingProcedure
) {
if (isErrorOrSpecialType(subType, constraintPosition) || isErrorOrSpecialType(superType, constraintPosition)) return
if (subType == null || superType == null) return
assert(!ErrorUtils.isFunctionPlaceholder(superType)) {
"The type for " + constraintPosition + " shouldn't be a placeholder for function type"
}
// function literal { x -> ... } goes without declaring receiver type
// and can be considered as extension function if one is expected
val newSubType = if (constraintKind == SUB_TYPE && ErrorUtils.isFunctionPlaceholder(subType)) {
if (isMyTypeVariable(superType)) {
// the constraint binds type parameter and a function type,
// we don't add it without knowing whether it's a function type or an extension function type
return
}
createCorrespondingFunctionTypeForFunctionPlaceholder(subType, superType)
}
else {
subType
}
fun simplifyConstraint(subType: JetType, superType: JetType) {
// can be equal for the recursive invocations: fun foo(i: Int) : T { ... return foo(i); } => T <: T
// the right processing of constraints connecting type variables is not supported yet
if (isMyTypeVariable(subType) && isMyTypeVariable(superType)) return
if (isMyTypeVariable(subType)) {
val boundKind = if (constraintKind == SUB_TYPE) UPPER_BOUND else EXACT_BOUND
generateTypeParameterConstraint(subType, superType, boundKind, constraintPosition)
return
}
if (isMyTypeVariable(superType)) {
val boundKind = if (constraintKind == SUB_TYPE) LOWER_BOUND else EXACT_BOUND
generateTypeParameterConstraint(superType, subType, boundKind, constraintPosition)
return
}
// if superType is nullable and subType is not nullable, unsafe call or type mismatch error will be generated later,
// but constraint system should be solved anyway
val subTypeNotNullable = TypeUtils.makeNotNullable(subType)
val superTypeNotNullable = TypeUtils.makeNotNullable(superType)
if (constraintKind == EQUAL) {
typeCheckingProcedure.equalTypes(subTypeNotNullable, superTypeNotNullable)
}
else {
typeCheckingProcedure.isSubtypeOf(subTypeNotNullable, superTypeNotNullable)
}
}
simplifyConstraint(newSubType, superType)
}
private fun generateTypeParameterConstraint(
parameterType: JetType,
constrainingType: JetType,
boundKind: TypeBounds.BoundKind,
constraintPosition: ConstraintPosition
) {
var newConstrainingType = constrainingType
// Here we are handling the case when T! gets a bound Foo (or Foo?)
// In this case, type parameter T is supposed to get the bound Foo!
// Example:
// val c: Collection = Collections.singleton(null : Foo?)
// Constraints for T are:
// Foo? <: T!
// Foo >: T!
// both Foo and Foo? transform to Foo! here
if (parameterType.isFlexible()) {
val typeVariable = parameterType.getCustomTypeVariable()
if (typeVariable != null) {
newConstrainingType = typeVariable.substitutionResult(constrainingType)
}
}
val typeBounds = getTypeBounds(parameterType)
if (!parameterType.isMarkedNullable() || !TypeUtils.isNullableType(newConstrainingType)) {
typeBounds.addBound(boundKind, newConstrainingType, constraintPosition)
return
}
// For parameter type T:
// constraint T? = Int? should transform to T >: Int and T <: Int?
// constraint T? = Int! should transform to T >: Int and T <: Int!
// constraints T? >: Int?; T? >: Int! should transform to T >: Int
val notNullConstrainingType = TypeUtils.makeNotNullable(newConstrainingType)
if (boundKind == EXACT_BOUND || boundKind == LOWER_BOUND) {
typeBounds.addBound(LOWER_BOUND, notNullConstrainingType, constraintPosition)
}
// constraints T? <: Int?; T? <: Int! should transform to T <: Int?; T <: Int! correspondingly
if (boundKind == EXACT_BOUND || boundKind == UPPER_BOUND) {
typeBounds.addBound(UPPER_BOUND, newConstrainingType, constraintPosition)
}
}
private fun generateTypeParameterCaptureConstraint(
parameterType: JetType,
constrainingTypeProjection: TypeProjection,
constraintPosition: ConstraintPosition
) {
val typeVariable = getMyTypeVariable(parameterType)!!
if (!KotlinBuiltIns.isNullableAny(typeVariable.getUpperBoundsAsType())
&& constrainingTypeProjection.getProjectionKind() == Variance.IN_VARIANCE) {
errors.add(CannotCapture(constraintPosition, typeVariable))
}
val typeBounds = getTypeBounds(typeVariable)
val typeProjection = if (parameterType.isMarkedNullable()) {
TypeProjectionImpl(constrainingTypeProjection.getProjectionKind(), TypeUtils.makeNotNullable(constrainingTypeProjection.getType()))
}
else {
constrainingTypeProjection
}
val capturedType = createCapturedType(typeProjection)
typeBounds.addBound(EXACT_BOUND, capturedType, constraintPosition)
}
public fun processDeclaredBoundConstraints() {
for ((typeParameterDescriptor, typeBounds) in typeParameterBounds) {
fun compoundPosition(bound: Bound) = CompoundConstraintPosition(
TYPE_BOUND_POSITION.position(typeParameterDescriptor.getIndex()), bound.position)
// todo order matters here
// it's important to create a separate variable here,
// because the following code may add new elements to typeBounds.bounds collection
val bounds = ArrayList(typeBounds.bounds)
for (declaredUpperBound in typeParameterDescriptor.getUpperBounds()) {
bounds.filter { it.kind != UPPER_BOUND }.forEach {
lowerOrExactBound ->
addSubtypeConstraint(lowerOrExactBound.constrainingType, declaredUpperBound, compoundPosition(lowerOrExactBound))
}
if (!isMyTypeVariable(declaredUpperBound)) continue
getTypeBounds(declaredUpperBound).bounds.filter { it.kind != LOWER_BOUND }.forEach {
upperOrExactBound ->
typeBounds.addBound(UPPER_BOUND, upperOrExactBound.constrainingType, compoundPosition(upperOrExactBound))
}
}
}
}
override fun getTypeVariables() = typeParameterBounds.keySet()
override fun getTypeBounds(typeVariable: TypeParameterDescriptor): TypeBoundsImpl {
if (!isMyTypeVariable(typeVariable)) {
throw IllegalArgumentException("TypeParameterDescriptor is not a type variable for constraint system: $typeVariable")
}
return typeParameterBounds[typeVariable]!!
}
private fun getTypeBounds(parameterType: JetType): TypeBoundsImpl {
assert (isMyTypeVariable(parameterType)) { "Type is not a type variable for constraint system: $parameterType" }
return getTypeBounds(getMyTypeVariable(parameterType)!!)
}
private fun isMyTypeVariable(typeVariable: TypeParameterDescriptor) = typeParameterBounds.contains(typeVariable)
private fun isMyTypeVariable(type: JetType): Boolean = getMyTypeVariable(type) != null
private fun getMyTypeVariable(type: JetType): TypeParameterDescriptor? {
val typeParameterDescriptor = type.getConstructor().getDeclarationDescriptor() as? TypeParameterDescriptor
return if (typeParameterDescriptor != null && isMyTypeVariable(typeParameterDescriptor)) typeParameterDescriptor else null
}
override fun getResultingSubstitutor() = replaceUninferredBySpecialErrorType().setApproximateCapturedTypes()
override fun getCurrentSubstitutor() = replaceUninferredBy(TypeUtils.DONT_CARE).setApproximateCapturedTypes()
}
fun createCorrespondingFunctionTypeForFunctionPlaceholder(
functionPlaceholder: JetType,
expectedType: JetType
): JetType {
assert(ErrorUtils.isFunctionPlaceholder(functionPlaceholder)) { "Function placeholder type expected: $functionPlaceholder" }
val functionPlaceholderTypeConstructor = functionPlaceholder.getConstructor() as FunctionPlaceholderTypeConstructor
val isExtension = KotlinBuiltIns.isExtensionFunctionType(expectedType)
val newArgumentTypes = if (!functionPlaceholderTypeConstructor.hasDeclaredArguments()) {
val typeParamSize = expectedType.getConstructor().getParameters().size()
// the first parameter is receiver (if present), the last one is return type,
// the remaining are function arguments
val functionArgumentsSize = if (isExtension) typeParamSize - 2 else typeParamSize - 1
val result = arrayListOf()
(1..functionArgumentsSize).forEach { result.add(DONT_CARE) }
result
}
else {
functionPlaceholderTypeConstructor.getArgumentTypes()
}
val receiverType = if (isExtension) DONT_CARE else null
return KotlinBuiltIns.getInstance().getFunctionType(Annotations.EMPTY, receiverType, newArgumentTypes, DONT_CARE)
}
private fun TypeSubstitutor.setApproximateCapturedTypes(): TypeSubstitutor {
return TypeSubstitutor.create(SubstitutionWithCapturedTypeApproximation(getSubstitution()))
}
private class SubstitutionWithCapturedTypeApproximation(val substitution: TypeSubstitution) : TypeSubstitution() {
override fun get(key: TypeConstructor?) = substitution[key]
override fun isEmpty() = substitution.isEmpty()
override fun approximateCapturedTypes() = true
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy