FIR: coerce to Unit when a lambda has early returns

^KT-39075 Fixed
This commit is contained in:
Jinseong Jeon
2021-02-05 10:38:45 -08:00
committed by Dmitriy Novozhilov
parent 83e3201677
commit 37a702b962
3 changed files with 26 additions and 11 deletions
@@ -11,7 +11,7 @@ FILE: coercionToUnitWithEarlyReturn.kt
public final fun foo(x: R|() -> kotlin/Unit|): R|kotlin/Unit| { public final fun foo(x: R|() -> kotlin/Unit|): R|kotlin/Unit| {
} }
public final fun main(x: R|A?|): 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 () { when () {
==(R|<local>/x|?.{ $subj$.R|kotlin/Any.hashCode|() }, Int(0)) -> { ==(R|<local>/x|?.{ $subj$.R|kotlin/Any.hashCode|() }, Int(0)) -> {
^@l Unit ^@l Unit
@@ -21,5 +21,5 @@ FILE: coercionToUnitWithEarlyReturn.kt
^ R|<local>/x|?.{ $subj$.R|/A.unit|() } ^ R|<local>/x|?.{ $subj$.R|/A.unit|() }
} }
<Inapplicable(INAPPLICABLE): /foo>#(R|<local>/lambda|) R|/foo|(R|<local>/lambda|)
} }
@@ -14,6 +14,5 @@ fun main(x: A?) {
x?.unit() x?.unit()
} }
// lambda has a type (() -> Unit?) foo(lambda)
<!INAPPLICABLE_CANDIDATE{LT}!><!INAPPLICABLE_CANDIDATE{PSI}!>foo<!>(lambda)<!>
} }
@@ -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.buildErrorTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildImplicitTypeRef import org.jetbrains.kotlin.fir.types.builder.buildImplicitTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef 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.fir.visitors.*
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
@@ -780,13 +781,28 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor
dataFlowAnalyzer, dataFlowAnalyzer,
) )
lambda.transformSingle(writer, expectedTypeRef.coneTypeSafe<ConeKotlinType>()?.toExpectedType()) lambda.transformSingle(writer, expectedTypeRef.coneTypeSafe<ConeKotlinType>()?.toExpectedType())
val returnTypes = dataFlowAnalyzer.returnExpressionsOfAnonymousFunction(lambda)
.mapNotNull { (it as? FirExpression)?.resultType?.coneType } val returnStatements = dataFlowAnalyzer.returnExpressionsOfAnonymousFunction(lambda)
lambda.replaceReturnTypeRef( val returnExpressionsExceptLast =
lambda.returnTypeRef.resolvedTypeFromPrototype( if (returnStatements.size > 1)
inferenceComponents.ctx.commonSuperTypeOrNull(returnTypes) ?: session.builtinTypes.unitType.type 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.replaceTypeRef(
lambda.constructFunctionalTypeRef( lambda.constructFunctionalTypeRef(
isSuspend = expectedTypeRef.coneTypeSafe<ConeKotlinType>()?.isSuspendFunctionType(session) == true isSuspend = expectedTypeRef.coneTypeSafe<ConeKotlinType>()?.isSuspendFunctionType(session) == true