diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt index 98410fcd7c5..0d776c244d5 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -2015,6 +2015,24 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.INCORRECT_LEFT_COMPONENT_OF_INTERSECTION) { firDiagnostic -> + IncorrectLeftComponentOfIntersectionImpl( + firDiagnostic as KtPsiDiagnostic, + token, + ) + } + add(FirErrors.INCORRECT_RIGHT_COMPONENT_OF_INTERSECTION) { firDiagnostic -> + IncorrectRightComponentOfIntersectionImpl( + firDiagnostic as KtPsiDiagnostic, + token, + ) + } + add(FirErrors.NULLABLE_ON_DEFINITELY_NOT_NULLABLE) { firDiagnostic -> + NullableOnDefinitelyNotNullableImpl( + firDiagnostic as KtPsiDiagnostic, + token, + ) + } add(FirErrors.EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED) { firDiagnostic -> ExtensionInClassReferenceNotAllowedImpl( firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.a), diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index 266d871baf9..44bd20a4b51 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt @@ -1435,6 +1435,18 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val causingTypes: String } + abstract class IncorrectLeftComponentOfIntersection : KtFirDiagnostic() { + override val diagnosticClass get() = IncorrectLeftComponentOfIntersection::class + } + + abstract class IncorrectRightComponentOfIntersection : KtFirDiagnostic() { + override val diagnosticClass get() = IncorrectRightComponentOfIntersection::class + } + + abstract class NullableOnDefinitelyNotNullable : KtFirDiagnostic() { + override val diagnosticClass get() = NullableOnDefinitelyNotNullable::class + } + abstract class ExtensionInClassReferenceNotAllowed : KtFirDiagnostic() { override val diagnosticClass get() = ExtensionInClassReferenceNotAllowed::class abstract val referencedDeclaration: KtCallableSymbol diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 426f6aac096..07ee64bcdc3 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -1722,6 +1722,21 @@ internal class InferredTypeVariableIntoPossibleEmptyIntersectionImpl( override val token: KtLifetimeToken, ) : KtFirDiagnostic.InferredTypeVariableIntoPossibleEmptyIntersection(), KtAbstractFirDiagnostic +internal class IncorrectLeftComponentOfIntersectionImpl( + override val firDiagnostic: KtPsiDiagnostic, + override val token: KtLifetimeToken, +) : KtFirDiagnostic.IncorrectLeftComponentOfIntersection(), KtAbstractFirDiagnostic + +internal class IncorrectRightComponentOfIntersectionImpl( + override val firDiagnostic: KtPsiDiagnostic, + override val token: KtLifetimeToken, +) : KtFirDiagnostic.IncorrectRightComponentOfIntersection(), KtAbstractFirDiagnostic + +internal class NullableOnDefinitelyNotNullableImpl( + override val firDiagnostic: KtPsiDiagnostic, + override val token: KtLifetimeToken, +) : KtFirDiagnostic.NullableOnDefinitelyNotNullable(), KtAbstractFirDiagnostic + internal class ExtensionInClassReferenceNotAllowedImpl( override val referencedDeclaration: KtCallableSymbol, override val firDiagnostic: KtPsiDiagnostic, 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 86e0f363deb..3d43ecceae8 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 @@ -705,6 +705,10 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { parameter("description") parameter("causingTypes") } + + val INCORRECT_LEFT_COMPONENT_OF_INTERSECTION by error() + val INCORRECT_RIGHT_COMPONENT_OF_INTERSECTION by error() + val NULLABLE_ON_DEFINITELY_NOT_NULLABLE by error() } val REFLECTION by object : DiagnosticGroup("Reflection") { 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 de8285adbe6..0b0765b3698 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 @@ -417,6 +417,9 @@ object FirErrors { val PLATFORM_CLASS_MAPPED_TO_KOTLIN by warning1(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) val INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION by error4, String, String>() val INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION by warning4, String, String>() + val INCORRECT_LEFT_COMPONENT_OF_INTERSECTION by error0() + val INCORRECT_RIGHT_COMPONENT_OF_INTERSECTION by error0() + val NULLABLE_ON_DEFINITELY_NOT_NULLABLE by error0() // 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/checkers/CommonTypeCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonTypeCheckers.kt index 89f57b4b203..899b2863b2d 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonTypeCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonTypeCheckers.kt @@ -12,6 +12,7 @@ object CommonTypeCheckers : TypeCheckers() { FirTypeAnnotationChecker, FirSuspendModifierChecker, FirDeprecatedTypeChecker, - FirOptInUsageTypeRefChecker + FirOptInUsageTypeRefChecker, + FirDefinitelyNotNullableChecker, ) } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/type/FirDefinitelyNotNullableChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/type/FirDefinitelyNotNullableChecker.kt new file mode 100644 index 00000000000..c5f987227a7 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/type/FirDefinitelyNotNullableChecker.kt @@ -0,0 +1,30 @@ +/* + * 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.fir.analysis.checkers.type + +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.types.* + +object FirDefinitelyNotNullableChecker : FirTypeRefChecker() { + override fun check(typeRef: FirTypeRef, context: CheckerContext, reporter: DiagnosticReporter) { + val intersection = (typeRef as? FirResolvedTypeRef)?.delegatedTypeRef as? FirIntersectionTypeRef ?: return + + if (intersection.isMarkedNullable) { + reporter.reportOn(intersection.source, FirErrors.NULLABLE_ON_DEFINITELY_NOT_NULLABLE, context) + } + + if (!intersection.isLeftValidForDefinitelyNotNullable) { + reporter.reportOn(intersection.leftType.source, FirErrors.INCORRECT_LEFT_COMPONENT_OF_INTERSECTION, context) + } + + if (!intersection.isRightValidForDefinitelyNotNullable) { + reporter.reportOn(intersection.rightType.source, FirErrors.INCORRECT_RIGHT_COMPONENT_OF_INTERSECTION, context) + } + } +} 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 2b6994bf1af..ed9a74c9af2 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 @@ -252,6 +252,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCOMPATIBLE_TYPE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCONSISTENT_TYPE_PARAMETER_BOUNDS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCONSISTENT_TYPE_PARAMETER_VALUES import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCORRECT_CHARACTER_LITERAL +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCORRECT_LEFT_COMPONENT_OF_INTERSECTION +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCORRECT_RIGHT_COMPONENT_OF_INTERSECTION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INC_DEC_SHOULD_NOT_RETURN_UNIT import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INFERENCE_ERROR import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INFERENCE_UNSUCCESSFUL_FORK @@ -359,6 +361,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_THIS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_TYPE_ARGUMENTS_ON_RHS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_VALUE_FOR_PARAMETER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NULLABLE_INLINE_PARAMETER +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NULLABLE_ON_DEFINITELY_NOT_NULLABLE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NULLABLE_SUPERTYPE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NULLABLE_TYPE_IN_CLASS_LITERAL_LHS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NULLABLE_TYPE_OF_ANNOTATION_MEMBER @@ -982,6 +985,18 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() { TO_STRING, TO_STRING ) + map.put( + NULLABLE_ON_DEFINITELY_NOT_NULLABLE, + "'!!' type cannot be marked as nullable" + ) + map.put( + INCORRECT_LEFT_COMPONENT_OF_INTERSECTION, + "Intersection types are only supported for definitely non-nullable types: left part should be a type parameter with nullable bounds" + ) + map.put( + INCORRECT_RIGHT_COMPONENT_OF_INTERSECTION, + "Intersection types are only supported for definitely non-nullable types: right part should be non-nullable Any" + ) map.put(TYPE_MISMATCH, "Type mismatch: inferred type is {1} but {0} was expected", TO_STRING, TO_STRING, NOT_RENDERED) map.put(THROWABLE_TYPE_MISMATCH, "Throwable type mismatch: actual type is {0}", TO_STRING, NOT_RENDERED) 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 fcc6a7917f5..88a07c40d72 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 @@ -128,6 +128,7 @@ private fun ConeDiagnostic.toKtDiagnostic( is ConeLocalVariableNoTypeOrInitializer -> runIf(variable.isLocalMember) { FirErrors.VARIABLE_WITH_NO_TYPE_NO_INITIALIZER.createOn(source) } + is ConeForbiddenIntersection -> null // reported in FirDefinitelyNotNullableChecker is ConeUnderscoreIsReserved -> FirErrors.UNDERSCORE_IS_RESERVED.createOn(this.source) is ConeUnderscoreUsageWithoutBackticks -> FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS.createOn(this.source) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirTypeResolverImpl.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirTypeResolverImpl.kt index 812f4205e4a..bfde838ef1b 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirTypeResolverImpl.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirTypeResolverImpl.kt @@ -523,14 +523,11 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() { is FirDynamicTypeRef -> ConeDynamicType.create(session) to null is FirIntersectionTypeRef -> { val leftType = typeRef.leftType.coneType - val rightType = typeRef.rightType.coneType - - if (rightType.isAny && leftType is ConeTypeParameterType) { + if (leftType is ConeTypeParameterType) { ConeDefinitelyNotNullType(leftType) to null } else { - ConeErrorType(ConeUnsupported("Intersection types are not supported yet", typeRef.source)) to null + ConeErrorType(ConeForbiddenIntersection) to null } - } else -> error(typeRef.render()) } diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/diagnostics/ConeDiagnostics.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/diagnostics/ConeDiagnostics.kt index bd7b895c8f1..317ef394621 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/diagnostics/ConeDiagnostics.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/diagnostics/ConeDiagnostics.kt @@ -237,3 +237,7 @@ class ConeAmbiguousAlteredAssign(val altererNames: List) : ConeDiagnost override val reason: String get() = "Assign altered by multiple extensions" } + +object ConeForbiddenIntersection : ConeDiagnostic { + override val reason: String get() = "Such an intersection type is not allowed" +} diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/FirTypeUtils.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/FirTypeUtils.kt index f13845e28bd..fdb235d1226 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/FirTypeUtils.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/FirTypeUtils.kt @@ -191,3 +191,7 @@ val ConeKotlinType.canBeNull: Boolean } } +val FirIntersectionTypeRef.isLeftValidForDefinitelyNotNullable + get() = leftType.coneType.let { it is ConeTypeParameterType && it.canBeNull && !it.isMarkedNullable } + +val FirIntersectionTypeRef.isRightValidForDefinitelyNotNullable get() = rightType.coneType.isAny diff --git a/compiler/testData/diagnostics/tests/explicitDefinitelyNotNullableViaIntersection/bareTypes.fir.kt b/compiler/testData/diagnostics/tests/explicitDefinitelyNotNullableViaIntersection/bareTypes.fir.kt deleted file mode 100644 index f4ebe732911..00000000000 --- a/compiler/testData/diagnostics/tests/explicitDefinitelyNotNullableViaIntersection/bareTypes.fir.kt +++ /dev/null @@ -1,7 +0,0 @@ -// !LANGUAGE: +DefinitelyNonNullableTypes - -fun main(x: Collection) { - if (x is List & Any) {} - - val w: List & Any = null!! -} diff --git a/compiler/testData/diagnostics/tests/explicitDefinitelyNotNullableViaIntersection/bareTypes.kt b/compiler/testData/diagnostics/tests/explicitDefinitelyNotNullableViaIntersection/bareTypes.kt index 2071d8a927c..4c06a6e77e0 100644 --- a/compiler/testData/diagnostics/tests/explicitDefinitelyNotNullableViaIntersection/bareTypes.kt +++ b/compiler/testData/diagnostics/tests/explicitDefinitelyNotNullableViaIntersection/bareTypes.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !LANGUAGE: +DefinitelyNonNullableTypes fun main(x: Collection) { diff --git a/compiler/testData/diagnostics/tests/explicitDefinitelyNotNullableViaIntersection/notApplicable.fir.kt b/compiler/testData/diagnostics/tests/explicitDefinitelyNotNullableViaIntersection/notApplicable.fir.kt index 1f9a6be8204..94c854ddb02 100644 --- a/compiler/testData/diagnostics/tests/explicitDefinitelyNotNullableViaIntersection/notApplicable.fir.kt +++ b/compiler/testData/diagnostics/tests/explicitDefinitelyNotNullableViaIntersection/notApplicable.fir.kt @@ -1,18 +1,18 @@ // !LANGUAGE: +DefinitelyNonNullableTypes -fun foo(x: T & Any, y: List<String & Any> & Any) {} +fun foo(x: T & Any, y: List<String & Any> & Any) {} -fun bar1(x: F? & Any) {} -fun bar2(x: F & Any?) {} -fun bar3(x: (F?) & Any) {} -fun bar4(x: (F & Any)?) {} +fun bar1(x: F? & Any) {} +fun bar2(x: F & Any?) {} +fun bar3(x: (F?) & Any) {} +fun bar4(x: (F & Any)?) {} -fun bar5(x: F & String) {} +fun bar5(x: F & String) {} -fun bar6(x: F & (F & Any)) {} -fun bar7(x: (F & Any) & Any) {} +fun bar6(x: F & (F & Any)) {} +fun bar7(x: (F & Any) & Any) {} fun bar8(x: (F & Any).() -> Unit) {} fun (F & Any).bar9(x: () -> Unit) {} -fun bar10(x: F & Any & String) {} -fun bar11(x: Double & Any & String) {} +fun bar10(x: F & Any & String) {} +fun bar11(x: Double & Any & String) {} diff --git a/compiler/testData/diagnostics/tests/explicitDefinitelyNotNullableViaIntersection/recursiveBounds.fir.kt b/compiler/testData/diagnostics/tests/explicitDefinitelyNotNullableViaIntersection/recursiveBounds.fir.kt index afcdbddf5c7..4d8c217b91d 100644 --- a/compiler/testData/diagnostics/tests/explicitDefinitelyNotNullableViaIntersection/recursiveBounds.fir.kt +++ b/compiler/testData/diagnostics/tests/explicitDefinitelyNotNullableViaIntersection/recursiveBounds.fir.kt @@ -1,11 +1,11 @@ // !LANGUAGE: +DefinitelyNonNullableTypes -fun > sort1() {} +fun T & Any>> sort1() {} fun ?> sort2() {} -class A1> +class A1K & Any>> class A2?> fun bar() {} -fun <E : E & Any> baz() {} +fun <E : E & Any> baz() {}