FIR: Do not assume indexed assignment is always Unit-coerced
There are three cases where FirUnitExpression might be:
- Empty lambda (not our case)
- Explicit return with no expression (run { return@run })
- Indexed assignment `a[0] = 1`
In the second case, we've got a convention that this should
unconditionally turn the lambda's return type to Unit (see KT-67867)
So, we coerce-to-unit any last expression independently of its type.
But we shouldn't do the same for the third case
^KT-74474 Fixed
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt
index 6f5a443..8bbda90 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt
@@ -59,8 +59,8 @@
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
-fun FirAnonymousFunction.shouldReturnUnit(returnStatements: Collection<FirExpression>): Boolean =
- isLambda && returnStatements.any { it is FirUnitExpression }
+fun FirAnonymousFunction.lambdaWithExplicitEmptyReturns(returnStatements: Collection<FirExpression>): Boolean =
+ isLambda && returnStatements.any { it is FirUnitExpression && it.source?.kind == KtFakeSourceElementKind.ImplicitUnit.Return }
/**
* Infers the return type of an anonymous function from return expressions in its body.
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt
index 8b2b797..893b87e 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt
@@ -18,7 +18,7 @@
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedReferenceError
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeLambdaArgumentConstraintPosition
import org.jetbrains.kotlin.fir.resolve.isImplicitUnitForEmptyLambda
-import org.jetbrains.kotlin.fir.resolve.shouldReturnUnit
+import org.jetbrains.kotlin.fir.resolve.lambdaWithExplicitEmptyReturns
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype
import org.jetbrains.kotlin.fir.types.*
@@ -234,7 +234,8 @@
val returnTypeRef = lambda.anonymousFunction.returnTypeRef.let {
it as? FirResolvedTypeRef ?: it.resolvedTypeFromPrototype(substituteAlreadyFixedVariables(lambda.returnType))
}
- val isUnitLambda = returnTypeRef.coneType.isUnitOrFlexibleUnit || lambda.anonymousFunction.shouldReturnUnit(returnArguments)
+ val isLastExpressionCoercedToUnit =
+ returnTypeRef.coneType.isUnitOrFlexibleUnit || lambda.anonymousFunction.lambdaWithExplicitEmptyReturns(returnArguments)
for (atom in returnAtoms) {
val expression = atom.expression
@@ -250,7 +251,7 @@
// }
// Things get even weirder if T has an upper bound incompatible with Unit.
val haveSubsystem = c.addSubsystemFromAtom(atom)
- if (isLastExpression && isUnitLambda) {
+ if (isLastExpression && isLastExpressionCoercedToUnit) {
// That "if" is necessary because otherwise we would force a lambda return type
// to be inferred from completed last expression.
// See `test1` at testData/diagnostics/tests/inference/coercionToUnit/afterBareReturn.kt
diff --git a/compiler/testData/diagnostics/tests/inference/coercionToUnit/lastStatementInNonUnitLambda.fir.kt b/compiler/testData/diagnostics/tests/inference/coercionToUnit/lastStatementInNonUnitLambda.fir.kt
index 911fd62..4e3bfcb 100644
--- a/compiler/testData/diagnostics/tests/inference/coercionToUnit/lastStatementInNonUnitLambda.fir.kt
+++ b/compiler/testData/diagnostics/tests/inference/coercionToUnit/lastStatementInNonUnitLambda.fir.kt
@@ -3,7 +3,7 @@
fun foo(x: () -> String) {}
fun main(a: Array<String>) {
- foo <!ARGUMENT_TYPE_MISMATCH!>{
- a[0] = ""
- }<!>
+ foo {
+ <!RETURN_TYPE_MISMATCH!>a[0] = ""<!>
+ }
}
diff --git a/compiler/testData/diagnostics/tests/inference/coercionToUnit/lastStatementInNonUnitLambdaWithRegularReturn.fir.kt b/compiler/testData/diagnostics/tests/inference/coercionToUnit/lastStatementInNonUnitLambdaWithRegularReturn.fir.kt
index 8e334f4..36837bd 100644
--- a/compiler/testData/diagnostics/tests/inference/coercionToUnit/lastStatementInNonUnitLambdaWithRegularReturn.fir.kt
+++ b/compiler/testData/diagnostics/tests/inference/coercionToUnit/lastStatementInNonUnitLambdaWithRegularReturn.fir.kt
@@ -1,4 +1,4 @@
-// RUN_PIPELINE_TILL: BACKEND
+// RUN_PIPELINE_TILL: FRONTEND
// ISSUE: KT-74478
fun foo(x: () -> String) {}
@@ -6,6 +6,6 @@
fun bar(a: MutableList<String>, b: Boolean) {
foo {
if (b) return@foo ""
- a[0] = "" // should be an error here
+ <!RETURN_TYPE_MISMATCH!>a[0] = ""<!> // should be an error here
}
}
diff --git a/compiler/testData/diagnostics/tests/inference/coercionToUnit/lastStatementInNonUnitLambdaWithRegularReturn.kt b/compiler/testData/diagnostics/tests/inference/coercionToUnit/lastStatementInNonUnitLambdaWithRegularReturn.kt
index 0900340..8c1e32b 100644
--- a/compiler/testData/diagnostics/tests/inference/coercionToUnit/lastStatementInNonUnitLambdaWithRegularReturn.kt
+++ b/compiler/testData/diagnostics/tests/inference/coercionToUnit/lastStatementInNonUnitLambdaWithRegularReturn.kt
@@ -1,4 +1,4 @@
-// RUN_PIPELINE_TILL: BACKEND
+// RUN_PIPELINE_TILL: FRONTEND
// ISSUE: KT-74478
fun foo(x: () -> String) {}