NI: propagate isNullabilityConstraint flag into constraint injector and inherit it

^KT-37510 Fixed
This commit is contained in:
Victor Petukhov
2020-04-07 13:50:50 +03:00
parent 19e352a1b5
commit 11d05c1abd
11 changed files with 142 additions and 26 deletions
@@ -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")
@@ -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
@@ -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
}
@@ -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)
@@ -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)
}
}
@@ -0,0 +1,3 @@
fun <K> materialize(): K? { return null }
val x: String? by lazy { materialize() }
@@ -0,0 +1,3 @@
fun <K> materialize(): K? { return null }
val x: String? by lazy { <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String?")!>materialize()<!> }
@@ -0,0 +1,4 @@
package
public val x: kotlin.String?
public fun </*0*/ K> materialize(): K?
@@ -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")
@@ -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())
}