FIR: coerce to Unit when a lambda has early returns
^KT-39075 Fixed
This commit is contained in:
committed by
Dmitriy Novozhilov
parent
83e3201677
commit
37a702b962
Vendored
+2
-2
@@ -11,7 +11,7 @@ FILE: coercionToUnitWithEarlyReturn.kt
|
||||
public final fun foo(x: R|() -> kotlin/Unit|): R|kotlin/Unit| {
|
||||
}
|
||||
public final fun main(x: R|A?|): R|kotlin/Unit| {
|
||||
lval lambda: R|() -> kotlin/Unit?| = l@fun <anonymous>(): R|kotlin/Unit?| {
|
||||
lval lambda: R|() -> kotlin/Unit| = l@fun <anonymous>(): R|kotlin/Unit| {
|
||||
when () {
|
||||
==(R|<local>/x|?.{ $subj$.R|kotlin/Any.hashCode|() }, Int(0)) -> {
|
||||
^@l Unit
|
||||
@@ -21,5 +21,5 @@ FILE: coercionToUnitWithEarlyReturn.kt
|
||||
^ R|<local>/x|?.{ $subj$.R|/A.unit|() }
|
||||
}
|
||||
|
||||
<Inapplicable(INAPPLICABLE): /foo>#(R|<local>/lambda|)
|
||||
R|/foo|(R|<local>/lambda|)
|
||||
}
|
||||
|
||||
+1
-2
@@ -14,6 +14,5 @@ fun main(x: A?) {
|
||||
x?.unit()
|
||||
}
|
||||
|
||||
// lambda has a type (() -> Unit?)
|
||||
<!INAPPLICABLE_CANDIDATE{LT}!><!INAPPLICABLE_CANDIDATE{PSI}!>foo<!>(lambda)<!>
|
||||
foo(lambda)
|
||||
}
|
||||
|
||||
+23
-7
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.builder.buildImplicitTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.impl.FirImplicitUnitTypeRef
|
||||
import org.jetbrains.kotlin.fir.visitors.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||
@@ -780,13 +781,28 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor
|
||||
dataFlowAnalyzer,
|
||||
)
|
||||
lambda.transformSingle(writer, expectedTypeRef.coneTypeSafe<ConeKotlinType>()?.toExpectedType())
|
||||
val returnTypes = dataFlowAnalyzer.returnExpressionsOfAnonymousFunction(lambda)
|
||||
.mapNotNull { (it as? FirExpression)?.resultType?.coneType }
|
||||
lambda.replaceReturnTypeRef(
|
||||
lambda.returnTypeRef.resolvedTypeFromPrototype(
|
||||
inferenceComponents.ctx.commonSuperTypeOrNull(returnTypes) ?: session.builtinTypes.unitType.type
|
||||
)
|
||||
)
|
||||
|
||||
val returnStatements = dataFlowAnalyzer.returnExpressionsOfAnonymousFunction(lambda)
|
||||
val returnExpressionsExceptLast =
|
||||
if (returnStatements.size > 1)
|
||||
returnStatements - lambda.body?.statements?.lastOrNull()
|
||||
else
|
||||
returnStatements
|
||||
val implicitReturns = returnExpressionsExceptLast.filter {
|
||||
(it as? FirExpression)?.typeRef is FirImplicitUnitTypeRef
|
||||
}
|
||||
val returnType =
|
||||
if (implicitReturns.isNotEmpty()) {
|
||||
// i.e., early return, e.g., l@{ ... return@l ... }
|
||||
// Note that the last statement will be coerced to Unit if needed.
|
||||
session.builtinTypes.unitType.type
|
||||
} else {
|
||||
// Otherwise, compute the common super type of all possible return expressions
|
||||
inferenceComponents.ctx.commonSuperTypeOrNull(
|
||||
returnStatements.mapNotNull { (it as? FirExpression)?.resultType?.coneType }
|
||||
) ?: session.builtinTypes.unitType.type
|
||||
}
|
||||
lambda.replaceReturnTypeRef(lambda.returnTypeRef.resolvedTypeFromPrototype(returnType))
|
||||
lambda.replaceTypeRef(
|
||||
lambda.constructFunctionalTypeRef(
|
||||
isSuspend = expectedTypeRef.coneTypeSafe<ConeKotlinType>()?.isSuspendFunctionType(session) == true
|
||||
|
||||
Reference in New Issue
Block a user