K2: Avoid using Nothing? as inference result in the majority of cases
Namely, do not choose `Nothing?` result type when fixing a variable that has other constraints besides the ones that came from the relevant type parameter's upper bounds. See more details in KT-55691. In K1, the case from specialCallWithMaterializeAndExpectedType.kt was working (inferred to String?) just because the branches were analyzed independently with `String?` expected type. This change became necessary after the previous commit when we united inference subsystems for if/when branches (see motivation there). NB: For K1, the behavior is left the same, but the code was refactored a bit. ^KT-55691 Fixed ^KT-56448 Fixed
This commit is contained in:
committed by
Space Team
parent
f12a4e08cf
commit
2bafcddf7a
@@ -0,0 +1,43 @@
|
||||
// SKIP_TXT
|
||||
// WITH_STDLIB
|
||||
// ISSUE: KT-56448
|
||||
// FILE: SomeJavaClass.java
|
||||
|
||||
public class SomeJavaClass {
|
||||
public static String getString() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: main.kt
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
interface XdEntity
|
||||
class XdIssue : XdEntity {
|
||||
val isRemoved: Boolean = true
|
||||
var votes: Int = 0
|
||||
}
|
||||
|
||||
fun getNullableIssue(): XdIssue? = null
|
||||
|
||||
fun <T : XdEntity> CharSequence.toXd(): T = null!!
|
||||
|
||||
fun XdIssue.duplicatesRootSearch(): XdIssue = this
|
||||
fun <T : XdEntity, R : Comparable<*>?> T.getOldValue(property: KProperty1<T, R>): R? = null
|
||||
|
||||
internal fun updateVotesForDuplicates(issue: XdIssue) {
|
||||
val toRecount = HashSet<XdIssue>()
|
||||
var oldDup = getNullableIssue()
|
||||
if (oldDup != null) {
|
||||
oldDup = try {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("XdIssue")!>SomeJavaClass.getString().toXd()<!>
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
val newDup = getNullableIssue()
|
||||
|
||||
if (oldDup != null && !oldDup.isRemoved) {
|
||||
toRecount.add(oldDup.duplicatesRootSearch())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// SKIP_TXT
|
||||
// WITH_STDLIB
|
||||
// ISSUE: KT-56448
|
||||
// FILE: SomeJavaClass.java
|
||||
|
||||
public class SomeJavaClass {
|
||||
public static String getString() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: main.kt
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
interface XdEntity
|
||||
class XdIssue : XdEntity {
|
||||
val isRemoved: Boolean = true
|
||||
var votes: Int = 0
|
||||
}
|
||||
|
||||
fun getNullableIssue(): XdIssue? = null
|
||||
|
||||
fun <T : XdEntity> CharSequence.toXd(): T = null!!
|
||||
|
||||
fun XdIssue.duplicatesRootSearch(): XdIssue = this
|
||||
fun <T : XdEntity, R : Comparable<*>?> T.getOldValue(property: KProperty1<T, R>): R? = null
|
||||
|
||||
internal fun updateVotesForDuplicates(issue: XdIssue) {
|
||||
val toRecount = HashSet<XdIssue>()
|
||||
var oldDup = getNullableIssue()
|
||||
if (oldDup != null) {
|
||||
oldDup = try {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("XdIssue")!>SomeJavaClass.getString().toXd()<!>
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
val newDup = getNullableIssue()
|
||||
|
||||
if (oldDup != null && !<!DEBUG_INFO_SMARTCAST!>oldDup<!>.isRemoved) {
|
||||
toRecount.add(<!DEBUG_INFO_SMARTCAST!>oldDup<!>.duplicatesRootSearch())
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// SKIP_TXT
|
||||
// ISSUE: KT-55691
|
||||
|
||||
fun <T> materialize(): T = TODO()
|
||||
fun <S> select(x: S, y: S): S = x
|
||||
|
||||
fun main() {
|
||||
// materialize's type argument is inferred to `Nothing?` in K1 and `String?` in K2
|
||||
val x: String? = select(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String?")!>materialize()<!>, null)
|
||||
|
||||
// materialize's type argument is inferred to `Nothing?` both in K1 and K2
|
||||
select(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Nothing?")!>materialize()<!>, null)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// SKIP_TXT
|
||||
// ISSUE: KT-55691
|
||||
|
||||
fun <T> materialize(): T = TODO()
|
||||
fun <S> select(x: S, y: S): S = x
|
||||
|
||||
fun main() {
|
||||
// materialize's type argument is inferred to `Nothing?` in K1 and `String?` in K2
|
||||
val x: String? = select(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Nothing?")!><!IMPLICIT_NOTHING_TYPE_ARGUMENT_AGAINST_NOT_NOTHING_EXPECTED_TYPE!>materialize<!>()<!>, null)
|
||||
|
||||
// materialize's type argument is inferred to `Nothing?` both in K1 and K2
|
||||
select(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Nothing?")!>materialize()<!>, null)
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||
|
||||
fun foo() {
|
||||
val s: String? = if (true) <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Nothing?")!>materialize()<!> else null
|
||||
}
|
||||
|
||||
fun <K> materialize(): K = TODO()
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||
|
||||
fun foo() {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// FIR_IDENTICAL
|
||||
// SKIP_TXT
|
||||
|
||||
class Out<out T : CharSequence?>(val t: T)
|
||||
|
||||
fun foo() {
|
||||
// We have two constraints here:
|
||||
// Nothing? <: T (from argument `null` type)
|
||||
// T <: CharSequence?
|
||||
// And we fix T to `Nothing?`, because it's still more preferrable than constraint from the upper bound
|
||||
val x1 = Out(null)
|
||||
bar(<!DEBUG_INFO_EXPRESSION_TYPE("Out<kotlin.Nothing?>")!>x1<!>)
|
||||
}
|
||||
|
||||
fun bar(w: Out<String?>) {}
|
||||
+1
-1
@@ -8,7 +8,7 @@ fun test() {
|
||||
val map: Map<String, Int> = mapOf("x" to 1)
|
||||
|
||||
val r1 = map.getOrDefault_Exact("y", null)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Nothing?")!>r1<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>r1<!>
|
||||
|
||||
val r2 = map.getOrDefault_Exact("y", null as Int?)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>r2<!>
|
||||
|
||||
@@ -28,12 +28,12 @@ FILE fqName:<root> fileName:/kt47527.kt
|
||||
VALUE_PARAMETER name:value index:0 type:kotlin.Any?
|
||||
BLOCK_BODY
|
||||
RETURN type=kotlin.Nothing from='public final fun test_2 (value: kotlin.Any?): kotlin.String? declared in <root>'
|
||||
CALL 'public final fun run <R> (block: kotlin.Function0<R of kotlin.StandardKt.run>): R of kotlin.StandardKt.run [inline] declared in kotlin.StandardKt' type=kotlin.Nothing? origin=null
|
||||
<R>: kotlin.Nothing?
|
||||
block: FUN_EXPR type=kotlin.Function0<kotlin.Nothing?> origin=LAMBDA
|
||||
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> () returnType:kotlin.Nothing?
|
||||
CALL 'public final fun run <R> (block: kotlin.Function0<R of kotlin.StandardKt.run>): R of kotlin.StandardKt.run [inline] declared in kotlin.StandardKt' type=kotlin.String? origin=null
|
||||
<R>: kotlin.String?
|
||||
block: FUN_EXPR type=kotlin.Function0<kotlin.String?> origin=LAMBDA
|
||||
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> () returnType:kotlin.String?
|
||||
BLOCK_BODY
|
||||
RETURN type=kotlin.Nothing from='local final fun <anonymous> (): kotlin.Nothing? declared in <root>.test_2'
|
||||
RETURN type=kotlin.Nothing from='local final fun <anonymous> (): kotlin.String? declared in <root>.test_2'
|
||||
BLOCK type=kotlin.Nothing? origin=SAFE_CALL
|
||||
VAR IR_TEMPORARY_VARIABLE name:tmp_1 type:kotlin.Any? [val]
|
||||
GET_VAR 'value: kotlin.Any? declared in <root>.test_2' type=kotlin.Any? origin=null
|
||||
|
||||
@@ -12,7 +12,7 @@ fun test_1(value: Any?): String? {
|
||||
}
|
||||
|
||||
fun test_2(value: Any?): String? {
|
||||
return run<Nothing?>(block = local fun <anonymous>(): Nothing? {
|
||||
return run<String?>(block = local fun <anonymous>(): String? {
|
||||
return { // BLOCK
|
||||
val tmp1_safe_receiver: Any? = value
|
||||
when {
|
||||
|
||||
Reference in New Issue
Block a user