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 9553f6187f2..9252c57aae6 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 @@ -27317,6 +27317,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/extensionReceiverAndVarargs.kt"); } + @Test + @TestMetadata("filteringOutOverrides.kt") + public void testFilteringOutOverrides() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/filteringOutOverrides.kt"); + } + @Test @TestMetadata("genericClash.kt") public void testGenericClash() 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 06ffff2e5ea..eab37bec9d3 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 @@ -27317,6 +27317,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/extensionReceiverAndVarargs.kt"); } + @Test + @TestMetadata("filteringOutOverrides.kt") + public void testFilteringOutOverrides() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/filteringOutOverrides.kt"); + } + @Test @TestMetadata("genericClash.kt") public void testGenericClash() 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 bd24dda26d6..3a4b7a32353 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 @@ -27317,6 +27317,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/extensionReceiverAndVarargs.kt"); } + @Test + @TestMetadata("filteringOutOverrides.kt") + public void testFilteringOutOverrides() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/filteringOutOverrides.kt"); + } + @Test @TestMetadata("genericClash.kt") public void testGenericClash() 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 78f9077e4d2..e956c3ef7fc 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 @@ -27329,6 +27329,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/extensionReceiverAndVarargs.kt"); } + @Test + @TestMetadata("filteringOutOverrides.kt") + public void testFilteringOutOverrides() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/filteringOutOverrides.kt"); + } + @Test @TestMetadata("genericClash.kt") public void testGenericClash() throws Exception { diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirOverrideUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirOverrideUtils.kt index b58ea026b4e..ae3dda4e543 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirOverrideUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirOverrideUtils.kt @@ -35,7 +35,7 @@ fun > filterOutOverridden( } // Whether f overrides g -private fun > overrides( +fun > overrides( f: MemberWithBaseScope, gMember: D, processAllOverridden: ProcessAllOverridden, diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConeOverloadConflictResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConeOverloadConflictResolver.kt index 7516a16401d..bb25979bea7 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConeOverloadConflictResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConeOverloadConflictResolver.kt @@ -14,8 +14,14 @@ import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents import org.jetbrains.kotlin.fir.resolve.inference.ConeTypeParameterBasedTypeVariable import org.jetbrains.kotlin.fir.resolve.inference.InferenceComponents import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap +import org.jetbrains.kotlin.fir.scopes.* +import org.jetbrains.kotlin.fir.scopes.impl.overrides import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag +import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.fir.unwrapSubstitutionOverrides import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition import org.jetbrains.kotlin.resolve.calls.results.FlatSignature @@ -44,6 +50,9 @@ class ConeOverloadConflictResolver( discriminateAbstracts: Boolean, ): Set = chooseMaximallySpecificCandidates(candidates, discriminateAbstracts, discriminateGenerics = true) + /** + * Partial mirror of [org.jetbrains.kotlin.resolve.calls.results.OverloadingConflictResolver.chooseMaximallySpecificCandidates] + */ private fun chooseMaximallySpecificCandidates( candidates: Set, discriminateAbstracts: Boolean, @@ -57,8 +66,11 @@ class ConeOverloadConflictResolver( else candidates + // The same logic as at + val noOverrides = filterOverrides(fixedCandidates) + return chooseMaximallySpecificCandidates( - fixedCandidates, + noOverrides, discriminateGenerics, discriminateAbstracts, discriminateSAMs = true, @@ -67,6 +79,55 @@ class ConeOverloadConflictResolver( ) } + /** + * See K1 version at OverridingUtil.filterOverrides + */ + private fun filterOverrides( + candidateSet: Set, + ): Set { + if (candidateSet.size <= 1) return candidateSet + + val result = mutableSetOf() + + // Assuming `overrides` is a partial order, this loop leaves minimal elements of `candidateSet` in `result`. + // Namely, it leaves in `result` only candidates, for any pair of them (x, y): !x.overrides(y) && !y.overrides(x) + // And for any pair original candidates (x, y) if x.overrides(y) && !y.overrides(x) then `x` belongs `result` + outerLoop@ for (me in candidateSet) { + val iterator = result.iterator() + while (iterator.hasNext()) { + val other = iterator.next() + if (me.overrides(other)) { + iterator.remove() + } else if (other.overrides(me)) { + continue@outerLoop + } + } + + result.add(me) + } + + require(result.isNotEmpty()) { "All candidates filtered out from $candidateSet" } + return result + } + + private fun Candidate.overrides(other: Candidate): Boolean { + if (symbol !is FirCallableSymbol || other.symbol !is FirCallableSymbol) return false + + val otherOriginal = other.symbol.unwrapSubstitutionOverrides() + if (symbol.unwrapSubstitutionOverrides>() == otherOriginal) return true + + val scope = originScope as? FirTypeScope ?: return false + + @Suppress("UNCHECKED_CAST") + val overriddenProducer = when (symbol) { + is FirNamedFunctionSymbol -> FirTypeScope::processOverriddenFunctions as ProcessAllOverridden> + is FirPropertySymbol -> FirTypeScope::processOverriddenProperties as ProcessAllOverridden> + else -> return false + } + + return overrides(MemberWithBaseScope(symbol, scope), otherOriginal, overriddenProducer) + } + private fun chooseCandidatesWithMostSpecificInvokeReceiver(candidates: Set): Set { val propertyReceiverCandidates = candidates.mapTo(mutableSetOf()) { it.callInfo.candidateForCommonInvokeReceiver diff --git a/compiler/testData/diagnostics/tests/resolve/overloadConflicts/filteringOutOverrides.fir.kt b/compiler/testData/diagnostics/tests/resolve/overloadConflicts/filteringOutOverrides.fir.kt new file mode 100644 index 00000000000..0ee49a2b3b5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/overloadConflicts/filteringOutOverrides.fir.kt @@ -0,0 +1,33 @@ +// ISSUE: KT-58524 + +interface MyGenericInterface { + fun update(f: (T) -> T) {} +} + +interface SubGenericInterface : MyGenericInterface { + override fun update(f: (F) -> F) {} +} + +interface SubInterfaceInt : SubGenericInterface { + override fun update(f: (Int) -> Int) {} +} + +fun foo1(a: MyGenericInterface) { + a as MyGenericInterface + + a.update { expectInt(it) } +} + +fun foo2(a: MyGenericInterface) { + a as SubGenericInterface + + a.update { expectInt(it) } +} + +fun foo3(a: MyGenericInterface) { + a as SubInterfaceInt + + a.update { expectInt(it) } +} + +fun expectInt(w: Int): Int = w \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/resolve/overloadConflicts/filteringOutOverrides.kt b/compiler/testData/diagnostics/tests/resolve/overloadConflicts/filteringOutOverrides.kt new file mode 100644 index 00000000000..c95d539622c --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/overloadConflicts/filteringOutOverrides.kt @@ -0,0 +1,33 @@ +// ISSUE: KT-58524 + +interface MyGenericInterface { + fun update(f: (T) -> T) {} +} + +interface SubGenericInterface : MyGenericInterface { + override fun update(f: (F) -> F) {} +} + +interface SubInterfaceInt : SubGenericInterface { + override fun update(f: (Int) -> Int) {} +} + +fun foo1(a: MyGenericInterface) { + a as MyGenericInterface + + a.update { expectInt(it) } +} + +fun foo2(a: MyGenericInterface) { + a as SubGenericInterface + + a.update { expectInt(it) } +} + +fun foo3(a: MyGenericInterface) { + a as SubInterfaceInt + + a.update { expectInt(it) } +} + +fun expectInt(w: Int): Int = w 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 c04825a65f2..9af6550c9fd 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 @@ -28101,6 +28101,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/extensionReceiverAndVarargs.kt"); } + @Test + @TestMetadata("filteringOutOverrides.kt") + public void testFilteringOutOverrides() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/filteringOutOverrides.kt"); + } + @Test @TestMetadata("genericClash.kt") public void testGenericClash() throws Exception {