Fix KT-71751
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt
index fe6f09a..5cc7450 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt
@@ -5,10 +5,12 @@
package org.jetbrains.kotlin.fir.resolve.transformers.body.resolve
+import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.fir.FirTargetElement
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.impl.FirElseIfTrueCondition
import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyExpressionBlock
+import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
@@ -237,10 +239,15 @@
val mayBeCoercionToUnitApplied = (data as? ResolutionMode.WithExpectedType)?.mayBeCoercionToUnitApplied == true
val resolutionModeForLhs =
- if (mayBeCoercionToUnitApplied && expectedType?.isUnitOrFlexibleUnit == true)
- withExpectedType(expectedType, mayBeCoercionToUnitApplied = true)
- else
- withExpectedType(expectedType?.withNullability(nullable = true, session.typeContext))
+ when {
+ // In general, it feels ok to use Dependent mode for elvis LHS, but we still do that for LV == 2.0
+ session.languageVersionSettings.supportsFeature(LanguageFeature.ElvisInferenceImprovementsIn21) ->
+ ResolutionMode.ContextDependent
+
+ mayBeCoercionToUnitApplied && expectedType?.isUnitOrFlexibleUnit == true ->
+ withExpectedType(expectedType, mayBeCoercionToUnitApplied = true)
+ else -> withExpectedType(expectedType?.withNullability(nullable = true, session.typeContext))
+ }
dataFlowAnalyzer.enterElvis(elvisExpression)
elvisExpression.transformLhs(transformer, resolutionModeForLhs)
dataFlowAnalyzer.exitElvisLhs(elvisExpression)
diff --git a/compiler/testData/codegen/box/unit/unitCoercionWithElvis.kt b/compiler/testData/codegen/box/unit/unitCoercionWithElvis.kt
index 9d79fda..90c061d 100644
--- a/compiler/testData/codegen/box/unit/unitCoercionWithElvis.kt
+++ b/compiler/testData/codegen/box/unit/unitCoercionWithElvis.kt
@@ -1,6 +1,5 @@
// WITH_STDLIB
// ISSUE: KT-71751
-// IGNORE_BACKEND_K2: ANY
fun launch(x: () -> Unit) {
x()
diff --git a/compiler/testData/diagnostics/tests/unitCoercion/elvisRHSRemainsNullable.fir.kt b/compiler/testData/diagnostics/tests/unitCoercion/elvisRHSRemainsNullable.fir.kt
deleted file mode 100644
index c9cafd5..0000000
--- a/compiler/testData/diagnostics/tests/unitCoercion/elvisRHSRemainsNullable.fir.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-// ISSUE: KT-71751
-
-fun <R> myRun(x: () -> R): R = TODO()
-fun launch(x: () -> Unit) {}
-fun baz() {}
-
-fun main() {
- launch {
- // We should not run Unit-coercion for `myRun { null }`
- myRun { null } <!USELESS_ELVIS!>?: baz()<!>
- }
-}
diff --git a/compiler/testData/diagnostics/tests/unitCoercion/elvisRHSRemainsNullable.kt b/compiler/testData/diagnostics/tests/unitCoercion/elvisRHSRemainsNullable.kt
index 3da2e507..14c4637 100644
--- a/compiler/testData/diagnostics/tests/unitCoercion/elvisRHSRemainsNullable.kt
+++ b/compiler/testData/diagnostics/tests/unitCoercion/elvisRHSRemainsNullable.kt
@@ -1,3 +1,4 @@
+// FIR_IDENTICAL
// ISSUE: KT-71751
fun <R> myRun(x: () -> R): R = TODO()
diff --git a/compiler/testData/diagnostics/tests/when/kt9972.fir.kt b/compiler/testData/diagnostics/tests/when/kt9972.fir.kt
index 4b3dde1..cea8d79 100644
--- a/compiler/testData/diagnostics/tests/when/kt9972.fir.kt
+++ b/compiler/testData/diagnostics/tests/when/kt9972.fir.kt
@@ -21,7 +21,7 @@
fun test2(): Int {
val x: String = <!INITIALIZER_TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH!>when {
- true -> <!TYPE_MISMATCH!>Any()<!>
+ true -> Any()
else -> null
} ?: return 0<!>
return x.hashCode()
diff --git a/compiler/testData/ir/irText/expressions/kt30796.fir.ir.txt b/compiler/testData/ir/irText/expressions/kt30796.fir.ir.txt
deleted file mode 100644
index dcde403..0000000
--- a/compiler/testData/ir/irText/expressions/kt30796.fir.ir.txt
+++ /dev/null
@@ -1,160 +0,0 @@
-FILE fqName:<root> fileName:/kt30796.kt
- FUN name:magic visibility:public modality:FINAL <T> () returnType:T of <root>.magic
- TYPE_PARAMETER name:T index:0 variance: superTypes:[kotlin.Any?] reified:false
- BLOCK_BODY
- THROW type=kotlin.Nothing
- CONSTRUCTOR_CALL 'public constructor <init> () declared in java.lang.Exception' type=java.lang.Exception origin=null
- FUN name:test visibility:public modality:FINAL <T> (value:T of <root>.test, value2:T of <root>.test) returnType:kotlin.Unit
- TYPE_PARAMETER name:T index:0 variance: superTypes:[kotlin.Any?] reified:false
- VALUE_PARAMETER name:value index:0 type:T of <root>.test
- VALUE_PARAMETER name:value2 index:1 type:T of <root>.test
- BLOCK_BODY
- VAR name:x1 type:kotlin.Any [val]
- BLOCK type=kotlin.Any origin=ELVIS
- VAR IR_TEMPORARY_VARIABLE name:tmp_0 type:T of <root>.test [val]
- GET_VAR 'value: T of <root>.test declared in <root>.test' type=T of <root>.test origin=null
- WHEN type=kotlin.Any origin=null
- BRANCH
- if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ
- arg0: GET_VAR 'val tmp_0: T of <root>.test declared in <root>.test' type=T of <root>.test origin=null
- arg1: CONST Null type=kotlin.Nothing? value=null
- then: CONST Int type=kotlin.Int value=42
- BRANCH
- if: CONST Boolean type=kotlin.Boolean value=true
- then: GET_VAR 'val tmp_0: T of <root>.test declared in <root>.test' type=T of <root>.test origin=null
- VAR name:x2 type:kotlin.Any [val]
- BLOCK type=kotlin.Any origin=ELVIS
- VAR IR_TEMPORARY_VARIABLE name:tmp_1 type:T of <root>.test [val]
- GET_VAR 'value: T of <root>.test declared in <root>.test' type=T of <root>.test origin=null
- WHEN type=kotlin.Any origin=null
- BRANCH
- if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ
- arg0: GET_VAR 'val tmp_1: T of <root>.test declared in <root>.test' type=T of <root>.test origin=null
- arg1: CONST Null type=kotlin.Nothing? value=null
- then: BLOCK type=kotlin.Any origin=ELVIS
- VAR IR_TEMPORARY_VARIABLE name:tmp_2 type:T of <root>.test [val]
- GET_VAR 'value2: T of <root>.test declared in <root>.test' type=T of <root>.test origin=null
- WHEN type=kotlin.Any origin=null
- BRANCH
- if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ
- arg0: GET_VAR 'val tmp_2: T of <root>.test declared in <root>.test' type=T of <root>.test origin=null
- arg1: CONST Null type=kotlin.Nothing? value=null
- then: CONST Int type=kotlin.Int value=42
- BRANCH
- if: CONST Boolean type=kotlin.Boolean value=true
- then: GET_VAR 'val tmp_2: T of <root>.test declared in <root>.test' type=T of <root>.test origin=null
- BRANCH
- if: CONST Boolean type=kotlin.Boolean value=true
- then: GET_VAR 'val tmp_1: T of <root>.test declared in <root>.test' type=T of <root>.test origin=null
- VAR name:x3 type:kotlin.Any [val]
- BLOCK type=kotlin.Any origin=ELVIS
- VAR IR_TEMPORARY_VARIABLE name:tmp_3 type:kotlin.Any? [val]
- BLOCK type=kotlin.Any? origin=ELVIS
- VAR IR_TEMPORARY_VARIABLE name:tmp_4 type:T of <root>.test [val]
- GET_VAR 'value: T of <root>.test declared in <root>.test' type=T of <root>.test origin=null
- WHEN type=kotlin.Any? origin=null
- BRANCH
- if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ
- arg0: GET_VAR 'val tmp_4: T of <root>.test declared in <root>.test' type=T of <root>.test origin=null
- arg1: CONST Null type=kotlin.Nothing? value=null
- then: GET_VAR 'value2: T of <root>.test declared in <root>.test' type=T of <root>.test origin=null
- BRANCH
- if: CONST Boolean type=kotlin.Boolean value=true
- then: GET_VAR 'val tmp_4: T of <root>.test declared in <root>.test' type=T of <root>.test origin=null
- WHEN type=kotlin.Any origin=null
- BRANCH
- if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ
- arg0: GET_VAR 'val tmp_3: kotlin.Any? declared in <root>.test' type=kotlin.Any? origin=null
- arg1: CONST Null type=kotlin.Nothing? value=null
- then: CONST Int type=kotlin.Int value=42
- BRANCH
- if: CONST Boolean type=kotlin.Boolean value=true
- then: GET_VAR 'val tmp_3: kotlin.Any? declared in <root>.test' type=kotlin.Any? origin=null
- VAR name:x4 type:kotlin.Any [val]
- BLOCK type=kotlin.Any origin=ELVIS
- VAR IR_TEMPORARY_VARIABLE name:tmp_5 type:kotlin.Any? [val]
- BLOCK type=kotlin.Any? origin=ELVIS
- VAR IR_TEMPORARY_VARIABLE name:tmp_6 type:T of <root>.test [val]
- GET_VAR 'value: T of <root>.test declared in <root>.test' type=T of <root>.test origin=null
- WHEN type=kotlin.Any? origin=null
- BRANCH
- if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ
- arg0: GET_VAR 'val tmp_6: T of <root>.test declared in <root>.test' type=T of <root>.test origin=null
- arg1: CONST Null type=kotlin.Nothing? value=null
- then: GET_VAR 'value2: T of <root>.test declared in <root>.test' type=T of <root>.test origin=null
- BRANCH
- if: CONST Boolean type=kotlin.Boolean value=true
- then: GET_VAR 'val tmp_6: T of <root>.test declared in <root>.test' type=T of <root>.test origin=null
- WHEN type=kotlin.Any origin=null
- BRANCH
- if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ
- arg0: GET_VAR 'val tmp_5: kotlin.Any? declared in <root>.test' type=kotlin.Any? origin=null
- arg1: CONST Null type=kotlin.Nothing? value=null
- then: CONST Int type=kotlin.Int value=42
- BRANCH
- if: CONST Boolean type=kotlin.Boolean value=true
- then: GET_VAR 'val tmp_5: kotlin.Any? declared in <root>.test' type=kotlin.Any? origin=null
- VAR name:x5 type:kotlin.Any [val]
- BLOCK type=kotlin.Any origin=ELVIS
- VAR IR_TEMPORARY_VARIABLE name:tmp_7 type:kotlin.Any? [val]
- CALL 'public final fun magic <T> (): T of <root>.magic declared in <root>' type=kotlin.Any? origin=null
- <T>: kotlin.Any?
- WHEN type=kotlin.Any origin=null
- BRANCH
- if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ
- arg0: GET_VAR 'val tmp_7: kotlin.Any? declared in <root>.test' type=kotlin.Any? origin=null
- arg1: CONST Null type=kotlin.Nothing? value=null
- then: CONST Int type=kotlin.Int value=42
- BRANCH
- if: CONST Boolean type=kotlin.Boolean value=true
- then: GET_VAR 'val tmp_7: kotlin.Any? declared in <root>.test' type=kotlin.Any? origin=null
- VAR name:x6 type:kotlin.Any [val]
- BLOCK type=kotlin.Any origin=ELVIS
- VAR IR_TEMPORARY_VARIABLE name:tmp_8 type:kotlin.Any? [val]
- BLOCK type=kotlin.Any? origin=ELVIS
- VAR IR_TEMPORARY_VARIABLE name:tmp_9 type:T of <root>.test [val]
- GET_VAR 'value: T of <root>.test declared in <root>.test' type=T of <root>.test origin=null
- WHEN type=kotlin.Any? origin=null
- BRANCH
- if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ
- arg0: GET_VAR 'val tmp_9: T of <root>.test declared in <root>.test' type=T of <root>.test origin=null
- arg1: CONST Null type=kotlin.Nothing? value=null
- then: CALL 'public final fun magic <T> (): T of <root>.magic declared in <root>' type=kotlin.Any? origin=null
- <T>: kotlin.Any?
- BRANCH
- if: CONST Boolean type=kotlin.Boolean value=true
- then: GET_VAR 'val tmp_9: T of <root>.test declared in <root>.test' type=T of <root>.test origin=null
- WHEN type=kotlin.Any origin=null
- BRANCH
- if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ
- arg0: GET_VAR 'val tmp_8: kotlin.Any? declared in <root>.test' type=kotlin.Any? origin=null
- arg1: CONST Null type=kotlin.Nothing? value=null
- then: CONST Int type=kotlin.Int value=42
- BRANCH
- if: CONST Boolean type=kotlin.Boolean value=true
- then: GET_VAR 'val tmp_8: kotlin.Any? declared in <root>.test' type=kotlin.Any? origin=null
- VAR name:x7 type:kotlin.Any [val]
- BLOCK type=kotlin.Any origin=ELVIS
- VAR IR_TEMPORARY_VARIABLE name:tmp_10 type:kotlin.Any? [val]
- BLOCK type=kotlin.Any? origin=ELVIS
- VAR IR_TEMPORARY_VARIABLE name:tmp_11 type:kotlin.Any? [val]
- CALL 'public final fun magic <T> (): T of <root>.magic declared in <root>' type=kotlin.Any? origin=null
- <T>: kotlin.Any?
- WHEN type=kotlin.Any? origin=null
- BRANCH
- if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ
- arg0: GET_VAR 'val tmp_11: kotlin.Any? declared in <root>.test' type=kotlin.Any? origin=null
- arg1: CONST Null type=kotlin.Nothing? value=null
- then: GET_VAR 'value: T of <root>.test declared in <root>.test' type=T of <root>.test origin=null
- BRANCH
- if: CONST Boolean type=kotlin.Boolean value=true
- then: GET_VAR 'val tmp_11: kotlin.Any? declared in <root>.test' type=kotlin.Any? origin=null
- WHEN type=kotlin.Any origin=null
- BRANCH
- if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ
- arg0: GET_VAR 'val tmp_10: kotlin.Any? declared in <root>.test' type=kotlin.Any? origin=null
- arg1: CONST Null type=kotlin.Nothing? value=null
- then: CONST Int type=kotlin.Int value=42
- BRANCH
- if: CONST Boolean type=kotlin.Boolean value=true
- then: GET_VAR 'val tmp_10: kotlin.Any? declared in <root>.test' type=kotlin.Any? origin=null
diff --git a/compiler/testData/ir/irText/expressions/kt30796.fir.kt.txt b/compiler/testData/ir/irText/expressions/kt30796.fir.kt.txt
deleted file mode 100644
index 4fa7d10..0000000
--- a/compiler/testData/ir/irText/expressions/kt30796.fir.kt.txt
+++ /dev/null
@@ -1,85 +0,0 @@
-fun <T : Any?> magic(): T {
- throw Exception()
-}
-
-fun <T : Any?> test(value: T, value2: T) {
- val x1: Any = { // BLOCK
- val tmp_0: T = value
- when {
- EQEQ(arg0 = tmp_0, arg1 = null) -> 42
- else -> tmp_0
- }
- }
- val x2: Any = { // BLOCK
- val tmp_1: T = value
- when {
- EQEQ(arg0 = tmp_1, arg1 = null) -> { // BLOCK
- val tmp_2: T = value2
- when {
- EQEQ(arg0 = tmp_2, arg1 = null) -> 42
- else -> tmp_2
- }
- }
- else -> tmp_1
- }
- }
- val x3: Any = { // BLOCK
- val tmp_3: Any? = { // BLOCK
- val tmp_4: T = value
- when {
- EQEQ(arg0 = tmp_4, arg1 = null) -> value2
- else -> tmp_4
- }
- }
- when {
- EQEQ(arg0 = tmp_3, arg1 = null) -> 42
- else -> tmp_3
- }
- }
- val x4: Any = { // BLOCK
- val tmp_5: Any? = { // BLOCK
- val tmp_6: T = value
- when {
- EQEQ(arg0 = tmp_6, arg1 = null) -> value2
- else -> tmp_6
- }
- }
- when {
- EQEQ(arg0 = tmp_5, arg1 = null) -> 42
- else -> tmp_5
- }
- }
- val x5: Any = { // BLOCK
- val tmp_7: Any? = magic<Any?>()
- when {
- EQEQ(arg0 = tmp_7, arg1 = null) -> 42
- else -> tmp_7
- }
- }
- val x6: Any = { // BLOCK
- val tmp_8: Any? = { // BLOCK
- val tmp_9: T = value
- when {
- EQEQ(arg0 = tmp_9, arg1 = null) -> magic<Any?>()
- else -> tmp_9
- }
- }
- when {
- EQEQ(arg0 = tmp_8, arg1 = null) -> 42
- else -> tmp_8
- }
- }
- val x7: Any = { // BLOCK
- val tmp_10: Any? = { // BLOCK
- val tmp_11: Any? = magic<Any?>()
- when {
- EQEQ(arg0 = tmp_11, arg1 = null) -> value
- else -> tmp_11
- }
- }
- when {
- EQEQ(arg0 = tmp_10, arg1 = null) -> 42
- else -> tmp_10
- }
- }
-}
diff --git a/compiler/testData/ir/irText/expressions/kt30796.kt b/compiler/testData/ir/irText/expressions/kt30796.kt
index bfb0346..cee6817 100644
--- a/compiler/testData/ir/irText/expressions/kt30796.kt
+++ b/compiler/testData/ir/irText/expressions/kt30796.kt
@@ -1,3 +1,4 @@
+// FIR_IDENTICAL
// IGNORE_BACKEND: JS_IR
// KT-61141: throws kotlin.Exception instead of java.lang.Exception
diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt
index 01db1a6..36db23a 100644
--- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt
+++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt
@@ -337,6 +337,7 @@
ProhibitReturningIncorrectNullabilityValuesFromSamConstructorLambdaOfJdkInterfaces(KOTLIN_2_1, kind = BUG_FIX), // KT-57014
ProhibitNothingAsCatchParameter(KOTLIN_2_1, kind = BUG_FIX), // KT-8322
NullableNothingInReifiedPosition(KOTLIN_2_1, kind = UNSTABLE_FEATURE), // KT-54227, KT-67675
+ ElvisInferenceImprovementsIn21(KOTLIN_2_1, kind = OTHER), // KT-71751
// It's not a fully blown LF, but mostly a way to manage potential unexpected semantic changes
// See the single usage at org.jetbrains.kotlin.fir.types.ConeTypeApproximator.fastPathSkipApproximation