[FE] Introduce warnings on possible empty intersection types, and improve errors reporting in general

^KT-52361 Fixed
This commit is contained in:
Victor Petukhov
2022-05-23 10:10:59 +02:00
committed by teamcity
parent e133ee3765
commit 6a34b184ac
53 changed files with 501 additions and 335 deletions
@@ -688,6 +688,12 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION by error<PsiElement> {
parameter<String>("typeVariableDescription")
parameter<Collection<ConeKotlinType>>("incompatibleTypes")
parameter<String>("kind")
}
val INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION by warning<PsiElement> {
parameter<String>("typeVariableDescription")
parameter<Collection<ConeKotlinType>>("incompatibleTypes")
}
}
@@ -412,7 +412,8 @@ object FirErrors {
val SMARTCAST_IMPOSSIBLE by error4<KtExpression, ConeKotlinType, FirExpression, String, Boolean>()
val REDUNDANT_NULLABLE by warning0<KtTypeReference>(SourceElementPositioningStrategies.REDUNDANT_NULLABLE)
val PLATFORM_CLASS_MAPPED_TO_KOTLIN by warning1<PsiElement, FqName>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
val INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION by error2<PsiElement, String, Collection<ConeKotlinType>>()
val INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION by error3<PsiElement, String, Collection<ConeKotlinType>, String>()
val INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION by warning2<PsiElement, String, Collection<ConeKotlinType>>()
// Reflection
val EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED by error1<KtExpression, FirCallableSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
@@ -254,6 +254,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INC_DEC_SHOULD_NO
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INFERENCE_ERROR
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INFERENCE_UNSUCCESSFUL_FORK
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INFIX_MODIFIER_REQUIRED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INITIALIZATION_BEFORE_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION
@@ -949,7 +950,14 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
map.put(INFERENCE_UNSUCCESSFUL_FORK, "Unsuccessful inference fork at position: {0}", TO_STRING)
map.put(
INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION,
"Type argument for a type parameter {0} can''t be inferred because it has incompatible upper bounds: {1}",
"Type argument for a type parameter {0} can''t be inferred because it has incompatible upper bounds ({2}): {1}",
TO_STRING,
RENDER_COLLECTION_OF_TYPES,
TO_STRING
)
map.put(
INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION,
"Type argument for a type parameter {0} has possible incompatible upper bounds: {1}",
TO_STRING,
RENDER_COLLECTION_OF_TYPES
)
@@ -35,6 +35,8 @@ import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.resolve.calls.inference.model.*
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
import org.jetbrains.kotlin.types.EmptyIntersectionTypeKind
import org.jetbrains.kotlin.types.isPossiblyEmpty
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
import org.jetbrains.kotlin.utils.addToStdlib.runIf
@@ -256,7 +258,7 @@ private fun mapInapplicableCandidateError(
)
}
is InferredEmptyIntersectionDiagnostic ->
reportInferredIntoEmptyIntersectionError(source, rootCause.typeVariable, rootCause.incompatibleTypes)
reportInferredIntoEmptyIntersectionError(source, rootCause.typeVariable, rootCause.incompatibleTypes, rootCause.kind)
else -> genericDiagnostic
}
}.distinct()
@@ -397,7 +399,8 @@ private fun ConstraintSystemError.toDiagnostic(
reportInferredIntoEmptyIntersectionError(
source,
typeVariable as ConeTypeVariable,
incompatibleTypes as Collection<ConeKotlinType>
incompatibleTypes as Collection<ConeKotlinType>,
kind
)
}
else -> null
@@ -408,11 +411,18 @@ private fun reportInferredIntoEmptyIntersectionError(
source: KtSourceElement,
typeVariable: ConeTypeVariable,
incompatibleTypes: Collection<ConeKotlinType>,
): KtDiagnosticWithParameters2<String, Collection<ConeKotlinType>>? {
kind: EmptyIntersectionTypeKind
): KtDiagnostic? {
val typeVariableText =
(typeVariable.typeConstructor.originalTypeParameter as? ConeTypeParameterLookupTag)?.name?.asString()
?: typeVariable.toString()
return FirErrors.INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION.createOn(source, typeVariableText, incompatibleTypes)
return if (kind.isPossiblyEmpty()) {
FirErrors.INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION.createOn(source, typeVariableText, incompatibleTypes)
} else {
FirErrors.INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION.createOn(
source, typeVariableText, incompatibleTypes, kind.description?.let { " ($it)" }.orEmpty()
)
}
}
private val NewConstraintError.lowerConeType: ConeKotlinType get() = lowerType as ConeKotlinType
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.fir.types
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.declarations.utils.modality
import org.jetbrains.kotlin.fir.diagnostics.ConeIntermediateDiagnostic
import org.jetbrains.kotlin.fir.isPrimitiveNumberOrUnsignedNumberType
import org.jetbrains.kotlin.fir.resolve.createSubstitutionForSupertype
@@ -17,6 +19,8 @@ import org.jetbrains.kotlin.fir.resolve.substitution.NoSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.createTypeSubstitutorByTypeConstructor
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousObjectSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.name.StandardClassIds
@@ -299,6 +303,13 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
return attributes.noInfer != null
}
override fun TypeConstructorMarker.isFinalClassConstructor(): Boolean {
val symbol = toClassLikeSymbol() ?: return false
if (symbol is FirAnonymousObjectSymbol) return true
val classSymbol = symbol as? FirRegularClassSymbol ?: return false
return classSymbol.fir.modality == Modality.FINAL
}
override fun TypeVariableMarker.freshTypeConstructor(): TypeConstructorMarker {
require(this is ConeTypeVariable)
return this.typeConstructor
@@ -277,7 +277,7 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty
}
}
private fun TypeConstructorMarker.toClassLikeSymbol(): FirClassLikeSymbol<*>? = (this as? ConeClassLikeLookupTag)?.toSymbol(session)
fun TypeConstructorMarker.toClassLikeSymbol(): FirClassLikeSymbol<*>? = (this as? ConeClassLikeLookupTag)?.toSymbol(session)
override fun TypeConstructorMarker.supertypes(): Collection<ConeKotlinType> {
if (this is ErrorTypeConstructor) return emptyList()
@@ -39,9 +39,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystem
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.*
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
import org.jetbrains.kotlin.resolve.deprecation.DeprecationLevelValue
import org.jetbrains.kotlin.types.AbstractNullabilityChecker
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
import org.jetbrains.kotlin.types.isDefinitelyEmpty
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.util.OperatorNameConventions
abstract class ResolutionStage {
@@ -584,15 +582,17 @@ internal object CheckIncompatibleTypeVariableUpperBounds : ResolutionStage() {
if (upperTypes.size <= 1 || variableWithConstraints.constraints.any { it.kind.isLower() })
continue
if (candidate.system.getEmptyIntersectionTypeKind(upperTypes).isDefinitelyEmpty()) {
sink.yieldDiagnostic(
@Suppress("UNCHECKED_CAST")
InferredEmptyIntersectionDiagnostic(
upperTypes as Collection<ConeKotlinType>,
variableWithConstraints.typeVariable as ConeTypeVariable
)
val emptyIntersectionKind = candidate.system.getEmptyIntersectionTypeKind(upperTypes).takeIf { it.isDefinitelyEmpty() }
?: continue
sink.yieldDiagnostic(
@Suppress("UNCHECKED_CAST")
InferredEmptyIntersectionDiagnostic(
upperTypes as Collection<ConeKotlinType>,
variableWithConstraints.typeVariable as ConeTypeVariable,
emptyIntersectionKind
)
}
)
}
}
}
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintSystemError
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability.*
import org.jetbrains.kotlin.types.EmptyIntersectionTypeKind
abstract class ResolutionDiagnostic(val applicability: CandidateApplicability)
@@ -30,7 +31,8 @@ class MixingNamedAndPositionArguments(override val argument: FirExpression) : In
class InferredEmptyIntersectionDiagnostic(
val incompatibleTypes: Collection<ConeKotlinType>,
val typeVariable: ConeTypeVariable
val typeVariable: ConeTypeVariable,
val kind: EmptyIntersectionTypeKind
) : ResolutionDiagnostic(INAPPLICABLE)
class TooManyArguments(
@@ -899,8 +899,10 @@ public interface Errors {
DiagnosticFactoryForDeprecation1<PsiElement, TypeParameterDescriptor> TYPE_INFERENCE_ONLY_INPUT_TYPES =
DiagnosticFactoryForDeprecation1.create(LanguageFeature.StrictOnlyInputTypesChecks);
DiagnosticFactoryForDeprecation2<PsiElement, String, Collection<KotlinType>> INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION =
DiagnosticFactoryForDeprecation2.create(LanguageFeature.ForbidInferringTypeVariablesIntoEmptyIntersection);
DiagnosticFactoryForDeprecation3<PsiElement, String, Collection<KotlinType>, String> INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION =
DiagnosticFactoryForDeprecation3.create(LanguageFeature.ForbidInferringTypeVariablesIntoEmptyIntersection);
DiagnosticFactory2<PsiElement, String, Collection<KotlinType>> INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION =
DiagnosticFactory2.create(WARNING);
DiagnosticFactory1<PsiElement, InferenceErrorData> TYPE_INFERENCE_UPPER_BOUND_VIOLATED = DiagnosticFactory1.create(ERROR);
DiagnosticFactory2<KtElement, KotlinType, KotlinType> TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH = DiagnosticFactory2.create(ERROR);
DiagnosticFactory0<PsiElement> TYPE_INFERENCE_CANDIDATE_WITH_SAM_AND_VARARG = DiagnosticFactory0.create(WARNING);
@@ -965,7 +965,8 @@ public class DefaultErrorMessages {
MAP.put(TYPE_INFERENCE_INCORPORATION_ERROR, "Type inference failed. Please try to specify type arguments explicitly.");
MAP.put(TYPE_INFERENCE_ONLY_INPUT_TYPES, "Type inference failed. The value of the type parameter {0} should be mentioned in input types " +
"(argument types, receiver type or expected type). Try to specify it explicitly.", NAME);
MAP.put(INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION, "Type argument for a type parameter {0} can''t be inferred because it has incompatible upper bounds: {1}", TO_STRING, RENDER_COLLECTION_OF_TYPES);
MAP.put(INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION, "Type argument for a type parameter {0} can''t be inferred because it has incompatible upper bounds {2}: {1}", TO_STRING, RENDER_COLLECTION_OF_TYPES, TO_STRING);
MAP.put(INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION, "Type argument for a type parameter {0} has possible incompatible upper bounds: {1}", TO_STRING, RENDER_COLLECTION_OF_TYPES);
MAP.put(TYPE_INFERENCE_UPPER_BOUND_VIOLATED, "{0}", TYPE_INFERENCE_UPPER_BOUND_VIOLATED_RENDERER);
MAP.put(TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH, "Type inference failed. Expected type mismatch: inferred type is {1} but {0} was expected", RENDER_TYPE, RENDER_TYPE);
MAP.put(TYPE_INFERENCE_CANDIDATE_WITH_SAM_AND_VARARG, "Please use spread operator to pass an array as vararg. It will be an error in 1.5.");
@@ -40,6 +40,7 @@ import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.checker.intersectWrappedTypes
import org.jetbrains.kotlin.types.error.ErrorUtils
import org.jetbrains.kotlin.types.expressions.ControlStructureTypingUtils
import org.jetbrains.kotlin.types.isPossiblyEmpty
import org.jetbrains.kotlin.types.model.TypeSystemInferenceExtensionContextDelegate
import org.jetbrains.kotlin.types.model.TypeVariableMarker
import org.jetbrains.kotlin.types.model.freshTypeConstructor
@@ -604,16 +605,23 @@ class DiagnosticReporterByTrackingStrategy(
InferredEmptyIntersectionError::class.java, InferredEmptyIntersectionWarning::class.java -> {
val typeVariable = (error as InferredEmptyIntersection).typeVariable
psiKotlinCall.psiCall.calleeExpression?.let {
psiKotlinCall.psiCall.calleeExpression?.let { expression ->
val typeVariableText = (typeVariable as? TypeVariableFromCallableDescriptor)?.originalTypeParameter?.name?.asString()
?: typeVariable.toString()
val errorFactory = if (error is InferredEmptyIntersectionError)
INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION.errorFactory
else INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION.warningFactory
trace.reportDiagnosticOnce(
@Suppress("UNCHECKED_CAST")
errorFactory.on(it, typeVariableText, error.incompatibleTypes as Collection<KotlinType>)
)
@Suppress("UNCHECKED_CAST")
val incompatibleTypes = error.incompatibleTypes as Collection<KotlinType>
val diagnostic = if (error.kind.isPossiblyEmpty()) {
INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION.on(expression, typeVariableText, incompatibleTypes)
} else {
INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION.on(
context.languageVersionSettings, expression, typeVariableText,
incompatibleTypes, error.kind.description?.let { " ($it)" }.orEmpty()
)
}
trace.reportDiagnosticOnce(diagnostic)
}
}
}
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.resolve.calls.inference.model
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability.*
import org.jetbrains.kotlin.types.EmptyIntersectionTypeKind
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeVariableMarker
@@ -138,16 +139,19 @@ class NoSuccessfulFork(val position: IncorporationConstraintPosition) : Constrai
sealed interface InferredEmptyIntersection {
val incompatibleTypes: Collection<KotlinTypeMarker>
val typeVariable: TypeVariableMarker
val kind: EmptyIntersectionTypeKind
}
class InferredEmptyIntersectionWarning(
override val incompatibleTypes: Collection<KotlinTypeMarker>,
override val typeVariable: TypeVariableMarker
override val typeVariable: TypeVariableMarker,
override val kind: EmptyIntersectionTypeKind,
) : ConstraintSystemError(RESOLVED), InferredEmptyIntersection
class InferredEmptyIntersectionError(
override val incompatibleTypes: Collection<KotlinTypeMarker>,
override val typeVariable: TypeVariableMarker
override val typeVariable: TypeVariableMarker,
override val kind: EmptyIntersectionTypeKind,
) : ConstraintSystemError(INAPPLICABLE), InferredEmptyIntersection
class OnlyInputTypesDiagnostic(val typeVariable: TypeVariableMarker) : ConstraintSystemError(INAPPLICABLE)
@@ -38,9 +38,7 @@ class NewConstraintSystemImpl(
private val typeVariablesTransaction: MutableList<TypeVariableMarker> = SmartList()
private val properTypesCache: MutableSet<KotlinTypeMarker> = SmartSet.create()
private val notProperTypesCache: MutableSet<KotlinTypeMarker> = SmartSet.create()
private val emptyIntersectionTypesCache: MutableSet<Collection<KotlinTypeMarker>> = SmartSet.create()
private val nonEmptyIntersectionTypesCache: MutableSet<Collection<KotlinTypeMarker>> = SmartSet.create()
private val intersectionTypesCache: MutableMap<Collection<KotlinTypeMarker>, EmptyIntersectionTypeKind> = mutableMapOf()
private var couldBeResolvedWithUnrestrictedBuilderInference: Boolean = false
override var atCompletionState: Boolean = false
@@ -444,37 +442,34 @@ class NewConstraintSystemImpl(
}
override fun getEmptyIntersectionTypeKind(types: Collection<KotlinTypeMarker>): EmptyIntersectionTypeKind {
if (types in emptyIntersectionTypesCache)
return EmptyIntersectionTypeKind.MULTIPLE_CLASSES
if (types in intersectionTypesCache)
return intersectionTypesCache.getValue(types)
if (types in nonEmptyIntersectionTypesCache)
return EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION
return types.computeEmptyIntersectionTypeKind().also {
when (it) {
EmptyIntersectionTypeKind.MULTIPLE_CLASSES -> emptyIntersectionTypesCache.add(types)
EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION -> nonEmptyIntersectionTypesCache.add(types)
}
return computeEmptyIntersectionTypeKind(types).also {
intersectionTypesCache[types] = it
}
}
private fun checkInferredEmptyIntersection(variable: TypeVariableMarker, resultType: KotlinTypeMarker) {
val intersectionTypeConstructor = resultType.typeConstructor().takeIf { it is IntersectionTypeConstructorMarker } ?: return
val isInferredEmptyIntersectionForbidden =
languageVersionSettings.supportsFeature(LanguageFeature.ForbidInferringTypeVariablesIntoEmptyIntersection)
val upperTypes = intersectionTypeConstructor.supertypes()
// Diagnostic with these incompatible types has already been reported at the resolution stage
if (upperTypes.size <= 1 || storage.errors.any { it is InferredEmptyIntersection && it.incompatibleTypes == upperTypes })
return
if (getEmptyIntersectionTypeKind(upperTypes).isDefinitelyEmpty()) {
// Remove existing errors from resolution stage because a completion error is more precise
storage.errors.removeIf { it is InferredEmptyIntersection }
val errorFactory =
if (isInferredEmptyIntersectionForbidden) ::InferredEmptyIntersectionError else ::InferredEmptyIntersectionWarning
addError(errorFactory(upperTypes, variable))
}
val emptyIntersectionKind = getEmptyIntersectionTypeKind(upperTypes).takeIf { it.isEmpty() } ?: return
// Remove existing errors from the resolution stage because a completion stage error is always more precise
storage.errors.removeIf { it is InferredEmptyIntersection }
val isInferredEmptyIntersectionForbidden =
languageVersionSettings.supportsFeature(LanguageFeature.ForbidInferringTypeVariablesIntoEmptyIntersection)
val errorFactory = if (emptyIntersectionKind.isDefinitelyEmpty() && isInferredEmptyIntersectionForbidden)
::InferredEmptyIntersectionError
else ::InferredEmptyIntersectionWarning
addError(errorFactory(upperTypes, variable, emptyIntersectionKind))
}
private fun checkMissedConstraints() {
@@ -901,14 +901,16 @@ internal object CheckIncompatibleTypeVariableUpperBounds : ResolutionPart() {
markCandidateForCompatibilityResolve(needToReportWarning = false)
continue
}
constraintSystem.getEmptyIntersectionTypeKind(upperTypes).isDefinitelyEmpty() -> {
val isInferredEmptyIntersectionForbidden =
callComponents.languageVersionSettings.supportsFeature(LanguageFeature.ForbidInferringTypeVariablesIntoEmptyIntersection)
else -> {
val emptyIntersectionKind = constraintSystem.getEmptyIntersectionTypeKind(upperTypes).takeIf { it.isDefinitelyEmpty() }
?: continue
val isInferredEmptyIntersectionForbidden = callComponents.languageVersionSettings.supportsFeature(
LanguageFeature.ForbidInferringTypeVariablesIntoEmptyIntersection
)
val errorFactory =
if (isInferredEmptyIntersectionForbidden) ::InferredEmptyIntersectionError else ::InferredEmptyIntersectionWarning
val errorFactory = if (isInferredEmptyIntersectionForbidden)
::InferredEmptyIntersectionError
else ::InferredEmptyIntersectionWarning
addError(errorFactory(upperTypes, variableWithConstraints.typeVariable))
addError(errorFactory(upperTypes, variableWithConstraints.typeVariable, emptyIntersectionKind))
}
}
}
@@ -1,6 +1,6 @@
/kt45461.kt:5:25: warning: parameter 'foo' is never used
fun <S : T> takeFoo(foo: Foo<in S>) {}
^
/kt45461.kt:10:19: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds: String, Int. This will become an error in Kotlin 1.9
/kt45461.kt:10:19: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds (multiple incompatible classes): String, Int. This will become an error in Kotlin 1.9
Bar<String>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31
^
@@ -0,0 +1,7 @@
/kt45461_10.kt:7:25: warning: parameter 'foo' is never used
fun <S : T> takeFoo(foo: Foo<in S>) {}
^
/kt45461_10.kt:16:21: warning: type argument for a type parameter S has possible incompatible upper bounds: Out<Int>, K
Bar<Out<Int>>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31
^
@@ -1,4 +1,6 @@
// FIR_IDENTICAL
// RENDER_DIAGNOSTICS_FULL_TEXT
class Foo<T>
class Bar<T> {
@@ -11,5 +13,5 @@ interface A
fun <K : Out<L>, L : N, N: A> main() {
val foo = Foo<K>()
Bar<Out<Int>>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31
Bar<Out<Int>>().<!INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION!>takeFoo<!>(foo) // error in 1.3.72, no error in 1.4.31
}
@@ -1,7 +1,7 @@
/kt45461_12.kt:5:25: warning: parameter 'foo' is never used
fun <S : T> takeFoo(foo: Foo<in S>) {}
^
/kt45461_12.kt:12:19: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds: String, K. This will become an error in Kotlin 1.9
/kt45461_12.kt:12:19: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds (multiple incompatible classes): String, K. This will become an error in Kotlin 1.9
Bar<String>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31
^
@@ -0,0 +1,7 @@
/kt45461_13.kt:7:25: warning: parameter 'foo' is never used
fun <S : T> takeFoo(foo: Foo<in S>) {}
^
/kt45461_13.kt:16:24: warning: type argument for a type parameter S has possible incompatible upper bounds: Out<String>, K
Bar<Out<String>>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31
^
@@ -1,4 +1,6 @@
// FIR_IDENTICAL
// RENDER_DIAGNOSTICS_FULL_TEXT
class Foo<T>
class Bar<T> {
@@ -11,5 +13,5 @@ class Out<out K>
fun <K : L, L : N, N> main() where N: Out<A> {
val foo = Foo<K>()
Bar<Out<String>>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31
Bar<Out<String>>().<!INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION!>takeFoo<!>(foo) // error in 1.3.72, no error in 1.4.31
}
@@ -1,6 +1,6 @@
/kt45461_15.kt:5:25: warning: parameter 'foo' is never used
fun <S : T> takeFoo(foo: Foo<in S>) {}
^
/kt45461_15.kt:14:21: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds: Inv<Int>, K. This will become an error in Kotlin 1.9
/kt45461_15.kt:14:21: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds (incompatible type arguments): Inv<Int>, K. This will become an error in Kotlin 1.9
Bar<Inv<Int>>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31
^
@@ -0,0 +1,15 @@
// FIR_IDENTICAL
class Foo<T>
class Bar<T> {
fun <S : T> takeFoo(foo: Foo<in S>) {}
}
class Inv<P>
interface A
fun <K : Inv<T>, T> main() where T: A, T: Number {
val foo = Foo<K>()
Bar<Inv<out Int>>().<!INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION!>takeFoo<!>(foo) // error in 1.3.72, no error in 1.4.31
}
@@ -11,5 +11,5 @@ interface A
fun <K : Inv<T>, T> main() where T: A, T: Number {
val foo = Foo<K>()
Bar<Inv<out Int>>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31
Bar<Inv<out Int>>().<!INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION!>takeFoo<!>(foo) // error in 1.3.72, no error in 1.4.31
}
@@ -1,6 +1,6 @@
/kt45461_19.kt:5:25: warning: parameter 'foo' is never used
fun <S : T> takeFoo(foo: Foo<in S>) {}
^
/kt45461_19.kt:13:26: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds: Out<Inv<Int>>, K. This will become an error in Kotlin 1.9
/kt45461_19.kt:13:26: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds (incompatible type arguments): Out<Inv<Int>>, K. This will become an error in Kotlin 1.9
Bar<Out<Inv<Int>>>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31
^
@@ -4,7 +4,7 @@
/kt45461_2.kt:8:10: warning: 'Int' is a final type, and thus a value of the type parameter is predetermined
fun <K : Int> main() {
^
/kt45461_2.kt:10:19: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds: String, K. This will become an error in Kotlin 1.9
/kt45461_2.kt:10:19: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds (multiple incompatible classes): String, K. This will become an error in Kotlin 1.9
Bar<String>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31
^
@@ -1,6 +1,6 @@
/kt45461_21.kt:5:25: warning: parameter 'foo' is never used
fun <S : T> takeFoo(foo: Foo<in S>) {}
^
/kt45461_21.kt:13:25: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds: In<Inv<Int>>, K. This will become an error in Kotlin 1.9
/kt45461_21.kt:13:25: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds (incompatible type arguments): In<Inv<Int>>, K. This will become an error in Kotlin 1.9
Bar<In<Inv<Int>>>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31
^
@@ -4,6 +4,6 @@
/kt45461_24.kt:10:10: warning: 'Inv<out Inv<out Int>>' is a final type, and thus a value of the type parameter is predetermined
fun <K : Inv<out Inv<out Int>>> main() {
^
/kt45461_24.kt:12:35: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds: Inv<in Inv<in Number>>, K. This will become an error in Kotlin 1.9
/kt45461_24.kt:12:35: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds (incompatible type arguments): Inv<in Inv<in Number>>, K. This will become an error in Kotlin 1.9
Bar<Inv<in Inv<in Number>>>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31
^
@@ -1,3 +1,3 @@
/kt45461_25.kt:11:34: error: type argument for a type parameter S can't be inferred because it has incompatible upper bounds: String, K, Float
/kt45461_25.kt:11:34: error: type argument for a type parameter S can't be inferred because it has incompatible upper bounds (multiple incompatible classes): String, K, Float
val x: Float = Bar<String>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31
^
@@ -8,5 +8,5 @@ class Bar<T> {
fun <K : L, L : N, N: <!FINAL_UPPER_BOUND!>Int<!>> main() {
val foo = Foo<K>()
val x: Float = Bar<String>().<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION("S; kotlin/String, K")!>takeFoo<!>(foo) // error in 1.3.72, no error in 1.4.31
val x: Float = Bar<String>().<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION("S; kotlin/String, K; multiple incompatible classes")!>takeFoo<!>(foo) // error in 1.3.72, no error in 1.4.31
}
@@ -8,5 +8,5 @@ class Bar<T> {
fun <K : L, L : N, N: <!FINAL_UPPER_BOUND!>Int<!>> main() {
val foo = Foo<K>()
val x: Float = Bar<String>().<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_ERROR("S; String, K, Float")!>takeFoo<!>(foo) // error in 1.3.72, no error in 1.4.31
val x: Float = Bar<String>().<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_ERROR("S; String, K, Float; (multiple incompatible classes)")!>takeFoo<!>(foo) // error in 1.3.72, no error in 1.4.31
}
@@ -1,4 +1,4 @@
/kt45461_26.kt:13:44: error: type argument for a type parameter S can't be inferred because it has incompatible upper bounds: Out<String>, K, Out<Float>
/kt45461_26.kt:13:44: error: type argument for a type parameter S can't be inferred because it has incompatible upper bounds (multiple incompatible classes): Out<String>, K, Out<Float>
val x: Out<Float> = Bar<Out<String>>().takeFoo(foo)
^
@@ -10,5 +10,5 @@ class Out<out K>
fun <K : L, L : N, N: <!FINAL_UPPER_BOUND!>Out<Int><!>> main() {
val foo = Foo<K>()
val x: Out<Float> = Bar<Out<String>>().<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION("S; Out<kotlin/String>, K")!>takeFoo<!>(foo)
val x: Out<Float> = Bar<Out<String>>().<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION("S; Out<kotlin/String>, K; multiple incompatible classes")!>takeFoo<!>(foo)
}
@@ -10,5 +10,5 @@ class Out<out K>
fun <K : L, L : N, N: <!FINAL_UPPER_BOUND!>Out<Int><!>> main() {
val foo = Foo<K>()
val x: Out<Float> = Bar<Out<String>>().<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_ERROR("S; Out<String>, K, Out<Float>")!>takeFoo<!>(foo)
val x: Out<Float> = Bar<Out<String>>().<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_ERROR("S; Out<String>, K, Out<Float>; (multiple incompatible classes)")!>takeFoo<!>(foo)
}
@@ -11,5 +11,5 @@ interface B : A<Int>
fun <K : Out<A<String>>> main() {
val foo = Foo<K>()
Bar<Out<B>>().<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_WARNING!>takeFoo<!>(foo) // error in 1.3.72, no error in 1.4.31
Bar<Out<B>>().<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_WARNING("S; Out<B>, K; (incompatible supertypes)")!>takeFoo<!>(foo) // error in 1.3.72, no error in 1.4.31
}
@@ -11,5 +11,5 @@ class B : A<Int>
fun <K : Out<A<String>>> main() {
val foo = Foo<K>()
Bar<Out<B>>().<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_WARNING!>takeFoo<!>(foo) // error in 1.3.72, no error in 1.4.31
Bar<Out<B>>().<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_WARNING("S; Out<B>, K; (incompatible supertypes)")!>takeFoo<!>(foo) // error in 1.3.72, no error in 1.4.31
}
@@ -1,6 +1,6 @@
/kt45461_5.kt:5:25: warning: parameter 'foo' is never used
fun <S : T> takeFoo(foo: Foo<in S>) {}
^
/kt45461_5.kt:10:19: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds: String, K. This will become an error in Kotlin 1.9
/kt45461_5.kt:10:19: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds (multiple incompatible classes): String, K. This will become an error in Kotlin 1.9
Bar<String>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31
^
@@ -4,6 +4,6 @@
/kt45461_8.kt:10:28: warning: 'Int' is a final type, and thus a value of the type parameter is predetermined
fun <K : Inv<L>, L : N, N: Int> main() {
^
/kt45461_8.kt:12:24: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds: Inv<Number>, K. This will become an error in Kotlin 1.9
/kt45461_8.kt:12:24: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds (incompatible type arguments): Inv<Number>, K. This will become an error in Kotlin 1.9
Bar<Inv<Number>>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31
^
@@ -1,7 +1,7 @@
/kt45461_9.kt:5:25: warning: parameter 'foo' is never used
fun <S : T> takeFoo(foo: Foo<in S>) {}
^
/kt45461_9.kt:12:21: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds: Inv<Int>, Inv<Number>. This will become an error in Kotlin 1.9
/kt45461_9.kt:12:21: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds (incompatible type arguments): Inv<Int>, Inv<Number>. This will become an error in Kotlin 1.9
Bar<Inv<Int>>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31
^
@@ -4,7 +4,7 @@
/kt48765.kt:4:52: warning: parameter 'x2' is never used
fun <T1: Number, T2: A<Float, T1>> foo(x1: T2, x2: T1) {}
^
/kt48765.kt:8:13: warning: type argument for a type parameter T can't be inferred because it has incompatible upper bounds: String, Number. This will become an error in Kotlin 1.9
/kt48765.kt:8:13: warning: type argument for a type parameter T can't be inferred because it has incompatible upper bounds (multiple incompatible classes): String, Number. This will become an error in Kotlin 1.9
B().foo(x, foo())
^
/kt48765.kt:12:9: warning: 'String' is a final type, and thus a value of the type parameter is predetermined
@@ -1,4 +1,7 @@
/kt48935.kt:7:35: warning: parameter 'func' is never used
fun <T, V> exampleGenericFunction(func: V) where T: Base, V: (T) -> Unit {
^
/kt48935.kt:13:5: warning: type argument for a type parameter T has possible incompatible upper bounds: Base, DoesNotImplementBase
exampleGenericFunction(func) // expected this to be a compilation error as the T: Base constraint should not be satisfied
^
@@ -10,5 +10,5 @@ fun <T, V> exampleGenericFunction(func: V) where T: Base, V: (T) -> Unit {
fun main() {
val func: (DoesNotImplementBase) -> Unit = { }
exampleGenericFunction(func) // expected this to be a compilation error as the T: Base constraint should not be satisfied
<!INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION!>exampleGenericFunction<!>(func) // expected this to be a compilation error as the T: Base constraint should not be satisfied
}
@@ -9,5 +9,5 @@ fun <T, V> exampleGenericFunction(func: V) where T: Base, V: (T) -> Unit {
fun main() {
val func: (DoesNotImplementBase) -> Unit = { }
exampleGenericFunction(func) // expected this to be a compilation error as the T: Base constraint should not be satisfied
<!INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION!>exampleGenericFunction<!>(func) // expected this to be a compilation error as the T: Base constraint should not be satisfied
}
@@ -1,4 +1,4 @@
/kt49661.kt:10:5: warning: type argument for a type parameter T can't be inferred because it has incompatible upper bounds: Foo, Int. This will become an error in Kotlin 1.9
/kt49661.kt:10:5: warning: type argument for a type parameter T can't be inferred because it has incompatible upper bounds (multiple incompatible classes): Foo, Int. This will become an error in Kotlin 1.9
f<Int> { g() }
^
@@ -7,6 +7,6 @@ fun <V> genericIn(x: In<V>) {}
/selectFromCovariantAndContravariantTypes.kt:13:20: warning: parameter 'x' is never used
fun <V> genericOut(x: Out<V>) {}
^
/selectFromCovariantAndContravariantTypes.kt:16:5: warning: type argument for a type parameter V can't be inferred because it has incompatible upper bounds: A, B. This will become an error in Kotlin 1.9
/selectFromCovariantAndContravariantTypes.kt:16:5: warning: type argument for a type parameter V can't be inferred because it has incompatible upper bounds (multiple incompatible classes): A, B. This will become an error in Kotlin 1.9
genericIn(select(a, b))
^
@@ -14,7 +14,7 @@ val a1: A = select(
{ a: Int -> myPrint(a + this.length + 2) }
)
val a2 = select(
val a2 = <!INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION("")!>select<!>(
{ a: Int -> myPrint(a + this.<!UNRESOLVED_REFERENCE!>length<!> <!DEBUG_INFO_MISSING_UNRESOLVED!>+<!> 1) },
fun CharSequence.(a: Int) { myPrint(a + this.length + 2) },
{ a: Int -> myPrint(a + this.<!UNRESOLVED_REFERENCE!>length<!> <!DEBUG_INFO_MISSING_UNRESOLVED!>+<!> 3) }
@@ -13,7 +13,7 @@ fun testElvis(a: Int?, b: Int?) {
if (a != null) {
doInt(b ?: a)
}
doList(getList() ?: emptyListOfA()) //should be an error
<!INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION("T; a/A, kotlin/Int")!>doList<!>(getList() ?: emptyListOfA()) //should be an error
doList(getList() ?: strangeList { doInt(it) }) //lambda was not analyzed
}
@@ -13,7 +13,7 @@ fun testElvis(a: Int?, b: Int?) {
if (a != null) {
doInt(b ?: <!DEBUG_INFO_SMARTCAST!>a<!>)
}
doList(getList() ?: emptyListOfA()) //should be an error
<!INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION("")!>doList<!>(getList() ?: emptyListOfA()) //should be an error
doList(getList() ?: strangeList { doInt(it) }) //lambda was not analyzed
}
@@ -12,7 +12,7 @@ fun <T: A> emptyNullableListOfA(): List<T>? = null
//-------------------------------
fun testExclExcl() {
doList(emptyNullableListOfA()!!) //should be an error here
<!INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION("T; a/A, kotlin/Int")!>doList<!>(emptyNullableListOfA()!!) //should be an error here
val l: List<Int> = <!INITIALIZER_TYPE_MISMATCH, NEW_INFERENCE_ERROR!>id(emptyNullableListOfA()!!)<!>
doList(strangeNullableList { doInt(it) }!!) //lambda should be analyzed (at completion phase)
@@ -12,7 +12,7 @@ fun <T: A> emptyNullableListOfA(): List<T>? = null
//-------------------------------
fun testExclExcl() {
doList(emptyNullableListOfA()!!) //should be an error here
<!INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION("")!>doList<!>(emptyNullableListOfA()!!) //should be an error here
val l: List<Int> = <!TYPE_MISMATCH!><!TYPE_MISMATCH!>id<!>(<!TYPE_MISMATCH!>emptyNullableListOfA<!>()<!TYPE_MISMATCH!>!!<!>)<!>
doList(strangeNullableList { doInt(it) }!!) //lambda should be analyzed (at completion phase)
@@ -0,0 +1,297 @@
/*
* Copyright 2010-2022 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.resolve.checkers
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.EmptyIntersectionTypeKind
import org.jetbrains.kotlin.types.isDefinitelyEmpty
import org.jetbrains.kotlin.types.isPossiblyEmpty
import org.jetbrains.kotlin.types.model.*
internal object EmptyIntersectionTypeChecker {
fun computeEmptyIntersectionTypeKind(
context: TypeSystemInferenceExtensionContext,
types: Collection<KotlinTypeMarker>
): EmptyIntersectionTypeKind = with(context) {
if (types.isEmpty())
return EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION
@Suppress("NAME_SHADOWING")
val types = types.toList()
var possibleEmptyIntersectionKind: EmptyIntersectionTypeKind? = null
for (i in 0 until types.size) {
val firstType = types[i]
if (!mayCauseEmptyIntersection(firstType)) continue
val firstSubstitutedType by lazy { firstType.eraseContainingTypeParameters() }
for (j in i + 1 until types.size) {
val secondType = types[j]
if (!mayCauseEmptyIntersection(secondType)) continue
val secondSubstitutedType = secondType.eraseContainingTypeParameters()
if (!mayCauseEmptyIntersection(secondSubstitutedType) && !mayCauseEmptyIntersection(firstSubstitutedType)) continue
val kind = computeKindByHavingCommonSubtype(firstSubstitutedType, secondSubstitutedType)
if (kind.isDefinitelyEmpty())
return kind
if (kind.isPossiblyEmpty())
possibleEmptyIntersectionKind = kind
}
}
return possibleEmptyIntersectionKind ?: EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION
}
private fun TypeSystemInferenceExtensionContext.computeKindByHavingCommonSubtype(
first: KotlinTypeMarker, second: KotlinTypeMarker
): EmptyIntersectionTypeKind {
fun extractIntersectionComponentsIfNeeded(type: KotlinTypeMarker) =
if (type.typeConstructor() is IntersectionTypeConstructorMarker) {
type.typeConstructor().supertypes().toList()
} else listOf(type)
val expandedTypes = extractIntersectionComponentsIfNeeded(first) + extractIntersectionComponentsIfNeeded(second)
val typeCheckerState by lazy { newTypeCheckerState(errorTypesEqualToAnything = true, stubTypesEqualToAnything = true) }
var possibleEmptyIntersectionKind: EmptyIntersectionTypeKind? = null
for (i in expandedTypes.indices) {
val firstType = expandedTypes[i].withNullability(false)
val firstTypeConstructor = firstType.typeConstructor()
if (!mayCauseEmptyIntersection(firstType))
continue
for (j in i + 1 until expandedTypes.size) {
val secondType = expandedTypes[j].withNullability(false)
val secondTypeConstructor = secondType.typeConstructor()
if (!mayCauseEmptyIntersection(secondType))
continue
if (areEqualTypeConstructors(firstTypeConstructor, secondTypeConstructor) && secondTypeConstructor.parametersCount() == 0)
continue
if (AbstractTypeChecker.areRelatedBySubtyping(this, firstType, secondType))
continue
// If two classes aren't related by subtyping and no need to compare their type arguments, then they can't have a common subtype
if (
firstTypeConstructor.isDefinitelyClassTypeConstructor() && secondTypeConstructor.isDefinitelyClassTypeConstructor()
&& (firstTypeConstructor.parametersCount() == 0 || secondTypeConstructor.parametersCount() == 0)
) {
return EmptyIntersectionTypeKind.MULTIPLE_CLASSES
}
val superTypeByFirstConstructor = AbstractTypeChecker.findCorrespondingSupertypes(
typeCheckerState, firstType.lowerBoundIfFlexible(), secondTypeConstructor
).singleOrNull()
val superTypeBySecondConstructor = AbstractTypeChecker.findCorrespondingSupertypes(
typeCheckerState, secondType.lowerBoundIfFlexible(), firstTypeConstructor
).singleOrNull()
val anyInference = firstTypeConstructor.isInterface() || secondTypeConstructor.isInterface()
// Two classes can't have a common subtype if neither is a subtype of another
if (superTypeByFirstConstructor == null && superTypeBySecondConstructor == null && !anyInference)
return EmptyIntersectionTypeKind.MULTIPLE_CLASSES
if (anyInference && !canHaveCommonSubtypeWithInterface(firstType, secondType))
return EmptyIntersectionTypeKind.INCOMPATIBLE_SUPERTYPES
if (superTypeByFirstConstructor == null || superTypeBySecondConstructor == null) {
// don't have incompatible supertypes so can have a common subtype only if all types are interfaces
if (firstTypeConstructor.isFinalClassConstructor() || secondTypeConstructor.isFinalClassConstructor()) {
possibleEmptyIntersectionKind = EmptyIntersectionTypeKind.SINGLE_FINAL_CLASS
}
continue
}
val argumentsIntersectionKind =
computeKindByCheckingTypeArguments(superTypeByFirstConstructor, superTypeBySecondConstructor)
if (argumentsIntersectionKind.isDefinitelyEmpty())
return argumentsIntersectionKind
if (possibleEmptyIntersectionKind == null && argumentsIntersectionKind.isPossiblyEmpty())
possibleEmptyIntersectionKind = argumentsIntersectionKind
}
}
return possibleEmptyIntersectionKind ?: EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION
}
private fun TypeSystemInferenceExtensionContext.computeKindByCheckingTypeArguments(
firstType: KotlinTypeMarker,
secondType: KotlinTypeMarker,
): EmptyIntersectionTypeKind {
require(firstType.typeConstructor() == secondType.typeConstructor()) {
"Type constructors of the passed types should be the same to compare their arguments"
}
fun isSubtypeOf(firstType: KotlinTypeMarker, secondType: KotlinTypeMarker) =
AbstractTypeChecker.isSubtypeOf(this, firstType, secondType)
fun areEqualTypes(firstType: KotlinTypeMarker, secondType: KotlinTypeMarker) =
AbstractTypeChecker.equalTypes(this, firstType, secondType)
fun Boolean.toEmptyIntersectionKind() =
if (this) EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION else EmptyIntersectionTypeKind.INCOMPATIBLE_TYPE_ARGUMENTS
var possibleEmptyIntersectionKind: EmptyIntersectionTypeKind? = null
for ((i, argumentOfFirst) in firstType.getArguments().withIndex()) {
@Suppress("NAME_SHADOWING")
val argumentOfFirst = uncaptureIfNeeded(argumentOfFirst)
val argumentOfSecond = uncaptureIfNeeded(secondType.getArgument(i))
if (argumentOfFirst == argumentOfSecond || argumentOfFirst.isStarProjection() || argumentOfSecond.isStarProjection())
continue
val argumentTypeOfFirst = argumentOfFirst.getType()
val argumentTypeOfSecond = argumentOfSecond.getType()
val intersectionKindOfArguments = when {
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.INV, TypeVariance.INV) ->
areEqualTypes(argumentTypeOfFirst, argumentTypeOfSecond).toEmptyIntersectionKind()
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.INV, TypeVariance.OUT) -> {
isSubtypeOf(argumentTypeOfFirst, argumentTypeOfSecond).toEmptyIntersectionKind()
}
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.INV, TypeVariance.IN) -> {
isSubtypeOf(argumentTypeOfSecond, argumentTypeOfFirst).toEmptyIntersectionKind()
}
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.IN, TypeVariance.OUT) -> {
if (argumentTypeOfFirst.argumentsCount() == 0 && argumentTypeOfSecond.argumentsCount() == 0) {
isSubtypeOf(argumentTypeOfFirst, argumentTypeOfSecond).toEmptyIntersectionKind()
} else {
computeKindByHavingCommonSubtype(argumentTypeOfFirst, argumentTypeOfSecond)
}
}
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.OUT, TypeVariance.OUT)
|| areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.IN, TypeVariance.IN) -> {
computeKindByHavingCommonSubtype(argumentTypeOfFirst, argumentTypeOfSecond)
}
else -> true.toEmptyIntersectionKind()
}
if (intersectionKindOfArguments.isDefinitelyEmpty())
return intersectionKindOfArguments
if (possibleEmptyIntersectionKind == null && intersectionKindOfArguments.isPossiblyEmpty())
possibleEmptyIntersectionKind = intersectionKindOfArguments
}
return possibleEmptyIntersectionKind ?: EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION
}
private fun TypeSystemInferenceExtensionContext.canHaveCommonSubtypeWithInterface(
firstType: KotlinTypeMarker, secondType: KotlinTypeMarker
): Boolean {
require(firstType.typeConstructor().isInterface() || secondType.typeConstructor().isInterface()) {
"One of the passed type should be an interface"
}
@Suppress("NAME_SHADOWING")
val firstType = firstType.eraseContainingTypeParameters()
@Suppress("NAME_SHADOWING")
val secondType = secondType.eraseContainingTypeParameters()
// interface A<K>
// interface B: A<String>
// interface C: A<Int>
// B & C can't have common subtype due to having incompatible supertypes: A<String> and A<Int>
return !hasIncompatibleSuperTypes(firstType, secondType)
}
private fun TypeSystemInferenceExtensionContext.areIncompatibleSuperTypes(
firstType: KotlinTypeMarker, secondType: KotlinTypeMarker
): Boolean = firstType.typeConstructor() == secondType.typeConstructor()
&& !AbstractTypeChecker.equalTypes(
newTypeCheckerState(errorTypesEqualToAnything = true, stubTypesEqualToAnything = true),
firstType,
secondType
)
// interface A<T>
// interface B : A<Int>
// interface C : A<String>
// => B and C have incompatible supertypes
private fun TypeSystemInferenceExtensionContext.hasIncompatibleSuperTypes(
firstType: KotlinTypeMarker, secondType: KotlinTypeMarker
): Boolean {
val superTypesOfFirst = firstType.typeConstructor().supertypes()
val firstTypeSubstitutor = createSubstitutorForSuperTypes(firstType)
val superTypesOfSecond = secondType.typeConstructor().supertypes()
val secondTypeSubstitutor = createSubstitutorForSuperTypes(secondType)
for (superTypeOfFirst in superTypesOfFirst) {
@Suppress("NAME_SHADOWING")
val superTypeOfFirst = firstTypeSubstitutor?.safeSubstitute(superTypeOfFirst) ?: superTypeOfFirst
if (areIncompatibleSuperTypes(superTypeOfFirst, secondType))
return true
for (superTypeOfSecond in superTypesOfSecond) {
@Suppress("NAME_SHADOWING")
val superTypeOfSecond = secondTypeSubstitutor?.safeSubstitute(superTypeOfSecond) ?: superTypeOfSecond
if (
areIncompatibleSuperTypes(firstType, superTypeOfSecond)
|| areIncompatibleSuperTypes(superTypeOfFirst, superTypeOfSecond)
) return true
if (hasIncompatibleSuperTypes(superTypeOfFirst, superTypeOfSecond))
return true
}
}
return false
}
private fun TypeSystemInferenceExtensionContext.mayCauseEmptyIntersection(type: KotlinTypeMarker): Boolean {
val typeConstructor = type.typeConstructor()
if (!typeConstructor.isClassTypeConstructor() && !typeConstructor.isTypeParameterTypeConstructor())
return false
// Even two interfaces may be an empty intersection type:
// interface Inv<K>
// interface B : Inv<Int>
// `Inv<String> & B` or `Inv<String> & Inv<Int>` are empty
// So we don't filter out interfaces here
return !typeConstructor.isAnyConstructor() && !typeConstructor.isNothingConstructor()
}
private fun TypeSystemInferenceExtensionContext.areArgumentsOfSpecifiedVariances(
firstType: KotlinTypeMarker,
secondType: KotlinTypeMarker,
argumentIndex: Int,
variance1: TypeVariance,
variance2: TypeVariance,
): Boolean {
fun getEffectiveVariance(type: KotlinTypeMarker): TypeVariance? {
val argument = uncaptureIfNeeded(type.getArgument(argumentIndex))
val parameter = type.typeConstructor().getParameter(argumentIndex)
return AbstractTypeChecker.effectiveVariance(parameter.getVariance(), argument.getVariance())
}
val effectiveVariance1 = getEffectiveVariance(firstType)
val effectiveVariance2 = getEffectiveVariance(secondType)
return (effectiveVariance1 == variance1 && effectiveVariance2 == variance2)
|| (effectiveVariance1 == variance2 && effectiveVariance2 == variance1)
}
private fun TypeSystemInferenceExtensionContext.uncaptureIfNeeded(argument: TypeArgumentMarker): TypeArgumentMarker {
val type = argument.getType()
return if (type is CapturedTypeMarker) type.typeConstructorProjection() else argument
}
}
@@ -6,6 +6,19 @@
package org.jetbrains.kotlin.types
// TODO: add `SINGLE_FINAL_CLASS` later to report warnings
enum class EmptyIntersectionTypeKind { NOT_EMPTY_INTERSECTION, MULTIPLE_CLASSES }
enum class EmptyIntersectionTypeKind(val description: String? = null) {
NOT_EMPTY_INTERSECTION,
MULTIPLE_CLASSES("multiple incompatible classes"),
INCOMPATIBLE_SUPERTYPES("incompatible supertypes"),
INCOMPATIBLE_TYPE_ARGUMENTS("incompatible type arguments"),
SINGLE_FINAL_CLASS
}
fun EmptyIntersectionTypeKind.isDefinitelyEmpty(): Boolean = this == EmptyIntersectionTypeKind.MULTIPLE_CLASSES
fun EmptyIntersectionTypeKind.isDefinitelyEmpty(): Boolean =
this == EmptyIntersectionTypeKind.MULTIPLE_CLASSES
|| this == EmptyIntersectionTypeKind.INCOMPATIBLE_SUPERTYPES
|| this == EmptyIntersectionTypeKind.INCOMPATIBLE_TYPE_ARGUMENTS
fun EmptyIntersectionTypeKind.isPossiblyEmpty(): Boolean = this == EmptyIntersectionTypeKind.SINGLE_FINAL_CLASS
fun EmptyIntersectionTypeKind.isEmpty(): Boolean = this != EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.types.model
import org.jetbrains.kotlin.resolve.checkers.EmptyIntersectionTypeChecker
import org.jetbrains.kotlin.types.*
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
@@ -217,6 +218,8 @@ interface TypeSystemInferenceExtensionContext : TypeSystemContext, TypeSystemBui
fun KotlinTypeMarker.hasExactAnnotation(): Boolean
fun KotlinTypeMarker.hasNoInferAnnotation(): Boolean
fun TypeConstructorMarker.isFinalClassConstructor(): Boolean
fun TypeVariableMarker.freshTypeConstructor(): TypeConstructorMarker
fun CapturedTypeMarker.typeConstructorProjection(): TypeArgumentMarker
@@ -321,252 +324,13 @@ interface TypeSystemInferenceExtensionContext : TypeSystemContext, TypeSystemBui
return createCapturedType(starProjection, listOf(superType), lowerType = null, CaptureStatus.FROM_EXPRESSION)
}
fun Collection<KotlinTypeMarker>.computeEmptyIntersectionTypeKind(): EmptyIntersectionTypeKind {
if (this.isEmpty())
return EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION
val types = this.toList()
for (i in 0 until types.size) {
val firstType = types[i]
if (!firstType.mayCauseEmptyIntersection()) continue
val firstSubstitutedType by lazy { firstType.eraseContainingTypeParameters() }
for (j in i + 1 until types.size) {
val secondType = types[j]
if (!secondType.mayCauseEmptyIntersection()) continue
val secondSubstitutedType = secondType.eraseContainingTypeParameters()
if (!secondSubstitutedType.mayCauseEmptyIntersection() && !firstSubstitutedType.mayCauseEmptyIntersection()) continue
if (!canHaveCommonSubtype(firstSubstitutedType, secondSubstitutedType))
return EmptyIntersectionTypeKind.MULTIPLE_CLASSES
}
}
return EmptyIntersectionTypeKind.NOT_EMPTY_INTERSECTION
}
fun createSubstitutorForSuperTypes(baseType: KotlinTypeMarker): TypeSubstitutorMarker?
private fun areIncompatibleSuperTypes(firstType: KotlinTypeMarker, secondType: KotlinTypeMarker): Boolean =
firstType.typeConstructor() == secondType.typeConstructor()
&& !AbstractTypeChecker.equalTypes(
newTypeCheckerState(errorTypesEqualToAnything = true, stubTypesEqualToAnything = true),
firstType,
secondType
)
// interface A<T>
// interface B : A<Int>
// interface C : A<String>
// => B and C have incompatible supertypes
private fun hasIncompatibleSuperTypes(firstType: KotlinTypeMarker, secondType: KotlinTypeMarker): Boolean {
val superTypesOfFirst = firstType.typeConstructor().supertypes()
val firstTypeSubstitutor = createSubstitutorForSuperTypes(firstType)
val superTypesOfSecond = secondType.typeConstructor().supertypes()
val secondTypeSubstitutor = createSubstitutorForSuperTypes(secondType)
for (superTypeOfFirst in superTypesOfFirst) {
@Suppress("NAME_SHADOWING")
val superTypeOfFirst = firstTypeSubstitutor?.safeSubstitute(superTypeOfFirst) ?: superTypeOfFirst
if (areIncompatibleSuperTypes(superTypeOfFirst, secondType))
return true
for (superTypeOfSecond in superTypesOfSecond) {
@Suppress("NAME_SHADOWING")
val superTypeOfSecond = secondTypeSubstitutor?.safeSubstitute(superTypeOfSecond) ?: superTypeOfSecond
if (
areIncompatibleSuperTypes(firstType, superTypeOfSecond)
|| areIncompatibleSuperTypes(superTypeOfFirst, superTypeOfSecond)
) return true
if (hasIncompatibleSuperTypes(superTypeOfFirst, superTypeOfSecond))
return true
}
}
return false
}
private fun canHaveCommonSubtypeWithInterface(firstType: KotlinTypeMarker, secondType: KotlinTypeMarker): Boolean {
require(firstType.typeConstructor().isInterface() || secondType.typeConstructor().isInterface()) {
"One of the passed type should be an interface"
}
@Suppress("NAME_SHADOWING")
val firstType = firstType.eraseContainingTypeParameters()
@Suppress("NAME_SHADOWING")
val secondType = secondType.eraseContainingTypeParameters()
// interface A<K>
// interface B: A<String>
// interface C: A<Int>
// B & C can't have common subtype due to having incompatible supertypes: A<String> and A<Int>
return !hasIncompatibleSuperTypes(firstType, secondType)
}
private fun canHaveCommonSubtype(first: KotlinTypeMarker, second: KotlinTypeMarker): Boolean {
fun extractIntersectionComponentsIfNeeded(type: KotlinTypeMarker) =
if (type.typeConstructor() is IntersectionTypeConstructorMarker) {
type.typeConstructor().supertypes().toList()
} else listOf(type)
val expandedTypes = extractIntersectionComponentsIfNeeded(first) + extractIntersectionComponentsIfNeeded(second)
val typeCheckerState by lazy { newTypeCheckerState(errorTypesEqualToAnything = true, stubTypesEqualToAnything = true) }
for (i in expandedTypes.indices) {
val firstType = expandedTypes[i].withNullability(false)
val firstTypeConstructor = firstType.typeConstructor()
if (!firstType.mayCauseEmptyIntersection())
continue
for (j in i + 1 until expandedTypes.size) {
val secondType = expandedTypes[j].withNullability(false)
val secondTypeConstructor = secondType.typeConstructor()
if (!secondType.mayCauseEmptyIntersection())
continue
if (areEqualTypeConstructors(firstTypeConstructor, secondTypeConstructor) && secondTypeConstructor.parametersCount() == 0)
continue
if (AbstractTypeChecker.areRelatedBySubtyping(this, firstType, secondType))
continue
// If two classes aren't related by subtyping and no need to compare their type arguments, then they can't have a common subtype
if (
firstTypeConstructor.isDefinitelyClassTypeConstructor() && secondTypeConstructor.isDefinitelyClassTypeConstructor()
&& (firstTypeConstructor.parametersCount() == 0 || secondTypeConstructor.parametersCount() == 0)
) return false
val superTypeByFirstConstructor = AbstractTypeChecker.findCorrespondingSupertypes(
typeCheckerState, firstType.lowerBoundIfFlexible(), secondTypeConstructor
).singleOrNull()
val superTypeBySecondConstructor = AbstractTypeChecker.findCorrespondingSupertypes(
typeCheckerState, secondType.lowerBoundIfFlexible(), firstTypeConstructor
).singleOrNull()
val anyInference = firstTypeConstructor.isInterface() || secondTypeConstructor.isInterface()
// Two classes can't have a common subtype if neither is a subtype of another
if (superTypeByFirstConstructor == null && superTypeBySecondConstructor == null && !anyInference)
return false
if (anyInference && !canHaveCommonSubtypeWithInterface(firstType, secondType))
return false
if (superTypeByFirstConstructor == null || superTypeBySecondConstructor == null)
continue // don't have incompatible supertypes so can have a common subtype
if (!checkArgumentsOfTypesToBeAbleToHaveCommonSubtype(superTypeByFirstConstructor, superTypeBySecondConstructor))
return false
}
}
return true
}
private fun TypeArgumentMarker.uncaptureIfNeeded(): TypeArgumentMarker {
val type = getType()
return if (type is CapturedTypeMarker) type.typeConstructorProjection() else this
}
fun computeEmptyIntersectionTypeKind(types: Collection<KotlinTypeMarker>): EmptyIntersectionTypeKind =
EmptyIntersectionTypeChecker.computeEmptyIntersectionTypeKind(this, types)
private fun computeEffectiveVariance(parameter: TypeParameterMarker, argument: TypeArgumentMarker): TypeVariance? =
AbstractTypeChecker.effectiveVariance(parameter.getVariance(), argument.getVariance())
private fun KotlinTypeMarker.mayCauseEmptyIntersection(): Boolean {
val typeConstructor = typeConstructor()
if (!typeConstructor.isClassTypeConstructor() && !typeConstructor.isTypeParameterTypeConstructor())
return false
// Even two interfaces may be an empty intersection type:
// interface Inv<K>
// interface B : Inv<Int>
// `Inv<String> & B` or `Inv<String> & Inv<Int>` are empty
// So we don't filter out interfaces here
return !typeConstructor.isAnyConstructor() && !typeConstructor.isNothingConstructor()
}
private fun areArgumentsOfSpecifiedVariances(
firstType: KotlinTypeMarker,
secondType: KotlinTypeMarker,
argumentIndex: Int,
variance1: TypeVariance,
variance2: TypeVariance,
): Boolean {
fun getEffectiveVariance(type: KotlinTypeMarker): TypeVariance? {
val argument = type.getArgument(argumentIndex).uncaptureIfNeeded()
val parameter = type.typeConstructor().getParameter(argumentIndex)
return AbstractTypeChecker.effectiveVariance(parameter.getVariance(), argument.getVariance())
}
val effectiveVariance1 = getEffectiveVariance(firstType)
val effectiveVariance2 = getEffectiveVariance(secondType)
return (effectiveVariance1 == variance1 && effectiveVariance2 == variance2)
|| (effectiveVariance1 == variance2 && effectiveVariance2 == variance1)
}
private fun checkArgumentsOfTypesToBeAbleToHaveCommonSubtype(firstType: KotlinTypeMarker, secondType: KotlinTypeMarker): Boolean {
require(firstType.typeConstructor() == secondType.typeConstructor()) {
"Type constructors of the passed types should be the same to compare their arguments"
}
fun isSubtypeOf(firstType: KotlinTypeMarker, secondType: KotlinTypeMarker) =
AbstractTypeChecker.isSubtypeOf(this, firstType, secondType)
fun areEqualTypes(firstType: KotlinTypeMarker, secondType: KotlinTypeMarker) =
AbstractTypeChecker.equalTypes(this, firstType, secondType)
for ((i, argumentOfFirst) in firstType.getArguments().withIndex()) {
@Suppress("NAME_SHADOWING")
val argumentOfFirst = argumentOfFirst.uncaptureIfNeeded()
val argumentOfSecond = secondType.getArgument(i).uncaptureIfNeeded()
if (argumentOfFirst == argumentOfSecond || argumentOfFirst.isStarProjection() || argumentOfSecond.isStarProjection())
continue
val argumentTypeOfFirst = argumentOfFirst.getType()
val argumentTypeOfSecond = argumentOfSecond.getType()
when {
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.INV, TypeVariance.INV) ->
return areEqualTypes(argumentTypeOfFirst, argumentTypeOfSecond)
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.INV, TypeVariance.OUT) -> {
if (!isSubtypeOf(argumentTypeOfFirst, argumentTypeOfSecond))
return false
}
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.INV, TypeVariance.IN) -> {
if (!isSubtypeOf(argumentTypeOfSecond, argumentTypeOfFirst))
return false
}
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.IN, TypeVariance.OUT) -> {
if (argumentTypeOfFirst.argumentsCount() == 0 && argumentTypeOfSecond.argumentsCount() == 0) {
if (!isSubtypeOf(argumentTypeOfFirst, argumentTypeOfSecond))
return false
} else if (!canHaveCommonSubtype(argumentTypeOfFirst, argumentTypeOfSecond)) {
return false
}
}
areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.OUT, TypeVariance.OUT)
|| areArgumentsOfSpecifiedVariances(firstType, secondType, i, TypeVariance.IN, TypeVariance.IN) -> {
if (!canHaveCommonSubtype(argumentTypeOfFirst, argumentTypeOfSecond))
return false
}
}
}
return true
}
}
@@ -305,6 +305,12 @@ interface ClassicTypeSystemContext : TypeSystemInferenceExtensionContext, TypeSy
return DescriptorUtils.isInterface(declarationDescriptor)
}
override fun TypeConstructorMarker.isFinalClassConstructor(): Boolean {
require(this is TypeConstructor, this::errorMessage)
val classDescriptor = declarationDescriptor as? ClassDescriptor ?: return false
return classDescriptor.isFinalClass
}
override fun TypeConstructorMarker.isCommonFinalClassConstructor(): Boolean {
require(this is TypeConstructor, this::errorMessage)
val classDescriptor = declarationDescriptor as? ClassDescriptor ?: return false