From b0b7f39c75e9e4685125e44011585eb544a50ce1 Mon Sep 17 00:00:00 2001 From: "Denis.Zharkov" Date: Mon, 18 Jan 2021 15:13:37 +0300 Subject: [PATCH] FIR: Do not use return statement for type of a block expression Type of a block is a kind of irrelevant for lambdas: their type is much more complicated and defined via FirDataFlowAnalyzer#returnExpressionsOfAnonymousFunction at at FirCallCompleter.LambdaAnalyzerImpl#analyzeAndGetLambdaReturnArguments --- .../arguments/integerLiteralTypes.fir.txt | 4 +- .../resolve/cfg/lambdaAsReturnOfLambda.dot | 55 ++++++++---------- .../resolve/expresssions/access.fir.txt | 2 +- .../resolve/inference/lambdasReturns.fir.txt | 2 +- .../samConstructors/genericSam.fir.txt | 4 +- .../genericSamInferenceFromExpectType.fir.txt | 10 ++-- .../resolve/samConversions/genericSam.fir.txt | 4 +- .../resolve/samConversions/kotlinSam.fir.txt | 4 +- .../notSamBecauseOfSupertype.fir.txt | 2 +- .../callableReferences/implicitTypes.fir.txt | 2 +- .../resolveWithStdlib/j+k/MyMap.fir.txt | 2 +- .../fir/resolve/dfa/FirDataFlowAnalyzer.kt | 3 + .../dfa/cfg/ControlFlowGraphBuilder.kt | 5 ++ ...rCallCompletionResultsWriterTransformer.kt | 57 +++++++++++++------ .../body/resolve/BodyResolveUtils.kt | 5 +- .../FirDeclarationsResolveTransformer.kt | 24 +++++++- .../sam/samConversionToGeneric.fir.kt.txt | 4 +- .../sam/samConversionToGeneric.fir.txt | 12 ++-- .../stringVsTXArray.fir.kt.txt | 3 +- .../stringVsTXArray.fir.txt | 6 +- 20 files changed, 127 insertions(+), 83 deletions(-) diff --git a/compiler/fir/analysis-tests/testData/resolve/arguments/integerLiteralTypes.fir.txt b/compiler/fir/analysis-tests/testData/resolve/arguments/integerLiteralTypes.fir.txt index e2910694a10..7464269e561 100644 --- a/compiler/fir/analysis-tests/testData/resolve/arguments/integerLiteralTypes.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/arguments/integerLiteralTypes.fir.txt @@ -30,11 +30,11 @@ FILE: integerLiteralTypes.kt } )) R|/takeByte|(R|kotlin/run|( = run@fun (): R|kotlin/Byte| { - ^ Int(1) + ^ Byte(1) } )) R|/takeLong|(R|kotlin/run|( = run@fun (): R|kotlin/Long| { - ^ Int(1) + ^ Long(1) } )) } diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/lambdaAsReturnOfLambda.dot b/compiler/fir/analysis-tests/testData/resolve/cfg/lambdaAsReturnOfLambda.dot index ba85a36cc4b..5b40cbc0a17 100644 --- a/compiler/fir/analysis-tests/testData/resolve/cfg/lambdaAsReturnOfLambda.dot +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/lambdaAsReturnOfLambda.dot @@ -44,21 +44,14 @@ digraph lambdaAsReturnOfLambda_kt { 7 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } 18 [label="Postponed exit from lambda"]; - subgraph cluster_6 { - color=blue - 19 [label="Enter block"]; - 20 [label="Exit block"]; - } - 21 [label="Function call: R|/run| kotlin/Unit|>(...)"]; - 22 [label="Exit property" style="filled" fillcolor=red]; + 19 [label="Function call: R|/run| kotlin/Unit|>(...)"]; + 20 [label="Exit property" style="filled" fillcolor=red]; } 16 -> {17}; 17 -> {18 0}; 17 -> {0} [style=dashed]; 18 -> {19}; 19 -> {20}; - 20 -> {21}; - 21 -> {22}; 0 -> {1}; 1 -> {2}; 2 -> {3 8}; @@ -74,39 +67,39 @@ digraph lambdaAsReturnOfLambda_kt { 11 -> {12}; 12 -> {13}; - subgraph cluster_7 { + subgraph cluster_6 { color=red - 23 [label="Enter function bar" style="filled" fillcolor=red]; - subgraph cluster_8 { + 21 [label="Enter function bar" style="filled" fillcolor=red]; + subgraph cluster_7 { color=blue - 24 [label="Enter block"]; - 25 [label="Exit block"]; + 22 [label="Enter block"]; + 23 [label="Exit block"]; } - 26 [label="Exit function bar" style="filled" fillcolor=red]; + 24 [label="Exit function bar" style="filled" fillcolor=red]; } + 21 -> {22}; + 22 -> {23}; 23 -> {24}; - 24 -> {25}; - 25 -> {26}; - subgraph cluster_9 { + subgraph cluster_8 { color=red - 27 [label="Enter function run" style="filled" fillcolor=red]; - subgraph cluster_10 { + 25 [label="Enter function run" style="filled" fillcolor=red]; + subgraph cluster_9 { color=blue - 28 [label="Enter block"]; - 29 [label="Function call: R|/block|.R|SubstitutionOverride|()"]; - 30 [label="Jump: ^run R|/block|.R|SubstitutionOverride|()"]; - 31 [label="Stub" style="filled" fillcolor=gray]; - 32 [label="Exit block" style="filled" fillcolor=gray]; + 26 [label="Enter block"]; + 27 [label="Function call: R|/block|.R|SubstitutionOverride|()"]; + 28 [label="Jump: ^run R|/block|.R|SubstitutionOverride|()"]; + 29 [label="Stub" style="filled" fillcolor=gray]; + 30 [label="Exit block" style="filled" fillcolor=gray]; } - 33 [label="Exit function run" style="filled" fillcolor=red]; + 31 [label="Exit function run" style="filled" fillcolor=red]; } + 25 -> {26}; + 26 -> {27}; 27 -> {28}; - 28 -> {29}; - 29 -> {30}; - 30 -> {33}; + 28 -> {31}; + 28 -> {29} [style=dotted]; + 29 -> {30} [style=dotted]; 30 -> {31} [style=dotted]; - 31 -> {32} [style=dotted]; - 32 -> {33} [style=dotted]; } diff --git a/compiler/fir/analysis-tests/testData/resolve/expresssions/access.fir.txt b/compiler/fir/analysis-tests/testData/resolve/expresssions/access.fir.txt index e29e4aab883..5b203daae18 100644 --- a/compiler/fir/analysis-tests/testData/resolve/expresssions/access.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/expresssions/access.fir.txt @@ -36,7 +36,7 @@ FILE: access.kt ^plus String() } - public final fun R|Foo|.check(): R|ERROR CLASS: Ambiguity: plus, [kotlin/Int.plus, kotlin/Int.plus, kotlin/Int.plus, kotlin/Int.plus, kotlin/Int.plus, kotlin/Int.plus]| { + public final fun R|Foo|.check(): { ^check this@R|/Bar.check|.R|/Foo.abc|().#(this@R|/Bar|.R|/Bar.bar|()) } diff --git a/compiler/fir/analysis-tests/testData/resolve/inference/lambdasReturns.fir.txt b/compiler/fir/analysis-tests/testData/resolve/inference/lambdasReturns.fir.txt index 41ca1d4fa2d..4f178aecddb 100644 --- a/compiler/fir/analysis-tests/testData/resolve/inference/lambdasReturns.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/inference/lambdasReturns.fir.txt @@ -7,7 +7,7 @@ FILE: lambdasReturns.kt } public final fun foo(x: R|kotlin/String?|): R|kotlin/Unit| { lval r: R|kotlin/Int| = R|/myRun|( = myRun@fun (): R|kotlin/Int| { - lval y: R|kotlin/String| = R|/x| ?: ^@myRun R?C|/materialize|() + lval y: R|kotlin/String| = R|/x| ?: ^@myRun R|/materialize|() ^ R|/y|.R|kotlin/String.length| } ) diff --git a/compiler/fir/analysis-tests/testData/resolve/samConstructors/genericSam.fir.txt b/compiler/fir/analysis-tests/testData/resolve/samConstructors/genericSam.fir.txt index 727a3c0f878..3b88a0aa9ec 100644 --- a/compiler/fir/analysis-tests/testData/resolve/samConstructors/genericSam.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/samConstructors/genericSam.fir.txt @@ -1,10 +1,10 @@ FILE: main.kt public final fun main(): R|kotlin/Unit| { - R|/MyFunction|!|, R|ft!|>( = MyFunction@fun (x: R|ft!|): R|kotlin/String| { + R|/MyFunction|!|, R|ft!|>( = MyFunction@fun (x: R|ft!|): R|ft!| { ^ R|/x|.R|kotlin/Int.toInt|().R|kotlin/Any.toString|() } ) - R|/MyFunction|!|, R|ft!|>( = MyFunction@fun (x: R|kotlin/Int|): R|kotlin/String| { + R|/MyFunction|!|, R|ft!|>( = MyFunction@fun (x: R|kotlin/Int|): R|ft!| { ^ R|/x|.R|kotlin/Any.toString|() } ) diff --git a/compiler/fir/analysis-tests/testData/resolve/samConstructors/genericSamInferenceFromExpectType.fir.txt b/compiler/fir/analysis-tests/testData/resolve/samConstructors/genericSamInferenceFromExpectType.fir.txt index ef1f2089c4a..3feb87eebc0 100644 --- a/compiler/fir/analysis-tests/testData/resolve/samConstructors/genericSamInferenceFromExpectType.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/samConstructors/genericSamInferenceFromExpectType.fir.txt @@ -6,23 +6,23 @@ FILE: main.kt public final fun foo3(f: R|MyFunction|, x: R|X|): R|kotlin/Unit| { } public final fun main(): R|kotlin/Unit| { - R|/foo1|(R|/MyFunction|( = MyFunction@fun (x: R|ft!|): R|kotlin/String| { + R|/foo1|(R|/MyFunction|( = MyFunction@fun (x: R|ft!|): R|ft!| { ^ R|/x|.R|kotlin/Int.toInt|().R|kotlin/Any.toString|() } )) - R|/foo2|(R|/MyFunction|!|>( = MyFunction@fun (x: R|ft!|): R|kotlin/String| { + R|/foo2|(R|/MyFunction|!|>( = MyFunction@fun (x: R|ft!|): R|ft!| { ^ R|/x|.R|kotlin/Number.toInt|().R|kotlin/Any.toString|() } )) - #(R|/MyFunction|!|>( = MyFunction@fun (x: R|kotlin/Int|): R|kotlin/String| { + #(R|/MyFunction|!|>( = MyFunction@fun (x: R|kotlin/Int|): R|ft!| { ^ R|/x|.R|kotlin/Any.toString|() } )) - R|/foo3|!|>(R|/MyFunction|!|>( = MyFunction@fun (x: R|ft!|): R|kotlin/String| { + R|/foo3|!|>(R|/MyFunction|!|>( = MyFunction@fun (x: R|ft!|): R|ft!| { ^ R|/x|.R|kotlin/Int.plus|(Int(1)).R|kotlin/Any.toString|() } ), Int(1)) - R|/foo3|!|>(R|/MyFunction|!|>( = MyFunction@fun (x: R|kotlin/Number|): R|kotlin/String| { + R|/foo3|!|>(R|/MyFunction|!|>( = MyFunction@fun (x: R|kotlin/Number|): R|ft!| { ^ R|/x|.R|kotlin/Number.toInt|().R|kotlin/Any.toString|() } ), Int(2)) diff --git a/compiler/fir/analysis-tests/testData/resolve/samConversions/genericSam.fir.txt b/compiler/fir/analysis-tests/testData/resolve/samConversions/genericSam.fir.txt index 7df65bfaa24..65d8458fb85 100644 --- a/compiler/fir/analysis-tests/testData/resolve/samConversions/genericSam.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/samConversions/genericSam.fir.txt @@ -12,11 +12,11 @@ FILE: main.kt ^ R|/x|.R|kotlin/Any.toString|() } ) - Q|JavaUsage|.R|/JavaUsage.foo3|!|, R|ft!|>(foo3@fun (x: R|ft!|): R|kotlin/String| { + Q|JavaUsage|.R|/JavaUsage.foo3|!|, R|ft!|>(foo3@fun (x: R|ft!|): R|ft!| { ^ R|/x|.R|kotlin/Int.plus|(Int(1)).R|kotlin/Any.toString|() } , Int(1)) - Q|JavaUsage|.R|/JavaUsage.foo3|!|, R|ft!|>(foo3@fun (x: R|kotlin/Number|): R|kotlin/String| { + Q|JavaUsage|.R|/JavaUsage.foo3|!|, R|ft!|>(foo3@fun (x: R|kotlin/Number|): R|ft!| { ^ R|/x|.R|kotlin/Number.toInt|().R|kotlin/Any.toString|() } , Int(2)) diff --git a/compiler/fir/analysis-tests/testData/resolve/samConversions/kotlinSam.fir.txt b/compiler/fir/analysis-tests/testData/resolve/samConversions/kotlinSam.fir.txt index b2161f7954e..04e92984f55 100644 --- a/compiler/fir/analysis-tests/testData/resolve/samConversions/kotlinSam.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/samConversions/kotlinSam.fir.txt @@ -38,12 +38,12 @@ FILE: kotlinSam.kt } ) R|/foo1|(R|/f|) - #( = foo2@fun (x: R|kotlin/Nothing|): R|kotlin/Boolean| { + #( = foo2@fun (x: R|kotlin/Nothing|): R|kotlin/Nothing| { ^ CMP(>, R|/x|.#(Int(1))) } ) #(R|/f|) - #( = foo3@fun (x: R|kotlin/Nothing|): R|kotlin/Boolean| { + #( = foo3@fun (x: R|kotlin/Nothing|): R|kotlin/Nothing| { ^ CMP(>, R|/x|.#(Int(1))) } ) diff --git a/compiler/fir/analysis-tests/testData/resolve/samConversions/notSamBecauseOfSupertype.fir.txt b/compiler/fir/analysis-tests/testData/resolve/samConversions/notSamBecauseOfSupertype.fir.txt index d8145c5f91d..6bc71ae1046 100644 --- a/compiler/fir/analysis-tests/testData/resolve/samConversions/notSamBecauseOfSupertype.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/samConversions/notSamBecauseOfSupertype.fir.txt @@ -2,7 +2,7 @@ FILE: main.kt public final fun foo(m: R|MyRunnable|): R|kotlin/Unit| { } public final fun main(): R|kotlin/Unit| { - Q|JavaUsage|.#( = foo@fun (x: R|kotlin/Nothing|): R|kotlin/Boolean| { + Q|JavaUsage|.#( = foo@fun (x: R|kotlin/Nothing|): R|kotlin/Nothing| { ^ CMP(>, R|/x|.#(Int(1))) } ) diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/callableReferences/implicitTypes.fir.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/callableReferences/implicitTypes.fir.txt index 8ed1ac98753..a4aada100e8 100644 --- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/callableReferences/implicitTypes.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/callableReferences/implicitTypes.fir.txt @@ -11,6 +11,6 @@ FILE: implicitTypes.kt public final fun loop1(): R|(kotlin/Any?) -> kotlin/Nothing| { ^loop1 #(::#) } - public final fun loop2(): R|ERROR CLASS: cycle| { + public final fun loop2(): { ^loop2 R|/loop1|() } diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/j+k/MyMap.fir.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/j+k/MyMap.fir.txt index 5b1e9a42bf7..f378e0039a4 100644 --- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/j+k/MyMap.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/j+k/MyMap.fir.txt @@ -1,6 +1,6 @@ FILE: test.kt public final fun test(map: R|MyMap|): R|kotlin/Unit| { - lval result: R|ft<@FlexibleNullability kotlin/String, kotlin/String?>!| = R|/map|.R|kotlin/collections/getOrPut|!|, R|ft<@FlexibleNullability kotlin/String, kotlin/String?>!|>(String(key), = getOrPut@fun (): R|kotlin/String| { + lval result: R|ft<@FlexibleNullability kotlin/String, kotlin/String?>!| = R|/map|.R|kotlin/collections/getOrPut|!|, R|ft<@FlexibleNullability kotlin/String, kotlin/String?>!|>(String(key), = getOrPut@fun (): R|ft<@FlexibleNullability kotlin/String, kotlin/String?>!| { ^ String(value) } ) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt index b18d5c3f97f..58e5d30c9b8 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt @@ -143,6 +143,9 @@ abstract class FirDataFlowAnalyzer( return graphBuilder.returnExpressionsOfAnonymousFunction(function) } + fun isThereControlFlowInfoForAnonymousFunction(function: FirAnonymousFunction): Boolean = + graphBuilder.isThereControlFlowInfoForAnonymousFunction(function) + fun dropSubgraphFromCall(call: FirFunctionCall) { graphBuilder.dropSubgraphFromCall(call) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt index 864bd9c0f44..3a3e2ac5653 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt @@ -120,6 +120,11 @@ class ControlFlowGraphBuilder { // ----------------------------------- Public API ----------------------------------- + fun isThereControlFlowInfoForAnonymousFunction(function: FirAnonymousFunction): Boolean = + function.controlFlowGraphReference?.controlFlowGraph != null || + exitsOfAnonymousFunctions.containsKey(function.symbol) + + // This function might throw exception if !isThereControlFlowInfoForAnonymousFunction(function) fun returnExpressionsOfAnonymousFunction(function: FirAnonymousFunction): Collection { fun FirElement.extractArgument(): FirElement = when { this is FirReturnExpression && target.labeledElement.symbol == function.symbol -> result.extractArgument() 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 f2f93c05862..4ecdeb9444f 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 @@ -19,10 +19,7 @@ import org.jetbrains.kotlin.fir.resolve.calls.FirErrorReferenceWithCandidate import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate import org.jetbrains.kotlin.fir.resolve.calls.varargElementType import org.jetbrains.kotlin.fir.resolve.dfa.FirDataFlowAnalyzer -import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents -import org.jetbrains.kotlin.fir.resolve.inference.isBuiltinFunctionalType -import org.jetbrains.kotlin.fir.resolve.inference.isSuspendFunctionType -import org.jetbrains.kotlin.fir.resolve.inference.returnType +import org.jetbrains.kotlin.fir.resolve.inference.* import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirArrayOfCallTransformer import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.remapArgumentsWithVararg @@ -340,14 +337,22 @@ class FirCallCompletionResultsWriterTransformer( .let { finalSubstitutor.substituteOrSelf(it) } private fun Candidate.createArgumentsMapping(): ExpectedArgumentType? { - return argumentMapping?.map { (argument, valueParameter) -> + val lambdasReturnType = postponedAtoms.filterIsInstance().associate { + Pair(it.atom, finalSubstitutor.substituteOrSelf(substitutor.substituteOrSelf(it.returnType))) + } + + val arguments = argumentMapping?.map { (argument, valueParameter) -> val expectedType = if (valueParameter.isVararg) { valueParameter.returnTypeRef.substitute(this).varargElementType() } else { valueParameter.returnTypeRef.substitute(this) } argument.unwrapArgument() to expectedType - }?.toMap()?.toExpectedType() + }?.toMap() + + + if (lambdasReturnType.isEmpty() && arguments.isNullOrEmpty()) return null + return ExpectedArgumentType.ArgumentsMap(arguments ?: emptyMap(), lambdasReturnType) } override fun transformDelegatedConstructorCall( @@ -425,6 +430,13 @@ class FirCallCompletionResultsWriterTransformer( anonymousFunction: FirAnonymousFunction, data: ExpectedArgumentType?, ): CompositeTransformResult { + // 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({}, {}) + if (!dataFlowAnalyzer.isThereControlFlowInfoForAnonymousFunction(anonymousFunction)) return anonymousFunction.compose() + val expectedType = data?.getExpectedType(anonymousFunction)?.let { expectedArgumentType -> // From the argument mapping, the expected type of this anonymous function would be: when { @@ -455,10 +467,13 @@ class FirCallCompletionResultsWriterTransformer( needUpdateLambdaType = true } - val expectedReturnType = expectedType?.returnType(session) as? ConeClassLikeType val initialType = anonymousFunction.returnTypeRef.coneTypeSafe() - if (initialType != null) { - val finalType = expectedReturnType ?: finalSubstitutor.substituteOrNull(initialType) + val finalType = + expectedType?.returnType(session) as? ConeClassLikeType + ?: (data as? ExpectedArgumentType.ArgumentsMap)?.lambdasReturnTypes?.get(anonymousFunction) + ?: initialType?.let(finalSubstitutor::substituteOrSelf) + + if (finalType != null) { val resultType = anonymousFunction.returnTypeRef.withReplacedConeType(finalType) anonymousFunction.transformReturnTypeRef(StoreType, resultType) needUpdateLambdaType = true @@ -472,19 +487,24 @@ class FirCallCompletionResultsWriterTransformer( val result = transformElement(anonymousFunction, null) + val returnExpressionsOfAnonymousFunction: Collection = + dataFlowAnalyzer.returnExpressionsOfAnonymousFunction(anonymousFunction) + for (expression in returnExpressionsOfAnonymousFunction) { + expression.transform(this, finalType?.toExpectedType()) + } + val resultFunction = result.single if (resultFunction.returnTypeRef.coneTypeSafe() != null) { - val blockType = resultFunction.body?.typeRef?.coneTypeSafe() - resultFunction.replaceReturnTypeRef(resultFunction.returnTypeRef.withReplacedConeType(blockType)) + val lastExpressionType = + (returnExpressionsOfAnonymousFunction.lastOrNull() as? FirExpression) + ?.typeRef?.coneTypeSafe() + + resultFunction.replaceReturnTypeRef(resultFunction.returnTypeRef.withReplacedConeType(lastExpressionType)) resultFunction.replaceTypeRef( resultFunction.constructFunctionalTypeRef(isSuspend = expectedType?.isSuspendFunctionType(session) == true) ) } - for (expression in dataFlowAnalyzer.returnExpressionsOfAnonymousFunction(anonymousFunction)) { - expression.transform(this, null) - } - return result } @@ -622,7 +642,11 @@ class FirCallCompletionResultsWriterTransformer( } sealed class ExpectedArgumentType { - class ArgumentsMap(val map: Map) : ExpectedArgumentType() + class ArgumentsMap( + val map: Map, + val lambdasReturnTypes: Map + ) : ExpectedArgumentType() + class ExpectedType(val type: ConeKotlinType) : ExpectedArgumentType() object NoApproximation : ExpectedArgumentType() } @@ -633,7 +657,6 @@ private fun ExpectedArgumentType.getExpectedType(argument: FirExpression): ConeK ExpectedArgumentType.NoApproximation -> null } -private fun Map.toExpectedType(): ExpectedArgumentType = ExpectedArgumentType.ArgumentsMap(this) fun ConeKotlinType.toExpectedType(): ExpectedArgumentType = ExpectedArgumentType.ExpectedType(this) private fun FirExpression.unwrapArgument(): FirExpression = when (this) { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveUtils.kt index e4b3acf1a51..b2d3da6bd55 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveUtils.kt @@ -9,7 +9,9 @@ import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.declarations.FirValueParameter 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.FirBlock +import org.jetbrains.kotlin.fir.expressions.FirExpression +import org.jetbrains.kotlin.fir.expressions.FirNamedArgumentExpression import org.jetbrains.kotlin.fir.expressions.builder.buildVarargArgumentsExpression import org.jetbrains.kotlin.fir.resolve.firSymbolProvider import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol @@ -83,7 +85,6 @@ internal fun remapArgumentsWithVararg( fun FirBlock.writeResultType(session: FirSession) { val resultExpression = when (val statement = statements.lastOrNull()) { - is FirReturnExpression -> statement.result is FirExpression -> statement else -> null } 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 0d59e01234a..6fc9b4397a9 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 @@ -37,6 +37,7 @@ import org.jetbrains.kotlin.fir.types.builder.buildImplicitTypeRef import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef import org.jetbrains.kotlin.fir.visitors.* import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult import org.jetbrains.kotlin.utils.addToStdlib.safeAs open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransformer) : FirPartialBodyResolveTransformer(transformer) { @@ -442,7 +443,23 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor val body = result.body if (result.returnTypeRef is FirImplicitTypeRef && body != null) { - result.transformReturnTypeRef(transformer, withExpectedType(body.resultType)) + // 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) + .firstNotNullResult { (it as? FirExpression)?.resultType?.coneTypeSafe() } + + if (returnType != null) { + result.transformReturnTypeRef(transformer, withExpectedType(returnType)) + } else { + result.transformReturnTypeRef( + transformer, + withExpectedType(buildErrorTypeRef { + diagnostic = + ConeSimpleDiagnostic("Unresolved lambda return type", DiagnosticKind.InferenceError) + }) + ) + } } return result } @@ -506,11 +523,12 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor val body = result.body if (result.returnTypeRef is FirImplicitTypeRef) { val simpleFunction = function as? FirSimpleFunction - if (body != null) { + val returnExpression = (body?.statements?.single() as? FirReturnExpression)?.result + if (returnExpression != null && returnExpression.typeRef is FirResolvedTypeRef) { result.transformReturnTypeRef( transformer, withExpectedType( - body.resultType.approximatedIfNeededOrSelf( + returnExpression.resultType.approximatedIfNeededOrSelf( inferenceComponents.approximator, simpleFunction?.visibility, simpleFunction?.isInline == true ) ) diff --git a/compiler/testData/ir/irText/expressions/sam/samConversionToGeneric.fir.kt.txt b/compiler/testData/ir/irText/expressions/sam/samConversionToGeneric.fir.kt.txt index a81462a07eb..d4621f45e1a 100644 --- a/compiler/testData/ir/irText/expressions/sam/samConversionToGeneric.fir.kt.txt +++ b/compiler/testData/ir/irText/expressions/sam/samConversionToGeneric.fir.kt.txt @@ -6,14 +6,14 @@ fun test1(): J { } fun test2(): J { - return local fun (x: String): String { + return local fun (x: String): String? { return x } /*-> J */ } fun test3() { - return bar(j = local fun (x: String): String { + return bar(j = local fun (x: String): String? { return x } /*-> J? */) diff --git a/compiler/testData/ir/irText/expressions/sam/samConversionToGeneric.fir.txt b/compiler/testData/ir/irText/expressions/sam/samConversionToGeneric.fir.txt index f42df5fa5fd..00e1e8586da 100644 --- a/compiler/testData/ir/irText/expressions/sam/samConversionToGeneric.fir.txt +++ b/compiler/testData/ir/irText/expressions/sam/samConversionToGeneric.fir.txt @@ -13,11 +13,11 @@ FILE fqName: fileName:/samConversionToGeneric.kt BLOCK_BODY RETURN type=kotlin.Nothing from='public final fun test2 (): .J declared in ' TYPE_OP type=.J origin=SAM_CONVERSION typeOperand=.J - FUN_EXPR type=kotlin.Function1 origin=LAMBDA - FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (x:kotlin.String) returnType:kotlin.String + FUN_EXPR type=kotlin.Function1 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (x:kotlin.String) returnType:kotlin.String? VALUE_PARAMETER name:x index:0 type:kotlin.String BLOCK_BODY - RETURN type=kotlin.Nothing from='local final fun (x: kotlin.String): kotlin.String declared in .test2' + RETURN type=kotlin.Nothing from='local final fun (x: kotlin.String): kotlin.String? declared in .test2' GET_VAR 'x: kotlin.String declared in .test2.' type=kotlin.String origin=null FUN name:test3 visibility:public modality:FINAL <> () returnType:kotlin.Unit BLOCK_BODY @@ -25,11 +25,11 @@ FILE fqName: fileName:/samConversionToGeneric.kt CALL 'public open fun bar (j: .J.H.bar?>?): kotlin.Unit declared in .H' type=kotlin.Unit origin=null : kotlin.String? j: TYPE_OP type=.J.H.bar?>? origin=SAM_CONVERSION typeOperand=.J.H.bar?>? - FUN_EXPR type=kotlin.Function1 origin=LAMBDA - FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (x:kotlin.String) returnType:kotlin.String + FUN_EXPR type=kotlin.Function1 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (x:kotlin.String) returnType:kotlin.String? VALUE_PARAMETER name:x index:0 type:kotlin.String BLOCK_BODY - RETURN type=kotlin.Nothing from='local final fun (x: kotlin.String): kotlin.String declared in .test3' + RETURN type=kotlin.Nothing from='local final fun (x: kotlin.String): kotlin.String? declared in .test3' GET_VAR 'x: kotlin.String declared in .test3.' type=kotlin.String origin=null FUN name:test4 visibility:public modality:FINAL <> (a:kotlin.Any) returnType:kotlin.Unit VALUE_PARAMETER name:a index:0 type:kotlin.Any diff --git a/compiler/testData/ir/irText/types/nullChecks/nullCheckOnLambdaResult/stringVsTXArray.fir.kt.txt b/compiler/testData/ir/irText/types/nullChecks/nullCheckOnLambdaResult/stringVsTXArray.fir.kt.txt index ab0c1e2394d..a0f93bc18b4 100644 --- a/compiler/testData/ir/irText/types/nullChecks/nullCheckOnLambdaResult/stringVsTXArray.fir.kt.txt +++ b/compiler/testData/ir/irText/types/nullChecks/nullCheckOnLambdaResult/stringVsTXArray.fir.kt.txt @@ -3,8 +3,9 @@ fun useTX(x: T, fn: Function0): T { } fun testNoNullCheck(xs: Array) { - useTX(x = xs, fn = local fun (): String? { + useTX(x = xs, fn = local fun (): Serializable? { return string() } ) /*~> Unit */ } + diff --git a/compiler/testData/ir/irText/types/nullChecks/nullCheckOnLambdaResult/stringVsTXArray.fir.txt b/compiler/testData/ir/irText/types/nullChecks/nullCheckOnLambdaResult/stringVsTXArray.fir.txt index 09ed2f34a77..dda3f0be796 100644 --- a/compiler/testData/ir/irText/types/nullChecks/nullCheckOnLambdaResult/stringVsTXArray.fir.txt +++ b/compiler/testData/ir/irText/types/nullChecks/nullCheckOnLambdaResult/stringVsTXArray.fir.txt @@ -14,8 +14,8 @@ FILE fqName: fileName:/stringVsTXArray.kt CALL 'public final fun useTX (x: T of .useTX, fn: kotlin.Function0.useTX>): T of .useTX declared in ' type=java.io.Serializable? origin=null : java.io.Serializable? x: GET_VAR 'xs: kotlin.Array declared in .testNoNullCheck' type=kotlin.Array origin=null - fn: FUN_EXPR type=kotlin.Function0 origin=LAMBDA - FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:kotlin.String? + fn: FUN_EXPR type=kotlin.Function0 origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> () returnType:java.io.Serializable? BLOCK_BODY - RETURN type=kotlin.Nothing from='local final fun (): kotlin.String? declared in .testNoNullCheck' + RETURN type=kotlin.Nothing from='local final fun (): java.io.Serializable? declared in .testNoNullCheck' CALL 'public open fun string (): kotlin.String? declared in .J' type=kotlin.String? origin=null