FIR: handle situations with multiple implicit receivers
This commit is contained in:
@@ -72,6 +72,18 @@ class CallResolver(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (implicitDispatchReceiverValue !== implicitReceiverValue) {
|
||||||
|
// Two different implicit receivers
|
||||||
|
towerDataConsumer.consume(
|
||||||
|
TowerDataKind.TOWER_LEVEL,
|
||||||
|
MemberScopeTowerLevel(
|
||||||
|
session, scopeSession = components.scopeSession,
|
||||||
|
dispatchReceiver = implicitDispatchReceiverValue,
|
||||||
|
implicitExtensionReceiver = implicitReceiverValue
|
||||||
|
),
|
||||||
|
group++
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (scope in topLevelScopes) {
|
for (scope in topLevelScopes) {
|
||||||
|
|||||||
@@ -52,11 +52,11 @@ internal object CheckExplicitReceiverConsistency : ResolutionStage() {
|
|||||||
|
|
||||||
internal sealed class CheckReceivers : ResolutionStage() {
|
internal sealed class CheckReceivers : ResolutionStage() {
|
||||||
object Dispatch : CheckReceivers() {
|
object Dispatch : CheckReceivers() {
|
||||||
override fun ExplicitReceiverKind.shouldBeResolvedAsImplicit(): Boolean {
|
override fun ExplicitReceiverKind.shouldBeCheckedAgainstImplicit(): Boolean {
|
||||||
return this == EXTENSION_RECEIVER || this == NO_EXPLICIT_RECEIVER
|
return this == EXTENSION_RECEIVER // For NO_EXPLICIT_RECEIVER we can check extension receiver only
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun ExplicitReceiverKind.shouldBeResolvedAsExplicit(): Boolean {
|
override fun ExplicitReceiverKind.shouldBeCheckedAgainstExplicit(): Boolean {
|
||||||
return this == DISPATCH_RECEIVER || this == BOTH_RECEIVERS
|
return this == DISPATCH_RECEIVER || this == BOTH_RECEIVERS
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,11 +66,11 @@ internal sealed class CheckReceivers : ResolutionStage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
object Extension : CheckReceivers() {
|
object Extension : CheckReceivers() {
|
||||||
override fun ExplicitReceiverKind.shouldBeResolvedAsImplicit(): Boolean {
|
override fun ExplicitReceiverKind.shouldBeCheckedAgainstImplicit(): Boolean {
|
||||||
return this == DISPATCH_RECEIVER || this == NO_EXPLICIT_RECEIVER
|
return this == DISPATCH_RECEIVER || this == NO_EXPLICIT_RECEIVER
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun ExplicitReceiverKind.shouldBeResolvedAsExplicit(): Boolean {
|
override fun ExplicitReceiverKind.shouldBeCheckedAgainstExplicit(): Boolean {
|
||||||
return this == EXTENSION_RECEIVER || this == BOTH_RECEIVERS
|
return this == EXTENSION_RECEIVER || this == BOTH_RECEIVERS
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,9 +88,9 @@ internal sealed class CheckReceivers : ResolutionStage() {
|
|||||||
|
|
||||||
abstract fun Candidate.getReceiverValue(): ReceiverValue?
|
abstract fun Candidate.getReceiverValue(): ReceiverValue?
|
||||||
|
|
||||||
abstract fun ExplicitReceiverKind.shouldBeResolvedAsExplicit(): Boolean
|
abstract fun ExplicitReceiverKind.shouldBeCheckedAgainstExplicit(): Boolean
|
||||||
|
|
||||||
abstract fun ExplicitReceiverKind.shouldBeResolvedAsImplicit(): Boolean
|
abstract fun ExplicitReceiverKind.shouldBeCheckedAgainstImplicit(): Boolean
|
||||||
|
|
||||||
override suspend fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
|
override suspend fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
|
||||||
val expectedReceiverParameterValue = candidate.getReceiverValue()
|
val expectedReceiverParameterValue = candidate.getReceiverValue()
|
||||||
@@ -98,7 +98,7 @@ internal sealed class CheckReceivers : ResolutionStage() {
|
|||||||
val explicitReceiverKind = candidate.explicitReceiverKind
|
val explicitReceiverKind = candidate.explicitReceiverKind
|
||||||
|
|
||||||
if (expectedReceiverParameterValue != null) {
|
if (expectedReceiverParameterValue != null) {
|
||||||
if (explicitReceiverExpression != null && explicitReceiverKind.shouldBeResolvedAsExplicit()) {
|
if (explicitReceiverExpression != null && explicitReceiverKind.shouldBeCheckedAgainstExplicit()) {
|
||||||
resolveArgumentExpression(
|
resolveArgumentExpression(
|
||||||
candidate.csBuilder,
|
candidate.csBuilder,
|
||||||
argument = explicitReceiverExpression,
|
argument = explicitReceiverExpression,
|
||||||
@@ -113,7 +113,7 @@ internal sealed class CheckReceivers : ResolutionStage() {
|
|||||||
sink.yield()
|
sink.yield()
|
||||||
} else {
|
} else {
|
||||||
val argumentExtensionReceiverValue = candidate.implicitExtensionReceiverValue
|
val argumentExtensionReceiverValue = candidate.implicitExtensionReceiverValue
|
||||||
if (argumentExtensionReceiverValue != null && explicitReceiverKind.shouldBeResolvedAsImplicit()) {
|
if (argumentExtensionReceiverValue != null && explicitReceiverKind.shouldBeCheckedAgainstImplicit()) {
|
||||||
resolvePlainArgumentType(
|
resolvePlainArgumentType(
|
||||||
candidate.csBuilder,
|
candidate.csBuilder,
|
||||||
argumentType = argumentExtensionReceiverValue.type,
|
argumentType = argumentExtensionReceiverValue.type,
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
class A {
|
||||||
|
fun foo() = 1
|
||||||
|
|
||||||
|
fun B.bar() = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
fun foo() = 2
|
||||||
|
|
||||||
|
fun A.bar() = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test(a: A, b: B) {
|
||||||
|
with(b) {
|
||||||
|
with(a) {
|
||||||
|
foo()
|
||||||
|
bar()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with(a) {
|
||||||
|
with(b) {
|
||||||
|
foo()
|
||||||
|
bar()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
FILE: implicitReceiverOrder.kt
|
||||||
|
public final class A : R|kotlin/Any| {
|
||||||
|
public constructor(): R|A| {
|
||||||
|
super<R|kotlin/Any|>()
|
||||||
|
}
|
||||||
|
|
||||||
|
public final fun foo(): R|kotlin/Int| {
|
||||||
|
^foo Int(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
public final fun R|B|.bar(): R|kotlin/Int| {
|
||||||
|
^bar Int(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public final class B : R|kotlin/Any| {
|
||||||
|
public constructor(): R|B| {
|
||||||
|
super<R|kotlin/Any|>()
|
||||||
|
}
|
||||||
|
|
||||||
|
public final fun foo(): R|kotlin/Int| {
|
||||||
|
^foo Int(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
public final fun R|A|.bar(): R|kotlin/Int| {
|
||||||
|
^bar Int(4)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public final fun test(a: R|A|, b: R|B|): R|kotlin/Unit| {
|
||||||
|
R|kotlin/with|<R|B|, R|kotlin/Int|>(R|<local>/b|, <L> = with@fun R|B|.<anonymous>(it: R|B|): R|kotlin/Unit| {
|
||||||
|
R|kotlin/with|<R|A|, R|kotlin/Int|>(R|<local>/a|, <L> = with@fun R|A|.<anonymous>(it: R|A|): R|kotlin/Unit| {
|
||||||
|
R|/A.foo|()
|
||||||
|
R|/B.bar|()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
R|kotlin/with|<R|A|, R|kotlin/Int|>(R|<local>/a|, <L> = with@fun R|A|.<anonymous>(it: R|A|): R|kotlin/Unit| {
|
||||||
|
R|kotlin/with|<R|B|, R|kotlin/Int|>(R|<local>/b|, <L> = with@fun R|B|.<anonymous>(it: R|B|): R|kotlin/Unit| {
|
||||||
|
R|/B.foo|()
|
||||||
|
R|/A.bar|()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// Copy of IR test
|
||||||
|
|
||||||
|
object A
|
||||||
|
object B
|
||||||
|
|
||||||
|
interface IFoo {
|
||||||
|
val A.foo: B get() = B
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IInvoke {
|
||||||
|
operator fun B.invoke() = 42
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test(fooImpl: IFoo, invokeImpl: IInvoke) {
|
||||||
|
with(A) {
|
||||||
|
with(fooImpl) {
|
||||||
|
foo
|
||||||
|
with(invokeImpl) {
|
||||||
|
foo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
FILE: multipleImplicitReceivers.kt
|
||||||
|
public final object A : R|kotlin/Any| {
|
||||||
|
private constructor(): R|A| {
|
||||||
|
super<R|kotlin/Any|>()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public final object B : R|kotlin/Any| {
|
||||||
|
private constructor(): R|B| {
|
||||||
|
super<R|kotlin/Any|>()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public abstract interface IFoo : R|kotlin/Any| {
|
||||||
|
public open val R|A|.foo: R|B|
|
||||||
|
public get(): R|B| {
|
||||||
|
^ Q|B|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public abstract interface IInvoke : R|kotlin/Any| {
|
||||||
|
public open operator fun R|B|.invoke(): R|kotlin/Int| {
|
||||||
|
^invoke Int(42)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public final fun test(fooImpl: R|IFoo|, invokeImpl: R|IInvoke|): R|kotlin/Unit| {
|
||||||
|
R|kotlin/with|<R|A|, R|kotlin/Int|>(Q|A|, <L> = with@fun R|A|.<anonymous>(it: R|A|): R|kotlin/Unit| {
|
||||||
|
R|kotlin/with|<R|IFoo|, R|kotlin/Int|>(R|<local>/fooImpl|, <L> = with@fun R|IFoo|.<anonymous>(it: R|IFoo|): R|kotlin/Unit| {
|
||||||
|
R|/IFoo.foo|
|
||||||
|
R|kotlin/with|<R|IInvoke|, R|kotlin/Int|>(R|<local>/invokeImpl|, <L> = with@fun R|IInvoke|.<anonymous>(it: R|IInvoke|): R|kotlin/Unit| {
|
||||||
|
R|/IFoo.foo|.R|/IInvoke.invoke|()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
Generated
+10
@@ -79,11 +79,21 @@ public class FirResolveTestCaseWithStdlibGenerated extends AbstractFirResolveTes
|
|||||||
runTest("compiler/fir/resolve/testData/resolve/stdlib/helloWorld.kt");
|
runTest("compiler/fir/resolve/testData/resolve/stdlib/helloWorld.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("implicitReceiverOrder.kt")
|
||||||
|
public void testImplicitReceiverOrder() throws Exception {
|
||||||
|
runTest("compiler/fir/resolve/testData/resolve/stdlib/implicitReceiverOrder.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("mapList.kt")
|
@TestMetadata("mapList.kt")
|
||||||
public void testMapList() throws Exception {
|
public void testMapList() throws Exception {
|
||||||
runTest("compiler/fir/resolve/testData/resolve/stdlib/mapList.kt");
|
runTest("compiler/fir/resolve/testData/resolve/stdlib/mapList.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("multipleImplicitReceivers.kt")
|
||||||
|
public void testMultipleImplicitReceivers() throws Exception {
|
||||||
|
runTest("compiler/fir/resolve/testData/resolve/stdlib/multipleImplicitReceivers.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("recursiveBug.kt")
|
@TestMetadata("recursiveBug.kt")
|
||||||
public void testRecursiveBug() throws Exception {
|
public void testRecursiveBug() throws Exception {
|
||||||
runTest("compiler/fir/resolve/testData/resolve/stdlib/recursiveBug.kt");
|
runTest("compiler/fir/resolve/testData/resolve/stdlib/recursiveBug.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user