From 1f120ecd20e76be88d4a24d9431e348c27e58565 Mon Sep 17 00:00:00 2001 From: "Denis.Zharkov" Date: Thu, 25 May 2023 19:09:11 +0200 Subject: [PATCH] K2: Adjust invoke priorities with K1 - At first, get rid of a kind of interceptTowerGroup callback and use explicit towerGroup construction instead - Get rid of INVOKE_RECEIVER (not sure exactly why it was needed) - Make comparison consistent with tower-levels priority of K2 (see the comments in the compareTo code) ^KT-37375 Fixed ^KT-58940 Open --- ...CompilerTestFE10TestdataTestGenerated.java | 6 + ...sticCompilerFE10TestDataTestGenerated.java | 6 + ...eeOldFrontendDiagnosticsTestGenerated.java | 6 + ...siOldFrontendDiagnosticsTestGenerated.java | 6 + .../tower/FirInvokeResolveTowerExtension.kt | 17 +- .../fir/resolve/calls/tower/TowerGroup.kt | 174 +++++++++++++++--- .../invoke/avoidTypeCheckerRecursion.fir.kt | 23 +++ .../invoke/avoidTypeCheckerRecursion.kt | 23 +++ .../invoke/closeInvokesFarVariable.fir.kt | 17 -- .../resolve/invoke/closeInvokesFarVariable.kt | 1 + .../invoke/closerVariableMatterMore.fir.kt | 14 -- .../invoke/closerVariableMatterMore.kt | 1 + .../test/runners/DiagnosticTestGenerated.java | 6 + 13 files changed, 232 insertions(+), 68 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/resolve/invoke/avoidTypeCheckerRecursion.fir.kt create mode 100644 compiler/testData/diagnostics/tests/resolve/invoke/avoidTypeCheckerRecursion.kt delete mode 100644 compiler/testData/diagnostics/tests/resolve/invoke/closeInvokesFarVariable.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/resolve/invoke/closerVariableMatterMore.fir.kt diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java index 47d26bc8463..d78bc253e72 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java @@ -27449,6 +27449,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/resolve/invoke"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true, "multiplatform"); } + @Test + @TestMetadata("avoidTypeCheckerRecursion.kt") + public void testAvoidTypeCheckerRecursion() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/invoke/avoidTypeCheckerRecursion.kt"); + } + @Test @TestMetadata("closeInvokesFarVariable.kt") public void testCloseInvokesFarVariable() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java index 9b5d42ad073..c29a96d5f9f 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java @@ -27449,6 +27449,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/resolve/invoke"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true, "multiplatform"); } + @Test + @TestMetadata("avoidTypeCheckerRecursion.kt") + public void testAvoidTypeCheckerRecursion() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/invoke/avoidTypeCheckerRecursion.kt"); + } + @Test @TestMetadata("closeInvokesFarVariable.kt") public void testCloseInvokesFarVariable() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java index 28e2458cf03..c561dd17054 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java @@ -27449,6 +27449,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/resolve/invoke"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true, "multiplatform"); } + @Test + @TestMetadata("avoidTypeCheckerRecursion.kt") + public void testAvoidTypeCheckerRecursion() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/invoke/avoidTypeCheckerRecursion.kt"); + } + @Test @TestMetadata("closeInvokesFarVariable.kt") public void testCloseInvokesFarVariable() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java index 34a28415a77..9cb4f613665 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java @@ -27461,6 +27461,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/resolve/invoke"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true, "multiplatform"); } + @Test + @TestMetadata("avoidTypeCheckerRecursion.kt") + public void testAvoidTypeCheckerRecursion() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/invoke/avoidTypeCheckerRecursion.kt"); + } + @Test @TestMetadata("closeInvokesFarVariable.kt") public void testCloseInvokesFarVariable() throws Exception { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/FirInvokeResolveTowerExtension.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/FirInvokeResolveTowerExtension.kt index 67576f27ebf..f589e6207f6 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/FirInvokeResolveTowerExtension.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/FirInvokeResolveTowerExtension.kt @@ -240,7 +240,7 @@ internal class FirInvokeResolveTowerExtension( // For calls having a form of "x.(f)()" fun enqueueResolveTasksForImplicitInvokeCall(info: CallInfo, receiverExpression: FirExpression) { val explicitReceiverValue = ExpressionReceiverValue(receiverExpression) - val task = createInvokeFunctionResolveTask(info, TowerGroup.EmptyRoot) + val task = createInvokeFunctionResolveTask(info, TowerGroup.EmptyRootForInvokeReceiver) manager.enqueueResolverTask { task.runResolverForInvoke( info, explicitReceiverValue, @@ -369,11 +369,8 @@ private class InvokeFunctionResolveTask( candidateFactory, ) { - override fun interceptTowerGroup(towerGroup: TowerGroup): TowerGroup { - val invokeGroup = towerGroup.InvokeResolvePriority(InvokeResolvePriority.COMMON_INVOKE) - val max = maxOf(invokeGroup, receiverGroup) - return max.InvokeReceiver(receiverGroup) - } + private fun TowerGroup.withGivenInvokeReceiverGroup(invokeResolvePriority: InvokeResolvePriority): TowerGroup = + InvokeReceiver(receiverGroup, invokeResolvePriority) suspend fun runResolverForInvoke( info: CallInfo, @@ -418,7 +415,7 @@ private class InvokeFunctionResolveTask( ) { processLevel( invokeReceiverValue.toMemberScopeTowerLevel(), - info, TowerGroup.Member.InvokeResolvePriority(InvokeResolvePriority.INVOKE_EXTENSION), + info, TowerGroup.Member.withGivenInvokeReceiverGroup(InvokeResolvePriority.INVOKE_EXTENSION), ExplicitReceiverKind.DISPATCH_RECEIVER ) } @@ -434,8 +431,8 @@ private class InvokeFunctionResolveTask( val towerGroup = TowerGroup .Implicit(depth) - .InvokeExtension - .InvokeResolvePriority(InvokeResolvePriority.INVOKE_EXTENSION) + .InvokeExtensionWithImplicitReceiver + .withGivenInvokeReceiverGroup(InvokeResolvePriority.INVOKE_EXTENSION) processLevel( invokeReceiverValue.toMemberScopeTowerLevel(), @@ -453,7 +450,7 @@ private class InvokeFunctionResolveTask( explicitReceiverKind: ExplicitReceiverKind ) = processLevel( towerLevel, callInfo, - group.InvokeResolvePriority(InvokeResolvePriority.COMMON_INVOKE), + group.withGivenInvokeReceiverGroup(InvokeResolvePriority.COMMON_INVOKE), explicitReceiverKind ) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerGroup.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerGroup.kt index 87445986c5a..8c488c6094a 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerGroup.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerGroup.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.fir.resolve.calls.tower import java.lang.Long.toBinaryString +import java.lang.Long.compareUnsigned sealed class TowerGroupKind(val index: Byte) : Comparable { abstract class WithDepth(index: Byte, val depth: Int) : TowerGroupKind(index) { @@ -42,7 +43,7 @@ sealed class TowerGroupKind(val index: Byte) : Comparable { class ContextReceiverGroup(depth: Int) : WithDepth(7, depth) - data object InvokeExtension : TowerGroupKind(8) + data object InvokeExtensionWithImplicitReceiver : TowerGroupKind(8) data object QualifierValue : TowerGroupKind(9) @@ -145,11 +146,29 @@ private constructor( } } + private fun compareDebugKinds(aDebugKinds: Array, bDebugKinds: Array): Int { + var index = 0 + while (index < aDebugKinds.size) { + if (index >= bDebugKinds.size) return 1 + when { + aDebugKinds[index] < bDebugKinds[index] -> return -1 + aDebugKinds[index] > bDebugKinds[index] -> return 1 + } + index++ + } + if (index < bDebugKinds.size) return -1 + + return 0 + } + + val DEBUG_KINDS_COMPARATOR: Comparator> = Comparator(::compareDebugKinds) + private fun kindOf(kind: TowerGroupKind): TowerGroup { return TowerGroup(subscript(0, kind), debugKindArrayOf(kind)) } val EmptyRoot = TowerGroup(0, EMPTY_KIND_ARRAY) + val EmptyRootForInvokeReceiver = TowerGroup(0, EMPTY_KIND_ARRAY, InvokeResolvePriority.INVOKE_RECEIVER) val Start = kindOf(TowerGroupKind.Start) @@ -186,11 +205,28 @@ private constructor( fun ContextReceiverGroup(depth: Int) = kindOf(TowerGroupKind.ContextReceiverGroup(depth)) - val InvokeExtension get() = kindOf(TowerGroupKind.InvokeExtension) + val InvokeExtensionWithImplicitReceiver get() = kindOf(TowerGroupKind.InvokeExtensionWithImplicitReceiver) fun TopPrioritized(depth: Int) = kindOf(TowerGroupKind.TopPrioritized(depth)) - fun InvokeReceiver(receiverGroup: TowerGroup) = TowerGroup(code, debugKinds, invokeResolvePriority, receiverGroup) + fun InvokeReceiver( + receiverGroup: TowerGroup, + invokeResolvePriority: InvokeResolvePriority + ): TowerGroup { + require(receiverGroup.invokeResolvePriority == InvokeResolvePriority.INVOKE_RECEIVER) { + "Receivers for invoke should be resolved with INVOKE_RECEIVER, but ${receiverGroup.invokeResolvePriority} found" + } + + require(invokeResolvePriority != InvokeResolvePriority.NONE && invokeResolvePriority != InvokeResolvePriority.INVOKE_RECEIVER) { + "invokeResolvePriority should be non-trivial when receiverGroup is specified" + } + + require(receiverGroup.receiverGroup == null) { + "receiverGroup should be trivial, but ${receiverGroup.receiverGroup} was found" + } + + return TowerGroup(code, debugKinds, invokeResolvePriority, receiverGroup) + } // Treating `a.foo()` common calls as more prioritized than `a.foo.invoke()` // It's not the same as TowerGroupKind because it's not about tower levels, but rather a different dimension semantically. @@ -201,38 +237,94 @@ private constructor( } private fun debugCompareTo(other: TowerGroup): Int { - var index = 0 - while (index < debugKinds.size) { - if (index >= other.debugKinds.size) return 1 - when { - debugKinds[index] < other.debugKinds[index] -> return -1 - debugKinds[index] > other.debugKinds[index] -> return 1 - } - index++ - } - if (index < other.debugKinds.size) return -1 + // In case of `receiverGroup` presence, it means that candidate is invoke+variable or invokeExtension+variable. + // In both cases `receiverGroup` means the tower group of a receiver variable. + // For regular invoke+variable, this@debugKinds mean the tower level where the "invoke" function has been found. + // For invokeExtension, this@debugKinds is either TowerGroup.Member in case of explicit extension receiver value, + // like `explicitReceiver.myPropertyOfExtensionFunctionType()` + // or TowerGroup.Implicit(depth).InvokeExtensionWithImplicitReceiver in case of implicit extension receiver value + // like `with(myReceiver) { myPropertyOfExtensionFunctionType() }` + val receiverDebugKinds = receiverGroup?.debugKinds ?: emptyArray() + val otherReceiverDebugKinds = other.receiverGroup?.debugKinds ?: emptyArray() - val actualResult = invokeResolvePriority.compareTo(other.invokeResolvePriority) - if (actualResult == 0) { - return if (receiverGroup == null || other.receiverGroup == null) 0 - else receiverGroup.debugCompareTo(other.receiverGroup) + // Maximums define how far resolution algorithm should go to find the candidate + val thisMax = maxOf(debugKinds, receiverDebugKinds, DEBUG_KINDS_COMPARATOR) + val otherMax = maxOf(other.debugKinds, otherReceiverDebugKinds, DEBUG_KINDS_COMPARATOR) + + // If maximum tower groups are different, then just use more prioritized of maximums. + // It seems more or less obvious, because if one candidate uses X group and other uses Y and X > Y, + // then for the second candidate we wouldn't even need to continue resolution to the level of X, like we would already found + // complete and probably successful candidate on Y. + val maxResult = compareDebugKinds(thisMax, otherMax) + if (maxResult != 0) return maxResult + + // If any of the candidate is not invoke/invokeExtension, choose it + // Otherwise, prefer invoke and then invokeExtension ones + val invokeKindPriority = invokeResolvePriority.compareTo(other.invokeResolvePriority) + if (invokeKindPriority != 0) return invokeKindPriority + + // NB: thisMax == otherMax and kinds of invokes are the same + return if (compareDebugKinds(receiverDebugKinds, thisMax) == 0 && compareDebugKinds(otherReceiverDebugKinds, thisMax) == 0) { + // If both variables/receivers are obtained from the maximum current level, compare invoke/invokeExtension levels + // Also, here it might be viewed as comparing minimums of both candidates + compareDebugKinds(debugKinds, other.debugKinds) + } else { + // Otherwise prefer, the one with the closest variable/receiver + // See how groups are prioritized at org.jetbrains.kotlin.resolve.calls.tower.AbstractInvokeTowerProcessor.process + // Also, see the test testData/diagnostics/tests/resolve/invoke/closerVariableMatterMore.kt + // There, we have two candidates A, B for which maxTowerGroup(A) == maxTowerGroup(B) && minTowerGroup(A) == minTowerGroup(B) + // But we don't assume them as equally placed preferring one with the closest receiver. + // + // NB: receiverDebugKinds == otherReceiverDebugKinds => this == other + // Proof: + // - Let's assume receiverDebugKinds == otherReceiverDebugKinds + // - We know that thisMax == otherMax + // - If receiverDebugKinds == thisMax then otherReceiverDebugKinds == thisMax, so we wouldn't come to the `else` section + // - That means that receiverDebugKinds != thisMax && otherReceiverDebugKinds != thisMax + // - Thus we have thisMax == debugKinds (because thisMax = maxOf(debugKinds, receiverDebugKinds) + // - And the same for otherMax == otherDebugKinds + // + // Considering thisMax == otherMax we have debugKinds == otherDebugKinds + // And with initial condition of receiverDebugKinds == otherReceiverDebugKinds we've got candidates from completely the same level + compareDebugKinds(receiverDebugKinds, otherReceiverDebugKinds) } - return actualResult } override fun compareTo(other: TowerGroup): Int = run { - val result = java.lang.Long.compareUnsigned(code, other.code) - if (result != 0) return@run result - val actualResult = invokeResolvePriority.compareTo(other.invokeResolvePriority) - if (actualResult == 0) { - return@run if (receiverGroup == null || other.receiverGroup == null) 0 - else receiverGroup.compareTo(other.receiverGroup) + // See detailed algorithm description at `debugCompareTo` + // Fast-path + if (receiverGroup == null && other.receiverGroup == null && + invokeResolvePriority == InvokeResolvePriority.NONE && other.invokeResolvePriority == InvokeResolvePriority.NONE + ) { + return@run compareUnsigned(code, other.code) + } + + val receiverCode = receiverGroup?.code ?: 0 // TowerGroup.Start.code + val otherReceiverCode = other.receiverGroup?.code ?: 0 + + val thisMax: Long = if (compareUnsigned(code, receiverCode) >= 0) code else receiverCode + val otherMax: Long = if (compareUnsigned(other.code, otherReceiverCode) >= 0) other.code else otherReceiverCode + + val resultMax = compareUnsigned(thisMax, otherMax) + if (resultMax != 0) return@run resultMax + + val invokeKindPriority = invokeResolvePriority.compareTo(other.invokeResolvePriority) + if (invokeKindPriority != 0) return invokeKindPriority + + return if (compareUnsigned(receiverCode, thisMax) == 0 && compareUnsigned(otherReceiverCode, thisMax) == 0) { + compareUnsigned(code, other.code) + } else { + compareUnsigned(receiverCode, otherReceiverCode) } - return@run actualResult }.also { if (DEBUG) { val debugResult = debugCompareTo(other) - require(debugResult == it) { "Kind comparison incorrect: $this vs $other, expected: $it, $debugResult" } + require(debugResult == it) { + "Kind comparison incorrect: $this vs $other, expected: $it, $debugResult" + } + require((this == other) == (debugResult == 0)) { + "Equality and compareTo should work consistently, but $debugResult found" + } } } @@ -266,5 +358,33 @@ private constructor( } enum class InvokeResolvePriority { - NONE, INVOKE_RECEIVER, COMMON_INVOKE, INVOKE_EXTENSION; + /** + * Looking for regular functions (or variables when the callee is a variable access) + */ + NONE, + + /** + * When resolving a function call, that kind of priority signifies looking for a variable candidates that might serve as a receivers + * for "invoke" calls. + * + * Semantically, that kind of priority is redundant and using NONE works just fine, but having it, helps not to trigger computation of + * property return type when we've already found some successful regular candidate. + * + * See testData/diagnostics/tests/resolve/invoke/errors/typeCheckerRanRecursive.kt as an example which would fail if we use NONE instead + * of INVOKE_RECEIVER + */ + INVOKE_RECEIVER, + + /** + * Looking for "invoke()" function member or extension that would match for already found variable that would be used as a receiver + */ + COMMON_INVOKE, + + /** + * Resolving "invoke()" call when the found variable candidate has a type of extension function type, and we've got a matching extension + * receiver (whether explicit or implicit one). + * + * It should be de-prioritized comparing with regular "invoke()" (COMMON_INVOKE). + */ + INVOKE_EXTENSION } diff --git a/compiler/testData/diagnostics/tests/resolve/invoke/avoidTypeCheckerRecursion.fir.kt b/compiler/testData/diagnostics/tests/resolve/invoke/avoidTypeCheckerRecursion.fir.kt new file mode 100644 index 00000000000..4f2151a11c1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/invoke/avoidTypeCheckerRecursion.fir.kt @@ -0,0 +1,23 @@ +// ISSUE: KT-58940 + +// FILE: JavaIndex.java +public class JavaIndex { + public String getIndexer() { return ""; } +} + +// FILE: main.kt +abstract class KotlinIndex : JavaIndex() { + fun indexer(x: Int): String = "" +} + +class MyKotlinIndex : KotlinIndex() { + // `indexer(1)` call should just request the member function that might be resolved successfully + // and should not request `indexer` variable (as invoke candidates are anyway less prioritized), thus avoiding TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM + val INDEXER = indexer(1) + + override fun getIndexer() = INDEXER +} + +fun main() { + MyKotlinIndex().getIndexer().length +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/resolve/invoke/avoidTypeCheckerRecursion.kt b/compiler/testData/diagnostics/tests/resolve/invoke/avoidTypeCheckerRecursion.kt new file mode 100644 index 00000000000..f9c45a78265 --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/invoke/avoidTypeCheckerRecursion.kt @@ -0,0 +1,23 @@ +// ISSUE: KT-58940 + +// FILE: JavaIndex.java +public class JavaIndex { + public String getIndexer() { return ""; } +} + +// FILE: main.kt +abstract class KotlinIndex : JavaIndex() { + fun indexer(x: Int): String = "" +} + +class MyKotlinIndex : KotlinIndex() { + // `indexer(1)` call should just request the member function that might be resolved successfully + // and should not request `indexer` variable (as invoke candidates are anyway less prioritized), thus avoiding TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM + val INDEXER = indexer(1) + + override fun getIndexer() = INDEXER +} + +fun main() { + MyKotlinIndex().getIndexer().length +} diff --git a/compiler/testData/diagnostics/tests/resolve/invoke/closeInvokesFarVariable.fir.kt b/compiler/testData/diagnostics/tests/resolve/invoke/closeInvokesFarVariable.fir.kt deleted file mode 100644 index 2927aa5e3e7..00000000000 --- a/compiler/testData/diagnostics/tests/resolve/invoke/closeInvokesFarVariable.fir.kt +++ /dev/null @@ -1,17 +0,0 @@ -// ISSUE: KT-37375 - -val foo: Any = Any() - -fun bar() { - operator fun Any.invoke(): String = "" - - fun baz() { - operator fun Any.invoke(): Int = 1 - - fun barbaz() { - takeInt(foo()) - } - } -} - -fun takeInt(x: Int) {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/resolve/invoke/closeInvokesFarVariable.kt b/compiler/testData/diagnostics/tests/resolve/invoke/closeInvokesFarVariable.kt index 8480c37646e..5c2b201ff29 100644 --- a/compiler/testData/diagnostics/tests/resolve/invoke/closeInvokesFarVariable.kt +++ b/compiler/testData/diagnostics/tests/resolve/invoke/closeInvokesFarVariable.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // ISSUE: KT-37375 val foo: Any = Any() diff --git a/compiler/testData/diagnostics/tests/resolve/invoke/closerVariableMatterMore.fir.kt b/compiler/testData/diagnostics/tests/resolve/invoke/closerVariableMatterMore.fir.kt deleted file mode 100644 index 4a53d7ec425..00000000000 --- a/compiler/testData/diagnostics/tests/resolve/invoke/closerVariableMatterMore.fir.kt +++ /dev/null @@ -1,14 +0,0 @@ -// ISSUE: KT-37375 - -fun takeDouble(x: Double) {} - -val bar: Int = 1 -operator fun Double.invoke(): Double = 1.0 // (1) - -fun test_1() { - val bar: Double = 2.0 - operator fun Int.invoke(): Int = 1 // (2) - - val res = bar() // should resolve to (1) - takeDouble(res) // should be OK -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/resolve/invoke/closerVariableMatterMore.kt b/compiler/testData/diagnostics/tests/resolve/invoke/closerVariableMatterMore.kt index 7f6c1ed84cb..7a995905ff4 100644 --- a/compiler/testData/diagnostics/tests/resolve/invoke/closerVariableMatterMore.kt +++ b/compiler/testData/diagnostics/tests/resolve/invoke/closerVariableMatterMore.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // ISSUE: KT-37375 fun takeDouble(x: Double) {} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index ae33e63aa31..3eba964476c 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -28293,6 +28293,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/resolve/invoke"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true); } + @Test + @TestMetadata("avoidTypeCheckerRecursion.kt") + public void testAvoidTypeCheckerRecursion() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/invoke/avoidTypeCheckerRecursion.kt"); + } + @Test @TestMetadata("closeInvokesFarVariable.kt") public void testCloseInvokesFarVariable() throws Exception {