NI: propagate isNullabilityConstraint flag into constraint injector and inherit it
^KT-37510 Fixed
This commit is contained in:
+18
@@ -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")
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+24
-8
@@ -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
|
||||
}
|
||||
|
||||
+7
-3
@@ -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<OnlyInputTypeConstraintPosition>()
|
||||
|
||||
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)
|
||||
|
||||
+23
-8
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun <K> materialize(): K? { return null }
|
||||
|
||||
val x: String? by lazy { materialize() }
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun <K> materialize(): K? { return null }
|
||||
|
||||
val x: String? by lazy { <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String?")!>materialize()<!> }
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public val x: kotlin.String?
|
||||
public fun </*0*/ K> materialize(): K?
|
||||
+18
@@ -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")
|
||||
|
||||
compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java
Generated
+18
@@ -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")
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user