diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestWithStdlibGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestWithStdlibGenerated.java index e89ed940800..1de62d8f1bf 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestWithStdlibGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestWithStdlibGenerated.java @@ -2167,6 +2167,24 @@ public class FirOldFrontendDiagnosticsTestWithStdlibGenerated extends AbstractFi runTest("compiler/testData/diagnostics/testsWithStdLib/inference/delegates/kt32249.kt"); } } + + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/inference/nothingType") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class NothingType extends AbstractFirOldFrontendDiagnosticsTestWithStdlib { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInNothingType() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/inference/nothingType"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("dontInferToNullableNothingInDelegates.kt") + public void testDontInferToNullableNothingInDelegates() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/nothingType/dontInferToNullableNothingInDelegates.kt"); + } + } } @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/inline") diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/CoroutineInferenceUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/CoroutineInferenceUtil.kt index b7583d0b14b..d661cd30795 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/CoroutineInferenceUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/CoroutineInferenceUtil.kt @@ -259,7 +259,7 @@ class CoroutineInferenceSupport( private val allowOnlyTrivialConstraints: Boolean ) : ClassicTypeCheckerContext(errorTypeEqualsToAnything = true) { - override fun addSubtypeConstraint(subType: KotlinTypeMarker, superType: KotlinTypeMarker): Boolean? { + override fun addSubtypeConstraint(subType: KotlinTypeMarker, superType: KotlinTypeMarker, isFromNullabilityConstraint: Boolean): Boolean? { require(subType is UnwrappedType) require(superType is UnwrappedType) val typeTemplate = subType as? TypeTemplate ?: superType as? TypeTemplate diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/AbstractTypeCheckerContextForConstraintSystem.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/AbstractTypeCheckerContextForConstraintSystem.kt index e681c5a14c6..055f6e06435 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/AbstractTypeCheckerContextForConstraintSystem.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/AbstractTypeCheckerContextForConstraintSystem.kt @@ -28,7 +28,11 @@ abstract class AbstractTypeCheckerContextForConstraintSystem : AbstractTypeCheck // super and sub type isSingleClassifierType abstract fun addUpperConstraint(typeVariable: TypeConstructorMarker, superType: KotlinTypeMarker) - abstract fun addLowerConstraint(typeVariable: TypeConstructorMarker, subType: KotlinTypeMarker) + abstract fun addLowerConstraint( + typeVariable: TypeConstructorMarker, + subType: KotlinTypeMarker, + isFromNullabilityConstraint: Boolean = false + ) override fun getLowerCapturedTypePolicy(subType: SimpleTypeMarker, superType: CapturedTypeMarker): LowerCapturedTypePolicy { return when { @@ -52,7 +56,11 @@ abstract class AbstractTypeCheckerContextForConstraintSystem : AbstractTypeCheck * then we can get wrong result. * override val sameConstructorPolicy get() = SeveralSupertypesWithSameConstructorPolicy.TAKE_FIRST_FOR_SUBTYPING */ - final override fun addSubtypeConstraint(subType: KotlinTypeMarker, superType: KotlinTypeMarker): Boolean? { + final override fun addSubtypeConstraint( + subType: KotlinTypeMarker, + superType: KotlinTypeMarker, + isFromNullabilityConstraint: Boolean + ): Boolean? { val hasNoInfer = subType.isTypeVariableWithNoInfer() || superType.isTypeVariableWithNoInfer() if (hasNoInfer) return true @@ -64,10 +72,10 @@ abstract class AbstractTypeCheckerContextForConstraintSystem : AbstractTypeCheck val mySuperType = if (hasExact) extractTypeForProjectedType(superType, out = false) ?: superType.removeExactAnnotation() else superType - val result = internalAddSubtypeConstraint(mySubType, mySuperType) + val result = internalAddSubtypeConstraint(mySubType, mySuperType, isFromNullabilityConstraint) if (!hasExact) return result - val result2 = internalAddSubtypeConstraint(mySuperType, mySubType) + val result2 = internalAddSubtypeConstraint(mySuperType, mySubType, isFromNullabilityConstraint) if (result == null && result2 == null) return null return (result ?: true) && (result2 ?: true) @@ -93,13 +101,17 @@ abstract class AbstractTypeCheckerContextForConstraintSystem : AbstractTypeCheck private fun KotlinTypeMarker.isTypeVariableWithNoInfer() = hasNoInferAnnotation() && anyBound(this@AbstractTypeCheckerContextForConstraintSystem::isMyTypeVariable) - private fun internalAddSubtypeConstraint(subType: KotlinTypeMarker, superType: KotlinTypeMarker): Boolean? { + private fun internalAddSubtypeConstraint( + subType: KotlinTypeMarker, + superType: KotlinTypeMarker, + isFromNullabilityConstraint: Boolean + ): Boolean? { assertInputTypes(subType, superType) var answer: Boolean? = null if (superType.anyBound(this::isMyTypeVariable)) { - answer = simplifyLowerConstraint(superType, subType) + answer = simplifyLowerConstraint(superType, subType, isFromNullabilityConstraint) } if (subType.anyBound(this::isMyTypeVariable)) { @@ -178,7 +190,11 @@ abstract class AbstractTypeCheckerContextForConstraintSystem : AbstractTypeCheck * * => (Foo..Bar) <: T! -- (Foo!! .. Bar) <: T */ - private fun simplifyLowerConstraint(typeVariable: KotlinTypeMarker, subType: KotlinTypeMarker): Boolean { + private fun simplifyLowerConstraint( + typeVariable: KotlinTypeMarker, + subType: KotlinTypeMarker, + isFromNullabilityConstraint: Boolean = false + ): Boolean { val lowerConstraint = when (typeVariable) { is SimpleTypeMarker -> /* @@ -229,7 +245,7 @@ abstract class AbstractTypeCheckerContextForConstraintSystem : AbstractTypeCheck else -> error("sealed") } - addLowerConstraint(typeVariable.typeConstructor(), lowerConstraint) + addLowerConstraint(typeVariable.typeConstructor(), lowerConstraint, isFromNullabilityConstraint) return true } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt index 0bdcf7e2b6a..6af600c7610 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt @@ -31,7 +31,8 @@ class ConstraintIncorporator( fun addNewIncorporatedConstraint( lowerType: KotlinTypeMarker, upperType: KotlinTypeMarker, - shouldTryUseDifferentFlexibilityForUpperType: Boolean + shouldTryUseDifferentFlexibilityForUpperType: Boolean, + isFromNullabilityConstraint: Boolean = false ) fun addNewIncorporatedConstraint(typeVariable: TypeVariableMarker, type: KotlinTypeMarker, constraintContext: ConstraintContext) @@ -62,7 +63,7 @@ class ConstraintIncorporator( if (constraint.kind != ConstraintKind.LOWER) { getConstraintsForVariable(typeVariable).forEach { if (it.kind != ConstraintKind.UPPER) { - addNewIncorporatedConstraint(it.type, constraint.type, shouldBeTypeVariableFlexible) + addNewIncorporatedConstraint(it.type, constraint.type, shouldBeTypeVariableFlexible, it.isNullabilityConstraint) } } } @@ -231,7 +232,10 @@ class ConstraintIncorporator( val inputTypePosition = baseConstraint.position.from.safeAs() - val isNullabilityConstraint = isUsefulForNullabilityConstraint && newConstraint.isNullableNothing() + val isNewConstraintUsefulForNullability = isUsefulForNullabilityConstraint && newConstraint.isNullableNothing() + val isOtherConstraintUsefulForNullability = otherConstraint.isNullabilityConstraint && otherConstraint.type.isNullableNothing() + val isNullabilityConstraint = isNewConstraintUsefulForNullability || isOtherConstraintUsefulForNullability + val constraintContext = ConstraintContext(kind, derivedFrom, inputTypePosition, isNullabilityConstraint) addNewIncorporatedConstraint(targetVariable, newConstraint, constraintContext) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt index b8689a1b7a2..a4e4791f39d 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt @@ -181,13 +181,15 @@ class ConstraintInjector( fun runIsSubtypeOf( lowerType: KotlinTypeMarker, upperType: KotlinTypeMarker, - shouldTryUseDifferentFlexibilityForUpperType: Boolean = false + shouldTryUseDifferentFlexibilityForUpperType: Boolean = false, + isFromNullabilityConstraint: Boolean = false ) { fun isSubtypeOf(upperType: KotlinTypeMarker) = AbstractTypeChecker.isSubtypeOf( this@TypeCheckerContext as AbstractTypeCheckerContext, lowerType, - upperType + upperType, + isFromNullabilityConstraint ) if (!isSubtypeOf(upperType)) { @@ -215,8 +217,11 @@ class ConstraintInjector( override fun addUpperConstraint(typeVariable: TypeConstructorMarker, superType: KotlinTypeMarker) = addConstraint(typeVariable, superType, UPPER) - override fun addLowerConstraint(typeVariable: TypeConstructorMarker, subType: KotlinTypeMarker) = - addConstraint(typeVariable, subType, LOWER) + override fun addLowerConstraint( + typeVariable: TypeConstructorMarker, + subType: KotlinTypeMarker, + isFromNullabilityConstraint: Boolean + ) = addConstraint(typeVariable, subType, LOWER, isFromNullabilityConstraint) private fun isCapturedTypeFromSubtyping(type: KotlinTypeMarker) = when ((type as? CapturedTypeMarker)?.captureStatus()) { @@ -226,22 +231,32 @@ class ConstraintInjector( error("Captured type for incorporation shouldn't escape from incorporation: $type\n" + renderBaseConstraint()) } - private fun addConstraint(typeVariableConstructor: TypeConstructorMarker, type: KotlinTypeMarker, kind: ConstraintKind) { + private fun addConstraint( + typeVariableConstructor: TypeConstructorMarker, + type: KotlinTypeMarker, + kind: ConstraintKind, + isFromNullabilityConstraint: Boolean = false + ) { val typeVariable = c.allTypeVariables[typeVariableConstructor] ?: error("Should by type variableConstructor: $typeVariableConstructor. ${c.allTypeVariables.values}") - addNewIncorporatedConstraint(typeVariable, type, ConstraintContext(kind, emptySet(), isNullabilityConstraint = false)) + addNewIncorporatedConstraint( + typeVariable, + type, + ConstraintContext(kind, emptySet(), isNullabilityConstraint = isFromNullabilityConstraint) + ) } // from ConstraintIncorporator.Context override fun addNewIncorporatedConstraint( lowerType: KotlinTypeMarker, upperType: KotlinTypeMarker, - shouldTryUseDifferentFlexibilityForUpperType: Boolean + shouldTryUseDifferentFlexibilityForUpperType: Boolean, + isFromNullabilityConstraint: Boolean ) { if (lowerType === upperType) return if (c.isAllowedType(lowerType) && c.isAllowedType(upperType)) { - runIsSubtypeOf(lowerType, upperType, shouldTryUseDifferentFlexibilityForUpperType) + runIsSubtypeOf(lowerType, upperType, shouldTryUseDifferentFlexibilityForUpperType, isFromNullabilityConstraint) } } diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/nothingType/dontInferToNullableNothingInDelegates.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/nothingType/dontInferToNullableNothingInDelegates.fir.kt new file mode 100644 index 00000000000..ca63a70b493 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/nothingType/dontInferToNullableNothingInDelegates.fir.kt @@ -0,0 +1,3 @@ +fun materialize(): K? { return null } + +val x: String? by lazy { materialize() } diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/nothingType/dontInferToNullableNothingInDelegates.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/nothingType/dontInferToNullableNothingInDelegates.kt new file mode 100644 index 00000000000..6d9d4ba74ee --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/nothingType/dontInferToNullableNothingInDelegates.kt @@ -0,0 +1,3 @@ +fun materialize(): K? { return null } + +val x: String? by lazy { materialize() } diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/nothingType/dontInferToNullableNothingInDelegates.txt b/compiler/testData/diagnostics/testsWithStdLib/inference/nothingType/dontInferToNullableNothingInDelegates.txt new file mode 100644 index 00000000000..abc9e4b194c --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/nothingType/dontInferToNullableNothingInDelegates.txt @@ -0,0 +1,4 @@ +package + +public val x: kotlin.String? +public fun materialize(): K? diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index 21be62be8d7..40b1b32afa2 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -3182,6 +3182,24 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW runTest("compiler/testData/diagnostics/testsWithStdLib/inference/delegates/kt32249.kt"); } } + + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/inference/nothingType") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class NothingType extends AbstractDiagnosticsTestWithStdLib { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInNothingType() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/inference/nothingType"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("dontInferToNullableNothingInDelegates.kt") + public void testDontInferToNullableNothingInDelegates() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/nothingType/dontInferToNullableNothingInDelegates.kt"); + } + } } @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/inline") diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java index 2f69c8b98a4..d57a071048b 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java @@ -3182,6 +3182,24 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno runTest("compiler/testData/diagnostics/testsWithStdLib/inference/delegates/kt32249.kt"); } } + + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/inference/nothingType") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class NothingType extends AbstractDiagnosticsTestWithStdLibUsingJavac { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInNothingType() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/inference/nothingType"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("dontInferToNullableNothingInDelegates.kt") + public void testDontInferToNullableNothingInDelegates() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/nothingType/dontInferToNullableNothingInDelegates.kt"); + } + } } @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/inline") diff --git a/core/type-system/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt b/core/type-system/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt index aa6143151ee..69c6fa66297 100644 --- a/core/type-system/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt +++ b/core/type-system/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt @@ -47,7 +47,12 @@ abstract class AbstractTypeCheckerContext : TypeSystemContext { } open fun getLowerCapturedTypePolicy(subType: SimpleTypeMarker, superType: CapturedTypeMarker): LowerCapturedTypePolicy = CHECK_SUBTYPE_AND_LOWER - open fun addSubtypeConstraint(subType: KotlinTypeMarker, superType: KotlinTypeMarker): Boolean? = null + + open fun addSubtypeConstraint( + subType: KotlinTypeMarker, + superType: KotlinTypeMarker, + isFromNullabilityConstraint: Boolean = false + ): Boolean? = null enum class LowerCapturedTypePolicy { CHECK_ONLY_LOWER, @@ -162,9 +167,17 @@ object AbstractTypeChecker { return AbstractTypeChecker.equalTypes(context.newBaseTypeCheckerContext(false, stubTypesEqualToAnything), a, b) } - fun isSubtypeOf(context: AbstractTypeCheckerContext, subType: KotlinTypeMarker, superType: KotlinTypeMarker): Boolean { + fun isSubtypeOf( + context: AbstractTypeCheckerContext, + subType: KotlinTypeMarker, + superType: KotlinTypeMarker, + isFromNullabilityConstraint: Boolean = false + ): Boolean { if (subType === superType) return true - return with(context) { completeIsSubTypeOf(prepareType(refineType(subType)), prepareType(refineType(superType))) } + + return with(context) { + completeIsSubTypeOf(prepareType(refineType(subType)), prepareType(refineType(superType)), isFromNullabilityConstraint) + } } fun equalTypes(context: AbstractTypeCheckerContext, a: KotlinTypeMarker, b: KotlinTypeMarker): Boolean = with(context) { @@ -186,14 +199,18 @@ object AbstractTypeChecker { } - private fun AbstractTypeCheckerContext.completeIsSubTypeOf(subType: KotlinTypeMarker, superType: KotlinTypeMarker): Boolean { + private fun AbstractTypeCheckerContext.completeIsSubTypeOf( + subType: KotlinTypeMarker, + superType: KotlinTypeMarker, + isFromNullabilityConstraint: Boolean + ): Boolean { checkSubtypeForSpecialCases(subType.lowerBoundIfFlexible(), superType.upperBoundIfFlexible())?.let { - addSubtypeConstraint(subType, superType) + addSubtypeConstraint(subType, superType, isFromNullabilityConstraint) return it } // we should add constraints with flexible types, otherwise we never get flexible type as answer in constraint system - addSubtypeConstraint(subType, superType)?.let { return it } + addSubtypeConstraint(subType, superType, isFromNullabilityConstraint)?.let { return it } return isSubtypeOfForSingleClassifierType(subType.lowerBoundIfFlexible(), superType.upperBoundIfFlexible()) }