Move constraint system, type intersection, common supertypes to compiler

This commit is contained in:
Alexander Udalov
2015-10-24 01:52:02 +03:00
parent 302cd3ca2f
commit 5fc14e0224
10 changed files with 0 additions and 0 deletions
@@ -0,0 +1,34 @@
/*
* 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.resolve.calls.inference.constraintPosition.CompoundConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition
open class ConstraintError(val constraintPosition: ConstraintPosition)
class ParameterConstraintError(constraintPosition: ConstraintPosition): ConstraintError(constraintPosition)
class ErrorInConstrainingType(constraintPosition: ConstraintPosition): ConstraintError(constraintPosition)
class TypeInferenceError(constraintPosition: ConstraintPosition): ConstraintError(constraintPosition)
class CannotCapture(constraintPosition: ConstraintPosition, val typeVariable: TypeParameterDescriptor): ConstraintError(constraintPosition)
fun newTypeInferenceOrParameterConstraintError(constraintPosition: ConstraintPosition) =
if (constraintPosition.isParameter()) ParameterConstraintError(constraintPosition) else TypeInferenceError(constraintPosition)
@@ -0,0 +1,73 @@
/*
* 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.constraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.*
import java.util.*
public enum class ConstraintPositionKind {
RECEIVER_POSITION,
EXPECTED_TYPE_POSITION,
VALUE_PARAMETER_POSITION,
TYPE_BOUND_POSITION,
COMPOUND_CONSTRAINT_POSITION,
FROM_COMPLETER,
SPECIAL;
public fun position(): ConstraintPosition {
assert(this in setOf(RECEIVER_POSITION, EXPECTED_TYPE_POSITION, FROM_COMPLETER, SPECIAL))
return ConstraintPositionImpl(this)
}
public fun position(index: Int): ConstraintPosition {
assert(this in setOf(VALUE_PARAMETER_POSITION, TYPE_BOUND_POSITION))
return ConstraintPositionWithIndex(this, index)
}
}
public interface ConstraintPosition {
val kind: ConstraintPositionKind
fun isStrong(): Boolean = kind != TYPE_BOUND_POSITION
fun isParameter(): Boolean = kind in setOf(VALUE_PARAMETER_POSITION, RECEIVER_POSITION)
}
private data class ConstraintPositionImpl(override val kind: ConstraintPositionKind) : ConstraintPosition {
override fun toString() = "$kind"
}
private data class ConstraintPositionWithIndex(override val kind: ConstraintPositionKind, val index: Int) : ConstraintPosition {
override fun toString() = "$kind($index)"
}
class CompoundConstraintPosition(vararg positions: ConstraintPosition) : ConstraintPosition {
override val kind: ConstraintPositionKind
get() = COMPOUND_CONSTRAINT_POSITION
val positions: Collection<ConstraintPosition> =
positions.flatMap { if (it is CompoundConstraintPosition) it.positions else listOf(it) }.toSet()
override fun isStrong() = positions.any { it.isStrong() }
override fun toString() = "$kind(${positions.joinToString()})"
}
fun ConstraintPosition.derivedFrom(kind: ConstraintPositionKind): Boolean {
return if (this !is CompoundConstraintPosition) this.kind == kind else positions.any { it.kind == kind }
}
@@ -0,0 +1,90 @@
/*
* 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.resolve.calls.inference.constraintPosition.ConstraintPosition
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.Variance
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>,
variance: (TypeParameterDescriptor) -> Variance,
mapToOriginal: (TypeParameterDescriptor) -> TypeParameterDescriptor,
external: Boolean = false
)
/**
* Returns a set of all non-external registered type variables.
*/
public fun getTypeVariables(): Set<TypeParameterDescriptor>
/**
* Adds a constraint that the constraining type is a subtype of the subject type.<p/>
* Asserts that only subject type may contain registered type variables. <p/>
*
* For example, for {@code "fun <T> id(t: T) {}"} to infer <tt>T</tt> in invocation <tt>"id(1)"</tt>
* should be generated a constraint <tt>"Int is a subtype of T"</tt> 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. <p/>
* Asserts that only subject type may contain registered type variables. <p/>
*
* For example, for {@code "fun <T> create() : T"} to infer <tt>T</tt> in invocation <tt>"val i: Int = create()"</tt>
* should be generated a constraint <tt>"Int is a supertype of T"</tt> 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
/**
* Returns the resulting type constraints of solving the constraint system for specific type variable. <p/>
* Throws IllegalArgumentException if the type variable was not registered.
*/
public fun getTypeBounds(typeVariable: TypeParameterDescriptor): TypeBounds
/**
* Returns a result of solving the constraint system (mapping from the type variable to the resulting type projection). <p/>
* In the resulting substitution should be concerned: <p/>
* - type constraints <p/>
* - variance of the type variable // not implemented yet <p/>
* - type parameter bounds (that can bind type variables with each other). // not implemented yet
* 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
/**
* Returns a 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
/**
* Returns a substitution only for type parameters that have result values, otherwise returns a type parameter itself.
*/
public fun getPartialSubstitutor(): TypeSubstitutor
}
@@ -0,0 +1,580 @@
/*
* 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.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.annotations.FilteredAnnotations
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.TypeBounds.Bound
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.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.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 {
data class Constraint(val kind: ConstraintKind, val subtype: KotlinType, val superType: KotlinType, val position: ConstraintPosition)
public enum class ConstraintKind {
SUB_TYPE,
EQUAL
}
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>()
public val constraintErrors: List<ConstraintError>
get() = errors
private val initialConstraints = ArrayList<Constraint>()
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 }
}
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>,
variance: (TypeParameterDescriptor) -> Variance,
mapToOriginal: (TypeParameterDescriptor) -> TypeParameterDescriptor,
external: Boolean
) {
if (external) externalTypeParameters.addAll(typeVariables)
for (typeVariable in typeVariables) {
allTypeParameterBounds.put(typeVariable, TypeBoundsImpl(typeVariable, variance(typeVariable)))
val original = mapToOriginal(typeVariable)
originalToVariables[original] = typeVariable
variablesToOriginal[typeVariable] = original
}
for ((typeVariable, typeBounds) in allTypeParameterBounds) {
for (declaredUpperBound in typeVariable.getUpperBounds()) {
if (declaredUpperBound.isDefaultBound()) continue //todo remove this line (?)
val context = ConstraintContext(TYPE_BOUND_POSITION.position(typeVariable.getIndex()))
addBound(typeVariable, declaredUpperBound, UPPER_BOUND, context)
}
}
}
val TypeParameterDescriptor.correspondingType: KotlinType
get() = cachedTypeForVariable.getOrPut(this) {
KotlinTypeImpl.create(Annotations.EMPTY, this.getTypeConstructor(), false, listOf(), KtScope.Empty)
}
fun KotlinType.isProper() = !TypeUtils.containsSpecialType(this) {
type -> type.getConstructor().getDeclarationDescriptor() in getAllTypeVariables()
}
fun KotlinType.getNestedTypeVariables(original: Boolean = true): List<TypeParameterDescriptor> {
return getNestedArguments().map { typeProjection ->
typeProjection.getType().getConstructor().getDeclarationDescriptor() as? TypeParameterDescriptor
}.filterNotNull().filter { if (original) it in originalToVariables.keySet() else it in getAllTypeVariables() }
}
public fun copy(): ConstraintSystem = createNewConstraintSystemFromThis { true }
public fun filterConstraintsOut(excludePositionKind: ConstraintPositionKind): ConstraintSystem {
return createNewConstraintSystemFromThis { !it.derivedFrom(excludePositionKind) }
}
private fun createNewConstraintSystemFromThis(
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
val newSubjectType = originalToVariablesSubstitutor.substitute(subjectType, Variance.INVARIANT)
addConstraint(SUB_TYPE, newSubjectType, constrainingType, ConstraintContext(constraintPosition, initial = true))
}
override fun addSubtypeConstraint(constrainingType: KotlinType?, subjectType: KotlinType, constraintPosition: ConstraintPosition) {
val newSubjectType = originalToVariablesSubstitutor.substitute(subjectType, Variance.INVARIANT)
addConstraint(SUB_TYPE, constrainingType, newSubjectType, ConstraintContext(constraintPosition, initial = true))
}
fun addConstraint(
constraintKind: ConstraintKind,
subType: KotlinType?,
superType: KotlinType?,
constraintContext: ConstraintContext
) {
val constraintPosition = constraintContext.position
// when processing nested constraints, `derivedFrom` information should be reset
val newConstraintContext = ConstraintContext(constraintContext.position, derivedFrom = null, initial = false)
val typeCheckingProcedure = TypeCheckingProcedure(object : TypeCheckingProcedureCallbacks {
private var depth = 0
override fun assertEqualTypes(a: KotlinType, b: KotlinType, typeCheckingProcedure: TypeCheckingProcedure): Boolean {
depth++
doAddConstraint(EQUAL, a, b, newConstraintContext, typeCheckingProcedure)
depth--
return true
}
override fun assertEqualTypeConstructors(a: TypeConstructor, b: TypeConstructor): Boolean {
return a == b
}
override fun assertSubtype(subtype: KotlinType, supertype: KotlinType, typeCheckingProcedure: TypeCheckingProcedure): Boolean {
depth++
doAddConstraint(SUB_TYPE, subtype, supertype, newConstraintContext, typeCheckingProcedure)
depth--
return true
}
override fun capture(typeVariable: KotlinType, typeProjection: TypeProjection): Boolean {
if (isMyTypeVariable(typeProjection.getType())) return false
val myTypeVariable = getMyTypeVariable(typeVariable)
if (myTypeVariable != null && constraintPosition.isParameter()) {
if (depth > 0) {
errors.add(CannotCapture(constraintPosition, myTypeVariable))
}
generateTypeParameterCaptureConstraint(typeVariable, typeProjection, newConstraintContext)
return true
}
return false
}
override fun noCorrespondingSupertype(subtype: KotlinType, supertype: KotlinType): Boolean {
errors.add(newTypeInferenceOrParameterConstraintError(constraintPosition))
return true
}
})
doAddConstraint(constraintKind, subType, superType, constraintContext, typeCheckingProcedure)
}
private fun isErrorOrSpecialType(type: KotlinType?, constraintPosition: ConstraintPosition): Boolean {
if (TypeUtils.isDontCarePlaceholder(type) || ErrorUtils.isUninferredParameter(type)) {
return true
}
if (type == null || (type.isError() && !type.isFunctionPlaceholder)) {
errors.add(ErrorInConstrainingType(constraintPosition))
return true
}
return false
}
private fun doAddConstraint(
constraintKind: ConstraintKind,
subType: KotlinType?,
superType: KotlinType?,
constraintContext: ConstraintContext,
typeCheckingProcedure: TypeCheckingProcedure
) {
val constraintPosition = constraintContext.position
if (isErrorOrSpecialType(subType, constraintPosition) || isErrorOrSpecialType(superType, constraintPosition)) return
if (subType == null || superType == null) return
if ((subType.hasExactAnnotation() || superType.hasExactAnnotation()) && (constraintKind != EQUAL)) {
return doAddConstraint(EQUAL, subType, superType, constraintContext, typeCheckingProcedure)
}
assert(!superType.isFunctionPlaceholder) {
"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 && subType.isFunctionPlaceholder) {
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
}
createTypeForFunctionPlaceholder(subType, superType)
}
else {
subType
}
fun simplifyConstraint(subType: KotlinType, superType: KotlinType) {
if (isMyTypeVariable(subType)) {
generateTypeParameterBound(subType, superType, constraintKind.toBound(), constraintContext)
return
}
if (isMyTypeVariable(superType)) {
generateTypeParameterBound(superType, subType, constraintKind.toBound().reverse(), constraintContext)
return
}
// if subType is nullable and superType is not nullable, unsafe call or type mismatch error will be generated later,
// but constraint system should be solved anyway
val subTypeNotNullable = if (constraintContext.initial) TypeUtils.makeNotNullable(subType) else subType
val superTypeNotNullable = if (constraintContext.initial) TypeUtils.makeNotNullable(superType) else superType
val result = if (constraintKind == EQUAL) {
typeCheckingProcedure.equalTypes(subTypeNotNullable, superTypeNotNullable)
}
else {
typeCheckingProcedure.isSubtypeOf(subTypeNotNullable, superType)
}
if (!result) errors.add(newTypeInferenceOrParameterConstraintError(constraintPosition))
}
if (constraintContext.initial) {
storeInitialConstraint(constraintKind, subType, superType, constraintPosition)
}
if (subType.hasNoInferAnnotation() || superType.hasNoInferAnnotation()) return
simplifyConstraint(newSubType, superType)
}
fun addBound(
typeVariable: TypeParameterDescriptor,
constrainingType: KotlinType,
kind: TypeBounds.BoundKind,
constraintContext: ConstraintContext
) {
val bound = Bound(typeVariable, constrainingType, kind, constraintContext.position,
constrainingType.isProper(), constraintContext.derivedFrom ?: emptySet())
val typeBounds = getTypeBounds(typeVariable)
if (typeBounds.bounds.contains(bound)) return
typeBounds.addBound(bound)
if (!bound.isProper) {
for (dependentTypeVariable in bound.constrainingType.getNestedTypeVariables(original = false)) {
val dependentBounds = usedInBounds.getOrPut(dependentTypeVariable) { arrayListOf() }
dependentBounds.add(bound)
}
}
incorporateBound(bound)
}
private fun generateTypeParameterBound(
parameterType: KotlinType,
constrainingType: KotlinType,
boundKind: TypeBounds.BoundKind,
constraintContext: ConstraintContext
) {
val typeVariable = getMyTypeVariable(parameterType)!!
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<Foo> = Collections.singleton(null : Foo?)
// Constraints for T are:
// Foo? <: T!
// Foo >: T!
// both Foo and Foo? transform to Foo! here
if (parameterType.isFlexible()) {
val customTypeVariable = parameterType.getCustomTypeVariable()
if (customTypeVariable != null) {
newConstrainingType = customTypeVariable.substitutionResult(constrainingType)
}
}
if (!parameterType.isMarkedNullable() || !TypeUtils.isNullableType(newConstrainingType)) {
addBound(typeVariable, newConstrainingType, boundKind, constraintContext)
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) {
addBound(typeVariable, notNullConstrainingType, LOWER_BOUND, constraintContext)
}
// constraints T? <: Int?; T? <: Int! should transform to T <: Int?; T <: Int! correspondingly
if (boundKind == EXACT_BOUND || boundKind == UPPER_BOUND) {
addBound(typeVariable, newConstrainingType, UPPER_BOUND, constraintContext)
}
}
private fun generateTypeParameterCaptureConstraint(
parameterType: KotlinType,
constrainingTypeProjection: TypeProjection,
constraintContext: ConstraintContext
) {
val typeVariable = getMyTypeVariable(parameterType)!!
if (!typeVariable.upperBounds.let { it.size == 1 && it.single().isDefaultBound() } &&
constrainingTypeProjection.projectionKind == Variance.IN_VARIANCE) {
errors.add(CannotCapture(constraintContext.position, typeVariable))
}
val typeProjection = if (parameterType.isMarkedNullable) {
TypeProjectionImpl(constrainingTypeProjection.projectionKind, TypeUtils.makeNotNullable(constrainingTypeProjection.type))
}
else {
constrainingTypeProjection
}
val capturedType = createCapturedType(typeProjection)
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 {
val variableForOriginal = originalToVariables[typeVariable]
if (variableForOriginal != null && variableForOriginal != typeVariable) {
return getTypeBounds(variableForOriginal)
}
if (!isMyTypeVariable(typeVariable)) {
throw IllegalArgumentException("TypeParameterDescriptor is not a type variable for constraint system: $typeVariable")
}
return allTypeParameterBounds[typeVariable]!!
}
fun isMyTypeVariable(typeVariable: TypeParameterDescriptor) = allTypeParameterBounds.contains(typeVariable)
fun isMyTypeVariable(type: KotlinType): Boolean = getMyTypeVariable(type) != null
fun getMyTypeVariable(type: KotlinType): TypeParameterDescriptor? {
val typeParameterDescriptor = type.getConstructor().getDeclarationDescriptor() as? TypeParameterDescriptor
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
typeBounds.setFixed()
val nestedTypeVariables = typeBounds.bounds.flatMap { it.constrainingType.getNestedTypeVariables(original = false) }
nestedTypeVariables.forEach { fixVariable(it) }
val value = typeBounds.value ?: return
addBound(typeVariable, value, TypeBounds.BoundKind.EXACT_BOUND, ConstraintContext(ConstraintPositionKind.FROM_COMPLETER.position()))
}
fun fixVariables() {
// todo variables should be fixed in the right order
val (external, functionTypeParameters) = getAllTypeVariables().partition { externalTypeParameters.contains(it) }
external.forEach { fixVariable(it) }
functionTypeParameters.forEach { fixVariable(it) }
}
}
fun createTypeForFunctionPlaceholder(
functionPlaceholder: KotlinType,
expectedType: KotlinType
): KotlinType {
if (!functionPlaceholder.isFunctionPlaceholder) return 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<KotlinType>()
(1..functionArgumentsSize).forEach { result.add(DONT_CARE) }
result
}
else {
functionPlaceholderTypeConstructor.argumentTypes
}
val receiverType = if (isExtension) DONT_CARE else null
return functionPlaceholder.builtIns.getFunctionType(Annotations.EMPTY, receiverType, newArgumentTypes, DONT_CARE)
}
private fun TypeSubstitutor.setApproximateCapturedTypes(): TypeSubstitutor {
return TypeSubstitutor.create(SubstitutionWithCapturedTypeApproximation(getSubstitution()))
}
private class SubstitutionWithCapturedTypeApproximation(substitution: TypeSubstitution) : DelegatedTypeSubstitution(substitution) {
override fun approximateCapturedTypes() = true
}
class SubstitutionFilteringInternalResolveAnnotations(substitution: TypeSubstitution) : DelegatedTypeSubstitution(substitution) {
override fun filterAnnotations(annotations: Annotations): Annotations {
if (!annotations.hasInternalAnnotationForResolve()) return annotations
return FilteredAnnotations(annotations) { !it.isInternalAnnotationForResolve() }
}
}
public fun ConstraintSystemImpl.registerTypeVariables(typeVariables: Map<TypeParameterDescriptor, Variance>) {
registerTypeVariables(typeVariables.keySet(), { typeVariables[it]!! }, { it })
}
public fun ConstraintSystemImpl.registerTypeVariables(
typeVariables: Collection<TypeParameterDescriptor>,
variance: (TypeParameterDescriptor) -> Variance
) {
registerTypeVariables(typeVariables, variance, { it })
}
public fun createTypeSubstitutor(conversion: (TypeParameterDescriptor) -> TypeParameterDescriptor?): TypeSubstitutor {
return TypeSubstitutor.create(object : TypeConstructorSubstitution() {
override fun get(key: TypeConstructor): TypeProjection? {
val descriptor = key.getDeclarationDescriptor()
if (descriptor !is TypeParameterDescriptor) return null
val typeParameterDescriptor = conversion(descriptor) ?: return null
val type = KotlinTypeImpl.create(Annotations.EMPTY, typeParameterDescriptor.getTypeConstructor(), false, listOf(), KtScope.Empty)
return TypeProjectionImpl(type)
}
})
}
@@ -0,0 +1,94 @@
/*
* 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.ConstraintPositionKind
public interface ConstraintSystemStatus {
/**
* Returns <tt>true</tt> if constraint system has a solution (has no contradiction and has enough information to infer each registered type variable).
*/
public fun isSuccessful(): Boolean
/**
* Return <tt>true</tt> if constraint system has no contradiction (it can be not successful because of the lack of information for a type variable).
*/
public fun hasContradiction(): Boolean
/**
* Returns <tt>true</tt> if type constraints for some type variable are contradicting. <p/>
*
* For example, for <pre>fun &lt;R&gt; foo(r: R, t: java.util.List&lt;R&gt;) {}</pre> in invocation <tt>foo(1, arrayList("s"))</tt>
* type variable <tt>R</tt> has two conflicting constraints: <p/>
* - <tt>"R is a supertype of Int"</tt> <p/>
* - <tt>"List&lt;R&gt; is a supertype of List&lt;String&gt;"</tt> which leads to <tt>"R is equal to String"</tt>
*/
public fun hasConflictingConstraints(): Boolean
/**
* Returns <tt>true</tt> if contradiction of type constraints comes from declared bounds for type parameters.
*
* For example, for <pre>fun &lt;R: Any&gt; foo(r: R) {}</pre> in invocation <tt>foo(null)</tt>
* upper bounds <tt>Any</tt> for type parameter <tt>R</tt> is violated. <p/>
*
* It's the special case of 'hasConflictingConstraints' case.
*/
public fun hasViolatedUpperBound(): Boolean
/**
* Returns <tt>true</tt> if there is no information for some registered type variable.
*
* For example, for <pre>fun &lt;E&gt; newList()</pre> in invocation <tt>"val nl = newList()"</tt>
* there is no information to infer type variable <tt>E</tt>.
*/
public fun hasUnknownParameters(): Boolean
/**
* Returns <tt>true</tt> if some constraint cannot be processed because of type constructor mismatch.
*
* For example, for <pre>fun &lt;R&gt; foo(t: List&lt;R&gt;) {}</pre> in invocation <tt>foo(hashSet("s"))</tt>
* there is type constructor mismatch: <tt>"HashSet&lt;String&gt; cannot be a subtype of List&lt;R&gt;"</tt>.
*/
public fun hasParameterConstraintError(): Boolean
/**
* Returns <tt>true</tt> if there is type constructor mismatch only in constraintPosition or
* constraint system is successful without constraints from this position.
*/
public fun hasOnlyErrorsDerivedFrom(kind: ConstraintPositionKind): Boolean
/**
* Returns <tt>true</tt> if there is an error in constraining types. <p/>
* Is used not to generate type inference error if there was one in argument types.
*/
public fun hasErrorInConstrainingTypes(): Boolean
/**
* Returns <tt>true</tt> if a user type contains the type projection that cannot be captured.
*
* For example, for <pre>fun &lt;T&gt; foo(t: Array&lt;Array&lt;T&gt;&gt;) {}</pre>
* in invocation <tt>foo(array)</tt> where array has type <tt>Array&lt;Array&lt;out Int&gt;&gt;</tt>.
*/
public fun hasCannotCaptureTypesError(): Boolean
/**
* Returns <tt>true</tt> if there's an error in constraint system incorporation.
*/
public fun hasTypeInferenceIncorporationError(): Boolean
public fun hasTypeParameterWithUnsatisfiedOnlyInputTypesError(): Boolean
}
@@ -0,0 +1,86 @@
/*
* 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.resolve.calls.inference.TypeBounds.BoundKind
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.EXACT_BOUND
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.LOWER_BOUND
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.UPPER_BOUND
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.Variance
public interface TypeBounds {
public val varianceOfPosition: Variance
public val typeVariable: TypeParameterDescriptor
public val bounds: Collection<Bound>
public val value: KotlinType?
get() = if (values.size() == 1) values.first() else null
public val values: Collection<KotlinType>
public enum class BoundKind {
LOWER_BOUND,
EXACT_BOUND,
UPPER_BOUND
}
public class Bound(
public val typeVariable: TypeParameterDescriptor,
public val constrainingType: KotlinType,
public val kind: BoundKind,
public val position: ConstraintPosition,
public val isProper: Boolean,
// to prevent infinite recursion in incorporation we store the variables that was substituted to derive this bound
public val derivedFrom: Set<TypeParameterDescriptor>
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || javaClass != other.javaClass) return false
val bound = other as Bound
if (typeVariable != bound.typeVariable) return false
if (constrainingType != bound.constrainingType) return false
if (kind != bound.kind) return false
if (position.isStrong() != bound.position.isStrong()) return false
return true
}
override fun hashCode(): Int {
var result = typeVariable.hashCode();
result = 31 * result + constrainingType.hashCode()
result = 31 * result + kind.hashCode()
result = 31 * result + if (position.isStrong()) 1 else 0
return result
}
override fun toString() = "Bound($constrainingType, $kind, $position, isProper = $isProper)"
}
}
fun BoundKind.reverse() = when (this) {
LOWER_BOUND -> UPPER_BOUND
UPPER_BOUND -> LOWER_BOUND
EXACT_BOUND -> EXACT_BOUND
}
@@ -0,0 +1,206 @@
/*
* 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.resolve.calls.inference.TypeBounds.Bound
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.*
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstructor
import org.jetbrains.kotlin.resolve.descriptorUtil.hasOnlyInputTypesAnnotation
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
public class TypeBoundsImpl(
override val typeVariable: TypeParameterDescriptor,
override val varianceOfPosition: Variance
) : TypeBounds {
override val bounds = ArrayList<Bound>()
private var resultValues: Collection<KotlinType>? = null
var isFixed: Boolean = false
private set
public fun setFixed() {
isFixed = true
}
public fun addBound(bound: Bound) {
resultValues = null
assert(bound.typeVariable == typeVariable) {
"$bound is added for incorrect type variable ${bound.typeVariable.getName()}. Expected: ${typeVariable.getName()}"
}
bounds.add(bound)
}
private fun filterBounds(bounds: Collection<Bound>, kind: BoundKind, errorValues: MutableCollection<KotlinType>? = null): Set<KotlinType> {
val result = LinkedHashSet<KotlinType>()
for (bound in bounds) {
if (bound.kind == kind) {
if (!ErrorUtils.containsErrorType(bound.constrainingType)) {
result.add(bound.constrainingType)
}
else {
errorValues?.add(bound.constrainingType)
}
}
}
return result
}
public fun filter(condition: (ConstraintPosition) -> Boolean): TypeBoundsImpl {
val result = TypeBoundsImpl(typeVariable, varianceOfPosition)
result.bounds.addAll(bounds.filter { condition(it.position) })
return result
}
override val values: Collection<KotlinType>
get() {
if (resultValues == null) {
resultValues = computeValues()
}
return resultValues!!
}
private fun computeValues(): Collection<KotlinType> {
val values = LinkedHashSet<KotlinType>()
val bounds = bounds.filter { it.isProper }
if (bounds.isEmpty()) {
return listOf()
}
val hasStrongBound = bounds.any { it.position.isStrong() }
if (!hasStrongBound) {
return listOf()
}
val exactBounds = filterBounds(bounds, EXACT_BOUND, values)
val bestFit = exactBounds.singleBestRepresentative()
if (bestFit != null) {
if (tryPossibleAnswer(bounds, bestFit)) {
return listOf(bestFit)
}
}
values.addAll(exactBounds)
val (numberLowerBounds, generalLowerBounds) =
filterBounds(bounds, LOWER_BOUND, values).partition { it.constructor is IntegerValueTypeConstructor }
val superTypeOfLowerBounds = CommonSupertypes.commonSupertypeForNonDenotableTypes(generalLowerBounds)
if (tryPossibleAnswer(bounds, superTypeOfLowerBounds)) {
return setOf(superTypeOfLowerBounds!!)
}
values.addIfNotNull(superTypeOfLowerBounds)
//todo
//fun <T> foo(t: T, consumer: Consumer<T>): T
//foo(1, c: Consumer<Any>) - infer Int, not Any here
val superTypeOfNumberLowerBounds = commonSupertypeForNumberTypes(numberLowerBounds)
if (tryPossibleAnswer(bounds, superTypeOfNumberLowerBounds)) {
return setOf(superTypeOfNumberLowerBounds!!)
}
values.addIfNotNull(superTypeOfNumberLowerBounds)
if (superTypeOfLowerBounds != null && superTypeOfNumberLowerBounds != null) {
val superTypeOfAllLowerBounds = CommonSupertypes.commonSupertypeForNonDenotableTypes(listOf(superTypeOfLowerBounds, superTypeOfNumberLowerBounds))
if (tryPossibleAnswer(bounds, superTypeOfAllLowerBounds)) {
return setOf(superTypeOfAllLowerBounds!!)
}
}
val upperBounds = filterBounds(bounds, TypeBounds.BoundKind.UPPER_BOUND, values)
if (upperBounds.isNotEmpty()) {
val intersectionOfUpperBounds = TypeIntersector.intersectTypes(KotlinTypeChecker.DEFAULT, upperBounds)
if (intersectionOfUpperBounds != null && tryPossibleAnswer(bounds, intersectionOfUpperBounds)) {
return setOf(intersectionOfUpperBounds)
}
}
values.addAll(filterBounds(bounds, TypeBounds.BoundKind.UPPER_BOUND))
if (values.size == 1 && typeVariable.hasOnlyInputTypesAnnotation() && !tryPossibleAnswer(bounds, values.first())) return listOf()
return values
}
private fun checkOnlyInputTypes(bounds: Collection<Bound>, possibleAnswer: KotlinType): Boolean {
if (!typeVariable.hasOnlyInputTypesAnnotation()) return true
// Only type mentioned in bounds might be the result
val typesInBoundsSet = bounds.filter { it.isProper && it.constrainingType.constructor.isDenotable }.map { it.constrainingType }.toSet()
// Flexible types are equal to inflexible
if (typesInBoundsSet.any { KotlinTypeChecker.DEFAULT.equalTypes(it, possibleAnswer) }) return true
// For non-denotable number types only, no valid types are mentioned, so common supertype is valid
val numberLowerBounds = filterBounds(bounds, LOWER_BOUND).filter { it.constructor is IntegerValueTypeConstructor }
val superTypeOfNumberLowerBounds = commonSupertypeForNumberTypes(numberLowerBounds)
if (possibleAnswer == superTypeOfNumberLowerBounds) return true
return false
}
private fun tryPossibleAnswer(bounds: Collection<Bound>, possibleAnswer: KotlinType?): Boolean {
if (possibleAnswer == null) return false
// a captured type might be an answer
if (!possibleAnswer.constructor.isDenotable && !possibleAnswer.isCaptured()) return false
if (!checkOnlyInputTypes(bounds, possibleAnswer)) return false
for (bound in bounds) {
when (bound.kind) {
LOWER_BOUND -> if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(bound.constrainingType, possibleAnswer)) {
return false
}
UPPER_BOUND -> if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(possibleAnswer, bound.constrainingType)) {
return false
}
EXACT_BOUND -> if (!KotlinTypeChecker.DEFAULT.equalTypes(bound.constrainingType, possibleAnswer)) {
return false
}
}
}
return true
}
private fun commonSupertypeForNumberTypes(numberLowerBounds: Collection<KotlinType>): KotlinType? {
if (numberLowerBounds.isEmpty()) return null
val intersectionOfSupertypes = getIntersectionOfSupertypes(numberLowerBounds)
return TypeUtils.getDefaultPrimitiveNumberType(intersectionOfSupertypes) ?:
CommonSupertypes.commonSupertype(numberLowerBounds)
}
private fun getIntersectionOfSupertypes(types: Collection<KotlinType>): Set<KotlinType> {
val upperBounds = HashSet<KotlinType>()
for (type in types) {
val supertypes = type.constructor.supertypes
if (upperBounds.isEmpty()) {
upperBounds.addAll(supertypes)
}
else {
upperBounds.retainAll(supertypes)
}
}
return upperBounds
}
}
@@ -0,0 +1,129 @@
/*
* 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.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.EQUAL
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.SUB_TYPE
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.Bound
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.EXACT_BOUND
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.LOWER_BOUND
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.UPPER_BOUND
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.CompoundConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.Variance.INVARIANT
import org.jetbrains.kotlin.types.typeUtil.getNestedArguments
import org.jetbrains.kotlin.types.typesApproximation.approximateCapturedTypes
import java.util.*
data class ConstraintContext(
val position: ConstraintPosition,
// see TypeBounds.Bound.derivedFrom
val derivedFrom: Set<TypeParameterDescriptor>? = null,
val initial: Boolean = false)
fun ConstraintSystemImpl.incorporateBound(newBound: Bound) {
val typeVariable = newBound.typeVariable
val typeBounds = getTypeBounds(typeVariable)
for (oldBoundIndex in typeBounds.bounds.indices) {
addConstraintFromBounds(typeBounds.bounds[oldBoundIndex], newBound)
}
val boundsUsedIn = getBoundsUsedIn(typeVariable)
for (index in boundsUsedIn.indices) {
val boundUsedIn = boundsUsedIn[index]
generateNewBound(boundUsedIn, newBound)
}
val constrainingType = newBound.constrainingType
if (isMyTypeVariable(constrainingType)) {
val context = ConstraintContext(newBound.position, newBound.derivedFrom)
addBound(getMyTypeVariable(constrainingType)!!, typeVariable.correspondingType, newBound.kind.reverse(), context)
return
}
constrainingType.getNestedTypeVariables().forEach {
val boundsForNestedVariable = getTypeBounds(it).bounds
for (index in boundsForNestedVariable.indices) {
generateNewBound(newBound, boundsForNestedVariable[index])
}
}
}
private fun ConstraintSystemImpl.addConstraintFromBounds(old: Bound, new: Bound) {
if (old == new) return
val oldType = old.constrainingType
val newType = new.constrainingType
val context = ConstraintContext(CompoundConstraintPosition(old.position, new.position), old.derivedFrom + new.derivedFrom)
when {
old.kind.ordinal() < new.kind.ordinal() -> addConstraint(SUB_TYPE, oldType, newType, context)
old.kind.ordinal() > new.kind.ordinal() -> addConstraint(SUB_TYPE, newType, oldType, context)
old.kind == new.kind && old.kind == EXACT_BOUND -> addConstraint(EQUAL, oldType, newType, context)
}
}
private fun ConstraintSystemImpl.generateNewBound(bound: Bound, substitution: Bound) {
// Let's have a bound 'T <=> My<R>', and a substitution 'R <=> Type'.
// Here <=> means lower_bound, upper_bound or exact_bound constraint.
// Then a new bound 'T <=> My<_/in/out Type>' can be generated.
val substitutedType = when (substitution.kind) {
EXACT_BOUND -> substitution.constrainingType
UPPER_BOUND -> CapturedType(TypeProjectionImpl(Variance.OUT_VARIANCE, substitution.constrainingType))
LOWER_BOUND -> CapturedType(TypeProjectionImpl(Variance.IN_VARIANCE, substitution.constrainingType))
}
val newTypeProjection = TypeProjectionImpl(substitutedType)
val substitutor = TypeSubstitutor.create(mapOf(substitution.typeVariable.getTypeConstructor() to newTypeProjection))
val type = substitutor.substitute(bound.constrainingType, INVARIANT) ?: return
val position = CompoundConstraintPosition(bound.position, substitution.position)
fun addNewBound(newConstrainingType: KotlinType, newBoundKind: BoundKind) {
// We don't generate new recursive constraints
val nestedTypeVariables = newConstrainingType.getNestedTypeVariables(original = false)
if (nestedTypeVariables.contains(bound.typeVariable)) return
// We don't generate constraint if a type variable was substituted twice
val derivedFrom = HashSet(bound.derivedFrom + substitution.derivedFrom)
if (derivedFrom.contains(substitution.typeVariable)) return
derivedFrom.add(substitution.typeVariable)
addBound(bound.typeVariable, newConstrainingType, newBoundKind, ConstraintContext(position, derivedFrom))
}
if (substitution.kind == EXACT_BOUND) {
addNewBound(type, bound.kind)
return
}
val approximationBounds = approximateCapturedTypes(type)
// todo
// if we allow non-trivial type projections, we bump into errors like
// "Empty intersection for types [MutableCollection<in ('Int'..'Int?')>, MutableCollection<out Any?>, MutableCollection<in Int>]"
fun KotlinType.containsConstrainingTypeWithoutProjection() = this.getNestedArguments().any {
it.getType().getConstructor() == substitution.constrainingType.getConstructor() && it.getProjectionKind() == Variance.INVARIANT
}
if (approximationBounds.upper.containsConstrainingTypeWithoutProjection() && bound.kind != LOWER_BOUND) {
addNewBound(approximationBounds.upper, UPPER_BOUND)
}
if (approximationBounds.lower.containsConstrainingTypeWithoutProjection() && bound.kind != UPPER_BOUND) {
addNewBound(approximationBounds.lower, LOWER_BOUND)
}
}
@@ -0,0 +1,332 @@
/*
* 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.types;
import kotlin.CollectionsKt;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor;
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.resolve.scopes.KtScope;
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt;
import java.util.*;
import static org.jetbrains.kotlin.types.TypeUtils.topologicallySortSuperclassesAndRecordAllInstances;
import static org.jetbrains.kotlin.types.Variance.IN_VARIANCE;
import static org.jetbrains.kotlin.types.Variance.OUT_VARIANCE;
public class CommonSupertypes {
@Nullable
public static KotlinType commonSupertypeForNonDenotableTypes(@NotNull Collection<KotlinType> types) {
if (types.isEmpty()) return null;
if (types.size() == 1) {
KotlinType type = types.iterator().next();
if (type.getConstructor() instanceof IntersectionTypeConstructor) {
return commonSupertypeForNonDenotableTypes(type.getConstructor().getSupertypes());
}
}
return commonSupertype(types);
}
@NotNull
public static KotlinType commonSupertype(@NotNull Collection<KotlinType> types) {
// Recursion should not be significantly deeper than the deepest type in question
// It can be slightly deeper, though: e.g. when initial types are simple, but their supertypes are complex
return findCommonSupertype(types, 0, maxDepth(types) + 3);
}
private static int maxDepth(@NotNull Collection<KotlinType> types) {
int max = 0;
for (KotlinType type : types) {
int depth = depth(type);
if (max < depth) {
max = depth;
}
}
return max;
}
private static int depth(@NotNull final KotlinType type) {
return 1 + maxDepth(CollectionsKt.map(type.getArguments(), new Function1<TypeProjection, KotlinType>() {
@Override
public KotlinType invoke(TypeProjection projection) {
if (projection.isStarProjection()) {
// any type is good enough for depth here
return type.getConstructor().getBuiltIns().getAnyType();
}
return projection.getType();
}
}));
}
@NotNull
private static KotlinType findCommonSupertype(@NotNull Collection<KotlinType> types, int recursionDepth, int maxDepth) {
assert recursionDepth <= maxDepth : "Recursion depth exceeded: " + recursionDepth + " > " + maxDepth + " for types " + types;
boolean hasFlexible = false;
List<KotlinType> upper = new ArrayList<KotlinType>(types.size());
List<KotlinType> lower = new ArrayList<KotlinType>(types.size());
Set<FlexibleTypeCapabilities> capabilities = new LinkedHashSet<FlexibleTypeCapabilities>();
for (KotlinType type : types) {
if (FlexibleTypesKt.isFlexible(type)) {
hasFlexible = true;
Flexibility flexibility = FlexibleTypesKt.flexibility(type);
upper.add(flexibility.getUpperBound());
lower.add(flexibility.getLowerBound());
capabilities.add(flexibility.getExtraCapabilities());
}
else {
upper.add(type);
lower.add(type);
}
}
if (!hasFlexible) return commonSuperTypeForInflexible(types, recursionDepth, maxDepth);
return DelegatingFlexibleType.create(
commonSuperTypeForInflexible(lower, recursionDepth, maxDepth),
commonSuperTypeForInflexible(upper, recursionDepth, maxDepth),
CollectionsKt.single(capabilities) // mixing different capabilities is not supported
);
}
@NotNull
private static KotlinType commonSuperTypeForInflexible(@NotNull Collection<KotlinType> types, int recursionDepth, int maxDepth) {
assert !types.isEmpty();
Collection<KotlinType> typeSet = new HashSet<KotlinType>(types);
KotlinType bestFit = FlexibleTypesKt.singleBestRepresentative(typeSet);
if (bestFit != null) return bestFit;
// If any of the types is nullable, the result must be nullable
// This also removed Nothing and Nothing? because they are subtypes of everything else
boolean nullable = false;
for (Iterator<KotlinType> iterator = typeSet.iterator(); iterator.hasNext();) {
KotlinType type = iterator.next();
assert type != null;
assert !FlexibleTypesKt.isFlexible(type) : "Flexible type " + type + " passed to commonSuperTypeForInflexible";
if (KotlinBuiltIns.isNothingOrNullableNothing(type)) {
iterator.remove();
}
if (type.isError()) {
return ErrorUtils.createErrorType("Supertype of error type " + type);
}
nullable |= type.isMarkedNullable();
}
// Everything deleted => it's Nothing or Nothing?
if (typeSet.isEmpty()) {
// TODO : attributes
KotlinBuiltIns builtIns = types.iterator().next().getConstructor().getBuiltIns();
return nullable ? builtIns.getNullableNothingType() : builtIns.getNothingType();
}
if (typeSet.size() == 1) {
return TypeUtils.makeNullableIfNeeded(typeSet.iterator().next(), nullable);
}
// constructor of the supertype -> all of its instantiations occurring as supertypes
Map<TypeConstructor, Set<KotlinType>> commonSupertypes = computeCommonRawSupertypes(typeSet);
while (commonSupertypes.size() > 1) {
Set<KotlinType> merge = new HashSet<KotlinType>();
for (Set<KotlinType> supertypes : commonSupertypes.values()) {
merge.addAll(supertypes);
}
commonSupertypes = computeCommonRawSupertypes(merge);
}
assert !commonSupertypes.isEmpty() : commonSupertypes + " <- " + types;
// constructor of the supertype -> all of its instantiations occurring as supertypes
Map.Entry<TypeConstructor, Set<KotlinType>> entry = commonSupertypes.entrySet().iterator().next();
// Reconstructing type arguments if possible
KotlinType result = computeSupertypeProjections(entry.getKey(), entry.getValue(), recursionDepth, maxDepth);
return TypeUtils.makeNullableIfNeeded(result, nullable);
}
// Raw supertypes are superclasses w/o type arguments
// @return TypeConstructor -> all instantiations of this constructor occurring as supertypes
@NotNull
private static Map<TypeConstructor, Set<KotlinType>> computeCommonRawSupertypes(@NotNull Collection<KotlinType> types) {
assert !types.isEmpty();
Map<TypeConstructor, Set<KotlinType>> constructorToAllInstances = new HashMap<TypeConstructor, Set<KotlinType>>();
Set<TypeConstructor> commonSuperclasses = null;
List<TypeConstructor> order = null;
for (KotlinType type : types) {
Set<TypeConstructor> visited = new HashSet<TypeConstructor>();
order = topologicallySortSuperclassesAndRecordAllInstances(type, constructorToAllInstances, visited);
if (commonSuperclasses == null) {
commonSuperclasses = visited;
}
else {
commonSuperclasses.retainAll(visited);
}
}
assert order != null;
Set<TypeConstructor> notSource = new HashSet<TypeConstructor>();
Map<TypeConstructor, Set<KotlinType>> result = new HashMap<TypeConstructor, Set<KotlinType>>();
for (TypeConstructor superConstructor : order) {
if (!commonSuperclasses.contains(superConstructor)) {
continue;
}
if (!notSource.contains(superConstructor)) {
result.put(superConstructor, constructorToAllInstances.get(superConstructor));
markAll(superConstructor, notSource);
}
}
return result;
}
// constructor - type constructor of a supertype to be instantiated
// types - instantiations of constructor occurring as supertypes of classes we are trying to intersect
@NotNull
private static KotlinType computeSupertypeProjections(@NotNull TypeConstructor constructor, @NotNull Set<KotlinType> types, int recursionDepth, int maxDepth) {
// we assume that all the given types are applications of the same type constructor
assert !types.isEmpty();
if (types.size() == 1) {
return types.iterator().next();
}
List<TypeParameterDescriptor> parameters = constructor.getParameters();
List<TypeProjection> newProjections = new ArrayList<TypeProjection>(parameters.size());
for (TypeParameterDescriptor parameterDescriptor : parameters) {
Set<TypeProjection> typeProjections = new HashSet<TypeProjection>();
for (KotlinType type : types) {
typeProjections.add(type.getArguments().get(parameterDescriptor.getIndex()));
}
newProjections.add(computeSupertypeProjection(parameterDescriptor, typeProjections, recursionDepth, maxDepth));
}
boolean nullable = false;
for (KotlinType type : types) {
nullable |= type.isMarkedNullable();
}
ClassifierDescriptor classifier = constructor.getDeclarationDescriptor();
KtScope newScope;
if (classifier instanceof ClassDescriptor) {
newScope = ((ClassDescriptor) classifier).getMemberScope(newProjections);
}
else if (classifier instanceof TypeParameterDescriptor) {
newScope = classifier.getDefaultType().getMemberScope();
}
else {
newScope = ErrorUtils.createErrorScope("A scope for common supertype which is not a normal classifier", true);
}
return KotlinTypeImpl.create(Annotations.Companion.getEMPTY(), constructor, nullable, newProjections, newScope);
}
@NotNull
private static TypeProjection computeSupertypeProjection(
@NotNull TypeParameterDescriptor parameterDescriptor,
@NotNull Set<TypeProjection> typeProjections,
int recursionDepth, int maxDepth
) {
TypeProjection singleBestProjection = FlexibleTypesKt.singleBestRepresentative(typeProjections);
if (singleBestProjection != null) {
return singleBestProjection;
}
if (recursionDepth >= maxDepth) {
// If recursion is too deep, we cut it by taking <out Any?> as an ultimate supertype argument
// Example: class A : Base<A>; class B : Base<B>, commonSuperType(A, B) = Base<out Any?>
return new TypeProjectionImpl(OUT_VARIANCE, DescriptorUtilsKt.getBuiltIns(parameterDescriptor).getNullableAnyType());
}
Set<KotlinType> ins = new HashSet<KotlinType>();
Set<KotlinType> outs = new HashSet<KotlinType>();
Variance variance = parameterDescriptor.getVariance();
switch (variance) {
case INVARIANT:
// Nothing
break;
case IN_VARIANCE:
outs = null;
break;
case OUT_VARIANCE:
ins = null;
break;
}
for (TypeProjection projection : typeProjections) {
Variance projectionKind = projection.getProjectionKind();
if (projectionKind.getAllowsInPosition()) {
if (ins != null) {
ins.add(projection.getType());
}
}
else {
ins = null;
}
if (projectionKind.getAllowsOutPosition()) {
if (outs != null) {
outs.add(projection.getType());
}
}
else {
outs = null;
}
}
if (outs != null) {
assert !outs.isEmpty() : "Out projections is empty for parameter " + parameterDescriptor + ", type projections " + typeProjections;
Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE;
KotlinType superType = findCommonSupertype(outs, recursionDepth + 1, maxDepth);
for (KotlinType upperBound: parameterDescriptor.getUpperBounds()) {
if (!TypeUtilsKt.isSubtypeOf(superType, upperBound)) {
return new StarProjectionImpl(parameterDescriptor);
}
}
return new TypeProjectionImpl(projectionKind, superType);
}
if (ins != null) {
assert !ins.isEmpty() : "In projections is empty for parameter " + parameterDescriptor + ", type projections " + typeProjections;
KotlinType intersection = TypeIntersector.intersectTypes(KotlinTypeChecker.DEFAULT, ins);
if (intersection == null) {
return new TypeProjectionImpl(OUT_VARIANCE, findCommonSupertype(parameterDescriptor.getUpperBounds(), recursionDepth + 1, maxDepth));
}
Variance projectionKind = variance == IN_VARIANCE ? Variance.INVARIANT : IN_VARIANCE;
return new TypeProjectionImpl(projectionKind, intersection);
}
else {
Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE;
return new TypeProjectionImpl(projectionKind, findCommonSupertype(parameterDescriptor.getUpperBounds(), recursionDepth + 1, maxDepth));
}
}
private static void markAll(@NotNull TypeConstructor typeConstructor, @NotNull Set<TypeConstructor> markerSet) {
markerSet.add(typeConstructor);
for (KotlinType type : typeConstructor.getSupertypes()) {
markAll(type.getConstructor(), markerSet);
}
}
}
@@ -0,0 +1,219 @@
/*
* 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.types;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
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.ConstraintSystemImpl;
import org.jetbrains.kotlin.resolve.scopes.ChainedScope;
import org.jetbrains.kotlin.resolve.scopes.KtScope;
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
import java.util.*;
import static org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImplKt.registerTypeVariables;
import static org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.SPECIAL;
import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt.getBuiltIns;
public class TypeIntersector {
public static boolean isIntersectionEmpty(@NotNull KotlinType typeA, @NotNull KotlinType typeB) {
return intersectTypes(KotlinTypeChecker.DEFAULT, new LinkedHashSet<KotlinType>(Arrays.asList(typeA, typeB))) == null;
}
@Nullable
public static KotlinType intersectTypes(@NotNull KotlinTypeChecker typeChecker, @NotNull Collection<KotlinType> types) {
assert !types.isEmpty() : "Attempting to intersect empty collection of types, this case should be dealt with on the call site.";
if (types.size() == 1) {
return types.iterator().next();
}
// Intersection of T1..Tn is an intersection of their non-null versions,
// made nullable is they all were nullable
KotlinType nothingOrNullableNothing = null;
boolean allNullable = true;
List<KotlinType> nullabilityStripped = new ArrayList<KotlinType>(types.size());
for (KotlinType type : types) {
if (type.isError()) continue;
if (KotlinBuiltIns.isNothingOrNullableNothing(type)) {
nothingOrNullableNothing = type;
}
allNullable &= type.isMarkedNullable();
nullabilityStripped.add(TypeUtils.makeNotNullable(type));
}
if (nothingOrNullableNothing != null) {
return TypeUtils.makeNullableAsSpecified(nothingOrNullableNothing, allNullable);
}
if (nullabilityStripped.isEmpty()) {
// All types were errors
return ErrorUtils.createErrorType("Intersection of error types: " + types);
}
// Now we remove types that have subtypes in the list
List<KotlinType> resultingTypes = new ArrayList<KotlinType>();
outer:
for (KotlinType type : nullabilityStripped) {
if (!TypeUtils.canHaveSubtypes(typeChecker, type)) {
for (KotlinType other : nullabilityStripped) {
// It makes sense to check for subtyping (other <: type), despite that
// type is not supposed to be open, for there're enums
if (!TypeUnifier.mayBeEqual(type, other) && !typeChecker.isSubtypeOf(type, other) && !typeChecker.isSubtypeOf(other, type)) {
return null;
}
}
return TypeUtils.makeNullableAsSpecified(type, allNullable);
}
else {
for (KotlinType other : nullabilityStripped) {
if (!type.equals(other) && typeChecker.isSubtypeOf(other, type)) {
continue outer;
}
}
}
// Don't add type if it is already present, to avoid trivial type intersections in result
for (KotlinType other : resultingTypes) {
if (typeChecker.equalTypes(other, type)) {
continue outer;
}
}
resultingTypes.add(type);
}
if (resultingTypes.isEmpty()) {
// If we ended up here, it means that all types from `nullabilityStripped` were excluded by the code above
// most likely, this is because they are all semantically interchangeable (e.g. List<Foo>! and List<Foo>),
// in that case, we can safely select the best representative out of that set and return it
// TODO: maybe return the most specific among the types that are subtypes to all others in the `nullabilityStripped`?
// TODO: e.g. among {Int, Int?, Int!}, return `Int` (now it returns `Int!`).
KotlinType bestRepresentative = FlexibleTypesKt.singleBestRepresentative(nullabilityStripped);
if (bestRepresentative == null) {
throw new AssertionError("Empty intersection for types " + types);
}
return TypeUtils.makeNullableAsSpecified(bestRepresentative, allNullable);
}
if (resultingTypes.size() == 1) {
return TypeUtils.makeNullableAsSpecified(resultingTypes.get(0), allNullable);
}
TypeConstructor constructor = new IntersectionTypeConstructor(Annotations.Companion.getEMPTY(), resultingTypes);
KtScope[] scopes = new KtScope[resultingTypes.size()];
int i = 0;
for (KotlinType type : resultingTypes) {
scopes[i] = type.getMemberScope();
i++;
}
return KotlinTypeImpl.create(
Annotations.Companion.getEMPTY(),
constructor,
allNullable,
Collections.<TypeProjection>emptyList(),
new IntersectionScope(constructor, scopes)
);
}
/**
* Note: this method was used in overload and override bindings to approximate type parameters with several bounds,
* but as it turned out at some point, that logic was inconsistent with Java rules, so it was simplified.
* Most of the other usages of this method are left untouched but probably should be investigated closely if they're still valid.
*/
@NotNull
public static KotlinType getUpperBoundsAsType(@NotNull TypeParameterDescriptor descriptor) {
List<KotlinType> upperBounds = descriptor.getUpperBounds();
assert !upperBounds.isEmpty() : "Upper bound list is empty: " + descriptor;
KotlinType upperBoundsAsType = intersectTypes(KotlinTypeChecker.DEFAULT, upperBounds);
return upperBoundsAsType != null ? upperBoundsAsType : getBuiltIns(descriptor).getNothingType();
}
// TODO: check intersectability, don't use a chained scope
private static class IntersectionScope extends ChainedScope {
public IntersectionScope(@NotNull TypeConstructor constructor, @NotNull KtScope[] scopes) {
super(null, "member scope for intersection type " + constructor, scopes);
}
@NotNull
@Override
public DeclarationDescriptor getContainingDeclaration() {
throw new UnsupportedOperationException("Should not call getContainingDeclaration on intersection scope " + this);
}
}
private static class TypeUnifier {
private static class TypeParameterUsage {
private final TypeParameterDescriptor typeParameterDescriptor;
private final Variance howTheTypeParameterIsUsed;
public TypeParameterUsage(TypeParameterDescriptor typeParameterDescriptor, Variance howTheTypeParameterIsUsed) {
this.typeParameterDescriptor = typeParameterDescriptor;
this.howTheTypeParameterIsUsed = howTheTypeParameterIsUsed;
}
}
public static boolean mayBeEqual(@NotNull KotlinType type, @NotNull KotlinType other) {
return unify(type, other);
}
private static boolean unify(KotlinType withParameters, KotlinType expected) {
// T -> how T is used
final Map<TypeParameterDescriptor, Variance> parameters = new HashMap<TypeParameterDescriptor, Variance>();
Function1<TypeParameterUsage, Unit> processor = new Function1<TypeParameterUsage, Unit>() {
@Override
public Unit invoke(TypeParameterUsage parameterUsage) {
Variance howTheTypeIsUsedBefore = parameters.get(parameterUsage.typeParameterDescriptor);
if (howTheTypeIsUsedBefore == null) {
howTheTypeIsUsedBefore = Variance.INVARIANT;
}
parameters.put(parameterUsage.typeParameterDescriptor,
parameterUsage.howTheTypeParameterIsUsed.superpose(howTheTypeIsUsedBefore));
return Unit.INSTANCE;
}
};
processAllTypeParameters(withParameters, Variance.INVARIANT, processor);
processAllTypeParameters(expected, Variance.INVARIANT, processor);
ConstraintSystemImpl constraintSystem = new ConstraintSystemImpl();
registerTypeVariables(constraintSystem, parameters);
constraintSystem.addSubtypeConstraint(withParameters, expected, SPECIAL.position());
return constraintSystem.getStatus().isSuccessful();
}
private static void processAllTypeParameters(KotlinType type, Variance howThisTypeIsUsed, Function1<TypeParameterUsage, Unit> result) {
ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
if (descriptor instanceof TypeParameterDescriptor) {
result.invoke(new TypeParameterUsage((TypeParameterDescriptor) descriptor, howThisTypeIsUsed));
}
for (TypeProjection projection : type.getArguments()) {
if (projection.isStarProjection()) continue;
processAllTypeParameters(projection.getType(), projection.getProjectionKind(), result);
}
}
}
}