diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 4722ec986d1..4aeaf464cbe 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -688,6 +688,12 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION by error { parameter("typeVariableDescription") parameter>("incompatibleTypes") + parameter("kind") + } + + val INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION by warning { + parameter("typeVariableDescription") + parameter>("incompatibleTypes") } } diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 84fd65723ad..291ebd642f4 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -412,7 +412,8 @@ object FirErrors { val SMARTCAST_IMPOSSIBLE by error4() val REDUNDANT_NULLABLE by warning0(SourceElementPositioningStrategies.REDUNDANT_NULLABLE) val PLATFORM_CLASS_MAPPED_TO_KOTLIN by warning1(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) - val INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION by error2>() + val INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION by error3, String>() + val INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION by warning2>() // Reflection val EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED by error1>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt index ff200796b2a..0bc8c9d54e7 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt @@ -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 ) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt index 8585baaa53c..07f64286430 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt @@ -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 + incompatibleTypes as Collection, + kind ) } else -> null @@ -408,11 +411,18 @@ private fun reportInferredIntoEmptyIntersectionError( source: KtSourceElement, typeVariable: ConeTypeVariable, incompatibleTypes: Collection, -): KtDiagnosticWithParameters2>? { + 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 diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt index 762efcfc83c..bf98564dfaf 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt @@ -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 diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeTypeContext.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeTypeContext.kt index 16d1ba9c215..0a79b86e6e2 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeTypeContext.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeTypeContext.kt @@ -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 { if (this is ErrorTypeConstructor) return emptyList() diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt index 9968f8e08fb..e1a663889e8 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt @@ -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, - variableWithConstraints.typeVariable as ConeTypeVariable - ) + val emptyIntersectionKind = candidate.system.getEmptyIntersectionTypeKind(upperTypes).takeIf { it.isDefinitelyEmpty() } + ?: continue + + sink.yieldDiagnostic( + @Suppress("UNCHECKED_CAST") + InferredEmptyIntersectionDiagnostic( + upperTypes as Collection, + variableWithConstraints.typeVariable as ConeTypeVariable, + emptyIntersectionKind ) - } + ) } } } diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt index 7ff697c7cad..a0d38d4bb87 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt @@ -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, - val typeVariable: ConeTypeVariable + val typeVariable: ConeTypeVariable, + val kind: EmptyIntersectionTypeKind ) : ResolutionDiagnostic(INAPPLICABLE) class TooManyArguments( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index edf4cfd828c..4d2704d8964 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -899,8 +899,10 @@ public interface Errors { DiagnosticFactoryForDeprecation1 TYPE_INFERENCE_ONLY_INPUT_TYPES = DiagnosticFactoryForDeprecation1.create(LanguageFeature.StrictOnlyInputTypesChecks); - DiagnosticFactoryForDeprecation2> INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION = - DiagnosticFactoryForDeprecation2.create(LanguageFeature.ForbidInferringTypeVariablesIntoEmptyIntersection); + DiagnosticFactoryForDeprecation3, String> INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION = + DiagnosticFactoryForDeprecation3.create(LanguageFeature.ForbidInferringTypeVariablesIntoEmptyIntersection); + DiagnosticFactory2> INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION = + DiagnosticFactory2.create(WARNING); DiagnosticFactory1 TYPE_INFERENCE_UPPER_BOUND_VIOLATED = DiagnosticFactory1.create(ERROR); DiagnosticFactory2 TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH = DiagnosticFactory2.create(ERROR); DiagnosticFactory0 TYPE_INFERENCE_CANDIDATE_WITH_SAM_AND_VARARG = DiagnosticFactory0.create(WARNING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 09dda1e1f54..ce938f8688a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -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."); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt index 80a9bf2d35b..7a04738f5a1 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt @@ -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) - ) + + @Suppress("UNCHECKED_CAST") + val incompatibleTypes = error.incompatibleTypes as Collection + + 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) } } } diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintPositionAndErrors.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintPositionAndErrors.kt index b0310bf6ccc..d890d5a0ef3 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintPositionAndErrors.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintPositionAndErrors.kt @@ -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 val typeVariable: TypeVariableMarker + val kind: EmptyIntersectionTypeKind } class InferredEmptyIntersectionWarning( override val incompatibleTypes: Collection, - override val typeVariable: TypeVariableMarker + override val typeVariable: TypeVariableMarker, + override val kind: EmptyIntersectionTypeKind, ) : ConstraintSystemError(RESOLVED), InferredEmptyIntersection class InferredEmptyIntersectionError( override val incompatibleTypes: Collection, - override val typeVariable: TypeVariableMarker + override val typeVariable: TypeVariableMarker, + override val kind: EmptyIntersectionTypeKind, ) : ConstraintSystemError(INAPPLICABLE), InferredEmptyIntersection class OnlyInputTypesDiagnostic(val typeVariable: TypeVariableMarker) : ConstraintSystemError(INAPPLICABLE) diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt index e9956094f3d..7810ed98ede 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt @@ -38,9 +38,7 @@ class NewConstraintSystemImpl( private val typeVariablesTransaction: MutableList = SmartList() private val properTypesCache: MutableSet = SmartSet.create() private val notProperTypesCache: MutableSet = SmartSet.create() - private val emptyIntersectionTypesCache: MutableSet> = SmartSet.create() - private val nonEmptyIntersectionTypesCache: MutableSet> = SmartSet.create() - + private val intersectionTypesCache: MutableMap, EmptyIntersectionTypeKind> = mutableMapOf() private var couldBeResolvedWithUnrestrictedBuilderInference: Boolean = false override var atCompletionState: Boolean = false @@ -444,37 +442,34 @@ class NewConstraintSystemImpl( } override fun getEmptyIntersectionTypeKind(types: Collection): 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() { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt index 29ed8e1fe83..ba1fa2cff33 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt @@ -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)) } } } diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461.diag.txt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461.diag.txt index 9058817500f..1b990ee3fda 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461.diag.txt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461.diag.txt @@ -1,6 +1,6 @@ /kt45461.kt:5:25: warning: parameter 'foo' is never used fun takeFoo(foo: Foo) {} ^ -/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().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 ^ diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_10.diag.txt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_10.diag.txt new file mode 100644 index 00000000000..2c9847bdf2e --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_10.diag.txt @@ -0,0 +1,7 @@ +/kt45461_10.kt:7:25: warning: parameter 'foo' is never used + fun takeFoo(foo: Foo) {} + ^ +/kt45461_10.kt:16:21: warning: type argument for a type parameter S has possible incompatible upper bounds: Out, K + Bar>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 + ^ + diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_10.kt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_10.kt index eb2e36f1f0d..fece20e88a7 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_10.kt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_10.kt @@ -1,4 +1,6 @@ // FIR_IDENTICAL +// RENDER_DIAGNOSTICS_FULL_TEXT + class Foo class Bar { @@ -11,5 +13,5 @@ interface A fun , L : N, N: A> main() { val foo = Foo() - Bar>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 + Bar>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 } diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_12.diag.txt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_12.diag.txt index 738a99557ef..569aa64f905 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_12.diag.txt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_12.diag.txt @@ -1,7 +1,7 @@ /kt45461_12.kt:5:25: warning: parameter 'foo' is never used fun takeFoo(foo: Foo) {} ^ -/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().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 ^ diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_13.diag.txt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_13.diag.txt new file mode 100644 index 00000000000..cc9a358d925 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_13.diag.txt @@ -0,0 +1,7 @@ +/kt45461_13.kt:7:25: warning: parameter 'foo' is never used + fun takeFoo(foo: Foo) {} + ^ +/kt45461_13.kt:16:24: warning: type argument for a type parameter S has possible incompatible upper bounds: Out, K + Bar>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 + ^ + diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_13.kt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_13.kt index 07a7d423662..8e42949579a 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_13.kt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_13.kt @@ -1,4 +1,6 @@ // FIR_IDENTICAL +// RENDER_DIAGNOSTICS_FULL_TEXT + class Foo class Bar { @@ -11,5 +13,5 @@ class Out fun main() where N: Out { val foo = Foo() - Bar>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 + Bar>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 } diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_15.diag.txt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_15.diag.txt index d82bcc6ce3f..66c25773319 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_15.diag.txt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_15.diag.txt @@ -1,6 +1,6 @@ /kt45461_15.kt:5:25: warning: parameter 'foo' is never used fun takeFoo(foo: Foo) {} ^ -/kt45461_15.kt:14:21: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds: Inv, 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, K. This will become an error in Kotlin 1.9 Bar>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 ^ diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_16.diag.txt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_16.diag.txt new file mode 100644 index 00000000000..bef61b6b39f --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_16.diag.txt @@ -0,0 +1,15 @@ +// FIR_IDENTICAL +class Foo + +class Bar { + fun takeFoo(foo: Foo) {} +} + +class Inv

+ +interface A + +fun , T> main() where T: A, T: Number { + val foo = Foo() + Bar>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 +} diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_16.kt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_16.kt index 977ce4ae8d5..bef61b6b39f 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_16.kt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_16.kt @@ -11,5 +11,5 @@ interface A fun , T> main() where T: A, T: Number { val foo = Foo() - Bar>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 + Bar>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 } diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_19.diag.txt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_19.diag.txt index d3157fb7ca6..2956863849c 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_19.diag.txt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_19.diag.txt @@ -1,6 +1,6 @@ /kt45461_19.kt:5:25: warning: parameter 'foo' is never used fun takeFoo(foo: Foo) {} ^ -/kt45461_19.kt:13:26: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds: Out>, 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>, K. This will become an error in Kotlin 1.9 Bar>>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 ^ diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_2.diag.txt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_2.diag.txt index 40f6db97030..7ee69dc9f53 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_2.diag.txt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_2.diag.txt @@ -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 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().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 ^ diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_21.diag.txt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_21.diag.txt index 72b6a50b5f1..9e2e1202fb2 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_21.diag.txt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_21.diag.txt @@ -1,6 +1,6 @@ /kt45461_21.kt:5:25: warning: parameter 'foo' is never used fun takeFoo(foo: Foo) {} ^ -/kt45461_21.kt:13:25: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds: In>, 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>, K. This will become an error in Kotlin 1.9 Bar>>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 ^ diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_24.diag.txt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_24.diag.txt index b80e61d727b..97b33b76bf8 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_24.diag.txt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_24.diag.txt @@ -4,6 +4,6 @@ /kt45461_24.kt:10:10: warning: 'Inv>' is a final type, and thus a value of the type parameter is predetermined fun >> 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>, 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>, K. This will become an error in Kotlin 1.9 Bar>>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 ^ diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_25.diag.txt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_25.diag.txt index 728abc814fe..ee162d75610 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_25.diag.txt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_25.diag.txt @@ -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().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 ^ diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_25.fir.kt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_25.fir.kt index 1363fe122a4..017a1cfe576 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_25.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_25.fir.kt @@ -8,5 +8,5 @@ class Bar { fun Int> main() { val foo = Foo() - val x: Float = Bar().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 + val x: Float = Bar().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 } diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_25.kt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_25.kt index dbce722b317..515bb9a4209 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_25.kt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_25.kt @@ -8,5 +8,5 @@ class Bar { fun Int> main() { val foo = Foo() - val x: Float = Bar().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 + val x: Float = Bar().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 } diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_26.diag.txt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_26.diag.txt index f404a5b6fbe..6c3395a5721 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_26.diag.txt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_26.diag.txt @@ -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, K, Out +/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, K, Out val x: Out = Bar>().takeFoo(foo) ^ diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_26.fir.kt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_26.fir.kt index cb7348b5480..27f18809bf2 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_26.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_26.fir.kt @@ -10,5 +10,5 @@ class Out fun Out> main() { val foo = Foo() - val x: Out = Bar>()., K")!>takeFoo(foo) + val x: Out = Bar>()., K; multiple incompatible classes")!>takeFoo(foo) } diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_26.kt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_26.kt index 597645a5ab5..f7d0a58ee92 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_26.kt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_26.kt @@ -10,5 +10,5 @@ class Out fun Out> main() { val foo = Foo() - val x: Out = Bar>()., K, Out")!>takeFoo(foo) + val x: Out = Bar>()., K, Out; (multiple incompatible classes)")!>takeFoo(foo) } diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_31.kt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_31.kt index 24364ceab0b..090e9e009e1 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_31.kt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_31.kt @@ -11,5 +11,5 @@ interface B : A fun >> main() { val foo = Foo() - Bar>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 + Bar>()., K; (incompatible supertypes)")!>takeFoo(foo) // error in 1.3.72, no error in 1.4.31 } diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_34.kt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_34.kt index a3dbc03234e..4754e4b11f6 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_34.kt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_34.kt @@ -11,5 +11,5 @@ class B : A fun >> main() { val foo = Foo() - Bar>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 + Bar>()., K; (incompatible supertypes)")!>takeFoo(foo) // error in 1.3.72, no error in 1.4.31 } diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_5.diag.txt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_5.diag.txt index 53a82992858..cfdd003dfcf 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_5.diag.txt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_5.diag.txt @@ -1,6 +1,6 @@ /kt45461_5.kt:5:25: warning: parameter 'foo' is never used fun takeFoo(foo: Foo) {} ^ -/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().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 ^ diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_8.diag.txt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_8.diag.txt index 99b5c109a45..60ec5d6efbb 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_8.diag.txt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_8.diag.txt @@ -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 , 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, 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, K. This will become an error in Kotlin 1.9 Bar>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 ^ diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_9.diag.txt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_9.diag.txt index 5b0199c6867..fbc76d0fc73 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_9.diag.txt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt45461_9.diag.txt @@ -1,7 +1,7 @@ /kt45461_9.kt:5:25: warning: parameter 'foo' is never used fun takeFoo(foo: Foo) {} ^ -/kt45461_9.kt:12:21: warning: type argument for a type parameter S can't be inferred because it has incompatible upper bounds: Inv, Inv. 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, Inv. This will become an error in Kotlin 1.9 Bar>().takeFoo(foo) // error in 1.3.72, no error in 1.4.31 ^ diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt48765.diag.txt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt48765.diag.txt index b21b6a0e868..98adcd66db6 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt48765.diag.txt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt48765.diag.txt @@ -4,7 +4,7 @@ /kt48765.kt:4:52: warning: parameter 'x2' is never used fun > 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 diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt48935.diag.txt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt48935.diag.txt index f1aa80bfbf2..53515f07872 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt48935.diag.txt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt48935.diag.txt @@ -1,4 +1,7 @@ /kt48935.kt:7:35: warning: parameter 'func' is never used fun 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 + ^ diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt48935.kt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt48935.kt index eed86cb45c9..0ffc1386ffa 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt48935.kt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt48935.kt @@ -10,5 +10,5 @@ fun 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 + exampleGenericFunction(func) // expected this to be a compilation error as the T: Base constraint should not be satisfied } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt48935_5.kt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt48935_5.kt index 9822d12bf7d..f8e93eac92f 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt48935_5.kt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt48935_5.kt @@ -9,5 +9,5 @@ fun 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 + exampleGenericFunction(func) // expected this to be a compilation error as the T: Base constraint should not be satisfied } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt49661.diag.txt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt49661.diag.txt index 50ae1d0c34c..293d53104fd 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt49661.diag.txt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt49661.diag.txt @@ -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 { g() } ^ diff --git a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/selectFromCovariantAndContravariantTypes.diag.txt b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/selectFromCovariantAndContravariantTypes.diag.txt index e962f8de3d3..78919205927 100644 --- a/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/selectFromCovariantAndContravariantTypes.diag.txt +++ b/compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/selectFromCovariantAndContravariantTypes.diag.txt @@ -7,6 +7,6 @@ fun genericIn(x: In) {} /selectFromCovariantAndContravariantTypes.kt:13:20: warning: parameter 'x' is never used fun genericOut(x: Out) {} ^ -/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)) ^ diff --git a/compiler/testData/diagnostics/tests/inference/selectOfLambdaWithExtensionDisabled.kt b/compiler/testData/diagnostics/tests/inference/selectOfLambdaWithExtensionDisabled.kt index 549d54327fa..b8244de011b 100644 --- a/compiler/testData/diagnostics/tests/inference/selectOfLambdaWithExtensionDisabled.kt +++ b/compiler/testData/diagnostics/tests/inference/selectOfLambdaWithExtensionDisabled.kt @@ -14,7 +14,7 @@ val a1: A = select( { a: Int -> myPrint(a + this.length + 2) } ) -val a2 = select( +val a2 = select( { a: Int -> myPrint(a + this.length + 1) }, fun CharSequence.(a: Int) { myPrint(a + this.length + 2) }, { a: Int -> myPrint(a + this.length + 3) } diff --git a/compiler/testData/diagnostics/tests/resolve/specialConstructions/elvisAsCall.fir.kt b/compiler/testData/diagnostics/tests/resolve/specialConstructions/elvisAsCall.fir.kt index ecc4fe08214..861dc646a0a 100644 --- a/compiler/testData/diagnostics/tests/resolve/specialConstructions/elvisAsCall.fir.kt +++ b/compiler/testData/diagnostics/tests/resolve/specialConstructions/elvisAsCall.fir.kt @@ -13,7 +13,7 @@ fun testElvis(a: Int?, b: Int?) { if (a != null) { doInt(b ?: a) } - doList(getList() ?: emptyListOfA()) //should be an error + doList(getList() ?: emptyListOfA()) //should be an error doList(getList() ?: strangeList { doInt(it) }) //lambda was not analyzed } diff --git a/compiler/testData/diagnostics/tests/resolve/specialConstructions/elvisAsCall.kt b/compiler/testData/diagnostics/tests/resolve/specialConstructions/elvisAsCall.kt index cb44030c595..214fd36720f 100644 --- a/compiler/testData/diagnostics/tests/resolve/specialConstructions/elvisAsCall.kt +++ b/compiler/testData/diagnostics/tests/resolve/specialConstructions/elvisAsCall.kt @@ -13,7 +13,7 @@ fun testElvis(a: Int?, b: Int?) { if (a != null) { doInt(b ?: a) } - doList(getList() ?: emptyListOfA()) //should be an error + doList(getList() ?: emptyListOfA()) //should be an error doList(getList() ?: strangeList { doInt(it) }) //lambda was not analyzed } diff --git a/compiler/testData/diagnostics/tests/resolve/specialConstructions/exclExclAsCall.fir.kt b/compiler/testData/diagnostics/tests/resolve/specialConstructions/exclExclAsCall.fir.kt index 9ab0c68ba6d..14b8e5b0926 100644 --- a/compiler/testData/diagnostics/tests/resolve/specialConstructions/exclExclAsCall.fir.kt +++ b/compiler/testData/diagnostics/tests/resolve/specialConstructions/exclExclAsCall.fir.kt @@ -12,7 +12,7 @@ fun emptyNullableListOfA(): List? = null //------------------------------- fun testExclExcl() { - doList(emptyNullableListOfA()!!) //should be an error here + doList(emptyNullableListOfA()!!) //should be an error here val l: List = id(emptyNullableListOfA()!!) doList(strangeNullableList { doInt(it) }!!) //lambda should be analyzed (at completion phase) diff --git a/compiler/testData/diagnostics/tests/resolve/specialConstructions/exclExclAsCall.kt b/compiler/testData/diagnostics/tests/resolve/specialConstructions/exclExclAsCall.kt index 52d11c382ca..7f91e8af42a 100644 --- a/compiler/testData/diagnostics/tests/resolve/specialConstructions/exclExclAsCall.kt +++ b/compiler/testData/diagnostics/tests/resolve/specialConstructions/exclExclAsCall.kt @@ -12,7 +12,7 @@ fun emptyNullableListOfA(): List? = null //------------------------------- fun testExclExcl() { - doList(emptyNullableListOfA()!!) //should be an error here + doList(emptyNullableListOfA()!!) //should be an error here val l: List = id(emptyNullableListOfA()!!) doList(strangeNullableList { doInt(it) }!!) //lambda should be analyzed (at completion phase) diff --git a/core/compiler.common/src/org/jetbrains/kotlin/resolve/checkers/EmptyIntersectionTypeChecker.kt b/core/compiler.common/src/org/jetbrains/kotlin/resolve/checkers/EmptyIntersectionTypeChecker.kt new file mode 100644 index 00000000000..0aee2d10bb5 --- /dev/null +++ b/core/compiler.common/src/org/jetbrains/kotlin/resolve/checkers/EmptyIntersectionTypeChecker.kt @@ -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 + ): 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 + // interface B: A + // interface C: A + // B & C can't have common subtype due to having incompatible supertypes: A and A + 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 + // interface B : A + // interface C : A + // => 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 + // interface B : Inv + // `Inv & B` or `Inv & Inv` 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 + } +} \ No newline at end of file diff --git a/core/compiler.common/src/org/jetbrains/kotlin/types/EmptyIntersectionTypeKind.kt b/core/compiler.common/src/org/jetbrains/kotlin/types/EmptyIntersectionTypeKind.kt index e55382668b8..d285fe5373b 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/types/EmptyIntersectionTypeKind.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/types/EmptyIntersectionTypeKind.kt @@ -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 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 128a231ee48..235419bdbf3 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 @@ -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.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 - // interface B : A - // interface C : A - // => 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 - // interface B: A - // interface C: A - // B & C can't have common subtype due to having incompatible supertypes: A and A - 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): 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 - // interface B : Inv - // `Inv & B` or `Inv & Inv` 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 - } } 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 91bc4443973..28997a49e70 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt @@ -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