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 18cb425f02b..2c25750ea39 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 @@ -2074,6 +2074,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.DEFINITELY_NON_NULLABLE_AS_REIFIED) { firDiagnostic -> + DefinitelyNonNullableAsReifiedImpl( + firDiagnostic as KtPsiDiagnostic, + token, + ) + } add(FirErrors.FINAL_UPPER_BOUND) { firDiagnostic -> FinalUpperBoundImpl( firSymbolBuilder.typeBuilder.buildKtType(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 1b192d8d3bc..f865eecb467 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 @@ -1474,6 +1474,10 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val type: KtType } + abstract class DefinitelyNonNullableAsReified : KtFirDiagnostic() { + override val diagnosticClass get() = DefinitelyNonNullableAsReified::class + } + abstract class FinalUpperBound : KtFirDiagnostic() { override val diagnosticClass get() = FinalUpperBound::class abstract val type: KtType 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 c848402d402..f533fbfe733 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 @@ -1772,6 +1772,11 @@ internal class ReifiedTypeForbiddenSubstitutionImpl( override val token: KtLifetimeToken, ) : KtFirDiagnostic.ReifiedTypeForbiddenSubstitution(), KtAbstractFirDiagnostic +internal class DefinitelyNonNullableAsReifiedImpl( + override val firDiagnostic: KtPsiDiagnostic, + override val token: KtLifetimeToken, +) : KtFirDiagnostic.DefinitelyNonNullableAsReified(), KtAbstractFirDiagnostic + internal class FinalUpperBoundImpl( override val type: KtType, 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 cf790a178ac..535fca95ca1 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 @@ -666,6 +666,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val REIFIED_TYPE_FORBIDDEN_SUBSTITUTION by error { parameter("type") } + val DEFINITELY_NON_NULLABLE_AS_REIFIED by error() val FINAL_UPPER_BOUND by warning { parameter("type") 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 d5be0d73966..e1eca6d78cd 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 @@ -413,6 +413,7 @@ object FirErrors { val TYPE_PARAMETER_AS_REIFIED by error1() val TYPE_PARAMETER_AS_REIFIED_ARRAY by deprecationError1(ProhibitNonReifiedArraysAsReifiedTypeArguments) val REIFIED_TYPE_FORBIDDEN_SUBSTITUTION by error1() + val DEFINITELY_NON_NULLABLE_AS_REIFIED by error0() val FINAL_UPPER_BOUND by warning1() val UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE by error0() val BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER by error0() diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReifiedChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReifiedChecker.kt index 1a900a32ca9..e28cf0b1c0d 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReifiedChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReifiedChecker.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.expression +import org.jetbrains.kotlin.KtRealSourceElementKind import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.diagnostics.chooseFactory @@ -20,19 +21,27 @@ import org.jetbrains.kotlin.name.StandardClassIds object FirReifiedChecker : FirQualifiedAccessExpressionChecker() { override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { - val calleReference = expression.calleeReference + val calleeReference = expression.calleeReference val typeArguments = expression.typeArguments - val typeParameters = calleReference.toResolvedCallableSymbol()?.typeParameterSymbols ?: return + val typeParameters = calleeReference.toResolvedCallableSymbol()?.typeParameterSymbols ?: return val count = minOf(typeArguments.size, typeParameters.size) for (index in 0 until count) { val typeArgumentProjection = typeArguments.elementAt(index) - val source = typeArgumentProjection.source ?: calleReference.source - val typeArgument = typeArgumentProjection.toConeTypeProjection().type + val source = typeArgumentProjection.source ?: calleeReference.source ?: continue + + val typeArgument = typeArgumentProjection.toConeTypeProjection().type ?: continue val typeParameter = typeParameters[index] - if (source != null && typeParameter.isReifiedTypeParameterOrFromKotlinArray()) { - checkArgumentAndReport(typeArgument, source, false, context, reporter) + if (typeParameter.isReifiedTypeParameterOrFromKotlinArray()) { + checkArgumentAndReport( + typeArgument, + source, + isExplicit = typeArgumentProjection.source?.kind == KtRealSourceElementKind, + isArray = false, + context, + reporter + ) } } } @@ -44,34 +53,35 @@ object FirReifiedChecker : FirQualifiedAccessExpressionChecker() { } private fun checkArgumentAndReport( - typeArgument: ConeKotlinType?, + typeArgument: ConeKotlinType, source: KtSourceElement, + isExplicit: Boolean, isArray: Boolean, context: CheckerContext, reporter: DiagnosticReporter ) { - if (typeArgument?.classId == StandardClassIds.Array) { - checkArgumentAndReport(typeArgument.typeArguments[0].type, source, true, context, reporter) + if (typeArgument.classId == StandardClassIds.Array) { + val nestedTypeArgument = typeArgument.typeArguments[0].type ?: return + checkArgumentAndReport(nestedTypeArgument, source, isExplicit, isArray = true, context, reporter) return } if (typeArgument is ConeTypeParameterType) { - val factory = if (isArray) { - FirErrors.TYPE_PARAMETER_AS_REIFIED_ARRAY.chooseFactory(context) - } else { - FirErrors.TYPE_PARAMETER_AS_REIFIED - } val symbol = typeArgument.lookupTag.typeParameterSymbol if (!symbol.isReified) { - reporter.reportOn(source, factory, symbol, context) + reporter.reportOn( + source, + if (isArray) FirErrors.TYPE_PARAMETER_AS_REIFIED_ARRAY.chooseFactory(context) else FirErrors.TYPE_PARAMETER_AS_REIFIED, + symbol, + context + ) } - } else if (typeArgument != null && typeArgument.cannotBeReified()) { + } else if (typeArgument is ConeDefinitelyNotNullType && isExplicit) { + // We sometimes infer type arguments to DNN types, which seems to be ok. Only report explicit DNN types written by user. + reporter.reportOn(source, FirErrors.DEFINITELY_NON_NULLABLE_AS_REIFIED, context) + } else if (typeArgument.isNothing || typeArgument.isNullableNothing || typeArgument is ConeCapturedType) { reporter.reportOn(source, FirErrors.REIFIED_TYPE_FORBIDDEN_SUBSTITUTION, typeArgument, context) - return } } - private fun ConeKotlinType.cannotBeReified(): Boolean { - return this.isNothing || this.isNullableNothing || this is ConeCapturedType - } } 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 bd557971b06..c6b57d55842 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 @@ -147,6 +147,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DATA_CLASS_VARARG import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DATA_CLASS_WITHOUT_PARAMETERS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DECLARATION_CANT_BE_INLINED +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DEFINITELY_NON_NULLABLE_AS_REIFIED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATED_PROPERTY_INSIDE_VALUE_CLASS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATED_PROPERTY_IN_INTERFACE @@ -1219,6 +1220,10 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() { "Cannot use ''{0}'' as reified type parameter", RENDER_TYPE ) + map.put( + DEFINITELY_NON_NULLABLE_AS_REIFIED, + "Cannot use definitely-non-nullable type as reified type argument", + ) map.put( FINAL_UPPER_BOUND, "''{0}'' is a final type, and thus a value of the type parameter is predetermined", diff --git a/compiler/testData/diagnostics/tests/explicitDefinitelyNotNullableViaIntersection/reifiedArguments.fir.kt b/compiler/testData/diagnostics/tests/explicitDefinitelyNotNullableViaIntersection/reifiedArguments.fir.kt deleted file mode 100644 index 2d0b75fc98e..00000000000 --- a/compiler/testData/diagnostics/tests/explicitDefinitelyNotNullableViaIntersection/reifiedArguments.fir.kt +++ /dev/null @@ -1,8 +0,0 @@ -// SKIP_TXT -// !LANGUAGE: +DefinitelyNonNullableTypes - -inline fun foo() {} - -inline fun bar() { - foo() -} diff --git a/compiler/testData/diagnostics/tests/explicitDefinitelyNotNullableViaIntersection/reifiedArguments.kt b/compiler/testData/diagnostics/tests/explicitDefinitelyNotNullableViaIntersection/reifiedArguments.kt index 0e4832c703e..750054b5b16 100644 --- a/compiler/testData/diagnostics/tests/explicitDefinitelyNotNullableViaIntersection/reifiedArguments.kt +++ b/compiler/testData/diagnostics/tests/explicitDefinitelyNotNullableViaIntersection/reifiedArguments.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // SKIP_TXT // !LANGUAGE: +DefinitelyNonNullableTypes @@ -6,3 +7,12 @@ inline fun foo() {} inline fun bar() { foo<F & Any>() } + +class KAnnotatedElement(val annotations: List) + +inline fun Iterable<*>.firstIsInstanceOrNull(): T? { + return null +} + +private inline fun KAnnotatedElement.findAnnotation(): T? = + annotations.firstIsInstanceOrNull() \ No newline at end of file