diff --git a/.idea/artifacts/kotlin_dom_api_compat_1_8_255_SNAPSHOT.xml b/.idea/artifacts/kotlin_dom_api_compat_1_8_255_SNAPSHOT.xml
new file mode 100644
index 00000000000..76279d18930
--- /dev/null
+++ b/.idea/artifacts/kotlin_dom_api_compat_1_8_255_SNAPSHOT.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/libraries/kotlin-dom-api-compat/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/compiler/fir/analysis-tests/testData/resolve/expresssions/invoke/threeReceiversCorrect.fir.txt b/compiler/fir/analysis-tests/testData/resolve/expresssions/invoke/threeReceiversCorrect.fir.txt
index d2e9e007b0d..04bff26682d 100644
--- a/compiler/fir/analysis-tests/testData/resolve/expresssions/invoke/threeReceiversCorrect.fir.txt
+++ b/compiler/fir/analysis-tests/testData/resolve/expresssions/invoke/threeReceiversCorrect.fir.txt
@@ -31,9 +31,9 @@ FILE: threeReceiversCorrect.kt
(this@R|special/anonymous|, R|/b|).R|/A.foo|.R|SubstitutionOverride|(R|/c|)
}
)
- ^ R|/with|(R|/b|, = with@fun R|B|.(): R|kotlin/Unit| {
+ R|/with|(R|/b|, = with@fun R|B|.(): R|kotlin/Unit| {
(this@R|special/anonymous|, this@R|special/anonymous|).R|/A.foo|.R|SubstitutionOverride|(R|/c|)
- ^ R|/with|(R|/c|, = with@fun R|C|.(): R|kotlin/Unit| {
+ R|/with|(R|/c|, = with@fun R|C|.(): R|kotlin/Unit| {
(this@R|special/anonymous|, this@R|special/anonymous|).R|/A.foo|.R|SubstitutionOverride|(this@R|special/anonymous|)
}
)
diff --git a/compiler/fir/analysis-tests/testData/resolve/inference/coercionToUnitWithEarlyReturn.fir.txt b/compiler/fir/analysis-tests/testData/resolve/inference/coercionToUnitWithEarlyReturn.fir.txt
index aa2e21b484d..17f9b7a04b7 100644
--- a/compiler/fir/analysis-tests/testData/resolve/inference/coercionToUnitWithEarlyReturn.fir.txt
+++ b/compiler/fir/analysis-tests/testData/resolve/inference/coercionToUnitWithEarlyReturn.fir.txt
@@ -18,7 +18,7 @@ FILE: coercionToUnitWithEarlyReturn.kt
}
}
- ^ R|/x|?.{ $subj$.R|/A.unit|() }
+ R|/x|?.{ $subj$.R|/A.unit|() }
}
R|/foo|(R|/lambda|)
diff --git a/compiler/fir/analysis-tests/testData/resolve/smartcasts/receivers/implicitReceivers.fir.txt b/compiler/fir/analysis-tests/testData/resolve/smartcasts/receivers/implicitReceivers.fir.txt
index e6e8b67dbf5..a4008d940d7 100644
--- a/compiler/fir/analysis-tests/testData/resolve/smartcasts/receivers/implicitReceivers.fir.txt
+++ b/compiler/fir/analysis-tests/testData/resolve/smartcasts/receivers/implicitReceivers.fir.txt
@@ -51,7 +51,7 @@ FILE: implicitReceivers.kt
}
public final fun test_3(a: R|kotlin/Any|, b: R|kotlin/Any|, c: R|kotlin/Any|): R|kotlin/Unit| {
R|kotlin/with|(R|/a|, = wa@fun R|kotlin/Any|.(): R|kotlin/Unit| {
- ^ R|kotlin/with|(R|/b|, = wb@fun R|kotlin/Any|.(): R|kotlin/Unit| {
+ R|kotlin/with|(R|/b|, = wb@fun R|kotlin/Any|.(): R|kotlin/Unit| {
R|kotlin/with|(R|/c|, = wc@fun R|kotlin/Any|.(): R|kotlin/Unit| {
(this@R|special/anonymous| as R|A|)
this@R|special/anonymous|.R|/A.foo|()
diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/diagnostics/functionReturnTypeMismatchChecker/basic.fir.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/diagnostics/functionReturnTypeMismatchChecker/basic.fir.txt
index 1e9a515cd7b..a3b5ff1ffe0 100644
--- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/diagnostics/functionReturnTypeMismatchChecker/basic.fir.txt
+++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/diagnostics/functionReturnTypeMismatchChecker/basic.fir.txt
@@ -48,7 +48,7 @@ FILE: basic.kt
foo@fun (): R|kotlin/Unit| {
^@foo Unit
}
- .R|/foo|(foo@fun R|A|.(): R|kotlin/Unit| {
+ .R|/foo#|(foo@fun R|A|.(): R|kotlin/Unit| {
this@R|special/anonymous|.R|/A.bar|()
^@foo Int(10)
}
diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/diagnostics/functionReturnTypeMismatchChecker/basic.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/diagnostics/functionReturnTypeMismatchChecker/basic.kt
index 2ce0b1ab128..06f8aea7865 100644
--- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/diagnostics/functionReturnTypeMismatchChecker/basic.kt
+++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/diagnostics/functionReturnTypeMismatchChecker/basic.kt
@@ -37,7 +37,7 @@ fun errorWithLambda(): String {
return@foo
} foo {
bar()
- return@foo 10
+ return@foo 10
}
return ""
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 95b125106bd..6db901cc88b 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
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.fir.diagnostics.ConeStubDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.builder.*
+import org.jetbrains.kotlin.fir.expressions.impl.FirUnitExpression
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.references.FirSuperReference
@@ -48,6 +49,7 @@ import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
+import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds
@@ -63,6 +65,39 @@ import kotlin.contracts.contract
fun List.toTypeProjections(): Array =
asReversed().flatMap { it.typeArgumentList.typeArguments.map { typeArgument -> typeArgument.toConeTypeProjection() } }.toTypedArray()
+fun FirAnonymousFunction.shouldReturnUnit(returnStatements: Collection): Boolean =
+ isLambda && returnStatements.any { it is FirUnitExpression }
+
+fun FirAnonymousFunction.isExplicitlySuspend(session: FirSession): Boolean =
+ typeRef.coneTypeSafe()?.isSuspendFunctionType(session) == true
+
+fun FirAnonymousFunction.addReturnToLastStatementIfNeeded() {
+ // If this lambda's resolved, expected return type is Unit, we don't need an explicit return statement.
+ // During conversion (to backend IR), the last expression will be coerced to Unit if needed.
+ if (returnTypeRef.isUnit) return
+
+ val body = this.body ?: return
+ val lastStatement = body.statements.lastOrNull() as? FirExpression ?: return
+ if (lastStatement is FirReturnExpression) return
+
+ val returnType = (body.typeRef as? FirResolvedTypeRef) ?: return
+ if (returnType.isNothing || returnType.isUnit) return
+
+ val returnTarget = FirFunctionTarget(null, isLambda = isLambda).also { it.bind(this) }
+ val returnExpression = buildReturnExpression {
+ source = lastStatement.source?.fakeElement(KtFakeSourceElementKind.ImplicitReturn.FromLastStatement)
+ result = lastStatement
+ target = returnTarget
+ }
+ body.transformStatements(
+ object : FirTransformer() {
+ override fun transformElement(element: E, data: Nothing?): E =
+ @Suppress("UNCHECKED_CAST")
+ if (element == lastStatement) returnExpression as E else element
+ }, null
+ )
+}
+
fun FirFunction.constructFunctionalType(isSuspend: Boolean = false): ConeLookupTagBasedType {
val receiverTypeRef = when (this) {
is FirSimpleFunction -> receiverParameter
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt
index 5567953cbf2..48af548d5ff 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt
@@ -167,18 +167,22 @@ class CandidateFactory private constructor(
}
}
-fun PostponedArgumentsAnalyzerContext.addSubsystemFromExpression(statement: FirStatement) {
- when (statement) {
+fun PostponedArgumentsAnalyzerContext.addSubsystemFromExpression(statement: FirStatement): Boolean {
+ return when (statement) {
is FirQualifiedAccessExpression,
is FirWhenExpression,
is FirTryExpression,
is FirCheckNotNullCall,
- is FirElvisExpression
- -> (statement as FirResolvable).candidate()?.let { addOtherSystem(it.system.asReadOnlyStorage()) }
+ is FirElvisExpression -> {
+ val candidate = (statement as FirResolvable).candidate() ?: return false
+ addOtherSystem(candidate.system.asReadOnlyStorage())
+ true
+ }
is FirSafeCallExpression -> addSubsystemFromExpression(statement.selector)
is FirWrappedArgumentExpression -> addSubsystemFromExpression(statement.expression)
- is FirBlock -> statement.returnExpressions().forEach { addSubsystemFromExpression(it) }
+ is FirBlock -> statement.returnExpressions().any { addSubsystemFromExpression(it) }
+ else -> false
}
}
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 367ccfa2dec..567dca4bd72 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
@@ -14,12 +14,12 @@ import org.jetbrains.kotlin.fir.references.builder.buildErrorNamedReference
import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedReferenceError
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeLambdaArgumentConstraintPosition
+import org.jetbrains.kotlin.fir.resolve.shouldReturnUnit
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.ConeTypeVariable
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
-import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.isMarkedNullable
import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzerContext
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
@@ -170,23 +170,29 @@ class PostponedArgumentsAnalyzer(
) {
val (returnArguments, inferenceSession) = results
- returnArguments.forEach { c.addSubsystemFromExpression(it) }
val checkerSink: CheckerSink = CheckerSinkImpl(candidate)
val builder = c.getBuilder()
val lastExpression = lambda.atom.body?.statements?.lastOrNull() as? FirExpression
var hasExpressionInReturnArguments = false
- // No constraint for return expressions of lambda if it has Unit return type.
- val lambdaReturnType = lambda.returnType.let(substitute).takeUnless { it.isUnitOrFlexibleUnit }
+ val lambdaReturnType = lambda.returnType.let(substitute)
returnArguments.forEach {
+ val haveSubsystem = c.addSubsystemFromExpression(it)
if (it !is FirExpression) return@forEach
+ // If the lambda returns Unit, the last expression is not returned and should not be constrained.
+ // TODO (KT-55837) questionable moment inherited from FE1.0 (the `haveSubsystem` case):
+ // fun foo(): T
+ // run {
+ // if (p) return@run
+ // foo() // T = Unit, even though there is no implicit return
+ // }
+ // Things get even weirder if T has an upper bound incompatible with Unit.
+ if (it == lastExpression && !haveSubsystem &&
+ (expectedReturnType?.isUnitOrFlexibleUnit == true || lambda.atom.shouldReturnUnit(returnArguments))
+ ) return@forEach
+
hasExpressionInReturnArguments = true
- // If it is the last expression, and the expected type is Unit, that expression will be coerced to Unit.
- // If the last expression is of Unit type, of course it's not coercion-to-Unit case.
- val lastExpressionCoercedToUnit =
- it == lastExpression && expectedReturnType?.isUnitOrFlexibleUnit == true && !it.typeRef.coneType.isUnitOrFlexibleUnit
- // No constraint for the last expression of lambda if it will be coerced to Unit.
- if (!lastExpressionCoercedToUnit && !builder.hasContradiction) {
+ if (!builder.hasContradiction) {
candidate.resolveArgumentExpression(
builder,
it,
@@ -200,7 +206,7 @@ class PostponedArgumentsAnalyzer(
}
}
- if (!hasExpressionInReturnArguments && lambdaReturnType != null) {
+ if (!hasExpressionInReturnArguments && !lambdaReturnType.isUnitOrFlexibleUnit) {
builder.addSubtypeConstraint(
components.session.builtinTypes.unitType.type,
lambdaReturnType,
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt
index 4e646cc172e..443af40bfc3 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt
@@ -42,7 +42,6 @@ import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildStarProjection
import org.jetbrains.kotlin.fir.types.builder.buildTypeProjectionWithVariance
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
-import org.jetbrains.kotlin.fir.types.impl.FirImplicitUnitTypeRef
import org.jetbrains.kotlin.fir.visitors.FirDefaultTransformer
import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.fir.visitors.transformSingle
@@ -575,12 +574,10 @@ class FirCallCompletionResultsWriterTransformer(
anonymousFunction: FirAnonymousFunction,
data: ExpectedArgumentType?,
): FirStatement {
- // This case is not common, and happens when there are anonymous function arguments that aren't mapped to any parameter in the call
- // So, we don't run body resolve transformation for them, thus there's no control flow info either
- // Control flow info is necessary prerequisite because we collect return expressions in that function
- //
- // Example: second lambda in the call like list.filter({}, {})
- val returnExpressionsOfAnonymousFunction = dataFlowAnalyzer.returnExpressionsOfAnonymousFunctionOrNull(anonymousFunction)
+ // The case where we can't find any return expressions not common, and happens when there are anonymous function arguments
+ // that aren't mapped to any parameter in the call. So, we don't run body resolve transformation for them, thus there's
+ // no control flow info either. Example: second lambda in the call like list.filter({}, {})
+ val returnStatements = dataFlowAnalyzer.returnExpressionsOfAnonymousFunctionOrNull(anonymousFunction)
?: return transformImplicitTypeRefInAnonymousFunction(anonymousFunction)
val expectedType = data?.getExpectedType(anonymousFunction)?.let { expectedArgumentType ->
@@ -609,7 +606,7 @@ class FirCallCompletionResultsWriterTransformer(
}
}
- var needUpdateLambdaType = false
+ var needUpdateLambdaType = anonymousFunction.typeRef is FirImplicitTypeRef
val receiverParameter = anonymousFunction.receiverParameter
val initialReceiverType = receiverParameter?.typeRef?.coneTypeSafe()
@@ -619,64 +616,39 @@ class FirCallCompletionResultsWriterTransformer(
needUpdateLambdaType = true
}
- val initialType = anonymousFunction.returnTypeRef.coneTypeSafe()
+ val initialReturnType = anonymousFunction.returnTypeRef.coneTypeSafe()
+ val expectedReturnType = initialReturnType?.let { finalSubstitutor.substituteOrSelf(it) }
+ ?: expectedType?.returnType(session) as? ConeClassLikeType
+ ?: (data as? ExpectedArgumentType.ArgumentsMap)?.lambdasReturnTypes?.get(anonymousFunction)
- val finalType = if (anonymousFunction.isLambda) {
- expectedType?.returnType(session) as? ConeClassLikeType
- ?: (data as? ExpectedArgumentType.ArgumentsMap)?.lambdasReturnTypes?.get(anonymousFunction)
- ?: initialType?.let(finalSubstitutor::substituteOrSelf)
- } else {
- initialType?.let(finalSubstitutor::substituteOrSelf)
- ?: expectedType?.returnType(session) as? ConeClassLikeType
- ?: (data as? ExpectedArgumentType.ArgumentsMap)?.lambdasReturnTypes?.get(anonymousFunction)
+ val newData = expectedReturnType?.toExpectedType()
+ val result = transformElement(anonymousFunction, newData)
+ for (expression in returnStatements) {
+ expression.transformSingle(this, newData)
}
- if (finalType != null) {
- if (anonymousFunction.returnTypeRef !is FirImplicitUnitTypeRef) {
- val resultType = anonymousFunction.returnTypeRef.withReplacedConeType(finalType)
- anonymousFunction.replaceReturnTypeRef(resultType)
- }
+ // Prefer the expected type over the inferred one - the latter is a subtype of the former in valid code,
+ // and there will be ARGUMENT_TYPE_MISMATCH errors on the lambda's return expressions in invalid code.
+ val resultReturnType = expectedReturnType
+ ?: session.typeContext.commonSuperTypeOrNull(returnStatements.mapNotNull { (it as? FirExpression)?.resultType?.coneType })
+ ?: session.builtinTypes.unitType.type
+
+ if (initialReturnType != resultReturnType) {
+ result.replaceReturnTypeRef(result.returnTypeRef.resolvedTypeFromPrototype(resultReturnType))
+ session.lookupTracker?.recordTypeResolveAsLookup(result.returnTypeRef, result.source, context.file.source)
needUpdateLambdaType = true
}
if (needUpdateLambdaType) {
- val resolvedTypeRef =
- anonymousFunction.constructFunctionalTypeRef(
- isSuspend = expectedType?.isSuspendFunctionType(session) == true ||
- (expectedType == null && anonymousFunction.isSuspendFunctionType())
- )
- anonymousFunction.replaceTypeRef(resolvedTypeRef)
- session.lookupTracker?.recordTypeResolveAsLookup(resolvedTypeRef, anonymousFunction.source, context.file.source)
+ val isSuspend = expectedType?.isSuspendFunctionType(session) ?: result.isExplicitlySuspend(session)
+ result.replaceTypeRef(result.constructFunctionalTypeRef(isSuspend))
+ session.lookupTracker?.recordTypeResolveAsLookup(result.typeRef, result.source, context.file.source)
}
-
- val result = transformElement(anonymousFunction, null)
-
- for (expression in returnExpressionsOfAnonymousFunction) {
- expression.transform(this, finalType?.toExpectedType())
- }
-
- if (result.returnTypeRef.coneTypeSafe() != null) {
- val lastExpressionType =
- (returnExpressionsOfAnonymousFunction.lastOrNull() as? FirExpression)
- ?.typeRef?.coneTypeSafe()
-
- val newReturnTypeRef = result.returnTypeRef.withReplacedConeType(lastExpressionType)
- result.replaceReturnTypeRef(newReturnTypeRef)
- val resolvedTypeRef =
- result.constructFunctionalTypeRef(isSuspend = expectedType?.isSuspendFunctionType(session) == true)
- result.replaceTypeRef(resolvedTypeRef)
- session.lookupTracker?.let {
- it.recordTypeResolveAsLookup(newReturnTypeRef, anonymousFunction.source, context.file.source)
- it.recordTypeResolveAsLookup(resolvedTypeRef, anonymousFunction.source, context.file.source)
- }
- }
-
+ // Have to delay this until the type is written to avoid adding a return if the type is Unit.
+ result.addReturnToLastStatementIfNeeded()
return result
}
- private fun FirAnonymousFunction.isSuspendFunctionType() =
- typeRef.coneTypeSafe()?.isSuspendFunctionType(session) == true
-
private fun transformImplicitTypeRefInAnonymousFunction(
anonymousFunction: FirAnonymousFunction
): FirStatement {
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt
index 69ebd386f84..a8815fe7552 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt
@@ -22,8 +22,6 @@ import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.expressions.*
-import org.jetbrains.kotlin.fir.expressions.builder.buildReturnExpression
-import org.jetbrains.kotlin.fir.expressions.builder.buildUnitExpression
import org.jetbrains.kotlin.fir.expressions.impl.FirLazyBlock
import org.jetbrains.kotlin.fir.references.FirResolvedErrorReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
@@ -36,7 +34,6 @@ import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeLocalVariableNoTypeOrIni
import org.jetbrains.kotlin.fir.resolve.inference.FirStubTypeTransformer
import org.jetbrains.kotlin.fir.resolve.inference.ResolvedLambdaAtom
import org.jetbrains.kotlin.fir.resolve.inference.extractLambdaInfoFromFunctionalType
-import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.createTypeSubstitutorByTypeConstructor
import org.jetbrains.kotlin.fir.resolve.transformers.FirCallCompletionResultsWriterTransformer
import org.jetbrains.kotlin.fir.resolve.transformers.FirStatusResolver
@@ -49,8 +46,6 @@ 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.FirDefaultTransformer
import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.fir.visitors.transformSingle
import org.jetbrains.kotlin.name.Name
@@ -590,34 +585,6 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve
return result
}
- private fun transformAnonymousFunctionWithLambdaResolution(
- anonymousFunction: FirAnonymousFunction, lambdaResolution: ResolutionMode.LambdaResolution
- ): FirAnonymousFunction {
- val expectedReturnType =
- lambdaResolution.expectedReturnTypeRef ?: anonymousFunction.returnTypeRef.takeUnless { it is FirImplicitTypeRef }
- val result = transformFunction(anonymousFunction, withExpectedType(expectedReturnType)) as FirAnonymousFunction
- val body = result.body
- if (result.returnTypeRef is FirImplicitTypeRef && body != null) {
- // TODO: This part seems unnecessary because for lambdas in dependent context will be completed and their type
- // should be replaced there properly
- val returnType =
- dataFlowAnalyzer.returnExpressionsOfAnonymousFunction(result)
- .firstNotNullOfOrNull { (it as? FirExpression)?.resultType?.coneTypeSafe() }
- val resolutionMode = if (returnType != null) {
- withExpectedType(returnType)
- } else {
- withExpectedType(buildErrorTypeRef {
- diagnostic =
- ConeSimpleDiagnostic("Unresolved lambda return type", DiagnosticKind.InferenceError)
- })
- }
-
- result.transformReturnTypeRef(transformer, resolutionMode)
- }
-
- return result
- }
-
override fun transformSimpleFunction(
simpleFunction: FirSimpleFunction,
data: ResolutionMode
@@ -785,39 +752,40 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve
return when (data) {
is ResolutionMode.ContextDependent, is ResolutionMode.ContextDependentDelegate -> {
context.withAnonymousFunction(anonymousFunction, components, data) {
- anonymousFunction.addReturn()
+ anonymousFunction
}
}
is ResolutionMode.LambdaResolution -> {
- context.withAnonymousFunction(anonymousFunction, components, data) {
- withFullBodyResolve {
- transformAnonymousFunctionWithLambdaResolution(anonymousFunction, data).addReturn()
- }
- }
+ val expectedReturnTypeRef =
+ data.expectedReturnTypeRef ?: anonymousFunction.returnTypeRef.takeUnless { it is FirImplicitTypeRef }
+ transformAnonymousFunctionBody(anonymousFunction, expectedReturnTypeRef, data)
}
- is ResolutionMode.WithExpectedType,
- is ResolutionMode.ContextIndependent,
- is ResolutionMode.ReceiverResolution,
- is ResolutionMode.WithSuggestedType -> {
- val expectedTypeRef = when (data) {
- is ResolutionMode.WithExpectedType -> {
- data.expectedTypeRef
- }
- is ResolutionMode.WithSuggestedType -> {
- data.suggestedTypeRef
- }
- else -> {
- buildImplicitTypeRef()
- }
- }
- transformAnonymousFunctionWithExpectedType(anonymousFunction, expectedTypeRef, data)
- }
- is ResolutionMode.WithStatus, is ResolutionMode.WithExpectedTypeFromCast -> {
+ is ResolutionMode.WithExpectedType ->
+ transformAnonymousFunctionWithExpectedType(anonymousFunction, data.expectedTypeRef, data)
+ is ResolutionMode.WithSuggestedType ->
+ transformAnonymousFunctionWithExpectedType(anonymousFunction, data.suggestedTypeRef, data)
+ is ResolutionMode.ContextIndependent, is ResolutionMode.ReceiverResolution ->
+ transformAnonymousFunctionWithExpectedType(anonymousFunction, buildImplicitTypeRef(), data)
+ is ResolutionMode.WithStatus, is ResolutionMode.WithExpectedTypeFromCast ->
throw AssertionError("Should not be here in WithStatus/WithExpectedTypeFromCast mode")
- }
}
}
+
+ private fun transformAnonymousFunctionBody(
+ anonymousFunction: FirAnonymousFunction,
+ expectedReturnTypeRef: FirTypeRef?,
+ data: ResolutionMode
+ ): FirAnonymousFunction {
+ // `transformFunction` will replace both `typeRef` and `returnTypeRef`, so make sure to keep the former.
+ val lambdaType = anonymousFunction.typeRef
+ return context.withAnonymousFunction(anonymousFunction, components, data) {
+ withFullBodyResolve {
+ transformFunction(anonymousFunction, withExpectedType(expectedReturnTypeRef)) as FirAnonymousFunction
+ }
+ }.apply { replaceTypeRef(lambdaType) }
+ }
+
private fun transformAnonymousFunctionWithExpectedType(
anonymousFunction: FirAnonymousFunction,
expectedTypeRef: FirTypeRef,
@@ -829,12 +797,10 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve
)
}
var lambda = anonymousFunction
- val initialReturnTypeRef = lambda.returnTypeRef
val valueParameters = when {
resolvedLambdaAtom != null -> obtainValueParametersFromResolvedLambdaAtom(resolvedLambdaAtom, lambda)
else -> obtainValueParametersFromExpectedType(expectedTypeRef.coneTypeSafe(), lambda)
}
- val returnTypeRefFromResolvedAtom = resolvedLambdaAtom?.returnType?.let { lambda.returnTypeRef.resolvedTypeFromPrototype(it) }
lambda = buildAnonymousFunctionCopy(lambda) {
receiverParameter = lambda.receiverParameter?.takeIf { it.typeRef !is FirImplicitTypeRef }
?: resolvedLambdaAtom?.receiver?.let { coneKotlinType ->
@@ -857,71 +823,38 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve
this.valueParameters.clear()
this.valueParameters.addAll(valueParameters)
- returnTypeRef = (lambda.returnTypeRef as? FirResolvedTypeRef)
- ?: returnTypeRefFromResolvedAtom
- ?: lambda.returnTypeRef
- }
- lambda = lambda.transformValueParameters(ImplicitToErrorTypeTransformer, null)
- val bodyExpectedType = returnTypeRefFromResolvedAtom ?: expectedTypeRef
- context.withAnonymousFunction(lambda, components, data) {
- withFullBodyResolve {
- lambda = transformFunction(lambda, withExpectedType(bodyExpectedType)) as FirAnonymousFunction
- }
- }
- // To separate function and separate commit
- val writer = FirCallCompletionResultsWriterTransformer(
- session,
- ConeSubstitutor.Empty,
- components.returnTypeCalculator,
- session.typeApproximator,
- dataFlowAnalyzer,
- components.integerLiteralAndOperatorApproximationTransformer,
- components.context
- )
- lambda.transformSingle(writer, expectedTypeRef.coneTypeSafe()?.toExpectedType())
+ }.transformValueParameters(ImplicitToErrorTypeTransformer, null)
- 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 initialReturnTypeRef = lambda.returnTypeRef as? FirResolvedTypeRef
+ val expectedReturnTypeRef = initialReturnTypeRef
+ ?: resolvedLambdaAtom?.returnType?.let { lambda.returnTypeRef.resolvedTypeFromPrototype(it) }
+ lambda = transformAnonymousFunctionBody(lambda, expectedReturnTypeRef ?: components.noExpectedType, data)
+
+ if (initialReturnTypeRef == null) {
+ lambda.replaceReturnTypeRef(lambda.computeReturnTypeRef(expectedReturnTypeRef))
+ session.lookupTracker?.recordTypeResolveAsLookup(lambda.returnTypeRef, lambda.source, context.file.source)
}
- val returnType = when {
- initialReturnTypeRef is FirResolvedTypeRef -> {
- initialReturnTypeRef.coneType
- }
- implicitReturns.isNotEmpty() || (lambda.returnType?.isUnit == true && lambda.isLambda) -> {
- // i.e., early return, e.g., l@{ ... return@l ... }
- // Note that the last statement will be coerced to Unit if needed.
- // also we don't coerce to Unit anonymous functions, only lambdas
- session.builtinTypes.unitType.type
- }
- else -> {
- // Otherwise, compute the common super type of all possible return expressions
- session.typeContext.commonSuperTypeOrNull(
- returnStatements.mapNotNull { (it as? FirExpression)?.resultType?.coneType }
- ) ?: session.builtinTypes.unitType.type
- }
- }
- if (lambda.returnTypeRef !is FirImplicitUnitTypeRef) {
- lambda.replaceReturnTypeRef(
- initialReturnTypeRef.resolvedTypeFromPrototype(returnType).also {
- session.lookupTracker?.recordTypeResolveAsLookup(it, lambda.source, components.file.source)
- }
- )
- }
- lambda.replaceTypeRef(
- lambda.constructFunctionalTypeRef(
- isSuspend = expectedTypeRef.coneTypeSafe()?.isSuspendFunctionType(session) == true
- ).also {
- session.lookupTracker?.recordTypeResolveAsLookup(it, lambda.source, components.file.source)
- }
- )
- return lambda.addReturn()
+ lambda.replaceTypeRef(lambda.constructFunctionalTypeRef(resolvedLambdaAtom?.isSuspend == true))
+ session.lookupTracker?.recordTypeResolveAsLookup(lambda.typeRef, lambda.source, context.file.source)
+ lambda.addReturnToLastStatementIfNeeded()
+ return lambda
+ }
+
+ private fun FirAnonymousFunction.computeReturnTypeRef(expected: FirResolvedTypeRef?): FirResolvedTypeRef {
+ // Any lambda expression assigned to `(...) -> Unit` returns Unit
+ if (isLambda && expected?.type?.isUnit == true) return expected
+ // `lambda@ { return@lambda }` always returns Unit
+ val returnStatements = dataFlowAnalyzer.returnExpressionsOfAnonymousFunction(this)
+ if (shouldReturnUnit(returnStatements)) return session.builtinTypes.unitType
+ // Here is a questionable moment where we could prefer the expected type over an inferred one.
+ // In correct code this doesn't matter, as all return expression types should be subtypes of the expected type.
+ // In incorrect code, this would change diagnostics: we can get errors either on the entire lambda, or only on its
+ // return statements. The former kind of makes more sense, but the latter is more readable.
+ val inferredFromReturnStatements =
+ session.typeContext.commonSuperTypeOrNull(returnStatements.mapNotNull { (it as? FirExpression)?.resultType?.coneType })
+ return inferredFromReturnStatements?.let { returnTypeRef.resolvedTypeFromPrototype(it) }
+ ?: session.builtinTypes.unitType // Empty lambda returns Unit
}
private fun obtainValueParametersFromResolvedLambdaAtom(
@@ -982,47 +915,6 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve
}
}
- private fun FirAnonymousFunction.addReturn(): FirAnonymousFunction {
- // If this lambda's resolved, expected return type is Unit, we don't need an explicit return statement.
- // During conversion (to backend IR), the last expression will be coerced to Unit if needed.
- // As per KT-41005, we should not force coercion to Unit for nullable return type, though.
- if (returnTypeRef.isUnit && body?.typeRef?.isMarkedNullable == false) {
- return this
- }
- val lastStatement = body?.statements?.lastOrNull()
- val returnType = (body?.typeRef as? FirResolvedTypeRef) ?: return this
- val returnNothing = returnType.isNothing || returnType.isUnit
- if (lastStatement is FirExpression && !returnNothing) {
- body?.transformChildren(
- object : FirDefaultTransformer() {
- override fun transformElement(element: E, data: FirExpression): E {
- if (element == lastStatement) {
- val returnExpression = buildReturnExpression {
- source = element.source?.fakeElement(KtFakeSourceElementKind.ImplicitReturn.FromLastStatement)
- result = lastStatement
- target = FirFunctionTarget(null, isLambda = this@addReturn.isLambda).also {
- it.bind(this@addReturn)
- }
- }
- @Suppress("UNCHECKED_CAST")
- return (returnExpression as E)
- }
- return element
- }
-
- override fun transformReturnExpression(
- returnExpression: FirReturnExpression,
- data: FirExpression
- ): FirStatement {
- return returnExpression
- }
- },
- buildUnitExpression()
- )
- }
- return this
- }
-
override fun transformBackingField(
backingField: FirBackingField,
data: ResolutionMode,
diff --git a/compiler/testData/diagnostics/tests/controlStructures/ifToAnyDiscriminatingUsages.fir.kt b/compiler/testData/diagnostics/tests/controlStructures/ifToAnyDiscriminatingUsages.fir.kt
index e1ada18d66e..1eb65c77223 100644
--- a/compiler/testData/diagnostics/tests/controlStructures/ifToAnyDiscriminatingUsages.fir.kt
+++ b/compiler/testData/diagnostics/tests/controlStructures/ifToAnyDiscriminatingUsages.fir.kt
@@ -27,7 +27,7 @@ fun testResultOfAnonFun2() =
fun testReturnFromAnonFun() =
run(fun () {
- return if (true) 42 else println()
+ return if (true) 42 else println()
})
fun testReturn1() =
diff --git a/compiler/testData/diagnostics/tests/inference/coercionToUnit/afterBareReturn.fir.kt b/compiler/testData/diagnostics/tests/inference/coercionToUnit/afterBareReturn.fir.kt
index 974ddc2e21f..2b90328b8ab 100644
--- a/compiler/testData/diagnostics/tests/inference/coercionToUnit/afterBareReturn.fir.kt
+++ b/compiler/testData/diagnostics/tests/inference/coercionToUnit/afterBareReturn.fir.kt
@@ -34,7 +34,7 @@ fun test4() = run {
fun main() {
// all ok
- expectUnit(test1())
+ expectUnit(test1())
expectUnit(test2())
expectUnit(test3())
expectUnit(test4())
diff --git a/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.fir.kt b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.fir.kt
index 78caf68098d..db821b0f188 100644
--- a/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.fir.kt
+++ b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.fir.kt
@@ -139,8 +139,8 @@ fun main() {
* K <: (C) -> Unit -> TypeVariable(_RP1) >: C
* K == (B) -> Unit -> TypeVariable(_RP1) == B
*/
- val x17: (C) -> Unit = selectB(id { it }, id { it }, id<(B) -> Unit> { x -> x })
- val x18: (C) -> Unit = select(id { it }, { it }, id<(B) -> Unit> { x -> x })
+ val x17: (C) -> Unit = selectB(id { it }, id { it }, id<(B) -> Unit> { x -> x })
+ val x18: (C) -> Unit = select(id { it }, { it }, id<(B) -> Unit> { x -> x })
// Resolution of extension/non-extension functions combination
val x19: String.() -> Unit = select(")!>id { this }, ")!>id(fun(x: String) {}))
diff --git a/compiler/testData/diagnostics/tests/regressions/resolveCollectionLiteralInsideLambda.fir.kt b/compiler/testData/diagnostics/tests/regressions/resolveCollectionLiteralInsideLambda.fir.kt
index 320ab3f96a4..13064873389 100644
--- a/compiler/testData/diagnostics/tests/regressions/resolveCollectionLiteralInsideLambda.fir.kt
+++ b/compiler/testData/diagnostics/tests/regressions/resolveCollectionLiteralInsideLambda.fir.kt
@@ -3,5 +3,5 @@
fun foo(l: () -> Unit) {}
fun bar(l: () -> String) {}
-val a = foo { [] }
-val b = bar { [] }
+val a = foo { [] }
+val b = bar { [] }
diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/suspendFunctions.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/suspendFunctions.fir.kt
index 7d960f86839..1520a1d44a4 100644
--- a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/suspendFunctions.fir.kt
+++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/suspendFunctions.fir.kt
@@ -19,7 +19,7 @@ fun main() {
val x1: suspend (Int) -> Unit = takeSuspend(")!>id { it }, ")!>{ x -> x })
// Here, the error should be
- val x2: (Int) -> Unit = takeSuspend(")!>id { it }, ")!>{ x -> x })
- val x3: suspend (Int) -> Unit = takeSimpleFunction(")!>id { it }, ")!>{ x -> x })
+ val x2: (Int) -> Unit = takeSuspend(")!>id { it }, ")!>{ x -> x })
+ val x3: suspend (Int) -> Unit = takeSimpleFunction(")!>id { it }, ")!>{ x -> x })
val x4: (Int) -> Unit = takeSimpleFunction(id Unit> {}, ")!>{})
}
diff --git a/compiler/testData/ir/irText/declarations/contextReceivers/functionalType.fir.ir.txt b/compiler/testData/ir/irText/declarations/contextReceivers/functionalType.fir.ir.txt
index ae2b6af8cdf..ea1bd615300 100644
--- a/compiler/testData/ir/irText/declarations/contextReceivers/functionalType.fir.ir.txt
+++ b/compiler/testData/ir/irText/declarations/contextReceivers/functionalType.fir.ir.txt
@@ -172,78 +172,77 @@ FILE fqName: fileName:/functionalType.kt
FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> ($receiver:.C) returnType:kotlin.Unit
$receiver: VALUE_PARAMETER name:$this$with type:.C
BLOCK_BODY
- RETURN type=kotlin.Nothing from='local final fun (): kotlin.Unit declared in .test'
- CALL 'public final fun with (receiver: T of kotlin.StandardKt.with, block: @[ExtensionFunctionType] kotlin.Function1): R of kotlin.StandardKt.with [inline] declared in kotlin.StandardKt' type=kotlin.Unit origin=null
- : .R
- : kotlin.Unit
- receiver: CONSTRUCTOR_CALL 'public constructor () [primary] declared in .R' type=.R origin=null
- block: FUN_EXPR type=@[ExtensionFunctionType] kotlin.Function1<.R, kotlin.Unit> origin=LAMBDA
- FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> ($receiver:.R) returnType:kotlin.Unit
- $receiver: VALUE_PARAMETER name:$this$with type:.R
- BLOCK_BODY
- CALL 'public final fun f1 (_context_receiver_0: .C, g: @[ExtensionFunctionType] kotlin.Function3<.C, .R, .Param, kotlin.Unit>): kotlin.Unit declared in ' type=kotlin.Unit origin=null
- $receiver: GET_VAR '$this$with: .R declared in .test..' type=.R origin=null
- _context_receiver_0: GET_VAR '$this$with: .C declared in .test.' type=.C origin=null
- g: GET_VAR 'val lf1: @[ExtensionFunctionType] kotlin.Function3<.C, .R, .Param, kotlin.Unit> [val] declared in .test' type=@[ExtensionFunctionType] kotlin.Function3<.C, .R, .Param, kotlin.Unit> origin=null
- CALL 'public final fun f1 (_context_receiver_0: .C, g: @[ExtensionFunctionType] kotlin.Function3<.C, .R, .Param, kotlin.Unit>): kotlin.Unit declared in ' type=kotlin.Unit origin=null
- $receiver: GET_VAR '$this$with: .R declared in .test..' type=.R origin=null
- _context_receiver_0: GET_VAR '$this$with: .C declared in .test.' type=.C origin=null
- g: FUN_EXPR type=@[ExtensionFunctionType] kotlin.Function3<.C, .R, .Param, kotlin.Unit> origin=LAMBDA
- FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> ($receiver:.R, _context_receiver_0:.C, :.Param) returnType:kotlin.Unit
- contextReceiverParametersCount: 1
- $receiver: VALUE_PARAMETER name:$this$f1 type:.R
- VALUE_PARAMETER name:_context_receiver_0 index:0 type:.C
- VALUE_PARAMETER UNDERSCORE_PARAMETER name: index:1 type:.Param
- BLOCK_BODY
- TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit
- CALL 'public final fun (): kotlin.Int declared in .R' type=kotlin.Int origin=GET_PROPERTY
- $this: GET_VAR '$this$f1: .R declared in .test...' type=.R origin=null
- TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit
- CALL 'public final fun (): kotlin.Int declared in .C' type=kotlin.Int origin=GET_PROPERTY
- $this: GET_VAR '$this$with: .C declared in .test.' type=.C origin=null
- CALL 'public final fun f2 (_context_receiver_0: .C, g: kotlin.Function2<.C, .Param, kotlin.Unit>): kotlin.Unit declared in ' type=kotlin.Unit origin=null
- _context_receiver_0: GET_VAR '$this$with: .C declared in .test.' type=.C origin=null
- g: GET_VAR 'val lf2: kotlin.Function2<.C, .Param, kotlin.Unit> [val] declared in .test' type=kotlin.Function2<.C, .Param, kotlin.Unit> origin=null
- CALL 'public final fun f2 (_context_receiver_0: .C, g: kotlin.Function2<.C, .Param, kotlin.Unit>): kotlin.Unit declared in ' type=kotlin.Unit origin=null
- _context_receiver_0: GET_VAR '$this$with: .C declared in .test.' type=.C origin=null
- g: FUN_EXPR type=kotlin.Function2<.C, .Param, kotlin.Unit> origin=LAMBDA
- FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (_context_receiver_0:.C, :.Param) returnType:kotlin.Unit
- contextReceiverParametersCount: 1
- VALUE_PARAMETER name:_context_receiver_0 index:0 type:.C
- VALUE_PARAMETER UNDERSCORE_PARAMETER name: index:1 type:.Param
- BLOCK_BODY
- TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit
- CALL 'public final fun (): kotlin.Int declared in .C' type=kotlin.Int origin=GET_PROPERTY
- $this: GET_VAR '$this$with: .C declared in .test.' type=.C origin=null
- CALL 'public final fun f3 (_context_receiver_0: .C, g: @[ExtensionFunctionType] kotlin.Function2<.C, .R, kotlin.Unit>): kotlin.Unit declared in ' type=kotlin.Unit origin=null
- $receiver: GET_VAR '$this$with: .R declared in .test..' type=.R origin=null
- _context_receiver_0: GET_VAR '$this$with: .C declared in .test.' type=.C origin=null
- g: GET_VAR 'val lf3: @[ExtensionFunctionType] kotlin.Function2<.C, .R, kotlin.Unit> [val] declared in .test' type=@[ExtensionFunctionType] kotlin.Function2<.C, .R, kotlin.Unit> origin=null
- CALL 'public final fun f3 (_context_receiver_0: .C, g: @[ExtensionFunctionType] kotlin.Function2<.C, .R, kotlin.Unit>): kotlin.Unit declared in ' type=kotlin.Unit origin=null
- $receiver: GET_VAR '$this$with: .R declared in .test..' type=.R origin=null
- _context_receiver_0: GET_VAR '$this$with: .C declared in .test.' type=.C origin=null
- g: FUN_EXPR type=@[ExtensionFunctionType] kotlin.Function2<.C, .R, kotlin.Unit> origin=LAMBDA
- FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> ($receiver:.R, _context_receiver_0:.C) returnType:kotlin.Unit
- contextReceiverParametersCount: 1
- $receiver: VALUE_PARAMETER name:$this$f3 type:.R
- VALUE_PARAMETER name:_context_receiver_0 index:0 type:.C
- BLOCK_BODY
- TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit
- CALL 'public final fun (): kotlin.Int declared in .R' type=kotlin.Int origin=GET_PROPERTY
- $this: GET_VAR '$this$f3: .R declared in .test...' type=.R origin=null
- TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit
- CALL 'public final fun (): kotlin.Int declared in .C' type=kotlin.Int origin=GET_PROPERTY
- $this: GET_VAR '$this$with: .C declared in .test.' type=.C origin=null
- CALL 'public final fun f4 (_context_receiver_0: .C, g: kotlin.Function1<.C, kotlin.Unit>): kotlin.Unit declared in ' type=kotlin.Unit origin=null
- _context_receiver_0: GET_VAR '$this$with: .C declared in .test.' type=.C origin=null
- g: GET_VAR 'val lf4: kotlin.Function1<.C, kotlin.Unit> [val] declared in .test' type=kotlin.Function1<.C, kotlin.Unit> origin=null
- CALL 'public final fun f4 (_context_receiver_0: .C, g: kotlin.Function1<.C, kotlin.Unit>): kotlin.Unit declared in