[NI] Implement New CommonSuperTypeCalculation

This commit is contained in:
Stanislav Erokhin
2017-04-13 22:22:54 +03:00
parent 6aac67aa7e
commit ff8a57dc26
6 changed files with 206 additions and 19 deletions
@@ -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)
}
}
}
@@ -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
*/