From 5f9876479eb5f390b9876c4331c50306c12b39d1 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Tue, 3 Mar 2020 14:52:31 +0300 Subject: [PATCH] [FIR] Add saving of local context in anonymous functions for further resolve #KT-37066 Fixed --- .../kotlin/fir/resolve/SessionHolder.kt | 15 +++++++- .../fir/resolve/inference/FirCallCompleter.kt | 4 +- .../resolve/inference/PostponedArguments.kt | 15 ++++---- .../FirAbstractBodyResolveTransformer.kt | 37 ++++++++++++------- .../body/resolve/FirBodyResolveTransformer.kt | 1 + .../FirDeclarationsResolveTransformer.kt | 1 + .../lambdaAsReturnStatementOfLambda.kt | 15 ++++++++ .../lambdaAsReturnStatementOfLambda.txt | 20 ++++++++++ .../resolve/lambdaArgInScopeFunction.kt | 4 +- .../resolve/lambdaArgInScopeFunction.txt | 16 ++++---- .../fir/FirDiagnosticsTestGenerated.java | 5 +++ ...DiagnosticsWithLightTreeTestGenerated.java | 5 +++ .../constraints/returnLambdaFromLambda.fir.kt | 4 +- 13 files changed, 106 insertions(+), 36 deletions(-) create mode 100644 compiler/fir/resolve/testData/resolve/inference/lambdaAsReturnStatementOfLambda.kt create mode 100644 compiler/fir/resolve/testData/resolve/inference/lambdaAsReturnStatementOfLambda.txt diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SessionHolder.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SessionHolder.kt index 173ebe84346..1002e2e17ba 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SessionHolder.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SessionHolder.kt @@ -9,6 +9,7 @@ import kotlinx.collections.immutable.PersistentList import org.jetbrains.kotlin.fir.FirCallResolver import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSymbolOwner +import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction import org.jetbrains.kotlin.fir.declarations.FirDeclaration import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.declarations.FirResolvePhase @@ -20,10 +21,18 @@ import org.jetbrains.kotlin.fir.resolve.transformers.* import org.jetbrains.kotlin.fir.scopes.FirScope import org.jetbrains.kotlin.fir.scopes.impl.FirLocalScope import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousFunctionSymbol import org.jetbrains.kotlin.fir.types.FirTypeRef typealias FirLocalScopes = PersistentList +class FirLocalContext( + val localScopes: FirLocalScopes, + val implicitReceiverStack: ImplicitReceiverStack +) + +typealias LocalContextForAnonymousFunctions = Map + interface SessionHolder { val session: FirSession } @@ -33,6 +42,7 @@ interface BodyResolveComponents : SessionHolder { val implicitReceiverStack: ImplicitReceiverStack val topLevelScopes: List val localScopes: FirLocalScopes + val localContextForAnonymousFunctions: LocalContextForAnonymousFunctions val noExpectedType: FirTypeRef val symbolProvider: FirSymbolProvider val file: FirFile @@ -51,4 +61,7 @@ interface BodyResolveComponents : SessionHolder { val AbstractFirBasedSymbol.phasedFir: D where D : FirDeclaration, D : FirSymbolOwner get() = phasedFir(FirResolvePhase.DECLARATIONS) -} + + fun saveContextForAnonymousFunction(anonymousFunction: FirAnonymousFunction) + fun dropContextForAnonymousFunction(anonymousFunction: FirAnonymousFunction) +} \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt index aca3a4478e5..8face419413 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt @@ -185,9 +185,11 @@ class FirCallCompleter( lambdaArgument.replaceValueParameters(lambdaArgument.valueParameters + listOfNotNull(itParam)) lambdaArgument.replaceReturnTypeRef(expectedReturnTypeRef ?: noExpectedType) - transformer.components.withImplicitReceiverStack(lambdaAtom.implicitReceiverStack) { + val localContext = localContextForAnonymousFunctions.getValue(lambdaArgument.symbol) + transformer.components.withLocalContext(localContext) { lambdaArgument.transformSingle(transformer, ResolutionMode.LambdaResolution(expectedReturnTypeRef)) } + dropContextForAnonymousFunction(lambdaArgument) val returnArguments = dataFlowAnalyzer.returnExpressionsOfAnonymousFunction(lambdaArgument) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArguments.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArguments.kt index d58564f4555..9bff3552949 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArguments.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArguments.kt @@ -9,10 +9,13 @@ import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess import org.jetbrains.kotlin.fir.expressions.FirStatement -import org.jetbrains.kotlin.fir.resolve.* +import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents +import org.jetbrains.kotlin.fir.resolve.DoubleColonLHS import org.jetbrains.kotlin.fir.resolve.calls.Candidate import org.jetbrains.kotlin.fir.resolve.calls.CandidateApplicability import org.jetbrains.kotlin.fir.resolve.calls.isExtensionFunctionType +import org.jetbrains.kotlin.fir.resolve.createFunctionalType +import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.symbols.StandardClassIds import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl import org.jetbrains.kotlin.fir.types.* @@ -135,8 +138,7 @@ private fun extraLambdaInfo( receiverType, parameters, returnType, - typeVariable.takeIf { newTypeVariableUsed }, - components.implicitReceiverStack.snapshot() + typeVariable.takeIf { newTypeVariableUsed } ) } @@ -163,8 +165,7 @@ internal fun extractLambdaInfoFromFunctionalType( receiverType, parameters, returnType, - typeVariableForLambdaReturnType = null, - components.implicitReceiverStack.snapshot() + typeVariableForLambdaReturnType = null ) } @@ -208,10 +209,8 @@ class ResolvedLambdaAtom( val receiver: ConeKotlinType?, val parameters: List, val returnType: ConeKotlinType, - val typeVariableForLambdaReturnType: TypeVariableForLambdaReturnType?, - val implicitReceiverStack: ImplicitReceiverStack + val typeVariableForLambdaReturnType: TypeVariableForLambdaReturnType? ) : PostponedResolvedAtomMarker { - override var analyzed: Boolean = false lateinit var returnStatements: List diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirAbstractBodyResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirAbstractBodyResolveTransformer.kt index 7feba02a2db..6323d940509 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirAbstractBodyResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirAbstractBodyResolveTransformer.kt @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.fir.scopes.FirScope import org.jetbrains.kotlin.fir.scopes.impl.FirLocalScope import org.jetbrains.kotlin.fir.scopes.impl.FirTypeResolveScopeForBodyResolve import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousFunctionSymbol import org.jetbrains.kotlin.fir.types.FirTypeRef import org.jetbrains.kotlin.fir.types.builder.buildImplicitTypeRef @@ -110,6 +111,8 @@ abstract class FirAbstractBodyResolveTransformer(phase: FirResolvePhase) : FirAb override val noExpectedType: FirTypeRef = buildImplicitTypeRef() + override val localContextForAnonymousFunctions: MutableMap = mutableMapOf() + @set:PrivateForInline override lateinit var file: FirFile internal set @@ -160,24 +163,18 @@ abstract class FirAbstractBodyResolveTransformer(phase: FirResolvePhase) : FirAb } @UseExperimental(PrivateForInline::class) - inline fun withImplicitReceiverStack(implicitReceiverStack: ImplicitReceiverStack, f: () -> T): T { + inline fun withLocalContext(localContext: FirLocalContext, f: () -> T): T { val existedStack = this.implicitReceiverStack - this.implicitReceiverStack = implicitReceiverStack + val existedLocalScopes = this.localScopes + + implicitReceiverStack = localContext.implicitReceiverStack + localScopes = localContext.localScopes + return try { f() } finally { - this.implicitReceiverStack = existedStack - } - } - - @UseExperimental(PrivateForInline::class) - inline fun withLocalScopes(localScopes: FirLocalScopes, l: () -> R): R { - val initialLocalScopes = this.localScopes - this.localScopes = localScopes - return try { - l() - } finally { - this.localScopes = initialLocalScopes + implicitReceiverStack = existedStack + localScopes = existedLocalScopes } } @@ -212,6 +209,18 @@ abstract class FirAbstractBodyResolveTransformer(phase: FirResolvePhase) : FirAb updateLastScope { storeBackingField(property) } } + override fun saveContextForAnonymousFunction(anonymousFunction: FirAnonymousFunction) { + localContextForAnonymousFunctions[anonymousFunction.symbol] = FirLocalContext(localScopes, implicitReceiverStack.snapshot()) + } + + override fun dropContextForAnonymousFunction(anonymousFunction: FirAnonymousFunction) { + localContextForAnonymousFunctions.remove(anonymousFunction.symbol) + } + + fun cleanContextForAnonymousFunction() { + localContextForAnonymousFunctions.clear() + } + @UseExperimental(PrivateForInline::class) private inline fun updateLastScope(transform: FirLocalScope.() -> FirLocalScope) { val lastScope = localScopes.lastOrNull() ?: return diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirBodyResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirBodyResolveTransformer.kt index 00c33cead9a..1e9f61553d8 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirBodyResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirBodyResolveTransformer.kt @@ -37,6 +37,7 @@ open class FirBodyResolveTransformer( private val controlFlowStatementsTransformer = FirControlFlowStatementsResolveTransformer(this) override fun transformFile(file: FirFile, data: ResolutionMode): CompositeTransformResult { + components.cleanContextForAnonymousFunction() @UseExperimental(PrivateForInline::class) components.file = file packageFqName = file.packageFqName 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 2c7683a213e..2510529da4d 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 @@ -477,6 +477,7 @@ class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransformer) anonymousFunction.transformReturnTypeRef(transformer, ResolutionMode.ContextIndependent) anonymousFunction.transformReceiverTypeRef(transformer, ResolutionMode.ContextIndependent) anonymousFunction.valueParameters.forEach { it.transformReturnTypeRef(transformer, ResolutionMode.ContextIndependent) } + components.saveContextForAnonymousFunction(anonymousFunction) } return when (data) { ResolutionMode.ContextDependent -> { diff --git a/compiler/fir/resolve/testData/resolve/inference/lambdaAsReturnStatementOfLambda.kt b/compiler/fir/resolve/testData/resolve/inference/lambdaAsReturnStatementOfLambda.kt new file mode 100644 index 00000000000..bb6f7ad51fe --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/inference/lambdaAsReturnStatementOfLambda.kt @@ -0,0 +1,15 @@ +// ISSUE: KT-37070 + +class A + +fun test(a: A) { + + val lambda = a.let { + { it } + } + + val alsoA = lambda() + takeA(alsoA) +} + +fun takeA(a: A) {} diff --git a/compiler/fir/resolve/testData/resolve/inference/lambdaAsReturnStatementOfLambda.txt b/compiler/fir/resolve/testData/resolve/inference/lambdaAsReturnStatementOfLambda.txt new file mode 100644 index 00000000000..2cc62a5cf6e --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/inference/lambdaAsReturnStatementOfLambda.txt @@ -0,0 +1,20 @@ +FILE: lambdaAsReturnStatementOfLambda.kt + public final class A : R|kotlin/Any| { + public constructor(): R|A| { + super() + } + + } + public final fun test(a: R|A|): R|kotlin/Unit| { + lval lambda: R|() -> A| = R|/a|.R|kotlin/let| A|>( = let@fun (it: R|A|): R|() -> A| { + ^ let@fun (): R|A| { + ^ R|/it| + } + + } + ) + lval alsoA: R|A| = R|/lambda|.R|FakeOverride|() + R|/takeA|(R|/alsoA|) + } + public final fun takeA(a: R|A|): R|kotlin/Unit| { + } diff --git a/compiler/fir/resolve/testData/resolve/lambdaArgInScopeFunction.kt b/compiler/fir/resolve/testData/resolve/lambdaArgInScopeFunction.kt index 71a3c91a16e..af380eaa627 100644 --- a/compiler/fir/resolve/testData/resolve/lambdaArgInScopeFunction.kt +++ b/compiler/fir/resolve/testData/resolve/lambdaArgInScopeFunction.kt @@ -18,7 +18,7 @@ fun case1(kotlinClass: KotlinClass?) { value.checkType { _() } val lambda = kotlinClass?.let { - {it} + {it} } lambda.checkType { _>() } @@ -33,7 +33,7 @@ fun case2(kotlinClass: KotlinClass) { value.checkType { _() } val lambda = kotlinClass.let { - {it} + {it} } lambda.checkType { _>() } diff --git a/compiler/fir/resolve/testData/resolve/lambdaArgInScopeFunction.txt b/compiler/fir/resolve/testData/resolve/lambdaArgInScopeFunction.txt index d3ccf247c59..82f56030f8a 100644 --- a/compiler/fir/resolve/testData/resolve/lambdaArgInScopeFunction.txt +++ b/compiler/fir/resolve/testData/resolve/lambdaArgInScopeFunction.txt @@ -21,14 +21,14 @@ FILE: lambdaArgInScopeFunction.kt this@R|special/anonymous|.R|tests/_checkType/_|() } ) - lval lambda: R|kotlin/Nothing?| = R|/kotlinClass|?.R|kotlin/let|( = let@fun (it: R|KotlinClass|): R|kotlin/Nothing| { - ^ let@fun (): { - ^ # + lval lambda: R|() -> KotlinClass| = R|/kotlinClass|?.R|kotlin/let| KotlinClass|>( = let@fun (it: R|KotlinClass|): R|() -> KotlinClass| { + ^ let@fun (): R|KotlinClass| { + ^ R|/it| } } ) - R|/lambda|.R|tests/_checkType/checkType|( = checkType@fun R|tests/_checkType/Inv|.(): R|kotlin/Unit| { + R|/lambda|.R|tests/_checkType/checkType| KotlinClass|>( = checkType@fun R|tests/_checkType/Inv?>|.(): R|kotlin/Unit| { ^ # KotlinClass?|>() } ) @@ -42,14 +42,14 @@ FILE: lambdaArgInScopeFunction.kt this@R|special/anonymous|.R|tests/_checkType/_|() } ) - lval lambda: R|kotlin/Nothing| = R|/kotlinClass|.R|kotlin/let|( = let@fun (it: R|KotlinClass|): R|kotlin/Nothing| { - ^ let@fun (): { - ^ # + lval lambda: R|() -> KotlinClass| = R|/kotlinClass|.R|kotlin/let| KotlinClass|>( = let@fun (it: R|KotlinClass|): R|() -> KotlinClass| { + ^ let@fun (): R|KotlinClass| { + ^ R|/it| } } ) - R|/lambda|.R|tests/_checkType/checkType|( = checkType@fun R|tests/_checkType/Inv|.(): R|kotlin/Unit| { + R|/lambda|.R|tests/_checkType/checkType| KotlinClass|>( = checkType@fun R|tests/_checkType/Inv>|.(): R|kotlin/Unit| { ^ # KotlinClass?|>() } ) diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java index 6a43dcc6f48..407c39989ed 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java @@ -1200,6 +1200,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest { runTest("compiler/fir/resolve/testData/resolve/inference/definitelyNotNullIntersectionType.kt"); } + @TestMetadata("lambdaAsReturnStatementOfLambda.kt") + public void testLambdaAsReturnStatementOfLambda() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/inference/lambdaAsReturnStatementOfLambda.kt"); + } + @TestMetadata("nestedLambdas.kt") public void testNestedLambdas() throws Exception { runTest("compiler/fir/resolve/testData/resolve/inference/nestedLambdas.kt"); diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java index 84b1d670b61..7c4127a565a 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java @@ -1200,6 +1200,11 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos runTest("compiler/fir/resolve/testData/resolve/inference/definitelyNotNullIntersectionType.kt"); } + @TestMetadata("lambdaAsReturnStatementOfLambda.kt") + public void testLambdaAsReturnStatementOfLambda() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/inference/lambdaAsReturnStatementOfLambda.kt"); + } + @TestMetadata("nestedLambdas.kt") public void testNestedLambdas() throws Exception { runTest("compiler/fir/resolve/testData/resolve/inference/nestedLambdas.kt"); diff --git a/compiler/testData/diagnostics/tests/inference/constraints/returnLambdaFromLambda.fir.kt b/compiler/testData/diagnostics/tests/inference/constraints/returnLambdaFromLambda.fir.kt index 76dc095659a..760463301bc 100644 --- a/compiler/testData/diagnostics/tests/inference/constraints/returnLambdaFromLambda.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/constraints/returnLambdaFromLambda.fir.kt @@ -6,14 +6,14 @@ fun testLambda() { if (x is String) return@myRun { it -> x.length + it } if (x !is Int) return@myRun { it -> it } - { it -> x + it } + { it -> x + it } } val twoLambda: (Int) -> Int = myRun { val x: Int = 1 run { val y: Int = 2 - { x + y } + { x + y } } }