diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/NewCommonSuperTypeCalculator.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/NewCommonSuperTypeCalculator.kt new file mode 100644 index 00000000000..92c2647f8ee --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/NewCommonSuperTypeCalculator.kt @@ -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 { + 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 { + // 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 { + 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 { + 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): List { + 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().apply { + type.anySuperTypeConstructor { add(it); false } + } + + private fun superTypeWithGivenConstructor(types: List, 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 and A <: List. + * Also suppose that B <: List. + * Note that common supertype for A and B is CS(List, List) & CS(List, List), + * but it is too complicated and we will return not so accurate type: CS(List, List, List) + */ + val correspondingSuperTypes = types.flatMap { + with(NewKotlinTypeChecker) { + typeCheckerContext.findCorrespondingSupertypes(it, constructor) + } + } + + val arguments = ArrayList(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 { + // Inv, Inv = Inv + 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, Inv) = Inv<*> + return StarProjectionImpl(parameter) + } + else { + asOut = true + } + } + else { + asOut = !thereIsIn + } + } + + // CS(Out, Out) = Out + // CS(In, In) = In + 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) + } + } +} diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt index 03c48a2620a..4c48ca7b3a2 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt @@ -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 Array.intersect(other: Iterable) { @@ -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): Collection { + private fun convertLowerTypesWithKnowledgeOfNumberTypes(lowerConstraints: Collection): List { 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? { + private fun commonSupertypeForNumberTypes(numberLowerBounds: List): 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): Set { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt b/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt index efab5200e72..4824b4178fd 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt @@ -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 diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/IntersectionType.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/IntersectionType.kt index 7101a1eca34..c6de2dc0129 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/IntersectionType.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/IntersectionType.kt @@ -23,6 +23,9 @@ import kotlin.collections.HashSet fun intersectWrappedTypes(types: Collection) = intersectTypes(types.map { it.unwrap() }) + +fun intersectTypes(types: List) = intersectTypes(types as List) as SimpleType + fun intersectTypes(types: List): UnwrappedType { when (types.size) { 0 -> error("Expected some types") @@ -45,7 +48,7 @@ fun intersectTypes(types: List): 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 { * * 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 { - return TypeIntersector.intersectTypes(types) -} object TypeIntersector { diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewCapturedType.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewCapturedType.kt index c650d889843..eac6c2c1fb3 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewCapturedType.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewCapturedType.kt @@ -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) } diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt index 26ae5c430a1..da13e7a78d2 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt @@ -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 { @@ -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 */