Move constraint system, type intersection, common supertypes to compiler
This commit is contained in:
@@ -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)
|
||||
+73
@@ -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 }
|
||||
}
|
||||
+90
@@ -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
|
||||
}
|
||||
+580
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
+94
@@ -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 <R> foo(r: R, t: java.util.List<R>) {}</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<R> is a supertype of List<String>"</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 <R: Any> 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 <E> 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 <R> foo(t: List<R>) {}</pre> in invocation <tt>foo(hashSet("s"))</tt>
|
||||
* there is type constructor mismatch: <tt>"HashSet<String> cannot be a subtype of List<R>"</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 <T> foo(t: Array<Array<T>>) {}</pre>
|
||||
* in invocation <tt>foo(array)</tt> where array has type <tt>Array<Array<out Int>></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
|
||||
}
|
||||
}
|
||||
+129
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user