From d2bdbd897836df5f0d5fcb4ffee90f3a1dcc8985 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Thu, 23 May 2019 12:25:14 +0300 Subject: [PATCH] FIR resolve: record & check implicit extension receiver type properly --- .../kotlin/fir/resolve/calls/CallResolver.kt | 67 +++++++++++++------ .../fir/resolve/calls/CandidateFactory.kt | 6 +- .../kotlin/fir/resolve/calls/ResolverParts.kt | 24 +++++-- .../fir/resolve/testData/resolve/extension.kt | 10 ++- .../resolve/testData/resolve/extension.txt | 12 ++++ .../resolve/stdlib/arrayFirstOrNull.txt | 2 +- .../resolve/stdlib/topLevelResolve.txt | 20 +++--- .../ir/irText/expressions/calls.fir.txt | 2 +- .../extensionPropertyGetterCall.fir.txt | 7 +- .../ir/irText/expressions/references.fir.txt | 7 +- .../ir/irText/lambdas/samAdapter.fir.txt | 3 +- 11 files changed, 111 insertions(+), 49 deletions(-) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt index e05f427c9d4..694198df82d 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt @@ -36,7 +36,6 @@ import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind import org.jetbrains.kotlin.utils.addToStdlib.cast -import java.util.* import kotlin.coroutines.* import kotlin.coroutines.intrinsics.createCoroutineUnintercepted @@ -92,6 +91,7 @@ class CheckerSinkImpl(override val components: InferenceComponents, var continua class Candidate( val symbol: ConeSymbol, val dispatchReceiverValue: ClassDispatchReceiverValue?, + val implicitExtensionReceiverValue: ImplicitReceiverValue?, val explicitReceiverKind: ExplicitReceiverKind, private val inferenceComponents: InferenceComponents, private val baseSystem: ConstraintStorage @@ -146,7 +146,11 @@ interface TowerScopeLevel { ): ProcessorAction interface TowerScopeLevelProcessor { - fun consumeCandidate(symbol: T, dispatchReceiverValue: ClassDispatchReceiverValue?): ProcessorAction + fun consumeCandidate( + symbol: T, + dispatchReceiverValue: ClassDispatchReceiverValue?, + implicitExtensionReceiverValue: ImplicitReceiverValue? + ): ProcessorAction } object Empty : TowerScopeLevel { @@ -183,7 +187,7 @@ abstract class SessionBasedTowerLevel(val session: FirSession) : TowerScopeLevel class MemberScopeTowerLevel( session: FirSession, val dispatchReceiver: ReceiverValue, - val implicitExtensionReceiver: ReceiverValue? = null + val implicitExtensionReceiver: ImplicitReceiverValue? = null ) : SessionBasedTowerLevel(session) { private fun processMembers( @@ -198,9 +202,9 @@ class MemberScopeTowerLevel( if (candidate is ConeCallableSymbol && candidate.hasConsistentExtensionReceiver(extensionReceiver)) { // NB: we do not check dispatchReceiverValue != null here, // because of objects & constructors (see comments in dispatchReceiverValue() implementation) - output.consumeCandidate(candidate, candidate.dispatchReceiverValue()) + output.consumeCandidate(candidate, candidate.dispatchReceiverValue(), implicitExtensionReceiver) } else if (candidate is ConeClassLikeSymbol) { - output.consumeCandidate(candidate, null) + output.consumeCandidate(candidate, null, implicitExtensionReceiver) } else { ProcessorAction.NEXT } @@ -208,7 +212,7 @@ class MemberScopeTowerLevel( ) return ProcessorAction.STOP val withSynthetic = FirSyntheticPropertiesScope(session, scope, ReturnTypeCalculatorWithJump(session)) return withSynthetic.processScopeMembers { symbol -> - output.consumeCandidate(symbol, symbol.dispatchReceiverValue()) + output.consumeCandidate(symbol, symbol.dispatchReceiverValue(), implicitExtensionReceiver) } } @@ -246,7 +250,7 @@ private fun ConeCallableSymbol.hasExtensionReceiver(): Boolean = (this as? FirCa class ScopeTowerLevel( session: FirSession, val scope: FirScope, - val implicitExtensionReceiver: ReceiverValue? = null + val implicitExtensionReceiver: ImplicitReceiverValue? = null ) : SessionBasedTowerLevel(session) { override fun processElementsByName( token: TowerScopeLevel.Token, @@ -262,22 +266,27 @@ class ScopeTowerLevel( TowerScopeLevel.Token.Properties -> scope.processPropertiesByName(name) { candidate -> if (candidate.hasConsistentExtensionReceiver(extensionReceiver) && candidate.dispatchReceiverValue() == null) { - processor.consumeCandidate(candidate as T, dispatchReceiverValue = null) + processor.consumeCandidate( + candidate as T, dispatchReceiverValue = null, + implicitExtensionReceiverValue = implicitExtensionReceiver + ) } else { ProcessorAction.NEXT } } TowerScopeLevel.Token.Functions -> scope.processFunctionsByName(name) { candidate -> if (candidate.hasConsistentExtensionReceiver(extensionReceiver) && candidate.dispatchReceiverValue() == null) { - processor.consumeCandidate(candidate as T, dispatchReceiverValue = null) + processor.consumeCandidate( + candidate as T, dispatchReceiverValue = null, + implicitExtensionReceiverValue = implicitExtensionReceiver) } else { ProcessorAction.NEXT } } TowerScopeLevel.Token.Objects -> scope.processClassifiersByNameWithAction(name, FirPosition.OTHER) { processor.consumeCandidate( - it as T, - dispatchReceiverValue = null + it as T, dispatchReceiverValue = null, + implicitExtensionReceiverValue = null ) } } @@ -309,13 +318,13 @@ class QualifiedReceiverTowerLevel(session: FirSession) : SessionBasedTowerLevel( return if (token == TowerScopeLevel.Token.Objects) { scope.processClassifiersByNameWithAction(name, FirPosition.OTHER) { - processor.consumeCandidate(it as T, null) + processor.consumeCandidate(it as T, null, null) } } else { scope.processCallables(name, token.cast()) { val fir = it.firUnsafe() if (fir.isStatic || it.callableId.classId == null) { - processor.consumeCandidate(it as T, null) + processor.consumeCandidate(it as T, null, null) } else { ProcessorAction.NEXT } @@ -346,14 +355,19 @@ class QualifiedReceiverTowerDataConsumer( name, explicitReceiver, processor = object : TowerScopeLevel.TowerScopeLevelProcessor { - override fun consumeCandidate(symbol: T, dispatchReceiverValue: ClassDispatchReceiverValue?): ProcessorAction { + override fun consumeCandidate( + symbol: T, + dispatchReceiverValue: ClassDispatchReceiverValue?, + implicitExtensionReceiverValue: ImplicitReceiverValue? + ): ProcessorAction { assert(dispatchReceiverValue == null) resultCollector.consumeCandidate( group, candidateFactory.createCandidate( symbol, - null, - ExplicitReceiverKind.NO_EXPLICIT_RECEIVER + dispatchReceiverValue = null, + implicitExtensionReceiverValue = null, + explicitReceiverKind = ExplicitReceiverKind.NO_EXPLICIT_RECEIVER ) ) return ProcessorAction.NEXT @@ -500,12 +514,17 @@ class ExplicitReceiverTowerDataConsumer( name, explicitReceiver = null, processor = object : TowerScopeLevel.TowerScopeLevelProcessor { - override fun consumeCandidate(symbol: T, dispatchReceiverValue: ClassDispatchReceiverValue?): ProcessorAction { + override fun consumeCandidate( + symbol: T, + dispatchReceiverValue: ClassDispatchReceiverValue?, + implicitExtensionReceiverValue: ImplicitReceiverValue? + ): ProcessorAction { resultCollector.consumeCandidate( group, candidateFactory.createCandidate( symbol, dispatchReceiverValue, + implicitExtensionReceiverValue, ExplicitReceiverKind.DISPATCH_RECEIVER ) ) @@ -520,12 +539,17 @@ class ExplicitReceiverTowerDataConsumer( name, explicitReceiver = explicitReceiver, processor = object : TowerScopeLevel.TowerScopeLevelProcessor { - override fun consumeCandidate(symbol: T, dispatchReceiverValue: ClassDispatchReceiverValue?): ProcessorAction { + override fun consumeCandidate( + symbol: T, + dispatchReceiverValue: ClassDispatchReceiverValue?, + implicitExtensionReceiverValue: ImplicitReceiverValue? + ): ProcessorAction { resultCollector.consumeCandidate( group, candidateFactory.createCandidate( symbol, dispatchReceiverValue, + implicitExtensionReceiverValue, ExplicitReceiverKind.EXTENSION_RECEIVER ) ) @@ -562,12 +586,17 @@ class NoExplicitReceiverTowerDataConsumer( name, explicitReceiver = null, processor = object : TowerScopeLevel.TowerScopeLevelProcessor { - override fun consumeCandidate(symbol: T, dispatchReceiverValue: ClassDispatchReceiverValue?): ProcessorAction { + override fun consumeCandidate( + symbol: T, + dispatchReceiverValue: ClassDispatchReceiverValue?, + implicitExtensionReceiverValue: ImplicitReceiverValue? + ): ProcessorAction { resultCollector.consumeCandidate( group, candidateFactory.createCandidate( symbol, dispatchReceiverValue, + implicitExtensionReceiverValue, ExplicitReceiverKind.NO_EXPLICIT_RECEIVER ) ) 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 ee99193c61d..4929f89b029 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 @@ -32,9 +32,13 @@ class CandidateFactory( fun createCandidate( symbol: ConeSymbol, dispatchReceiverValue: ClassDispatchReceiverValue?, + implicitExtensionReceiverValue: ImplicitReceiverValue?, explicitReceiverKind: ExplicitReceiverKind ): Candidate { - return Candidate(symbol, dispatchReceiverValue, explicitReceiverKind, inferenceComponents, baseSystem) + return Candidate( + symbol, dispatchReceiverValue, implicitExtensionReceiverValue, + explicitReceiverKind, inferenceComponents, baseSystem + ) } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt index faa70cd07d2..46304d12c8a 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt @@ -98,23 +98,35 @@ internal sealed class CheckReceivers : ResolutionStage() { abstract fun ExplicitReceiverKind.shouldBeResolvedAsImplicit(): Boolean override suspend fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) { - val receiverParameterValue = candidate.getReceiverValue() + val expectedReceiverParameterValue = candidate.getReceiverValue() val explicitReceiverExpression = callInfo.explicitReceiver val explicitReceiverKind = candidate.explicitReceiverKind - if (receiverParameterValue != null) { + if (expectedReceiverParameterValue != null) { if (explicitReceiverExpression != null && explicitReceiverKind.shouldBeResolvedAsExplicit()) { resolveArgumentExpression( candidate.csBuilder, - explicitReceiverExpression, - candidate.substitutor.substituteOrSelf(receiverParameterValue.type), - explicitReceiverExpression.typeRef, - sink, + argument = explicitReceiverExpression, + expectedType = candidate.substitutor.substituteOrSelf(expectedReceiverParameterValue.type), + expectedTypeRef = explicitReceiverExpression.typeRef, + sink = sink, isReceiver = true, isSafeCall = callInfo.isSafeCall, typeProvider = callInfo.typeProvider, acceptLambdaAtoms = { candidate.postponedAtoms += it } ) + } else { + val argumentExtensionReceiverValue = candidate.implicitExtensionReceiverValue + if (argumentExtensionReceiverValue != null && explicitReceiverKind.shouldBeResolvedAsImplicit()) { + resolvePlainArgumentType( + candidate.csBuilder, + argumentType = argumentExtensionReceiverValue.type, + expectedType = candidate.substitutor.substituteOrSelf(expectedReceiverParameterValue.type), + sink = sink, + isReceiver = true, + isSafeCall = callInfo.isSafeCall + ) + } } } } diff --git a/compiler/fir/resolve/testData/resolve/extension.kt b/compiler/fir/resolve/testData/resolve/extension.kt index dc61b6875ac..cbe661d5b0d 100644 --- a/compiler/fir/resolve/testData/resolve/extension.kt +++ b/compiler/fir/resolve/testData/resolve/extension.kt @@ -2,4 +2,12 @@ fun String.foo() {} fun String.bar() { foo() -} \ No newline at end of file +} + +class My { + fun bar() { + foo() + } +} + +fun My.foo() {} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/extension.txt b/compiler/fir/resolve/testData/resolve/extension.txt index ac84313931a..1fe03c7f442 100644 --- a/compiler/fir/resolve/testData/resolve/extension.txt +++ b/compiler/fir/resolve/testData/resolve/extension.txt @@ -4,3 +4,15 @@ FILE: extension.kt public final fun R|kotlin/String|.bar(): R|kotlin/Unit| { R|/foo|() } + public final class My : R|kotlin/Any| { + public constructor(): R|My| { + super() + } + + public final fun bar(): R|kotlin/Unit| { + R|/foo|() + } + + } + public final fun R|My|.foo(): R|kotlin/Unit| { + } diff --git a/compiler/fir/resolve/testData/resolve/stdlib/arrayFirstOrNull.txt b/compiler/fir/resolve/testData/resolve/stdlib/arrayFirstOrNull.txt index 3935a93b881..8457b9628b1 100644 --- a/compiler/fir/resolve/testData/resolve/stdlib/arrayFirstOrNull.txt +++ b/compiler/fir/resolve/testData/resolve/stdlib/arrayFirstOrNull.txt @@ -9,7 +9,7 @@ FILE: arrayFirstOrNull.kt } public final fun R|kotlin/Array|.firstOrNullX(): R|T|? { ^firstOrNullX when () { - #() -> { + R|kotlin/collections/isEmpty|() -> { Null(null) } else -> { diff --git a/compiler/fir/resolve/testData/resolve/stdlib/topLevelResolve.txt b/compiler/fir/resolve/testData/resolve/stdlib/topLevelResolve.txt index 367efa5a35a..47819e17fcd 100644 --- a/compiler/fir/resolve/testData/resolve/stdlib/topLevelResolve.txt +++ b/compiler/fir/resolve/testData/resolve/stdlib/topLevelResolve.txt @@ -19,37 +19,37 @@ FILE: topLevelResolve.kt ^id R|/arg| } public final fun testMap(): R|kotlin/Unit| { - lval first: R|kotlin/collections/List| = R|kotlin/collections/listOf|(Int(1), Int(2), Int(3)).R|kotlin/collections/map|( = map@fun (it: R|kotlin/Int|): R|kotlin/Function1| { + lval first: R|kotlin/collections/List| = R|kotlin/collections/listOf|(Int(1), Int(2), Int(3)).R|kotlin/collections/map|( = map@fun (it: R|kotlin/Int|): R|kotlin/Int| { R|/it|.R|kotlin/Int.times|(Int(2)) } ) - lval second: R|kotlin/collections/List| = R|kotlin/intArrayOf|(Int(4), Int(5), Int(6)).R|kotlin/collections/map|( = map@fun (it: R|kotlin/Int|): R|kotlin/Function1| { + lval second: R|kotlin/collections/List| = R|kotlin/intArrayOf|(Int(4), Int(5), Int(6)).R|kotlin/collections/map|( = map@fun (it: R|kotlin/Int|): R|kotlin/Int| { R|/it|.R|kotlin/Int.times|(Int(2)) } ) - lval withId: R|kotlin/collections/List| = R|kotlin/collections/listOf|(Int(1), Int(2), Int(3)).R|kotlin/collections/map|( = map@fun (it: R|kotlin/Int|): R|kotlin/Function1| { + lval withId: R|kotlin/collections/List| = R|kotlin/collections/listOf|(Int(1), Int(2), Int(3)).R|kotlin/collections/map|( = map@fun (it: R|kotlin/Int|): R|kotlin/Int| { R|/id|(R|/it|) } ) - lval stringToInt: R|kotlin/collections/List| = R|kotlin/collections/listOf|(String(alpha), String(omega)).R|kotlin/collections/map|( = map@fun (it: R|kotlin/String|): R|kotlin/Function1| { + lval stringToInt: R|kotlin/collections/List| = R|kotlin/collections/listOf|(String(alpha), String(omega)).R|kotlin/collections/map|( = map@fun (it: R|kotlin/String|): R|kotlin/Int| { R|/it|.R|kotlin/String.length| } ) - lval viaWith: R|kotlin/Nothing| = R|kotlin/with||, R|kotlin/Nothing|>(R|kotlin/collections/listOf|(Int(42)), = with@fun R|kotlin/collections/List|.(it: R|kotlin/collections/List|): R|kotlin/Function2, kotlin/collections/List, kotlin/Nothing>| { - #( = map@fun .(): { - it#.times#(it#) + lval viaWith: R|kotlin/collections/List| = R|kotlin/with||, R|kotlin/collections/List|>(R|kotlin/collections/listOf|(Int(42)), = with@fun R|kotlin/collections/List|.(it: R|kotlin/collections/List|): R|kotlin/collections/List| { + R|kotlin/collections/map|( = map@fun (it: R|kotlin/Int|): R|kotlin/Int| { + R|/it|.R|kotlin/Int.times|(R|/it|) } ) } ) } public final fun testWith(): R|kotlin/Unit| { - lval length: R|kotlin/Int| = R|kotlin/with|(String(), = with@fun R|kotlin/String|.(it: R|kotlin/String|): R|kotlin/Function2| { + lval length: R|kotlin/Int| = R|kotlin/with|(String(), = with@fun R|kotlin/String|.(it: R|kotlin/String|): R|kotlin/Int| { R|kotlin/String.length| } ) - lval indices: R|kotlin/Nothing| = R|kotlin/with|(String(), = with@fun R|kotlin/String|.(it: R|kotlin/String|): R|kotlin/Function2| { - # + lval indices: R|kotlin/ranges/IntRange| = R|kotlin/with|(String(), = with@fun R|kotlin/String|.(it: R|kotlin/String|): R|kotlin/ranges/IntRange| { + R|kotlin/text/indices| } ) lval indicesNoWith: R|kotlin/ranges/IntRange| = String().R|kotlin/text/indices| diff --git a/compiler/testData/ir/irText/expressions/calls.fir.txt b/compiler/testData/ir/irText/expressions/calls.fir.txt index 4d6fc881f86..0e37e8a53fe 100644 --- a/compiler/testData/ir/irText/expressions/calls.fir.txt +++ b/compiler/testData/ir/irText/expressions/calls.fir.txt @@ -37,6 +37,6 @@ FILE fqName: fileName:/calls.kt BLOCK_BODY RETURN type=kotlin.Nothing from='public final fun ext3 (x: kotlin.Int): kotlin.Int declared in ' CALL 'public final fun foo (x: kotlin.Int, y: kotlin.Int): kotlin.Int declared in ' type=kotlin.Int origin=null - x: ERROR_CALL 'Unresolved reference: #' type=IrErrorType + x: CALL 'public final fun ext1 (): kotlin.Int declared in ' type=kotlin.Int origin=null y: GET_VAR 'x: kotlin.Int declared in .ext3' type=kotlin.Int origin=null diff --git a/compiler/testData/ir/irText/expressions/extensionPropertyGetterCall.fir.txt b/compiler/testData/ir/irText/expressions/extensionPropertyGetterCall.fir.txt index f6dcebf6ac9..55808a9ddd3 100644 --- a/compiler/testData/ir/irText/expressions/extensionPropertyGetterCall.fir.txt +++ b/compiler/testData/ir/irText/expressions/extensionPropertyGetterCall.fir.txt @@ -5,8 +5,7 @@ FILE fqName: fileName:/extensionPropertyGetterCall.kt BLOCK_BODY RETURN type=kotlin.Nothing from='public final fun (): kotlin.String declared in ' CONST String type=kotlin.String value="OK" - FUN name:test5 visibility:public modality:FINAL <> () returnType:IrErrorType + FUN name:test5 visibility:public modality:FINAL <> () returnType:kotlin.String BLOCK_BODY - RETURN type=kotlin.Nothing from='public final fun test5 (): IrErrorType declared in ' - ERROR_CALL 'Unresolved reference: #' type=IrErrorType - + RETURN type=kotlin.Nothing from='public final fun test5 (): kotlin.String declared in ' + CALL 'public final fun (): kotlin.String declared in ' type=kotlin.String origin=null diff --git a/compiler/testData/ir/irText/expressions/references.fir.txt b/compiler/testData/ir/irText/expressions/references.fir.txt index a3d13c36e4c..a9a834e54d3 100644 --- a/compiler/testData/ir/irText/expressions/references.fir.txt +++ b/compiler/testData/ir/irText/expressions/references.fir.txt @@ -48,8 +48,7 @@ FILE fqName: fileName:/references.kt BLOCK_BODY RETURN type=kotlin.Nothing from='public final fun (): kotlin.String declared in ' CONST String type=kotlin.String value="OK" - FUN name:test5 visibility:public modality:FINAL <> () returnType:IrErrorType + FUN name:test5 visibility:public modality:FINAL <> () returnType:kotlin.String BLOCK_BODY - RETURN type=kotlin.Nothing from='public final fun test5 (): IrErrorType declared in ' - ERROR_CALL 'Unresolved reference: #' type=IrErrorType - + RETURN type=kotlin.Nothing from='public final fun test5 (): kotlin.String declared in ' + CALL 'public final fun (): kotlin.String declared in ' type=kotlin.String origin=null diff --git a/compiler/testData/ir/irText/lambdas/samAdapter.fir.txt b/compiler/testData/ir/irText/lambdas/samAdapter.fir.txt index e43e2170ee7..6c08dfe045f 100644 --- a/compiler/testData/ir/irText/lambdas/samAdapter.fir.txt +++ b/compiler/testData/ir/irText/lambdas/samAdapter.fir.txt @@ -9,5 +9,4 @@ FILE fqName: fileName:/samAdapter.kt ERROR_CALL 'Unresolved reference: println#' type=IrErrorType CONST String type=IrErrorType value="Hello, world!" FUNCTION_REFERENCE 'local final fun (): IrErrorType declared in .test1' type=IrErrorType origin=LAMBDA - ERROR_CALL 'Unresolved reference: #' type=IrErrorType - + ERROR_CALL 'Unresolved reference: #' type=IrErrorType