diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/withInInitializer.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/withInInitializer.txt index 119e30466b6..a9d6980bd18 100644 --- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/withInInitializer.txt +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/withInInitializer.txt @@ -13,8 +13,8 @@ FILE: withInInitializer.kt super() } - public final val list: R|kotlin/collections/List & java/io/Serializable)>| = R|kotlin/collections/listOf| & java/io/Serializable)|>(vararg(Int(1), Int(2), Int(3), String())) - public get(): R|kotlin/collections/List & java/io/Serializable)>| + public final val list: R|kotlin/collections/List & java/io/Serializable)>| = R|kotlin/collections/listOf| & java/io/Serializable)|>(vararg(Int(1), Int(2), Int(3), String())) + public get(): R|kotlin/collections/List & java/io/Serializable)>| public final val data: R|First| = R|/First.First|(Int(42)) public get(): R|First| diff --git a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypes.kt b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypes.kt index 1fa05488b79..efc28bda8b2 100644 --- a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypes.kt +++ b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypes.kt @@ -235,7 +235,7 @@ class ConeRawType(lowerBound: ConeKotlinType, upperBound: ConeKotlinType) : Cone */ class ConeIntersectionType( val intersectedTypes: Collection -) : ConeSimpleKotlinType(), TypeConstructorMarker { +) : ConeSimpleKotlinType(), IntersectionTypeConstructorMarker { override val typeArguments: Array get() = emptyArray() diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt index c92b0f51ab6..53636c0e757 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.fir.types import org.jetbrains.kotlin.fir.diagnostics.ConeIntermediateDiagnostic +import org.jetbrains.kotlin.fir.isPrimitiveNumberOrUnsignedNumberType import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.calls.NoSubstitutor import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider @@ -316,6 +317,11 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo return this.defaultType } + override fun KotlinTypeMarker.isSpecial(): Boolean { + // TODO + return false + } + override fun TypeConstructorMarker.isTypeVariable(): Boolean { return this is ConeTypeVariableTypeConstructor } @@ -362,4 +368,10 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo require(this is ConeIntegerLiteralType) return this.getApproximatedType() } + + override fun KotlinTypeMarker.isSignedOrUnsignedNumberType(): Boolean { + require(this is ConeKotlinType) + if (this !is ConeClassLikeType) return false + return isPrimitiveNumberOrUnsignedNumberType() + } } diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/Primitives.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/Primitives.kt index fe25ce4033c..f2f7e82b657 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/Primitives.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/Primitives.kt @@ -12,29 +12,35 @@ import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl import org.jetbrains.kotlin.name.ClassId object PrimitiveTypes { - val Boolean = StandardClassIds.Boolean.createType() - val Char = StandardClassIds.Char.createType() - val Byte = StandardClassIds.Byte.createType() - val Short = StandardClassIds.Short.createType() - val Int = StandardClassIds.Int.createType() - val Long = StandardClassIds.Long.createType() - val Float = StandardClassIds.Float.createType() - val Double = StandardClassIds.Double.createType() + val Boolean: ConeClassLikeType = StandardClassIds.Boolean.createType() + val Char: ConeClassLikeType = StandardClassIds.Char.createType() + val Byte: ConeClassLikeType = StandardClassIds.Byte.createType() + val Short: ConeClassLikeType = StandardClassIds.Short.createType() + val Int: ConeClassLikeType = StandardClassIds.Int.createType() + val Long: ConeClassLikeType = StandardClassIds.Long.createType() + val Float: ConeClassLikeType = StandardClassIds.Float.createType() + val Double: ConeClassLikeType = StandardClassIds.Double.createType() } private fun ClassId.createType(): ConeClassLikeType = ConeClassLikeTypeImpl(ConeClassLikeLookupTagImpl(this), emptyArray(), isNullable = false) -fun ConeClassLikeType.isDouble() = lookupTag.classId == StandardClassIds.Double -fun ConeClassLikeType.isFloat() = lookupTag.classId == StandardClassIds.Float -fun ConeClassLikeType.isLong() = lookupTag.classId == StandardClassIds.Long -fun ConeClassLikeType.isInt() = lookupTag.classId == StandardClassIds.Int -fun ConeClassLikeType.isShort() = lookupTag.classId == StandardClassIds.Short -fun ConeClassLikeType.isByte() = lookupTag.classId == StandardClassIds.Byte +fun ConeClassLikeType.isDouble(): Boolean = lookupTag.classId == StandardClassIds.Double +fun ConeClassLikeType.isFloat(): Boolean = lookupTag.classId == StandardClassIds.Float +fun ConeClassLikeType.isLong(): Boolean = lookupTag.classId == StandardClassIds.Long +fun ConeClassLikeType.isInt(): Boolean = lookupTag.classId == StandardClassIds.Int +fun ConeClassLikeType.isShort(): Boolean = lookupTag.classId == StandardClassIds.Short +fun ConeClassLikeType.isByte(): Boolean = lookupTag.classId == StandardClassIds.Byte -private val PRIMITIVE_NUMBER_CLASS_IDS = setOf( +fun ConeClassLikeType.isPrimitiveNumberType(): Boolean = lookupTag.classId in PRIMITIVE_NUMBER_CLASS_IDS +fun ConeClassLikeType.isPrimitiveUnsignedNumberType(): Boolean = lookupTag.classId in PRIMITIVE_UNSIGNED_NUMBER_CLASS_IDS +fun ConeClassLikeType.isPrimitiveNumberOrUnsignedNumberType(): Boolean = isPrimitiveNumberType() || isPrimitiveUnsignedNumberType() + +private val PRIMITIVE_NUMBER_CLASS_IDS: Set = setOf( StandardClassIds.Double, StandardClassIds.Float, StandardClassIds.Long, StandardClassIds.Int, StandardClassIds.Short, StandardClassIds.Byte ) -fun ConeClassLikeType.isPrimitiveNumberType() = lookupTag.classId in PRIMITIVE_NUMBER_CLASS_IDS +private val PRIMITIVE_UNSIGNED_NUMBER_CLASS_IDS: Set = setOf( + StandardClassIds.ULong, StandardClassIds.UInt, StandardClassIds.UShort, StandardClassIds.UByte +) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt index b42abc99fe0..524759d1138 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt @@ -7,7 +7,8 @@ package org.jetbrains.kotlin.resolve.calls.inference.components import org.jetbrains.kotlin.resolve.calls.components.CreateFreshVariablesSubstitutor.shouldBeFlexible import org.jetbrains.kotlin.resolve.calls.inference.model.* -import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.AbstractTypeApproximator +import org.jetbrains.kotlin.types.TypeApproximatorConfiguration import org.jetbrains.kotlin.types.model.* import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.kotlin.utils.SmartSet diff --git a/compiler/resolution/src/org/jetbrains/kotlin/types/AbstractTypeApproximator.kt b/compiler/resolution/src/org/jetbrains/kotlin/types/AbstractTypeApproximator.kt new file mode 100644 index 00000000000..30729fe7eb2 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/types/AbstractTypeApproximator.kt @@ -0,0 +1,532 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.types + +import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator.commonSuperType +import org.jetbrains.kotlin.types.model.* +import java.util.concurrent.ConcurrentHashMap + +abstract class AbstractTypeApproximator(val ctx: TypeSystemInferenceExtensionContext) : TypeSystemInferenceExtensionContext by ctx { + + private class ApproximationResult(val type: KotlinTypeMarker?) + + private val cacheForIncorporationConfigToSuperDirection = ConcurrentHashMap() + private val cacheForIncorporationConfigToSubtypeDirection = ConcurrentHashMap() + + private val referenceApproximateToSuperType: (SimpleTypeMarker, TypeApproximatorConfiguration, Int) -> KotlinTypeMarker? + get() = this::approximateSimpleToSuperType + private val referenceApproximateToSubType: (SimpleTypeMarker, TypeApproximatorConfiguration, Int) -> KotlinTypeMarker? + get() = this::approximateSimpleToSubType + + companion object { + const val CACHE_FOR_INCORPORATION_MAX_SIZE = 500 + } + + abstract fun createErrorType(message: String): SimpleTypeMarker + + // null means that this input type is the result, i.e. input type not contains not-allowed kind of types + // type <: resultType + fun approximateToSuperType(type: KotlinTypeMarker, conf: TypeApproximatorConfiguration): KotlinTypeMarker? = + approximateToSuperType(type, conf, -type.typeDepth()) + + // resultType <: type + fun approximateToSubType(type: KotlinTypeMarker, conf: TypeApproximatorConfiguration): KotlinTypeMarker? = + approximateToSubType(type, conf, -type.typeDepth()) + + fun clearCache() { + cacheForIncorporationConfigToSubtypeDirection.clear() + cacheForIncorporationConfigToSuperDirection.clear() + } + + private fun checkExceptionalCases( + type: KotlinTypeMarker, depth: Int, conf: TypeApproximatorConfiguration, toSuper: Boolean + ): ApproximationResult? { + return when { + type.isSpecial() -> + null.toApproximationResult() + + type.isError() -> + // todo -- fix builtIns. Now builtIns here is DefaultBuiltIns + (if (conf.errorType) null else type.defaultResult(toSuper)).toApproximationResult() + + depth > 3 -> + type.defaultResult(toSuper).toApproximationResult() + + else -> null + } + } + + private fun KotlinTypeMarker?.toApproximationResult(): ApproximationResult = ApproximationResult(this) + + private inline fun cachedValue( + type: KotlinTypeMarker, + conf: TypeApproximatorConfiguration, + toSuper: Boolean, + approximate: () -> KotlinTypeMarker? + ): KotlinTypeMarker? { + // Approximator depends on a configuration, so cache should take it into account + // Here, we cache only types for configuration "from incorporation", which is used most intensively + if (conf !is TypeApproximatorConfiguration.IncorporationConfiguration) return approximate() + + val cache = if (toSuper) cacheForIncorporationConfigToSuperDirection else cacheForIncorporationConfigToSubtypeDirection + + if (cache.size > CACHE_FOR_INCORPORATION_MAX_SIZE) return approximate() + + return cache.getOrPut(type, { approximate().toApproximationResult() }).type + } + + private fun approximateToSuperType(type: KotlinTypeMarker, conf: TypeApproximatorConfiguration, depth: Int): KotlinTypeMarker? { + checkExceptionalCases(type, depth, conf, toSuper = true)?.let { return it.type } + + return cachedValue(type, conf, toSuper = true) { + approximateTo( + prepareType(type), conf, { upperBound() }, + referenceApproximateToSuperType, depth + ) + } + } + + private fun approximateToSubType(type: KotlinTypeMarker, conf: TypeApproximatorConfiguration, depth: Int): KotlinTypeMarker? { + checkExceptionalCases(type, depth, conf, toSuper = false)?.let { return it.type } + + return cachedValue(type, conf, toSuper = false) { + approximateTo( + prepareType(type), conf, { lowerBound() }, + referenceApproximateToSubType, depth + ) + } + } + + // Don't call this method directly, it should be used only in approximateToSuperType/approximateToSubType (use these methods instead) + // This method contains detailed implementation only for type approximation, it doesn't check exceptional cases and doesn't use cache + private fun approximateTo( + type: KotlinTypeMarker, + conf: TypeApproximatorConfiguration, + bound: FlexibleTypeMarker.() -> SimpleTypeMarker, + approximateTo: (SimpleTypeMarker, TypeApproximatorConfiguration, depth: Int) -> KotlinTypeMarker?, + depth: Int + ): KotlinTypeMarker? { + when (type) { + is SimpleTypeMarker -> return approximateTo(type, conf, depth) + is FlexibleTypeMarker -> { + if (type.isDynamic()) { + return if (conf.dynamic) null else type.bound() + } else if (type.asRawType() != null) { + return if (conf.rawType) null else type.bound() + } + +// TODO: Restore check +// TODO: currently we can lose information about enhancement, should be fixed later +// assert(type is FlexibleTypeImpl || type is FlexibleTypeWithEnhancement) { +// "Unexpected subclass of FlexibleType: ${type::class.java.canonicalName}, type = $type" +// } + + if (conf.flexible) { + /** + * Let inputType = L_1..U_1; resultType = L_2..U_2 + * We should create resultType such as inputType <: resultType. + * It means that if A <: inputType, then A <: U_1. And, because inputType <: resultType, + * A <: resultType => A <: U_2. I.e. for every type A such A <: U_1, A <: U_2 => U_1 <: U_2. + * + * Similar for L_1 <: L_2: Let B : resultType <: B. L_2 <: B and L_1 <: B. + * I.e. for every type B such as L_2 <: B, L_1 <: B. For example B = L_2. + */ + val lowerBound = type.lowerBound() + val upperBound = type.upperBound() + + val lowerResult = approximateTo(lowerBound, conf, depth) + + val upperResult = if (type !is RawTypeMarker && lowerBound.typeConstructor() == upperBound.typeConstructor()) + lowerResult?.withNullability(upperBound.isMarkedNullable()) + else + approximateTo(upperBound, conf, depth) + if (lowerResult == null && upperResult == null) return null + + /** + * If C <: L..U then C <: L. + * inputType.lower <: lowerResult => inputType.lower <: lowerResult?.lowerIfFlexible() + * i.e. this type is correct. We use this type, because this type more flexible. + * + * If U_1 <: U_2.lower .. U_2.upper, then we know only that U_1 <: U_2.upper. + */ + return createFlexibleType( + lowerResult?.lowerBoundIfFlexible() ?: lowerBound, + upperResult?.upperBoundIfFlexible() ?: upperBound + ) + } else { + return type.bound().let { approximateTo(it, conf, depth) ?: it } + } + } + else -> error("sealed") + } + } + + private fun isIntersectionTypeEffectivelyNothing(constructor: IntersectionTypeConstructorMarker): Boolean { + // We consider intersection as Nothing only if one of it's component is a primitive number type + // It's intentional we're not trying to prove population of some type as it was in OI + + return constructor.supertypes().any { + !it.isMarkedNullable() && it.isSignedOrUnsignedNumberType() + } + } + + private fun approximateIntersectionType( + type: SimpleTypeMarker, + conf: TypeApproximatorConfiguration, + toSuper: Boolean, + depth: Int + ): KotlinTypeMarker? { + val typeConstructor = type.typeConstructor() + assert(typeConstructor.isIntersection()) { + "Should be intersection type: $type, typeConstructor class: ${typeConstructor::class.java.canonicalName}" + } + assert(typeConstructor.supertypes().isNotEmpty()) { + "Supertypes for intersection type should not be empty: $type" + } + + var thereIsApproximation = false + val newTypes = typeConstructor.supertypes().map { + val newType = if (toSuper) approximateToSuperType(it, conf, depth) else approximateToSubType(it, conf, depth) + if (newType != null) { + thereIsApproximation = true + newType + } else it + } + + /** + * For case ALLOWED: + * A <: A', B <: B' => A & B <: A' & B' + * + * For other case -- it's impossible to find some type except Nothing as subType for intersection type. + */ + val baseResult = when (conf.intersection) { + TypeApproximatorConfiguration.IntersectionStrategy.ALLOWED -> if (!thereIsApproximation) return null else intersectTypes(newTypes) + TypeApproximatorConfiguration.IntersectionStrategy.TO_FIRST -> if (toSuper) newTypes.first() else return type.defaultResult(toSuper = false) + // commonSupertypeCalculator should handle flexible types correctly + TypeApproximatorConfiguration.IntersectionStrategy.TO_COMMON_SUPERTYPE -> { + if (!toSuper) return type.defaultResult(toSuper = false) + val resultType = commonSuperType(newTypes) + approximateToSuperType(resultType, conf) ?: resultType + } + } + + return if (type.isMarkedNullable()) baseResult.withNullability(true) else baseResult + } + + private fun approximateCapturedType( + type: CapturedTypeMarker, + conf: TypeApproximatorConfiguration, + toSuper: Boolean, + depth: Int + ): KotlinTypeMarker? { + val supertypes = type.typeConstructor().supertypes() + val baseSuperType = when (supertypes.size) { + 0 -> nullableAnyType() // Let C = in Int, then superType for C and C? is Any? + 1 -> supertypes.single() + + // Consider the following example: + // A.getA()::class.java, where `getA()` returns some class from Java + // From `::class` we are getting type KClass>, where Cap have two supertypes: + // - Any (from declared upper bound of type parameter for KClass) + // - (A..A?) -- from A!, projection type of captured type + + // Now, after approximation we were getting type `KClass`, because { Any & (A..A?) } = A, + // but in old inference type was equal to `KClass`. + + // Important note that from the point of type system first type is more specific: + // Here, approximation of KClass> is a type KClass such that KClass> <: KClass => + // So, the the more specific type for T would be "some non-null (because of declared upper bound type) subtype of A", which is `out A` + + // But for now, to reduce differences in behaviour of old and new inference, we'll approximate such types to `KClass` + + // Once NI will be more stabilized, we'll use more specific type + + else -> { + val projection = type.typeConstructorProjection() + if (projection.isStarProjection()) intersectTypes(supertypes.toList()) + else projection.getType() + } + } + val baseSubType = type.lowerType() ?: nothingType() + + if (conf.capturedType(ctx, type)) { + /** + * Here everything is ok if bounds for this captured type should not be approximated. + * But. If such bounds contains some unauthorized types, then we cannot leave this captured type "as is". + * And we cannot create new capture type, because meaning of new captured type is not clear. + * So, we will just approximate such types + * + * todo handle flexible types + */ + if (approximateToSuperType(baseSuperType, conf, depth) == null && approximateToSubType(baseSubType, conf, depth) == null) { + return null + } + } + val baseResult = if (toSuper) approximateToSuperType(baseSuperType, conf, depth) ?: baseSuperType else approximateToSubType( + baseSubType, + conf, + depth + ) ?: baseSubType + + // C = in Int, Int <: C => Int? <: C? + // C = out Number, C <: Number => C? <: Number? + return when { + type.isMarkedNullable() -> baseResult.withNullability(true) + type.isProjectionNotNull() -> baseResult.withNullability(false) + else -> baseResult + } + } + + private fun approximateSimpleToSuperType(type: SimpleTypeMarker, conf: TypeApproximatorConfiguration, depth: Int) = + approximateTo(type, conf, toSuper = true, depth = depth) + + private fun approximateSimpleToSubType(type: SimpleTypeMarker, conf: TypeApproximatorConfiguration, depth: Int) = + approximateTo(type, conf, toSuper = false, depth = depth) + + private fun approximateTo( + type: SimpleTypeMarker, + conf: TypeApproximatorConfiguration, + toSuper: Boolean, + depth: Int + ): KotlinTypeMarker? { + if (type.argumentsCount() != 0) { + return approximateParametrizedType(type, conf, toSuper, depth + 1) + } + + val definitelyNotNullType = type.asDefinitelyNotNullType() + if (definitelyNotNullType != null) { + return approximateDefinitelyNotNullType(definitelyNotNullType, conf, toSuper, depth) + } + + val typeConstructor = type.typeConstructor() + + if (typeConstructor.isCapturedTypeConstructor()) { + val capturedType = type.asCapturedType() + require(capturedType != null) { + // KT-16147 + "Type is inconsistent -- somewhere we create type with typeConstructor = $typeConstructor " + + "and class: ${type::class.java.canonicalName}. type.toString() = $type" + } + return approximateCapturedType(capturedType, conf, toSuper, depth) + } + + if (typeConstructor.isIntersection()) { + return approximateIntersectionType(type, conf, toSuper, depth) + } + + if (typeConstructor is TypeVariableTypeConstructorMarker) { + return if (conf.typeVariable(typeConstructor)) null else type.defaultResult(toSuper) + } + + if (typeConstructor.isIntegerLiteralTypeConstructor()) { + return if (conf.integerLiteralType) + typeConstructor.getApproximatedIntegerLiteralType().withNullability(type.isMarkedNullable()) + else + null + } + + return null // simple classifier type + } + + private fun approximateDefinitelyNotNullType( + type: DefinitelyNotNullTypeMarker, + conf: TypeApproximatorConfiguration, + toSuper: Boolean, + depth: Int + ): KotlinTypeMarker? { + val originalType = type.original() + val approximatedOriginalType = + if (toSuper) approximateToSuperType(originalType, conf, depth) else approximateToSubType(originalType, conf, depth) + + return if (conf.definitelyNotNullType) { + approximatedOriginalType?.makeDefinitelyNotNullOrNotNull() + } else { + if (toSuper) + (approximatedOriginalType ?: originalType).withNullability(false) + else + type.defaultResult(toSuper) + } + } + + private fun isApproximateDirectionToSuper(effectiveVariance: TypeVariance, toSuper: Boolean) = + when (effectiveVariance) { + TypeVariance.OUT -> toSuper + TypeVariance.IN -> !toSuper + TypeVariance.INV -> throw AssertionError("Incorrect variance $effectiveVariance") + } + + private fun approximateParametrizedType( + type: SimpleTypeMarker, + conf: TypeApproximatorConfiguration, + toSuper: Boolean, + depth: Int + ): SimpleTypeMarker? { + val typeConstructor = type.typeConstructor() + if (typeConstructor.parametersCount() != type.argumentsCount()) { + return if (conf.errorType) { + createErrorType("Inconsistent type: $type (parameters.size = ${typeConstructor.parametersCount()}, arguments.size = ${type.argumentsCount()})") + } else type.defaultResult(toSuper) + } + + val newArguments = arrayOfNulls(type.argumentsCount()) + + loop@ for (index in 0 until type.argumentsCount()) { + val parameter = typeConstructor.getParameter(index) + val argument = type.getArgument(index) + + if (argument.isStarProjection()) continue + + val effectiveVariance = AbstractTypeChecker.effectiveVariance(parameter.getVariance(), argument.getVariance()) + + val argumentType = newArguments[index]?.getType() ?: argument.getType() + + val capturedType = argumentType.lowerBoundIfFlexible().asCapturedType() + val capturedStarProjectionOrNull = + capturedType?.typeConstructorProjection()?.takeIf { it.isStarProjection() } + + if (capturedStarProjectionOrNull != null && + (effectiveVariance == TypeVariance.OUT || effectiveVariance == TypeVariance.INV) && + toSuper && + capturedType.typeParameter() == parameter + ) { + newArguments[index] = capturedStarProjectionOrNull + continue@loop + } + + when (effectiveVariance) { + null -> { + return if (conf.errorType) { + createErrorType( + "Inconsistent type: $type ($index parameter has declared variance: ${parameter.getVariance()}, " + + "but argument variance is ${argument.getVariance()})" + ) + } else type.defaultResult(toSuper) + } + TypeVariance.OUT, TypeVariance.IN -> { + if ( + conf.intersectionTypesInContravariantPositions && + effectiveVariance == TypeVariance.IN && + argumentType.typeConstructor().isIntersection() + ) { + val argumentTypeConstructor = argumentType.typeConstructor() + if (argumentTypeConstructor.isIntersection() && isIntersectionTypeEffectivelyNothing(argumentTypeConstructor as IntersectionTypeConstructorMarker)) { + newArguments[index] = createStarProjection(parameter) + continue@loop + } + } + + /** + * Out <: Out + * Inv <: Inv + + * In <: In + * Inv <: Inv + */ + val approximatedArgument = argumentType.let { + if (isApproximateDirectionToSuper(effectiveVariance, toSuper)) { + approximateToSuperType(it, conf, depth) + } else { + approximateToSubType(it, conf, depth) + } + } ?: continue@loop + + if ( + conf.intersection != TypeApproximatorConfiguration.IntersectionStrategy.ALLOWED && + effectiveVariance == TypeVariance.OUT && + argumentType.typeConstructor().isIntersection() + ) { + var shouldReplaceWithStar = false + for (upperBoundIndex in 0 until parameter.upperBoundCount()) { + if (!AbstractTypeChecker.isSubtypeOf(ctx, approximatedArgument, parameter.getUpperBound(upperBoundIndex))) { + shouldReplaceWithStar = true + break + } + } + if (shouldReplaceWithStar) { + newArguments[index] = createStarProjection(parameter) + continue@loop + } + } + + if (parameter.getVariance() == TypeVariance.INV) { + newArguments[index] = createTypeArgument(approximatedArgument, effectiveVariance) + } else { + newArguments[index] = approximatedArgument.asTypeArgument() + } + } + TypeVariance.INV -> { + if (!toSuper) { + // Inv cannot be approximated to subType + val toSubType = approximateToSubType(argumentType, conf, depth) ?: continue@loop + + // Inv is supertype for Inv + if (!AbstractTypeChecker.equalTypes( + this, + argumentType, + toSubType + ) + ) return type.defaultResult(toSuper) + + // also Captured(out Nothing) = Nothing + newArguments[index] = toSubType.asTypeArgument() + continue@loop + } + + /** + * Example with non-trivial both type approximations: + * Inv> where C = in Int + * Inv> <: Inv> + * Inv> <: Inv> + * + * So such case is rare and we will chose Inv> for now. + * + * Note that for case Inv we will chose Inv, because it is more informative then Inv. + * May be we should do the same for deeper types, but not now. + */ + if (argumentType.typeConstructor().isCapturedTypeConstructor()) { + val subType = approximateToSubType(argumentType, conf, depth) ?: continue@loop + if (!subType.isTrivialSub()) { + newArguments[index] = createTypeArgument(subType, TypeVariance.IN) + continue@loop + } + } + + val approximatedSuperType = + approximateToSuperType(argumentType, conf, depth) ?: continue@loop // null means that this type we can leave as is + if (approximatedSuperType.isTrivialSuper()) { + val approximatedSubType = + approximateToSubType(argumentType, conf, depth) ?: continue@loop // seems like this is never null + if (!approximatedSubType.isTrivialSub()) { + newArguments[index] = createTypeArgument(approximatedSubType, TypeVariance.IN) + continue@loop + } + } + + if (AbstractTypeChecker.equalTypes(this, argumentType, approximatedSuperType)) { + newArguments[index] = approximatedSuperType.asTypeArgument() + } else { + newArguments[index] = createTypeArgument(approximatedSuperType, TypeVariance.OUT) + } + } + } + } + + if (newArguments.all { it == null }) return null + + val newArgumentsList = List(type.argumentsCount()) { index -> newArguments[index] ?: type.getArgument(index) } + return type.replaceArguments(newArgumentsList) + } + + private fun KotlinTypeMarker.defaultResult(toSuper: Boolean) = if (toSuper) nullableAnyType() else { + if (this is SimpleTypeMarker && isMarkedNullable()) nullableNothingType() else nothingType() + } + + // Any? or Any! + private fun KotlinTypeMarker.isTrivialSuper() = upperBoundIfFlexible().isNullableAny() + + // Nothing or Nothing! + private fun KotlinTypeMarker.isTrivialSub() = lowerBoundIfFlexible().isNothing() +} diff --git a/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt b/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt index 9dfe0c9883d..080e933fa1a 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt @@ -19,97 +19,8 @@ package org.jetbrains.kotlin.types import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings -import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator import org.jetbrains.kotlin.resolve.calls.components.ClassicTypeSystemContextForCS -import org.jetbrains.kotlin.types.TypeApproximatorConfiguration.IntersectionStrategy.* -import org.jetbrains.kotlin.types.checker.NewCapturedTypeConstructor -import org.jetbrains.kotlin.types.model.* -import org.jetbrains.kotlin.types.model.CaptureStatus.* -import org.jetbrains.kotlin.types.typeUtil.isSignedOrUnsignedNumberType -import java.util.concurrent.ConcurrentHashMap - - -open class TypeApproximatorConfiguration { - enum class IntersectionStrategy { - ALLOWED, - TO_FIRST, - TO_COMMON_SUPERTYPE - } - - open val flexible get() = false // simple flexible types (FlexibleTypeImpl) - open val dynamic get() = false // DynamicType - open val rawType get() = false // RawTypeImpl - open val errorType get() = false - open val integerLiteralType: Boolean = false // IntegerLiteralTypeConstructor - open val definitelyNotNullType get() = true - open val intersection: IntersectionStrategy = TO_COMMON_SUPERTYPE - open val intersectionTypesInContravariantPositions = false - - open val typeVariable: (TypeVariableTypeConstructorMarker) -> Boolean = { false } - open fun capturedType(ctx: TypeSystemInferenceExtensionContext, type: CapturedTypeMarker): Boolean = - false // true means that this type we can leave as is - - abstract class AllFlexibleSameValue : TypeApproximatorConfiguration() { - abstract val allFlexible: Boolean - - override val flexible get() = allFlexible - override val dynamic get() = allFlexible - override val rawType get() = allFlexible - } - - object LocalDeclaration : AllFlexibleSameValue() { - override val allFlexible get() = true - override val intersection get() = ALLOWED - override val errorType get() = true - override val integerLiteralType: Boolean get() = true - override val intersectionTypesInContravariantPositions: Boolean get() = true - } - - object PublicDeclaration : AllFlexibleSameValue() { - override val allFlexible get() = true - override val errorType get() = true - override val definitelyNotNullType get() = false - override val integerLiteralType: Boolean get() = true - override val intersectionTypesInContravariantPositions: Boolean get() = true - } - - abstract class AbstractCapturedTypesApproximation(val approximatedCapturedStatus: CaptureStatus) : - TypeApproximatorConfiguration.AllFlexibleSameValue() { - override val allFlexible get() = true - override val errorType get() = true - - // i.e. will be approximated only approximatedCapturedStatus captured types - override fun capturedType(ctx: TypeSystemInferenceExtensionContext, type: CapturedTypeMarker): Boolean = - type.captureStatus(ctx) != approximatedCapturedStatus - - override val intersection get() = ALLOWED - override val typeVariable: (TypeVariableTypeConstructorMarker) -> Boolean get() = { true } - } - - object IncorporationConfiguration : TypeApproximatorConfiguration.AbstractCapturedTypesApproximation(FOR_INCORPORATION) - object SubtypeCapturedTypesApproximation : TypeApproximatorConfiguration.AbstractCapturedTypesApproximation(FOR_SUBTYPING) - object InternalTypesApproximation : TypeApproximatorConfiguration.AbstractCapturedTypesApproximation(FROM_EXPRESSION) { - override val integerLiteralType: Boolean get() = true - override val intersectionTypesInContravariantPositions: Boolean get() = true - } - - object FinalApproximationAfterResolutionAndInference : - TypeApproximatorConfiguration.AbstractCapturedTypesApproximation(FROM_EXPRESSION) { - override val integerLiteralType: Boolean get() = true - override val intersectionTypesInContravariantPositions: Boolean get() = true - } - - object IntegerLiteralsTypesApproximation : TypeApproximatorConfiguration.AllFlexibleSameValue() { - override val integerLiteralType: Boolean get() = true - override val allFlexible: Boolean get() = true - override val intersection get() = ALLOWED - override val typeVariable: (TypeVariableTypeConstructorMarker) -> Boolean get() = { true } - override val errorType: Boolean get() = true - - override fun capturedType(ctx: TypeSystemInferenceExtensionContext, type: CapturedTypeMarker): Boolean = true - } -} - +import org.jetbrains.kotlin.types.model.SimpleTypeMarker class TypeApproximator(builtIns: KotlinBuiltIns) : AbstractTypeApproximator(ClassicTypeSystemContextForCS(builtIns)) { fun approximateDeclarationType(baseType: KotlinType, local: Boolean, languageVersionSettings: LanguageVersionSettings): UnwrappedType { @@ -128,524 +39,9 @@ class TypeApproximator(builtIns: KotlinBuiltIns) : AbstractTypeApproximator(Clas // resultType <: type fun approximateToSubType(type: UnwrappedType, conf: TypeApproximatorConfiguration): UnwrappedType? = super.approximateToSubType(type, conf) as UnwrappedType? + + override fun createErrorType(message: String): SimpleTypeMarker { + return ErrorUtils.createErrorType(message) + } } -abstract class AbstractTypeApproximator(val ctx: TypeSystemInferenceExtensionContext) : TypeSystemInferenceExtensionContext by ctx { - - private class ApproximationResult(val type: KotlinTypeMarker?) - - private val cacheForIncorporationConfigToSuperDirection = ConcurrentHashMap() - private val cacheForIncorporationConfigToSubtypeDirection = ConcurrentHashMap() - - private val referenceApproximateToSuperType get() = this::approximateSimpleToSuperType - private val referenceApproximateToSubType get() = this::approximateSimpleToSubType - - companion object { - const val CACHE_FOR_INCORPORATION_MAX_SIZE = 500 - } - - open fun createErrorType(message: String): SimpleTypeMarker = - ErrorUtils.createErrorType(message) - - - // null means that this input type is the result, i.e. input type not contains not-allowed kind of types - // type <: resultType - fun approximateToSuperType(type: KotlinTypeMarker, conf: TypeApproximatorConfiguration): KotlinTypeMarker? = - approximateToSuperType(type, conf, -type.typeDepth()) - - // resultType <: type - fun approximateToSubType(type: KotlinTypeMarker, conf: TypeApproximatorConfiguration): KotlinTypeMarker? = - approximateToSubType(type, conf, -type.typeDepth()) - - fun clearCache() { - cacheForIncorporationConfigToSubtypeDirection.clear() - cacheForIncorporationConfigToSuperDirection.clear() - } - - private fun checkExceptionalCases( - type: KotlinTypeMarker, depth: Int, conf: TypeApproximatorConfiguration, toSuper: Boolean - ): ApproximationResult? { - return when { - type is TypeUtils.SpecialType -> - null.toApproximationResult() - - type.isError() -> - // todo -- fix builtIns. Now builtIns here is DefaultBuiltIns - (if (conf.errorType) null else type.defaultResult(toSuper)).toApproximationResult() - - depth > 3 -> - type.defaultResult(toSuper).toApproximationResult() - - else -> null - } - } - - private fun KotlinTypeMarker?.toApproximationResult(): ApproximationResult = ApproximationResult(this) - - private inline fun cachedValue( - type: KotlinTypeMarker, - conf: TypeApproximatorConfiguration, - toSuper: Boolean, - approximate: () -> KotlinTypeMarker? - ): KotlinTypeMarker? { - // Approximator depends on a configuration, so cache should take it into account - // Here, we cache only types for configuration "from incorporation", which is used most intensively - if (conf !is TypeApproximatorConfiguration.IncorporationConfiguration) return approximate() - - val cache = if (toSuper) cacheForIncorporationConfigToSuperDirection else cacheForIncorporationConfigToSubtypeDirection - - if (cache.size > CACHE_FOR_INCORPORATION_MAX_SIZE) return approximate() - - return cache.getOrPut(type, { approximate().toApproximationResult() }).type - } - - private fun approximateToSuperType(type: KotlinTypeMarker, conf: TypeApproximatorConfiguration, depth: Int): KotlinTypeMarker? { - checkExceptionalCases(type, depth, conf, toSuper = true)?.let { return it.type } - - return cachedValue(type, conf, toSuper = true) { - approximateTo( - prepareType(type), conf, { upperBound() }, - referenceApproximateToSuperType, depth - ) - } - } - - private fun approximateToSubType(type: KotlinTypeMarker, conf: TypeApproximatorConfiguration, depth: Int): KotlinTypeMarker? { - checkExceptionalCases(type, depth, conf, toSuper = false)?.let { return it.type } - - return cachedValue(type, conf, toSuper = false) { - approximateTo( - prepareType(type), conf, { lowerBound() }, - referenceApproximateToSubType, depth - ) - } - } - - // Don't call this method directly, it should be used only in approximateToSuperType/approximateToSubType (use these methods instead) - // This method contains detailed implementation only for type approximation, it doesn't check exceptional cases and doesn't use cache - private fun approximateTo( - type: KotlinTypeMarker, - conf: TypeApproximatorConfiguration, - bound: FlexibleTypeMarker.() -> SimpleTypeMarker, - approximateTo: (SimpleTypeMarker, TypeApproximatorConfiguration, depth: Int) -> KotlinTypeMarker?, - depth: Int - ): KotlinTypeMarker? { - when (type) { - is SimpleTypeMarker -> return approximateTo(type, conf, depth) - is FlexibleTypeMarker -> { - if (type.isDynamic()) { - return if (conf.dynamic) null else type.bound() - } else if (type.asRawType() != null) { - return if (conf.rawType) null else type.bound() - } - -// TODO: Restore check -// TODO: currently we can lose information about enhancement, should be fixed later -// assert(type is FlexibleTypeImpl || type is FlexibleTypeWithEnhancement) { -// "Unexpected subclass of FlexibleType: ${type::class.java.canonicalName}, type = $type" -// } - - if (conf.flexible) { - /** - * Let inputType = L_1..U_1; resultType = L_2..U_2 - * We should create resultType such as inputType <: resultType. - * It means that if A <: inputType, then A <: U_1. And, because inputType <: resultType, - * A <: resultType => A <: U_2. I.e. for every type A such A <: U_1, A <: U_2 => U_1 <: U_2. - * - * Similar for L_1 <: L_2: Let B : resultType <: B. L_2 <: B and L_1 <: B. - * I.e. for every type B such as L_2 <: B, L_1 <: B. For example B = L_2. - */ - val lowerBound = type.lowerBound() - val upperBound = type.upperBound() - - val lowerResult = approximateTo(lowerBound, conf, depth) - - val upperResult = if (type !is RawTypeMarker && lowerBound.typeConstructor() == upperBound.typeConstructor()) - lowerResult?.withNullability(upperBound.isMarkedNullable()) - else - approximateTo(upperBound, conf, depth) - if (lowerResult == null && upperResult == null) return null - - /** - * If C <: L..U then C <: L. - * inputType.lower <: lowerResult => inputType.lower <: lowerResult?.lowerIfFlexible() - * i.e. this type is correct. We use this type, because this type more flexible. - * - * If U_1 <: U_2.lower .. U_2.upper, then we know only that U_1 <: U_2.upper. - */ - return createFlexibleType( - lowerResult?.lowerBoundIfFlexible() ?: lowerBound, - upperResult?.upperBoundIfFlexible() ?: upperBound - ) - } else { - return type.bound().let { approximateTo(it, conf, depth) ?: it } - } - } - else -> error("sealed") - } - } - - private fun isIntersectionTypeEffectivelyNothing(constructor: IntersectionTypeConstructor): Boolean { - // We consider intersection as Nothing only if one of it's component is a primitive number type - // It's intentional we're not trying to prove population of some type as it was in OI - - return constructor.supertypes.any { !it.isMarkedNullable && it.isSignedOrUnsignedNumberType() } - } - - private fun approximateIntersectionType( - type: SimpleTypeMarker, - conf: TypeApproximatorConfiguration, - toSuper: Boolean, - depth: Int - ): KotlinTypeMarker? { - val typeConstructor = type.typeConstructor() - assert(typeConstructor.isIntersection()) { - "Should be intersection type: $type, typeConstructor class: ${typeConstructor::class.java.canonicalName}" - } - assert(typeConstructor.supertypes().isNotEmpty()) { - "Supertypes for intersection type should not be empty: $type" - } - - var thereIsApproximation = false - val newTypes = typeConstructor.supertypes().map { - val newType = if (toSuper) approximateToSuperType(it, conf, depth) else approximateToSubType(it, conf, depth) - if (newType != null) { - thereIsApproximation = true - newType - } else it - } - - /** - * For case ALLOWED: - * A <: A', B <: B' => A & B <: A' & B' - * - * For other case -- it's impossible to find some type except Nothing as subType for intersection type. - */ - val baseResult = when (conf.intersection) { - 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) return type.defaultResult(toSuper = false) - val resultType = with(NewCommonSuperTypeCalculator) { commonSuperType(newTypes) } - approximateToSuperType(resultType, conf) ?: resultType - } - } - - return if (type.isMarkedNullable()) baseResult.withNullability(true) else baseResult - } - - private fun approximateCapturedType( - type: CapturedTypeMarker, - conf: TypeApproximatorConfiguration, - toSuper: Boolean, - depth: Int - ): KotlinTypeMarker? { - val supertypes = type.typeConstructor().supertypes() - val baseSuperType = when (supertypes.size) { - 0 -> nullableAnyType() // Let C = in Int, then superType for C and C? is Any? - 1 -> supertypes.single() - - // Consider the following example: - // A.getA()::class.java, where `getA()` returns some class from Java - // From `::class` we are getting type KClass>, where Cap have two supertypes: - // - Any (from declared upper bound of type parameter for KClass) - // - (A..A?) -- from A!, projection type of captured type - - // Now, after approximation we were getting type `KClass`, because { Any & (A..A?) } = A, - // but in old inference type was equal to `KClass`. - - // Important note that from the point of type system first type is more specific: - // Here, approximation of KClass> is a type KClass such that KClass> <: KClass => - // So, the the more specific type for T would be "some non-null (because of declared upper bound type) subtype of A", which is `out A` - - // But for now, to reduce differences in behaviour of old and new inference, we'll approximate such types to `KClass` - - // Once NI will be more stabilized, we'll use more specific type - - else -> { - val projection = type.typeConstructorProjection() - if (projection.isStarProjection()) intersectTypes(supertypes.toList()) - else projection.getType() - } - } - val baseSubType = type.lowerType() ?: nothingType() - - if (conf.capturedType(ctx, type)) { - /** - * Here everything is ok if bounds for this captured type should not be approximated. - * But. If such bounds contains some unauthorized types, then we cannot leave this captured type "as is". - * And we cannot create new capture type, because meaning of new captured type is not clear. - * So, we will just approximate such types - * - * todo handle flexible types - */ - if (approximateToSuperType(baseSuperType, conf, depth) == null && approximateToSubType(baseSubType, conf, depth) == null) { - return null - } - } - val baseResult = if (toSuper) approximateToSuperType(baseSuperType, conf, depth) ?: baseSuperType else approximateToSubType( - baseSubType, - conf, - depth - ) ?: baseSubType - - // C = in Int, Int <: C => Int? <: C? - // C = out Number, C <: Number => C? <: Number? - return when { - type.isMarkedNullable() -> baseResult.withNullability(true) - type.isProjectionNotNull() -> baseResult.withNullability(false) - else -> baseResult - } - } - - private fun approximateSimpleToSuperType(type: SimpleTypeMarker, conf: TypeApproximatorConfiguration, depth: Int) = - approximateTo(type, conf, toSuper = true, depth = depth) - - private fun approximateSimpleToSubType(type: SimpleTypeMarker, conf: TypeApproximatorConfiguration, depth: Int) = - approximateTo(type, conf, toSuper = false, depth = depth) - - private fun approximateTo( - type: SimpleTypeMarker, - conf: TypeApproximatorConfiguration, - toSuper: Boolean, - depth: Int - ): KotlinTypeMarker? { - if (type.argumentsCount() != 0) { - return approximateParametrizedType(type, conf, toSuper, depth + 1) - } - - val definitelyNotNullType = type.asDefinitelyNotNullType() - if (definitelyNotNullType != null) { - return approximateDefinitelyNotNullType(definitelyNotNullType, conf, toSuper, depth) - } - - val typeConstructor = type.typeConstructor() - - if (typeConstructor.isCapturedTypeConstructor()) { - val capturedType = type.asCapturedType() - require(capturedType != null) { - // KT-16147 - "Type is inconsistent -- somewhere we create type with typeConstructor = $typeConstructor " + - "and class: ${type::class.java.canonicalName}. type.toString() = $type" - } - return approximateCapturedType(capturedType, conf, toSuper, depth) - } - - if (typeConstructor.isIntersection()) { - return approximateIntersectionType(type, conf, toSuper, depth) - } - - if (typeConstructor is TypeVariableTypeConstructorMarker) { - return if (conf.typeVariable(typeConstructor)) null else type.defaultResult(toSuper) - } - - if (typeConstructor.isIntegerLiteralTypeConstructor()) { - return if (conf.integerLiteralType) - typeConstructor.getApproximatedIntegerLiteralType().withNullability(type.isMarkedNullable()) - else - null - } - - return null // simple classifier type - } - - private fun approximateDefinitelyNotNullType( - type: DefinitelyNotNullTypeMarker, - conf: TypeApproximatorConfiguration, - toSuper: Boolean, - depth: Int - ): KotlinTypeMarker? { - val originalType = type.original() - val approximatedOriginalType = - if (toSuper) approximateToSuperType(originalType, conf, depth) else approximateToSubType(originalType, conf, depth) - - return if (conf.definitelyNotNullType) { - approximatedOriginalType?.makeDefinitelyNotNullOrNotNull() - } else { - if (toSuper) - (approximatedOriginalType ?: originalType).withNullability(false) - else - type.defaultResult(toSuper) - } - } - - private fun isApproximateDirectionToSuper(effectiveVariance: TypeVariance, toSuper: Boolean) = - when (effectiveVariance) { - TypeVariance.OUT -> toSuper - TypeVariance.IN -> !toSuper - TypeVariance.INV -> throw AssertionError("Incorrect variance $effectiveVariance") - } - - private fun approximateParametrizedType( - type: SimpleTypeMarker, - conf: TypeApproximatorConfiguration, - toSuper: Boolean, - depth: Int - ): SimpleTypeMarker? { - val typeConstructor = type.typeConstructor() - if (typeConstructor.parametersCount() != type.argumentsCount()) { - return if (conf.errorType) { - createErrorType("Inconsistent type: $type (parameters.size = ${typeConstructor.parametersCount()}, arguments.size = ${type.argumentsCount()})") - } else type.defaultResult(toSuper) - } - - val newArguments = arrayOfNulls(type.argumentsCount()) - - loop@ for (index in 0 until type.argumentsCount()) { - val parameter = typeConstructor.getParameter(index) - val argument = type.getArgument(index) - - if (argument.isStarProjection()) continue - - val effectiveVariance = AbstractTypeChecker.effectiveVariance(parameter.getVariance(), argument.getVariance()) - - val argumentType = newArguments[index]?.getType() ?: argument.getType() - - val capturedType = argumentType.lowerBoundIfFlexible().asCapturedType() - val capturedStarProjectionOrNull = - capturedType?.typeConstructorProjection()?.takeIf { it.isStarProjection() } - - if (capturedStarProjectionOrNull != null && - (effectiveVariance == TypeVariance.OUT || effectiveVariance == TypeVariance.INV) && - toSuper && - capturedType.typeParameter() == parameter - ) { - newArguments[index] = capturedStarProjectionOrNull - continue@loop - } - - when (effectiveVariance) { - null -> { - return if (conf.errorType) { - createErrorType( - "Inconsistent type: $type ($index parameter has declared variance: ${parameter.getVariance()}, " + - "but argument variance is ${argument.getVariance()})" - ) - } else type.defaultResult(toSuper) - } - TypeVariance.OUT, TypeVariance.IN -> { - if ( - conf.intersectionTypesInContravariantPositions && - effectiveVariance == TypeVariance.IN && - argumentType.typeConstructor().isIntersection() - ) { - val intersectionTypeConstructor = argumentType.typeConstructor() as? IntersectionTypeConstructor - if (intersectionTypeConstructor != null && isIntersectionTypeEffectivelyNothing(intersectionTypeConstructor)) { - newArguments[index] = createStarProjection(parameter) - continue@loop - } - } - - /** - * Out <: Out - * Inv <: Inv - - * In <: In - * Inv <: Inv - */ - val approximatedArgument = argumentType.let { - if (isApproximateDirectionToSuper(effectiveVariance, toSuper)) { - approximateToSuperType(it, conf, depth) - } else { - approximateToSubType(it, conf, depth) - } - } ?: continue@loop - - if ( - conf.intersection != ALLOWED && - effectiveVariance == TypeVariance.OUT && - argumentType.typeConstructor().isIntersection() - ) { - var shouldReplaceWithStar = false - for (upperBoundIndex in 0 until parameter.upperBoundCount()) { - if (!AbstractTypeChecker.isSubtypeOf(ctx, approximatedArgument, parameter.getUpperBound(upperBoundIndex))) { - shouldReplaceWithStar = true - break - } - } - if (shouldReplaceWithStar) { - newArguments[index] = createStarProjection(parameter) - continue@loop - } - } - - if (parameter.getVariance() == TypeVariance.INV) { - newArguments[index] = createTypeArgument(approximatedArgument, effectiveVariance) - } else { - newArguments[index] = approximatedArgument.asTypeArgument() - } - } - TypeVariance.INV -> { - if (!toSuper) { - // Inv cannot be approximated to subType - val toSubType = approximateToSubType(argumentType, conf, depth) ?: continue@loop - - // Inv is supertype for Inv - if (!AbstractTypeChecker.equalTypes( - this, - argumentType, - toSubType - ) - ) return type.defaultResult(toSuper) - - // also Captured(out Nothing) = Nothing - newArguments[index] = toSubType.asTypeArgument() - continue@loop - } - - /** - * Example with non-trivial both type approximations: - * Inv> where C = in Int - * Inv> <: Inv> - * Inv> <: Inv> - * - * So such case is rare and we will chose Inv> for now. - * - * Note that for case Inv we will chose Inv, because it is more informative then Inv. - * May be we should do the same for deeper types, but not now. - */ - if (argumentType.typeConstructor() is NewCapturedTypeConstructor) { - val subType = approximateToSubType(argumentType, conf, depth) ?: continue@loop - if (!subType.isTrivialSub()) { - newArguments[index] = createTypeArgument(subType, TypeVariance.IN) - continue@loop - } - } - - val approximatedSuperType = - approximateToSuperType(argumentType, conf, depth) ?: continue@loop // null means that this type we can leave as is - if (approximatedSuperType.isTrivialSuper()) { - val approximatedSubType = - approximateToSubType(argumentType, conf, depth) ?: continue@loop // seems like this is never null - if (!approximatedSubType.isTrivialSub()) { - newArguments[index] = createTypeArgument(approximatedSubType, TypeVariance.IN) - continue@loop - } - } - - if (AbstractTypeChecker.equalTypes(this, argumentType, approximatedSuperType)) { - newArguments[index] = approximatedSuperType.asTypeArgument() - } else { - newArguments[index] = createTypeArgument(approximatedSuperType, TypeVariance.OUT) - } - } - } - } - - if (newArguments.all { it == null }) return null - - val newArgumentsList = List(type.argumentsCount()) { index -> newArguments[index] ?: type.getArgument(index) } - return type.replaceArguments(newArgumentsList) - } - - private fun KotlinTypeMarker.defaultResult(toSuper: Boolean) = if (toSuper) nullableAnyType() else { - if (this is SimpleTypeMarker && isMarkedNullable()) nullableNothingType() else nothingType() - } - - // Any? or Any! - private fun KotlinTypeMarker.isTrivialSuper() = upperBoundIfFlexible().isNullableAny() - - // Nothing or Nothing! - private fun KotlinTypeMarker.isTrivialSub() = lowerBoundIfFlexible().isNothing() -} diff --git a/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximatorConfiguration.kt b/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximatorConfiguration.kt new file mode 100644 index 00000000000..da46fdb6bd0 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximatorConfiguration.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.types + +import org.jetbrains.kotlin.types.model.* + +open class TypeApproximatorConfiguration { + enum class IntersectionStrategy { + ALLOWED, + TO_FIRST, + TO_COMMON_SUPERTYPE + } + + open val flexible: Boolean get() = false // simple flexible types (FlexibleTypeImpl) + open val dynamic: Boolean get() = false // DynamicType + open val rawType: Boolean get() = false // RawTypeImpl + open val errorType: Boolean get() = false + open val integerLiteralType: Boolean = false // IntegerLiteralTypeConstructor + open val definitelyNotNullType: Boolean get() = true + open val intersection: IntersectionStrategy = IntersectionStrategy.TO_COMMON_SUPERTYPE + open val intersectionTypesInContravariantPositions = false + + open val typeVariable: (TypeVariableTypeConstructorMarker) -> Boolean = { false } + open fun capturedType(ctx: TypeSystemInferenceExtensionContext, type: CapturedTypeMarker): Boolean = + false // true means that this type we can leave as is + + abstract class AllFlexibleSameValue : TypeApproximatorConfiguration() { + abstract val allFlexible: Boolean + + override val flexible: Boolean get() = allFlexible + override val dynamic: Boolean get() = allFlexible + override val rawType: Boolean get() = allFlexible + } + + object LocalDeclaration : AllFlexibleSameValue() { + override val allFlexible: Boolean get() = true + override val intersection: IntersectionStrategy get() = IntersectionStrategy.ALLOWED + override val errorType: Boolean get() = true + override val integerLiteralType: Boolean get() = true + override val intersectionTypesInContravariantPositions: Boolean get() = true + } + + object PublicDeclaration : AllFlexibleSameValue() { + override val allFlexible: Boolean get() = true + override val errorType: Boolean get() = true + override val definitelyNotNullType: Boolean get() = false + override val integerLiteralType: Boolean get() = true + override val intersectionTypesInContravariantPositions: Boolean get() = true + } + + abstract class AbstractCapturedTypesApproximation(val approximatedCapturedStatus: CaptureStatus) : + TypeApproximatorConfiguration.AllFlexibleSameValue() { + override val allFlexible: Boolean get() = true + override val errorType: Boolean get() = true + + // i.e. will be approximated only approximatedCapturedStatus captured types + override fun capturedType(ctx: TypeSystemInferenceExtensionContext, type: CapturedTypeMarker): Boolean = + type.captureStatus(ctx) != approximatedCapturedStatus + + override val intersection: IntersectionStrategy get() = IntersectionStrategy.ALLOWED + override val typeVariable: (TypeVariableTypeConstructorMarker) -> Boolean get() = { true } + } + + object IncorporationConfiguration : TypeApproximatorConfiguration.AbstractCapturedTypesApproximation(CaptureStatus.FOR_INCORPORATION) + object SubtypeCapturedTypesApproximation : TypeApproximatorConfiguration.AbstractCapturedTypesApproximation(CaptureStatus.FOR_SUBTYPING) + object InternalTypesApproximation : TypeApproximatorConfiguration.AbstractCapturedTypesApproximation(CaptureStatus.FROM_EXPRESSION) { + override val integerLiteralType: Boolean get() = true + override val intersectionTypesInContravariantPositions: Boolean get() = true + } + + object FinalApproximationAfterResolutionAndInference : + TypeApproximatorConfiguration.AbstractCapturedTypesApproximation(CaptureStatus.FROM_EXPRESSION) { + override val integerLiteralType: Boolean get() = true + override val intersectionTypesInContravariantPositions: Boolean get() = true + } + + object IntegerLiteralsTypesApproximation : TypeApproximatorConfiguration.AllFlexibleSameValue() { + override val integerLiteralType: Boolean get() = true + override val allFlexible: Boolean get() = true + override val intersection: IntersectionStrategy get() = IntersectionStrategy.ALLOWED + override val typeVariable: (TypeVariableTypeConstructorMarker) -> Boolean get() = { true } + override val errorType: Boolean get() = true + + override fun capturedType(ctx: TypeSystemInferenceExtensionContext, type: CapturedTypeMarker): Boolean = true + } +} diff --git a/compiler/testData/codegen/box/callableReference/function/kt32462.kt b/compiler/testData/codegen/box/callableReference/function/kt32462.kt index 2ecc89e260f..63d63684ad6 100644 --- a/compiler/testData/codegen/box/callableReference/function/kt32462.kt +++ b/compiler/testData/codegen/box/callableReference/function/kt32462.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +NewInference -// IGNORE_BACKEND_FIR: JVM_IR // KJS_WITH_FULL_RUNTIME // WITH_RUNTIME // ISSUE: KT-32462 @@ -13,4 +12,4 @@ fun decodeValue(value: String): Any { }(value.substring(2)) } -fun box(): String = "OK" \ No newline at end of file +fun box(): String = "OK" diff --git a/compiler/testData/diagnostics/tests/inference/capturedTypes/captureForNullableTypes.fir.kt b/compiler/testData/diagnostics/tests/inference/capturedTypes/captureForNullableTypes.fir.kt index 1f1c83f0fbf..502b407bb8f 100644 --- a/compiler/testData/diagnostics/tests/inference/capturedTypes/captureForNullableTypes.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/capturedTypes/captureForNullableTypes.fir.kt @@ -7,7 +7,7 @@ fun bar(a: Array): Array = null!! fun test1(a: Array) { val r: Array = bar(a) val t = bar(a) - t checkType { _>() } + t checkType { _>() } } fun foo(l: Array): Array> = null!! @@ -15,5 +15,5 @@ fun foo(l: Array): Array> = null!! fun test2(a: Array) { val r: Array> = foo(a) val t = foo(a) - t checkType { _>>() } -} \ No newline at end of file + t checkType { _>>() } +} diff --git a/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.fir.kt b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.fir.kt index 5e89719d737..decca17d2d2 100644 --- a/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.fir.kt @@ -187,8 +187,8 @@ fun main() { ")!>select(A3(), ")!>A3::foo1, { a -> a }, { it -> it }) // It's OK because `A3::foo2` is from companion of `A3` ")!>select(A3(), ")!>A3::foo2, { a -> a }, { it -> it }) - & java.io.Serializable>")!>select(A4(), { x: Number -> "" }) - & java.io.Serializable>")!>select(A5(), { x: Number, y: Int -> "" }) + & java.io.Serializable>")!>select(A4(), { x: Number -> "" }) + & java.io.Serializable>")!>select(A5(), { x: Number, y: Int -> "" }) select(A2(), id { a, b, c -> a; b; c }) ")!>select(id(A3()), { it }, { a -> a }) ")!>select(A3(), id(")!>A3::foo1)) @@ -202,7 +202,7 @@ fun main() { select(A4(), id { x: Number -> x }) ")!>select(id(A5()), id { x: Number, y: Int -> x;y }) ")!>select(id(A5()), id { x, y -> x;y }) - >")!>select(id(")!>A5()), id { x: Number, y: Int -> x;y }) + >")!>select(id(")!>A5()), id { x: Number, y: Int -> x;y }) val x55: Function2 = select(id(A5()), id { x, y -> x;y; 1f }) // Diffrerent lambda's parameters with proper CST @@ -212,11 +212,11 @@ fun main() { ")!>select({ x: Int -> }, id { x: Int, y: Number -> }) ")!>select(id { x: Int -> }, id { x: String -> }) ")!>select(id { x: Int -> }, id { x: Int, y: Number -> }) - & java.io.Serializable>")!>select({ x: Int -> 1 }, { x: String -> "" }) - >")!>select({ x: Int -> 1 }, { x: Int, y: Number -> 1f }) + & java.io.Serializable>")!>select({ x: Int -> 1 }, { x: String -> "" }) + >")!>select({ x: Int -> 1 }, { x: Int, y: Number -> 1f }) >")!>select(id { x: Int -> Inv(10) }, { x: String -> Inv("") }) ")!>select({ x: Int -> TODO() }, id { x: Int, y: Number -> Any() }) - ")!>select(id { x: Int -> null }, id { x: String -> "" }) + ")!>select(id { x: Int -> null }, id { x: String -> "" }) ")!>select(id { x: Int -> 10 }, id { x: Int, y: Number -> TODO() }) val x68: String.(String) -> String = select(id { x: String, y: String -> "10" }, id { x: String, y: String -> "TODO()" }) diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/11.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/11.fir.kt index cefb520df38..f69f3a25738 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/11.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/11.fir.kt @@ -9,16 +9,16 @@ fun case_1() { val x = case_1(Out(10), Inv(0.1)) if (x != null) { - & kotlin.Number? & kotlin.Comparable?")!>x - & kotlin.Number? & kotlin.Comparable?")!>x.equals(null) - & kotlin.Number? & kotlin.Comparable?")!>x.propT - & kotlin.Number? & kotlin.Comparable?")!>x.propAny - & kotlin.Number? & kotlin.Comparable?")!>x.propNullableT - & kotlin.Number? & kotlin.Comparable?")!>x.propNullableAny - & kotlin.Number? & kotlin.Comparable?")!>x.funT() - & kotlin.Number? & kotlin.Comparable?")!>x.funAny() - & kotlin.Number? & kotlin.Comparable?")!>x.funNullableT() - & kotlin.Number? & kotlin.Comparable?")!>x.funNullableAny() + & kotlin.Number? & kotlin.Comparable<*>?")!>x + & kotlin.Number? & kotlin.Comparable<*>?")!>x.equals(null) + & kotlin.Number? & kotlin.Comparable<*>?")!>x.propT + & kotlin.Number? & kotlin.Comparable<*>?")!>x.propAny + & kotlin.Number? & kotlin.Comparable<*>?")!>x.propNullableT + & kotlin.Number? & kotlin.Comparable<*>?")!>x.propNullableAny + & kotlin.Number? & kotlin.Comparable<*>?")!>x.funT() + & kotlin.Number? & kotlin.Comparable<*>?")!>x.funAny() + & kotlin.Number? & kotlin.Comparable<*>?")!>x.funNullableT() + & kotlin.Number? & kotlin.Comparable<*>?")!>x.funNullableAny() } } @@ -29,16 +29,16 @@ fun case_2(y: Int) { val x = case_2(Out(y), Inv(0.1)) if (x != null) { - & kotlin.Number? & kotlin.Comparable?")!>x - & kotlin.Number? & kotlin.Comparable?")!>x.equals(null) - & kotlin.Number? & kotlin.Comparable?")!>x.propT - & kotlin.Number? & kotlin.Comparable?")!>x.propAny - & kotlin.Number? & kotlin.Comparable?")!>x.propNullableT - & kotlin.Number? & kotlin.Comparable?")!>x.propNullableAny - & kotlin.Number? & kotlin.Comparable?")!>x.funT() - & kotlin.Number? & kotlin.Comparable?")!>x.funAny() - & kotlin.Number? & kotlin.Comparable?")!>x.funNullableT() - & kotlin.Number? & kotlin.Comparable?")!>x.funNullableAny() + & kotlin.Number? & kotlin.Comparable<*>?")!>x + & kotlin.Number? & kotlin.Comparable<*>?")!>x.equals(null) + & kotlin.Number? & kotlin.Comparable<*>?")!>x.propT + & kotlin.Number? & kotlin.Comparable<*>?")!>x.propAny + & kotlin.Number? & kotlin.Comparable<*>?")!>x.propNullableT + & kotlin.Number? & kotlin.Comparable<*>?")!>x.propNullableAny + & kotlin.Number? & kotlin.Comparable<*>?")!>x.funT() + & kotlin.Number? & kotlin.Comparable<*>?")!>x.funAny() + & kotlin.Number? & kotlin.Comparable<*>?")!>x.funNullableT() + & kotlin.Number? & kotlin.Comparable<*>?")!>x.funNullableAny() } } @@ -52,32 +52,32 @@ fun case_3(a: Int?, b: Float?, c: Double?, d: Boolean?) { false -> b null -> c }.apply { - ?")!>this + ?")!>this if (this != null) { - & kotlin.Number & kotlin.Comparable")!>this - & kotlin.Number & kotlin.Comparable")!>this.equals(null) - & kotlin.Number & kotlin.Comparable")!>this.propT - & kotlin.Number & kotlin.Comparable")!>this.propAny - & kotlin.Number & kotlin.Comparable")!>this.propNullableT - & kotlin.Number & kotlin.Comparable")!>this.propNullableAny - & kotlin.Number & kotlin.Comparable")!>this.funT() - & kotlin.Number & kotlin.Comparable")!>this.funAny() - & kotlin.Number & kotlin.Comparable")!>this.funNullableT() - & kotlin.Number & kotlin.Comparable")!>this.funNullableAny() + & kotlin.Number & kotlin.Comparable<*>")!>this + & kotlin.Number & kotlin.Comparable<*>")!>this.equals(null) + & kotlin.Number & kotlin.Comparable<*>")!>this.propT + & kotlin.Number & kotlin.Comparable<*>")!>this.propAny + & kotlin.Number & kotlin.Comparable<*>")!>this.propNullableT + & kotlin.Number & kotlin.Comparable<*>")!>this.propNullableAny + & kotlin.Number & kotlin.Comparable<*>")!>this.funT() + & kotlin.Number & kotlin.Comparable<*>")!>this.funAny() + & kotlin.Number & kotlin.Comparable<*>")!>this.funNullableT() + & kotlin.Number & kotlin.Comparable<*>")!>this.funNullableAny() } }.let { - ?")!>it + ?")!>it if (it != null) { - & kotlin.Number? & kotlin.Comparable?")!>it - & kotlin.Number? & kotlin.Comparable?")!>it.equals(null) - & kotlin.Number? & kotlin.Comparable?")!>it.propT - & kotlin.Number? & kotlin.Comparable?")!>it.propAny - & kotlin.Number? & kotlin.Comparable?")!>it.propNullableT - & kotlin.Number? & kotlin.Comparable?")!>it.propNullableAny - & kotlin.Number? & kotlin.Comparable?")!>it.funT() - & kotlin.Number? & kotlin.Comparable?")!>it.funAny() - & kotlin.Number? & kotlin.Comparable?")!>it.funNullableT() - & kotlin.Number? & kotlin.Comparable?")!>it.funNullableAny() + & kotlin.Number? & kotlin.Comparable<*>?")!>it + & kotlin.Number? & kotlin.Comparable<*>?")!>it.equals(null) + & kotlin.Number? & kotlin.Comparable<*>?")!>it.propT + & kotlin.Number? & kotlin.Comparable<*>?")!>it.propAny + & kotlin.Number? & kotlin.Comparable<*>?")!>it.propNullableT + & kotlin.Number? & kotlin.Comparable<*>?")!>it.propNullableAny + & kotlin.Number? & kotlin.Comparable<*>?")!>it.funT() + & kotlin.Number? & kotlin.Comparable<*>?")!>it.funAny() + & kotlin.Number? & kotlin.Comparable<*>?")!>it.funNullableT() + & kotlin.Number? & kotlin.Comparable<*>?")!>it.funNullableAny() } } } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/15.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/15.fir.kt index 96f9e5e2a62..86675992589 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/15.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/15.fir.kt @@ -90,9 +90,9 @@ fun case_6() { val x = select(Case6_1(), Case6_2(), null) if (x != null) { - >> & InterfaceWithTypeParameter2> & InterfaceWithTypeParameter2<*>>> & InterfaceWithTypeParameter1>>? & InterfaceWithTypeParameter2> & InterfaceWithTypeParameter2<*>>>?")!>x - >> & InterfaceWithTypeParameter2> & InterfaceWithTypeParameter2<*>>> & InterfaceWithTypeParameter1>>? & InterfaceWithTypeParameter2> & InterfaceWithTypeParameter2<*>>>?")!>x.ip1test1() - >> & InterfaceWithTypeParameter2> & InterfaceWithTypeParameter2<*>>> & InterfaceWithTypeParameter1>>? & InterfaceWithTypeParameter2> & InterfaceWithTypeParameter2<*>>>?")!>x.ip1test2() + >> & InterfaceWithTypeParameter2> & InterfaceWithTypeParameter2<*>>> & InterfaceWithTypeParameter1>>? & InterfaceWithTypeParameter2> & InterfaceWithTypeParameter2<*>>>?")!>x + >> & InterfaceWithTypeParameter2> & InterfaceWithTypeParameter2<*>>> & InterfaceWithTypeParameter1>>? & InterfaceWithTypeParameter2> & InterfaceWithTypeParameter2<*>>>?")!>x.ip1test1() + >> & InterfaceWithTypeParameter2> & InterfaceWithTypeParameter2<*>>> & InterfaceWithTypeParameter1>>? & InterfaceWithTypeParameter2> & InterfaceWithTypeParameter2<*>>>?")!>x.ip1test2() } } @@ -104,8 +104,8 @@ fun case_7() { val x = select(Case7_1(), Case7_2(), null) if (x != null) { - & java.io.Serializable>, out Inv & java.io.Serializable>> & InterfaceWithTwoTypeParameters & java.io.Serializable>, out Inv & java.io.Serializable>>?")!>x - & java.io.Serializable>, out Inv & java.io.Serializable>> & InterfaceWithTwoTypeParameters & java.io.Serializable>, out Inv & java.io.Serializable>>?")!>x.ip2test() + & java.io.Serializable>, out Inv & java.io.Serializable>> & InterfaceWithTwoTypeParameters & java.io.Serializable>, out Inv & java.io.Serializable>>?")!>x + & java.io.Serializable>, out Inv & java.io.Serializable>> & InterfaceWithTwoTypeParameters & java.io.Serializable>, out Inv & java.io.Serializable>>?")!>x.ip2test() } } @@ -117,24 +117,24 @@ fun case_8() { val x = select(Case8_1(), Case8_2(), null) if (x != null) { - & java.io.Serializable, out kotlin.Comparable & java.io.Serializable>, out kotlin.Comparable & java.io.Serializable> & ClassWithTwoTypeParameters & java.io.Serializable, out kotlin.Comparable & java.io.Serializable>, out kotlin.Comparable & java.io.Serializable>?")!>x - & java.io.Serializable, out kotlin.Comparable & java.io.Serializable>, out kotlin.Comparable & java.io.Serializable> & ClassWithTwoTypeParameters & java.io.Serializable, out kotlin.Comparable & java.io.Serializable>, out kotlin.Comparable & java.io.Serializable>?")!>x.test1() - val y = & java.io.Serializable, out kotlin.Comparable & java.io.Serializable>, out kotlin.Comparable & java.io.Serializable> & ClassWithTwoTypeParameters & java.io.Serializable, out kotlin.Comparable & java.io.Serializable>, out kotlin.Comparable & java.io.Serializable>?")!>x.test2() + & java.io.Serializable, out kotlin.Comparable<*> & java.io.Serializable>, out kotlin.Comparable<*> & java.io.Serializable> & ClassWithTwoTypeParameters & java.io.Serializable, out kotlin.Comparable<*> & java.io.Serializable>, out kotlin.Comparable<*> & java.io.Serializable>?")!>x + & java.io.Serializable, out kotlin.Comparable<*> & java.io.Serializable>, out kotlin.Comparable<*> & java.io.Serializable> & ClassWithTwoTypeParameters & java.io.Serializable, out kotlin.Comparable<*> & java.io.Serializable>, out kotlin.Comparable<*> & java.io.Serializable>?")!>x.test1() + val y = & java.io.Serializable, out kotlin.Comparable<*> & java.io.Serializable>, out kotlin.Comparable<*> & java.io.Serializable> & ClassWithTwoTypeParameters & java.io.Serializable, out kotlin.Comparable<*> & java.io.Serializable>, out kotlin.Comparable<*> & java.io.Serializable>?")!>x.test2() if (y != null) { - & java.io.Serializable, out kotlin.Comparable & java.io.Serializable> & ClassWithTwoTypeParameters & java.io.Serializable, out kotlin.Comparable & java.io.Serializable>?")!>y - & java.io.Serializable, out kotlin.Comparable & java.io.Serializable> & ClassWithTwoTypeParameters & java.io.Serializable, out kotlin.Comparable & java.io.Serializable>?")!>y.test1() - val z = & java.io.Serializable, out kotlin.Comparable & java.io.Serializable> & ClassWithTwoTypeParameters & java.io.Serializable, out kotlin.Comparable & java.io.Serializable>?")!>y.test2() + & java.io.Serializable, out kotlin.Comparable<*> & java.io.Serializable> & ClassWithTwoTypeParameters & java.io.Serializable, out kotlin.Comparable<*> & java.io.Serializable>?")!>y + & java.io.Serializable, out kotlin.Comparable<*> & java.io.Serializable> & ClassWithTwoTypeParameters & java.io.Serializable, out kotlin.Comparable<*> & java.io.Serializable>?")!>y.test1() + val z = & java.io.Serializable, out kotlin.Comparable<*> & java.io.Serializable> & ClassWithTwoTypeParameters & java.io.Serializable, out kotlin.Comparable<*> & java.io.Serializable>?")!>y.test2() if (z != null) { - & java.io.Serializable & kotlin.Comparable? & java.io.Serializable?")!>z - & java.io.Serializable & kotlin.Comparable? & java.io.Serializable?")!>z.equals(null) - & java.io.Serializable & kotlin.Comparable? & java.io.Serializable?")!>z.propT - & java.io.Serializable & kotlin.Comparable? & java.io.Serializable?")!>z.propAny - & java.io.Serializable & kotlin.Comparable? & java.io.Serializable?")!>z.propNullableT - & java.io.Serializable & kotlin.Comparable? & java.io.Serializable?")!>z.propNullableAny - & java.io.Serializable & kotlin.Comparable? & java.io.Serializable?")!>z.funT() - & java.io.Serializable & kotlin.Comparable? & java.io.Serializable?")!>z.funAny() - & java.io.Serializable & kotlin.Comparable? & java.io.Serializable?")!>z.funNullableT() - & java.io.Serializable & kotlin.Comparable? & java.io.Serializable?")!>z.funNullableAny() + & java.io.Serializable & kotlin.Comparable<*>? & java.io.Serializable?")!>z + & java.io.Serializable & kotlin.Comparable<*>? & java.io.Serializable?")!>z.equals(null) + & java.io.Serializable & kotlin.Comparable<*>? & java.io.Serializable?")!>z.propT + & java.io.Serializable & kotlin.Comparable<*>? & java.io.Serializable?")!>z.propAny + & java.io.Serializable & kotlin.Comparable<*>? & java.io.Serializable?")!>z.propNullableT + & java.io.Serializable & kotlin.Comparable<*>? & java.io.Serializable?")!>z.propNullableAny + & java.io.Serializable & kotlin.Comparable<*>? & java.io.Serializable?")!>z.funT() + & java.io.Serializable & kotlin.Comparable<*>? & java.io.Serializable?")!>z.funAny() + & java.io.Serializable & kotlin.Comparable<*>? & java.io.Serializable?")!>z.funNullableT() + & java.io.Serializable & kotlin.Comparable<*>? & java.io.Serializable?")!>z.funNullableAny() } } } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/51.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/51.fir.kt index 456d474b131..b45c9f2272e 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/51.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/51.fir.kt @@ -134,8 +134,8 @@ fun case_12(z: Any?) { return@let it as Int it as? Float ?: 10f } - ")!>y - ")!>y.toByte() + ")!>y + ")!>y.toByte() } /* @@ -158,8 +158,8 @@ fun case_14(z: Any?) { return@run this as Int this as? Float ?: 10f } - ")!>y - ")!>y.toByte() + ")!>y + ")!>y.toByte() } /* diff --git a/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt b/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt index 31fbd5c2364..f7418e26fd1 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt @@ -28,6 +28,8 @@ interface TypeVariableTypeConstructorMarker : TypeConstructorMarker interface CapturedTypeConstructorMarker : TypeConstructorMarker +interface IntersectionTypeConstructorMarker : TypeConstructorMarker + interface TypeSubstitutorMarker @@ -174,8 +176,12 @@ interface TypeSystemInferenceExtensionContext : TypeSystemContext, TypeSystemBui secondCandidate: KotlinTypeMarker ): KotlinTypeMarker + fun KotlinTypeMarker.isSpecial(): Boolean + fun TypeConstructorMarker.isTypeVariable(): Boolean fun TypeVariableTypeConstructorMarker.isContainedInInvariantOrContravariantPositions(): Boolean + + fun KotlinTypeMarker.isSignedOrUnsignedNumberType(): Boolean } @@ -202,6 +208,9 @@ interface TypeSystemContext : TypeSystemOptimizationContext { fun SimpleTypeMarker.asDefinitelyNotNullType(): DefinitelyNotNullTypeMarker? fun SimpleTypeMarker.isMarkedNullable(): Boolean + fun KotlinTypeMarker.isMarkedNullable(): Boolean = + this is SimpleTypeMarker && isMarkedNullable() + fun SimpleTypeMarker.withNullability(nullable: Boolean): SimpleTypeMarker fun SimpleTypeMarker.typeConstructor(): TypeConstructorMarker @@ -323,7 +332,7 @@ interface TypeSystemContext : TypeSystemOptimizationContext { fun intersectTypes(types: List): KotlinTypeMarker fun intersectTypes(types: List): SimpleTypeMarker - fun KotlinTypeMarker.isSimpleType() = asSimpleType() != null + fun KotlinTypeMarker.isSimpleType(): Boolean = asSimpleType() != null fun prepareType(type: KotlinTypeMarker): KotlinTypeMarker diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/IntersectionTypeConstructor.kt b/core/descriptors/src/org/jetbrains/kotlin/types/IntersectionTypeConstructor.kt index 5057f744a5e..60f87d51b78 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/IntersectionTypeConstructor.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/IntersectionTypeConstructor.kt @@ -23,10 +23,11 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.TypeIntersectionScope import org.jetbrains.kotlin.types.checker.KotlinTypeRefiner +import org.jetbrains.kotlin.types.model.IntersectionTypeConstructorMarker import org.jetbrains.kotlin.types.refinement.TypeRefinement import java.util.* -class IntersectionTypeConstructor(typesToIntersect: Collection) : TypeConstructor { +class IntersectionTypeConstructor(typesToIntersect: Collection) : TypeConstructor, IntersectionTypeConstructorMarker { private var alternative: KotlinType? = null private constructor( diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeSystemCommonBackendContext.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeSystemCommonBackendContext.kt index 30d66498052..d1d934b569d 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeSystemCommonBackendContext.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeSystemCommonBackendContext.kt @@ -36,9 +36,6 @@ interface TypeSystemCommonBackendContext : TypeSystemContext { fun TypeParameterMarker.getRepresentativeUpperBound(): KotlinTypeMarker fun KotlinTypeMarker.getSubstitutedUnderlyingType(): KotlinTypeMarker? - fun KotlinTypeMarker.isMarkedNullable(): Boolean = - this is SimpleTypeMarker && isMarkedNullable() - fun KotlinTypeMarker.makeNullable(): KotlinTypeMarker = asSimpleType()?.withNullability(true) ?: this diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt index cb4dcd9e0d2..170dfbb2011 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt @@ -6,8 +6,8 @@ package org.jetbrains.kotlin.types.checker import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.builtins.StandardNames.FqNames import org.jetbrains.kotlin.builtins.PrimitiveType +import org.jetbrains.kotlin.builtins.StandardNames.FqNames import org.jetbrains.kotlin.builtins.isBuiltinFunctionalTypeOrSubtype import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound import org.jetbrains.kotlin.utils.addToStdlib.safeAs import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract +import org.jetbrains.kotlin.types.typeUtil.isSignedOrUnsignedNumberType as classicIsSignedOrUnsignedNumberType interface ClassicTypeSystemContext : TypeSystemInferenceExtensionContext, TypeSystemCommonBackendContext { override fun TypeConstructorMarker.isDenotable(): Boolean { @@ -517,6 +518,11 @@ interface ClassicTypeSystemContext : TypeSystemInferenceExtensionContext, TypeSy errorSupportedOnlyInTypeInference() } + override fun KotlinTypeMarker.isSpecial(): Boolean { + require(this is KotlinType) + return this is TypeUtils.SpecialType + } + override fun TypeConstructorMarker.isTypeVariable(): Boolean { errorSupportedOnlyInTypeInference() } @@ -525,6 +531,11 @@ interface ClassicTypeSystemContext : TypeSystemInferenceExtensionContext, TypeSy errorSupportedOnlyInTypeInference() } + override fun KotlinTypeMarker.isSignedOrUnsignedNumberType(): Boolean { + require(this is KotlinType) + return classicIsSignedOrUnsignedNumberType() + } + override fun findCommonIntegerLiteralTypesSuperType(explicitSupertypes: List): SimpleTypeMarker? { @Suppress("UNCHECKED_CAST") explicitSupertypes as List