[NI] Implement New CommonSuperTypeCalculation
This commit is contained in:
+186
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright 2010-2017 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
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.checker.NewKotlinTypeChecker
|
||||
import org.jetbrains.kotlin.types.checker.TypeCheckerContext
|
||||
import org.jetbrains.kotlin.types.checker.anySuperTypeConstructor
|
||||
import org.jetbrains.kotlin.types.checker.intersectTypes
|
||||
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
|
||||
|
||||
object NewCommonSuperTypeCalculator {
|
||||
|
||||
fun commonSuperType(types: List<UnwrappedType>): UnwrappedType {
|
||||
if (types.isEmpty()) throw IllegalStateException("Empty collection for input")
|
||||
|
||||
types.singleOrNull()?.let { return it }
|
||||
|
||||
var thereIsFlexibleTypes = false
|
||||
|
||||
val lowers = types.map {
|
||||
when (it) {
|
||||
is SimpleType -> it
|
||||
is FlexibleType -> {
|
||||
if (it is DynamicType) return it
|
||||
// raw types are allowed here and will be transformed to FlexibleTypes
|
||||
|
||||
thereIsFlexibleTypes = true
|
||||
it.lowerBound
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val lowerSuperType = commonSuperTypeForSimpleTypes(lowers)
|
||||
if (!thereIsFlexibleTypes) return lowerSuperType
|
||||
|
||||
val upperSuperType = commonSuperTypeForSimpleTypes(types.map { it.upperIfFlexible() })
|
||||
return FlexibleTypeImpl(lowerSuperType, upperSuperType)
|
||||
}
|
||||
|
||||
private fun commonSuperTypeForSimpleTypes(types: List<SimpleType>): SimpleType {
|
||||
// i.e. result type also should be marked nullable
|
||||
val anyMarkedNullable = types.any { it.isMarkedNullable }
|
||||
val notNullTypes = if (anyMarkedNullable) types.map { it.makeNullableAsSpecified(false) } else types
|
||||
|
||||
val commonSuperTypes = commonSuperTypeForNotNullTypes(notNullTypes)
|
||||
|
||||
return if (anyMarkedNullable) commonSuperTypes.makeNullableAsSpecified(true) else commonSuperTypes
|
||||
}
|
||||
|
||||
private fun commonSuperTypeForNotNullTypes(types: List<SimpleType>): SimpleType {
|
||||
val filteredType = types.filterNot { targetType ->
|
||||
types.any { other -> targetType != other && NewKotlinTypeChecker.isSubtypeOf(targetType, other)}
|
||||
}
|
||||
// seems like all types are equal
|
||||
if (filteredType.isEmpty()) return types.first()
|
||||
|
||||
filteredType.singleOrNull()?.let { return it }
|
||||
|
||||
return findSuperTypeConstructorsAndIntersectResult(filteredType)
|
||||
}
|
||||
|
||||
private fun findSuperTypeConstructorsAndIntersectResult(types: List<SimpleType>): SimpleType {
|
||||
return intersectTypes(allCommonSuperTypeConstructors(types).map { superTypeWithGivenConstructor(types, it) })
|
||||
}
|
||||
|
||||
/**
|
||||
* Note that if there is captured type C, then no one else is not subtype of C => lowerType cannot help here
|
||||
*/
|
||||
private fun allCommonSuperTypeConstructors(types: List<SimpleType>): List<TypeConstructor> {
|
||||
val result = collectAllSupertypes(types.first())
|
||||
for (type in types) {
|
||||
if (type === types.first()) continue
|
||||
|
||||
result.retainAll(collectAllSupertypes(type))
|
||||
}
|
||||
return result.filterNot { target ->
|
||||
result.any { other ->
|
||||
other != target && other.supertypes.any { it.constructor == target }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectAllSupertypes(type: SimpleType) = LinkedHashSet<TypeConstructor>().apply {
|
||||
type.anySuperTypeConstructor { add(it); false }
|
||||
}
|
||||
|
||||
private fun superTypeWithGivenConstructor(types: List<SimpleType>, constructor: TypeConstructor): SimpleType {
|
||||
if (constructor.parameters.isEmpty()) return KotlinTypeFactory.simpleType(Annotations.EMPTY, constructor, emptyList(), nullable = false)
|
||||
|
||||
val typeCheckerContext = TypeCheckerContext(false)
|
||||
|
||||
/**
|
||||
* Sometimes one type can have several supertypes with given type constructor, suppose A <: List<Int> and A <: List<Double>.
|
||||
* Also suppose that B <: List<String>.
|
||||
* Note that common supertype for A and B is CS(List<Int>, List<String>) & CS(List<Double>, List<String>),
|
||||
* but it is too complicated and we will return not so accurate type: CS(List<Int>, List<Double>, List<String>)
|
||||
*/
|
||||
val correspondingSuperTypes = types.flatMap {
|
||||
with(NewKotlinTypeChecker) {
|
||||
typeCheckerContext.findCorrespondingSupertypes(it, constructor)
|
||||
}
|
||||
}
|
||||
|
||||
val arguments = ArrayList<TypeProjection>(constructor.parameters.size)
|
||||
for ((index, parameter) in constructor.parameters.withIndex()) {
|
||||
var thereIsStar = false
|
||||
val typeProjections = correspondingSuperTypes.mapNotNull {
|
||||
it.arguments.getOrNull(index)?.let {
|
||||
if (it.isStarProjection) {
|
||||
thereIsStar = true
|
||||
null
|
||||
} else it
|
||||
}
|
||||
}
|
||||
|
||||
val argument =
|
||||
if (thereIsStar || typeProjections.isEmpty()) {
|
||||
StarProjectionImpl(parameter)
|
||||
}
|
||||
else {
|
||||
calculateArgument(parameter, typeProjections)
|
||||
}
|
||||
|
||||
arguments.add(argument)
|
||||
}
|
||||
return KotlinTypeFactory.simpleType(Annotations.EMPTY, constructor, arguments, nullable = false)
|
||||
}
|
||||
|
||||
// no star projections in arguments
|
||||
private fun calculateArgument(parameter: TypeParameterDescriptor, arguments: List<TypeProjection>): TypeProjection {
|
||||
// Inv<A>, Inv<A> = Inv<A>
|
||||
if (parameter.variance == Variance.INVARIANT && arguments.all { it.projectionKind == Variance.INVARIANT }) {
|
||||
val first = arguments.first()
|
||||
if (arguments.all { it.type == first.type }) return first
|
||||
}
|
||||
|
||||
val asOut: Boolean
|
||||
if (parameter.variance != Variance.INVARIANT) {
|
||||
asOut = parameter.variance == Variance.OUT_VARIANCE
|
||||
}
|
||||
else {
|
||||
val thereIsOut = arguments.any { it.projectionKind == Variance.OUT_VARIANCE }
|
||||
val thereIsIn = arguments.any { it.projectionKind == Variance.IN_VARIANCE }
|
||||
if (thereIsOut) {
|
||||
if (thereIsIn) {
|
||||
// CS(Inv<out X>, Inv<in Y>) = Inv<*>
|
||||
return StarProjectionImpl(parameter)
|
||||
}
|
||||
else {
|
||||
asOut = true
|
||||
}
|
||||
}
|
||||
else {
|
||||
asOut = !thereIsIn
|
||||
}
|
||||
}
|
||||
|
||||
// CS(Out<X>, Out<Y>) = Out<CS(X, Y)>
|
||||
// CS(In<X>, In<Y>) = In<X & Y>
|
||||
if (asOut) {
|
||||
val type = commonSuperType(arguments.map { it.type.unwrap() })
|
||||
return if (parameter.variance != Variance.INVARIANT) return type.asTypeProjection() else TypeProjectionImpl(Variance.OUT_VARIANCE, type)
|
||||
}
|
||||
else {
|
||||
val type = intersectTypes(arguments.map { it.type.unwrap() })
|
||||
return if (parameter.variance != Variance.INVARIANT) return type.asTypeProjection() else TypeProjectionImpl(Variance.IN_VARIANCE, type)
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-7
@@ -17,7 +17,7 @@
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.components
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.resolve.calls.components.CommonSupertypeCalculator
|
||||
import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.components.FixationOrderCalculator.ResolveDirection
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind
|
||||
@@ -28,7 +28,6 @@ import org.jetbrains.kotlin.types.checker.intersectTypes
|
||||
import java.util.*
|
||||
|
||||
class ResultTypeResolver(
|
||||
val commonSupertypeCalculator: CommonSupertypeCalculator,
|
||||
val typeApproximator: TypeApproximator
|
||||
) {
|
||||
interface Context {
|
||||
@@ -43,7 +42,7 @@ class ResultTypeResolver(
|
||||
if (direction == ResolveDirection.TO_SUBTYPE || direction == ResolveDirection.UNKNOWN) {
|
||||
val lowerConstraints = variableWithConstraints.constraints.filter { it.kind == ConstraintKind.LOWER && c.isProperType(it.type) }
|
||||
if (lowerConstraints.isNotEmpty()) {
|
||||
val commonSupertype = commonSupertypeCalculator(convertLowerTypesWithKnowledgeOfNumberTypes(lowerConstraints))
|
||||
val commonSupertype = NewCommonSuperTypeCalculator.commonSuperType(convertLowerTypesWithKnowledgeOfNumberTypes(lowerConstraints))
|
||||
/**
|
||||
*
|
||||
* fun <T> Array<out T>.intersect(other: Iterable<T>) {
|
||||
@@ -76,7 +75,7 @@ class ResultTypeResolver(
|
||||
return typeApproximator.approximateToSubType(upperType, TypeApproximatorConfiguration.CapturedTypesApproximation) ?: upperType
|
||||
}
|
||||
|
||||
return return builtIns.anyType
|
||||
return builtIns.anyType
|
||||
}
|
||||
|
||||
fun findResultIfThereIsEqualsConstraint(
|
||||
@@ -101,7 +100,7 @@ class ResultTypeResolver(
|
||||
}
|
||||
|
||||
|
||||
private fun convertLowerTypesWithKnowledgeOfNumberTypes(lowerConstraints: Collection<Constraint>): Collection<UnwrappedType> {
|
||||
private fun convertLowerTypesWithKnowledgeOfNumberTypes(lowerConstraints: Collection<Constraint>): List<UnwrappedType> {
|
||||
if (lowerConstraints.isEmpty()) return emptyList()
|
||||
|
||||
val (numberLowerBounds, generalLowerBounds) = lowerConstraints.map { it.type }.partition { it.isNumberValueType() }
|
||||
@@ -120,11 +119,11 @@ class ResultTypeResolver(
|
||||
KotlinBuiltIns.isInt(this) ||
|
||||
KotlinBuiltIns.isLong(this)
|
||||
|
||||
private fun commonSupertypeForNumberTypes(numberLowerBounds: Collection<UnwrappedType>): UnwrappedType? {
|
||||
private fun commonSupertypeForNumberTypes(numberLowerBounds: List<UnwrappedType>): UnwrappedType? {
|
||||
if (numberLowerBounds.isEmpty()) return null
|
||||
val intersectionOfSupertypes = getIntersectionOfSupertypes(numberLowerBounds)
|
||||
return TypeUtils.getDefaultPrimitiveNumberType(intersectionOfSupertypes)?.unwrap() ?:
|
||||
commonSupertypeCalculator(numberLowerBounds)
|
||||
NewCommonSuperTypeCalculator.commonSuperType(numberLowerBounds)
|
||||
}
|
||||
|
||||
private fun getIntersectionOfSupertypes(types: Collection<UnwrappedType>): Set<UnwrappedType> {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.types
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.components.CommonSupertypeCalculator
|
||||
import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator
|
||||
import org.jetbrains.kotlin.resolve.calls.USE_NEW_INFERENCE
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableTypeConstructor
|
||||
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration.IntersectionStrategy.*
|
||||
@@ -76,7 +76,7 @@ open class TypeApproximatorConfiguration {
|
||||
object CapturedTypesApproximation : TypeApproximatorConfiguration.AbstractCapturedTypesApproximation(FROM_EXPRESSION)
|
||||
}
|
||||
|
||||
class TypeApproximator(private val commonSupertypeCalculator: CommonSupertypeCalculator) {
|
||||
class TypeApproximator {
|
||||
private val referenceApproximateToSuperType = this::approximateToSuperType
|
||||
private val referenceApproximateToSubType = this::approximateToSubType
|
||||
|
||||
@@ -181,7 +181,7 @@ class TypeApproximator(private val commonSupertypeCalculator: CommonSupertypeCal
|
||||
ALLOWED -> if (!thereIsApproximation) return null else intersectTypes(newTypes)
|
||||
TO_FIRST -> if (toSuper) newTypes.first() else return type.defaultResult(toSuper = false)
|
||||
// commonSupertypeCalculator should handle flexible types correctly
|
||||
TO_COMMON_SUPERTYPE -> if (toSuper) commonSupertypeCalculator(newTypes) else return type.defaultResult(toSuper = false)
|
||||
TO_COMMON_SUPERTYPE -> if (toSuper) NewCommonSuperTypeCalculator.commonSuperType(newTypes) else return type.defaultResult(toSuper = false)
|
||||
}
|
||||
|
||||
return if (type.isMarkedNullable) baseResult.makeNullableAsSpecified(true) else baseResult
|
||||
|
||||
@@ -23,6 +23,9 @@ import kotlin.collections.HashSet
|
||||
|
||||
fun intersectWrappedTypes(types: Collection<KotlinType>) = intersectTypes(types.map { it.unwrap() })
|
||||
|
||||
|
||||
fun intersectTypes(types: List<SimpleType>) = intersectTypes(types as List<UnwrappedType>) as SimpleType
|
||||
|
||||
fun intersectTypes(types: List<UnwrappedType>): UnwrappedType {
|
||||
when (types.size) {
|
||||
0 -> error("Expected some types")
|
||||
@@ -45,7 +48,7 @@ fun intersectTypes(types: List<UnwrappedType>): UnwrappedType {
|
||||
}
|
||||
|
||||
if (!hasFlexibleTypes) {
|
||||
return intersectTypes(lowerBounds)
|
||||
return TypeIntersector.intersectTypes(lowerBounds)
|
||||
}
|
||||
|
||||
val upperBounds = types.map { it.upperIfFlexible() }
|
||||
@@ -56,14 +59,9 @@ fun intersectTypes(types: List<UnwrappedType>): UnwrappedType {
|
||||
*
|
||||
* Note: when we construct intersection type of dynamic(or Raw type) & other type, we can get non-dynamic type. // todo discuss
|
||||
*/
|
||||
return KotlinTypeFactory.flexibleType(intersectTypes(lowerBounds), intersectTypes(upperBounds))
|
||||
return KotlinTypeFactory.flexibleType(TypeIntersector.intersectTypes(lowerBounds), TypeIntersector.intersectTypes(upperBounds))
|
||||
}
|
||||
|
||||
// types.size >= 2
|
||||
// It is incorrect see to nullability here, because of KT-12684
|
||||
private fun intersectTypes(types: List<SimpleType>): SimpleType {
|
||||
return TypeIntersector.intersectTypes(types)
|
||||
}
|
||||
|
||||
object TypeIntersector {
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ fun prepareArgumentTypeRegardingCaptureTypes(argumentType: UnwrappedType): Unwra
|
||||
}
|
||||
if (simpleType is NewCapturedType) {
|
||||
// todo may be we should respect flexible capture types also...
|
||||
return simpleType.constructor.supertypes.takeIf { it.isNotEmpty() }?.let(::intersectTypes) ?: argumentType.builtIns.nullableAnyType
|
||||
return simpleType.constructor.supertypes.takeIf { it.isNotEmpty() }?.let{ intersectTypes(it) } ?: argumentType.builtIns.nullableAnyType
|
||||
}
|
||||
return captureFromExpression(simpleType)
|
||||
}
|
||||
|
||||
@@ -227,7 +227,8 @@ object NewKotlinTypeChecker : KotlinTypeChecker {
|
||||
}
|
||||
|
||||
// nullability was checked earlier via nullabilityChecker
|
||||
private fun TypeCheckerContext.findCorrespondingSupertypes(
|
||||
// should be used only if you really sure that it is correct
|
||||
fun TypeCheckerContext.findCorrespondingSupertypes(
|
||||
baseType: SimpleType,
|
||||
constructor: TypeConstructor
|
||||
): List<SimpleType> {
|
||||
@@ -410,6 +411,9 @@ object NullabilityChecker {
|
||||
fun UnwrappedType.hasSupertypeWithGivenTypeConstructor(typeConstructor: TypeConstructor) =
|
||||
TypeCheckerContext(false).anySupertype(lowerIfFlexible(), { it.constructor == typeConstructor }, { SupertypesPolicy.LowerIfFlexible })
|
||||
|
||||
fun UnwrappedType.anySuperTypeConstructor(predicate: (TypeConstructor) -> Boolean) =
|
||||
TypeCheckerContext(false).anySupertype(lowerIfFlexible(), { predicate(it.constructor) }, { SupertypesPolicy.LowerIfFlexible })
|
||||
|
||||
/**
|
||||
* ClassType means that type constructor for this type is type for real class or interface
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user