From 1f6746dc74acab2316a345776f1b65477bff1057 Mon Sep 17 00:00:00 2001 From: Anastasiya Shadrina Date: Thu, 10 Jun 2021 15:21:32 +0700 Subject: [PATCH] [FE] Relax the "no subtyping between context receivers" limitation --- .../calls/components/ResolutionParts.kt | 66 +++++++------------ .../calls/components/SimpleArgumentsChecks.kt | 2 +- .../contextReceivers/genericOuterClass.kt | 4 +- .../tests/extensions/contextReceivers/lazy.kt | 8 +-- .../contextReceivers/typeParameterized.kt | 2 +- .../contextReceivers/typeParameterizedList.kt | 2 +- 6 files changed, 31 insertions(+), 53 deletions(-) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt index ace70a429c6..d1cfa7f4d43 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor +import org.jetbrains.kotlin.resolve.calls.components.CheckContextReceiversResolutionPart.candidateDescriptor import org.jetbrains.kotlin.resolve.calls.components.CheckReceivers.candidateDescriptor import org.jetbrains.kotlin.resolve.calls.components.CheckReceivers.checkReceiver import org.jetbrains.kotlin.resolve.calls.components.TypeArgumentsToParametersMapper.TypeArgumentsMapping.NoExplicitArguments @@ -17,6 +18,7 @@ import org.jetbrains.kotlin.resolve.calls.components.candidate.CallableReference import org.jetbrains.kotlin.resolve.calls.components.candidate.ResolutionCandidate import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemOperation import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem +import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible import org.jetbrains.kotlin.resolve.calls.inference.components.* import org.jetbrains.kotlin.resolve.calls.inference.model.* import org.jetbrains.kotlin.resolve.calls.inference.substitute @@ -30,7 +32,6 @@ import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo import org.jetbrains.kotlin.resolve.scopes.utils.parentsWithSelf import org.jetbrains.kotlin.types.* -import org.jetbrains.kotlin.types.checker.NewKotlinTypeChecker import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.model.TypeConstructorMarker import org.jetbrains.kotlin.types.model.typeConstructor @@ -807,53 +808,30 @@ internal object ErrorDescriptorResolutionPart : ResolutionPart() { internal object CheckContextReceiversResolutionPart : ResolutionPart() { private fun ResolutionCandidate.checkReceiver( - receiverArgument: SimpleKotlinCallArgument?, - receiverParameter: ReceiverParameterDescriptor? - ) { - if ((receiverArgument == null) != (receiverParameter == null)) { - error("Inconsistency receiver state for call $kotlinCall and candidate descriptor: $candidateDescriptor") + implicitReceivers: Collection, + candidateContextReceiverParameter: ReceiverParameterDescriptor + ): SimpleKotlinCallArgument? { + val csBuilder = getSystem().getBuilder() + for (implicitReceiver in implicitReceivers) { + val argument = ReceiverExpressionKotlinCallArgument(implicitReceiver) + val expectedTypeUnprepared = argument.getExpectedType(candidateContextReceiverParameter, callComponents.languageVersionSettings) + + val expectedType = prepareExpectedType(expectedTypeUnprepared) + val argumentType = captureFromTypeParameterUpperBoundIfNeeded(argument.receiver.stableType, expectedType) + val position = ReceiverConstraintPositionImpl(argument) + if (csBuilder.addSubtypeConstraintIfCompatible(argumentType, expectedType, position)) return argument } - if (receiverArgument == null || receiverParameter == null) return - val receiverInfo = ReceiverInfo( - isReceiver = true, - shouldReportUnsafeCall = true, - reportUnsafeCallAsUnsafeImplicitInvoke = false - ) - - resolveKotlinArgument(receiverArgument, receiverParameter, receiverInfo) - } - - private fun ResolutionCandidate.searchForAdditionalReceivers(): List? { - val result = mutableListOf() - val candidateReceivers = scopeTower.lexicalScope.parentsWithSelf - .flatMap { if (it is LexicalScope) scopeTower.getImplicitReceivers(it) else emptyList() } - - fun KotlinType.prepared(): KotlinType = if (containsTypeParameter()) replaceArgumentsWithStarProjections() else this - - for (receiver in candidateDescriptor.contextReceiverParameters) { - val expectedReceiverType = receiver.type - val expectedReceiverTypeClosestBound = - if (expectedReceiverType.isTypeParameter()) expectedReceiverType.supertypes().first() - else expectedReceiverType - val selectedCandidate = candidateReceivers.firstOrNull { candidateReceiver -> - val candidateReceiverType = candidateReceiver.receiverValue.type - NewKotlinTypeChecker.Default.isSubtypeOf(candidateReceiverType.prepared(), expectedReceiverTypeClosestBound.prepared()) - } ?: run { - this.diagnosticsFromResolutionParts.add(NoContextReceiver(receiver)) - return null - } - result.add(selectedCandidate) - } - return result.map { ReceiverExpressionKotlinCallArgument(it) } + diagnosticsFromResolutionParts.add(NoContextReceiver(candidateContextReceiverParameter)) + return null } override fun ResolutionCandidate.process(workIndex: Int) { - resolvedCall.contextReceiversArguments = searchForAdditionalReceivers() ?: return - for (i in resolvedCall.contextReceiversArguments.indices) { - checkReceiver( - resolvedCall.contextReceiversArguments[i], - candidateDescriptor.contextReceiverParameters[i] - ) + val implicitReceivers = scopeTower.lexicalScope.parentsWithSelf + .flatMap { if (it is LexicalScope) scopeTower.getImplicitReceivers(it) else emptyList() }.toList() + val contextReceiversArguments = mutableListOf() + for (candidateContextReceiverParameter in candidateDescriptor.contextReceiverParameters) { + contextReceiversArguments.add(findContextReceiver(implicitReceivers, candidateContextReceiverParameter) ?: return) } + resolvedCall.contextReceiversArguments = contextReceiversArguments } } \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt index 2a57a842589..390160f590b 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt @@ -153,7 +153,7 @@ private fun checkExpressionArgument( * } * */ -private fun captureFromTypeParameterUpperBoundIfNeeded(argumentType: UnwrappedType, expectedType: UnwrappedType): UnwrappedType { +fun captureFromTypeParameterUpperBoundIfNeeded(argumentType: UnwrappedType, expectedType: UnwrappedType): UnwrappedType { val expectedTypeConstructor = expectedType.upperIfFlexible().constructor if (argumentType.lowerIfFlexible().constructor.declarationDescriptor is TypeParameterDescriptor) { diff --git a/compiler/testData/diagnostics/tests/extensions/contextReceivers/genericOuterClass.kt b/compiler/testData/diagnostics/tests/extensions/contextReceivers/genericOuterClass.kt index 88c287e8730..ecd9a03f1a7 100644 --- a/compiler/testData/diagnostics/tests/extensions/contextReceivers/genericOuterClass.kt +++ b/compiler/testData/diagnostics/tests/extensions/contextReceivers/genericOuterClass.kt @@ -4,10 +4,10 @@ context(Collection

) class B

fun Int.foo() { A() - A() + A() } fun Collection.bar() { B() - B() + B() } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/extensions/contextReceivers/lazy.kt b/compiler/testData/diagnostics/tests/extensions/contextReceivers/lazy.kt index 8e4746a171a..4dbefeda667 100644 --- a/compiler/testData/diagnostics/tests/extensions/contextReceivers/lazy.kt +++ b/compiler/testData/diagnostics/tests/extensions/contextReceivers/lazy.kt @@ -14,28 +14,28 @@ fun f(lazy1: Lazy, lazy2: Lazy, lazyT: Lazy, lazyLazyT with(lazy2) { test1() test2() - test3() + test3() } } with(lazy2) { with(lazy1) { test1() test2() - test3() + test3() } } with(lazyT) { with(lazy1) { test1() test2() - test3() + test3() } } with(lazyLazyT) { with(lazy1) { test1() test2() - test3() + test3() } } with(lazy1) { diff --git a/compiler/testData/diagnostics/tests/extensions/contextReceivers/typeParameterized.kt b/compiler/testData/diagnostics/tests/extensions/contextReceivers/typeParameterized.kt index e3a5545c9cd..95bac5ac785 100644 --- a/compiler/testData/diagnostics/tests/extensions/contextReceivers/typeParameterized.kt +++ b/compiler/testData/diagnostics/tests/extensions/contextReceivers/typeParameterized.kt @@ -7,5 +7,5 @@ context(T) fun T.f(t: B) {} fun Int.main(a: A, b: B) { - a.f(b) + a.f(b) } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/extensions/contextReceivers/typeParameterizedList.kt b/compiler/testData/diagnostics/tests/extensions/contextReceivers/typeParameterizedList.kt index 313dd69f718..f844761f0a9 100644 --- a/compiler/testData/diagnostics/tests/extensions/contextReceivers/typeParameterizedList.kt +++ b/compiler/testData/diagnostics/tests/extensions/contextReceivers/typeParameterizedList.kt @@ -12,6 +12,6 @@ fun main() { A().f() } with(listOf("1", "2", "3")) { - A().f() + A().f() } } \ No newline at end of file