FIR: intersect flexible type bounds separately
^KT-54522 Fixed
This commit is contained in:
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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?>) {
|
||||
|
||||
+14
@@ -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
|
||||
}
|
||||
+13
@@ -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!>!
|
||||
}
|
||||
Generated
+6
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user