FIR: intersect flexible type bounds separately

^KT-54522 Fixed
This commit is contained in:
pyos
2022-10-18 14:19:52 +02:00
committed by Space Team
parent 8bc9fd91d5
commit ee6af9af5f
9 changed files with 69 additions and 21 deletions
@@ -39120,6 +39120,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
runTest("compiler/testData/diagnostics/testsWithStdLib/java/inheritedFunctionN.kt");
}
@Test
@TestMetadata("intersectFlexibleAndMutable.kt")
public void testIntersectFlexibleAndMutable() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/java/intersectFlexibleAndMutable.kt");
}
@Test
@TestMetadata("patternCompileCallableReference.kt")
public void testPatternCompileCallableReference() throws Exception {
@@ -39216,6 +39216,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
runTest("compiler/testData/diagnostics/testsWithStdLib/java/inheritedFunctionN.kt");
}
@Test
@TestMetadata("intersectFlexibleAndMutable.kt")
public void testIntersectFlexibleAndMutable() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/java/intersectFlexibleAndMutable.kt");
}
@Test
@TestMetadata("patternCompileCallableReference.kt")
public void testPatternCompileCallableReference() throws Exception {
@@ -39120,6 +39120,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
runTest("compiler/testData/diagnostics/testsWithStdLib/java/inheritedFunctionN.kt");
}
@Test
@TestMetadata("intersectFlexibleAndMutable.kt")
public void testIntersectFlexibleAndMutable() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/java/intersectFlexibleAndMutable.kt");
}
@Test
@TestMetadata("patternCompileCallableReference.kt")
public void testPatternCompileCallableReference() throws Exception {
@@ -177,8 +177,8 @@ class Fir2IrTypeConverter(
is ConeFlexibleType -> with(session.typeContext) {
if (upperBound is ConeClassLikeType) {
val upper = upperBound as ConeClassLikeType
val lower = lowerBound as? ConeClassLikeType ?: error("Expecting class-like type, got $lowerBound")
val intermediate = if (lower.lookupTag == upper.lookupTag) {
val lower = lowerBound
val intermediate = if (lower is ConeClassLikeType && lower.lookupTag == upper.lookupTag) {
lower.replaceArguments(upper.getArguments())
} else lower
(intermediate.withNullability(upper.isNullable) as ConeKotlinType)
@@ -27,6 +27,16 @@ object ConeTypeIntersector {
}
}
if (inputTypes.any { it is ConeFlexibleType }) {
// (A..B) & C = (A & C)..(B & C)
val lowerBound = intersectTypes(context, inputTypes.map { it.lowerBoundIfFlexible() })
val upperBound = intersectTypes(context, inputTypes.map { it.upperBoundIfFlexible() })
// Special case - if C is `Nothing?`, then the result is `Nothing!`; but if it is non-null,
// then this code is unreachable, so it's more useful to do resolution/diagnostics
// under the assumption that it is purely nullable.
return if (lowerBound.isNothing) upperBound else coneFlexibleOrSimpleType(context, lowerBound, upperBound)
}
/**
* resultNullability. Value description:
* ACCEPT_NULL means that all types marked nullable
@@ -37,11 +47,11 @@ object ConeTypeIntersector {
* UNKNOWN means, that we do not know, i.e. more precisely, all singleClassifier types marked nullable if any,
* and other types is captured types or type parameters without not-null upper bound. Example: `String? & T` such types we should leave as is.
*/
val isResultNullable = inputTypes.all { it.isNullable(context) }
val inputTypesWithNullability = inputTypes.mapTo(LinkedHashSet()) {
if (isResultNullable) it else it.makeConeTypeDefinitelyNotNullOrNotNull(context)
val isResultNotNullable = inputTypes.any { !it.isNullable(context) }
val inputTypesMadeNotNullIfNeeded = inputTypes.mapTo(LinkedHashSet()) {
if (isResultNotNullable) it.makeConeTypeDefinitelyNotNullOrNotNull(context) else it
}
if (inputTypesWithNullability.size == 1) return inputTypesWithNullability.single()
if (inputTypesMadeNotNullIfNeeded.size == 1) return inputTypesMadeNotNullIfNeeded.single()
/*
* Here we drop types from intersection set for cases like that:
@@ -54,25 +64,12 @@ object ConeTypeIntersector {
* We want to drop A from that set, because it's useless for type checking. But in case if
* A came from inference and B came from smartcast we want to save both types in intersection
*/
val resultList = inputTypesWithNullability.toMutableList()
val resultList = inputTypesMadeNotNullIfNeeded.toMutableList()
resultList.removeIfNonSingleErrorOrInRelation { candidate, other -> other.isStrictSubtypeOf(context, candidate) }
assert(resultList.isNotEmpty()) { "no types left after removing strict supertypes: ${inputTypes.joinToString()}" }
ConeIntegerLiteralIntersector.findCommonIntersectionType(resultList)?.let { return it }
/*
* For the case like it(ft(String..String?), String?), where ft(String..String?) == String?, we prefer to _keep_ flexible type.
* When a == b, the former, i.e., the one in the list will be filtered out, and the other one will remain.
* So, here, we sort the interim list such that flexible types appear later.
*/
resultList.sortWith { p0, p1 ->
when {
p0 is ConeFlexibleType && p1 is ConeFlexibleType -> 0
p0 is ConeFlexibleType -> 1
p1 is ConeFlexibleType -> -1
else -> 0
}
}
resultList.removeIfNonSingleErrorOrInRelation { candidate, other -> AbstractTypeChecker.equalTypes(context, candidate, other) }
assert(resultList.isNotEmpty()) { "no types left after removing equal types: ${inputTypes.joinToString()}" }
return resultList.singleOrNull() ?: ConeIntersectionType(resultList)
@@ -21,7 +21,7 @@ fun testWithUtil(map: ConcurrentHashMap<Int, String>): Int {
if (string == null) {
string = Util.getString()
}
return string<!UNSAFE_CALL!>.<!>length
return string.length
}
fun test(list: java.util.ArrayList<String?>) {
@@ -0,0 +1,14 @@
// FIR_IDENTICAL
// FILE: Util.java
import java.util.List;
public class Util {
public static <T> List<T> id(List<T> x) { return x; }
}
// FILE: main.kt
fun main() {
var list = mutableListOf(1)
list = Util.id(list)
list += 2
}
@@ -0,0 +1,13 @@
package
public fun main(): kotlin.Unit
public open class Util {
public constructor Util()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public open fun </*0*/ T : kotlin.Any!> id(/*0*/ x: kotlin.collections.(Mutable)List<T!>!): kotlin.collections.(Mutable)List<T!>!
}
@@ -39216,6 +39216,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/testsWithStdLib/java/inheritedFunctionN.kt");
}
@Test
@TestMetadata("intersectFlexibleAndMutable.kt")
public void testIntersectFlexibleAndMutable() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/java/intersectFlexibleAndMutable.kt");
}
@Test
@TestMetadata("patternCompileCallableReference.kt")
public void testPatternCompileCallableReference() throws Exception {