K2: Fix false-positive overload ambiguity after smart cast
Mostly, the change is just mirroring the same logic from K1 OverloadingConflictResolver ^KT-58524 Fixed
This commit is contained in:
committed by
Space Team
parent
efdecc63cb
commit
442844f165
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
@@ -35,7 +35,7 @@ fun <D : FirCallableSymbol<*>> filterOutOverridden(
|
||||
}
|
||||
|
||||
// Whether f overrides g
|
||||
private fun <D : FirCallableSymbol<*>> overrides(
|
||||
fun <D : FirCallableSymbol<*>> overrides(
|
||||
f: MemberWithBaseScope<D>,
|
||||
gMember: D,
|
||||
processAllOverridden: ProcessAllOverridden<D>,
|
||||
|
||||
+62
-1
@@ -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<Candidate> = chooseMaximallySpecificCandidates(candidates, discriminateAbstracts, discriminateGenerics = true)
|
||||
|
||||
/**
|
||||
* Partial mirror of [org.jetbrains.kotlin.resolve.calls.results.OverloadingConflictResolver.chooseMaximallySpecificCandidates]
|
||||
*/
|
||||
private fun chooseMaximallySpecificCandidates(
|
||||
candidates: Set<Candidate>,
|
||||
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<Candidate>,
|
||||
): Set<Candidate> {
|
||||
if (candidateSet.size <= 1) return candidateSet
|
||||
|
||||
val result = mutableSetOf<Candidate>()
|
||||
|
||||
// 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<FirCallableSymbol<*>>() == otherOriginal) return true
|
||||
|
||||
val scope = originScope as? FirTypeScope ?: return false
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val overriddenProducer = when (symbol) {
|
||||
is FirNamedFunctionSymbol -> FirTypeScope::processOverriddenFunctions as ProcessAllOverridden<FirCallableSymbol<*>>
|
||||
is FirPropertySymbol -> FirTypeScope::processOverriddenProperties as ProcessAllOverridden<FirCallableSymbol<*>>
|
||||
else -> return false
|
||||
}
|
||||
|
||||
return overrides(MemberWithBaseScope(symbol, scope), otherOriginal, overriddenProducer)
|
||||
}
|
||||
|
||||
private fun chooseCandidatesWithMostSpecificInvokeReceiver(candidates: Set<Candidate>): Set<Candidate> {
|
||||
val propertyReceiverCandidates = candidates.mapTo(mutableSetOf()) {
|
||||
it.callInfo.candidateForCommonInvokeReceiver
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// ISSUE: KT-58524
|
||||
|
||||
interface MyGenericInterface<T> {
|
||||
fun update(f: (T) -> T) {}
|
||||
}
|
||||
|
||||
interface SubGenericInterface<F> : MyGenericInterface<F> {
|
||||
override fun update(f: (F) -> F) {}
|
||||
}
|
||||
|
||||
interface SubInterfaceInt : SubGenericInterface<Int> {
|
||||
override fun update(f: (Int) -> Int) {}
|
||||
}
|
||||
|
||||
fun foo1(a: MyGenericInterface<Number>) {
|
||||
a <!UNCHECKED_CAST!>as MyGenericInterface<Int><!>
|
||||
|
||||
a.update { expectInt(it) }
|
||||
}
|
||||
|
||||
fun foo2(a: MyGenericInterface<Number>) {
|
||||
a <!UNCHECKED_CAST!>as SubGenericInterface<Int><!>
|
||||
|
||||
a.update { expectInt(it) }
|
||||
}
|
||||
|
||||
fun foo3(a: MyGenericInterface<Number>) {
|
||||
a as SubInterfaceInt
|
||||
|
||||
a.update { expectInt(it) }
|
||||
}
|
||||
|
||||
fun expectInt(w: Int): Int = w
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// ISSUE: KT-58524
|
||||
|
||||
interface MyGenericInterface<T> {
|
||||
fun update(f: (T) -> T) {}
|
||||
}
|
||||
|
||||
interface SubGenericInterface<F> : MyGenericInterface<F> {
|
||||
override fun update(f: (F) -> F) {}
|
||||
}
|
||||
|
||||
interface SubInterfaceInt : SubGenericInterface<Int> {
|
||||
override fun update(f: (Int) -> Int) {}
|
||||
}
|
||||
|
||||
fun foo1(a: MyGenericInterface<Number>) {
|
||||
a <!UNCHECKED_CAST!>as MyGenericInterface<Int><!>
|
||||
|
||||
<!DEBUG_INFO_SMARTCAST!>a<!>.update { expectInt(it) }
|
||||
}
|
||||
|
||||
fun foo2(a: MyGenericInterface<Number>) {
|
||||
a <!UNCHECKED_CAST!>as SubGenericInterface<Int><!>
|
||||
|
||||
<!DEBUG_INFO_SMARTCAST!>a<!>.update { expectInt(it) }
|
||||
}
|
||||
|
||||
fun foo3(a: MyGenericInterface<Number>) {
|
||||
a as SubInterfaceInt
|
||||
|
||||
<!DEBUG_INFO_SMARTCAST!>a<!>.update { expectInt(it) }
|
||||
}
|
||||
|
||||
fun expectInt(w: Int): Int = w
|
||||
Generated
+6
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user