K2: Support inference case with a mix of smart-cast and expected type
The idea is that we should not fix (i.e. choose any of the fork branches) on the stage of candidate processing before completion, but it's enough just to check that current state can be converged to success. And when completion starts, and we add expected type to the system, we've got more information to choose the correct fork branch. NB: The old `processForkConstraints` is being called just at the beginning of the completion phase. ^KT-43296 In Progress
This commit is contained in:
committed by
Space Team
parent
77e197d46a
commit
715a73c8fb
+6
@@ -14276,6 +14276,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
||||
runTest("compiler/testData/diagnostics/tests/inference/smartCastFork.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("smartCastForkForExpectType.kt")
|
||||
public void testSmartCastForkForExpectType() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inference/smartCastForkForExpectType.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("specialCallsWithCallableReferences.kt")
|
||||
public void testSpecialCallsWithCallableReferences() throws Exception {
|
||||
|
||||
+6
@@ -14276,6 +14276,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
||||
runTest("compiler/testData/diagnostics/tests/inference/smartCastFork.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("smartCastForkForExpectType.kt")
|
||||
public void testSmartCastForkForExpectType() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inference/smartCastForkForExpectType.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("specialCallsWithCallableReferences.kt")
|
||||
public void testSpecialCallsWithCallableReferences() throws Exception {
|
||||
|
||||
+6
@@ -14276,6 +14276,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
||||
runTest("compiler/testData/diagnostics/tests/inference/smartCastFork.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("smartCastForkForExpectType.kt")
|
||||
public void testSmartCastForkForExpectType() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inference/smartCastForkForExpectType.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("specialCallsWithCallableReferences.kt")
|
||||
public void testSpecialCallsWithCallableReferences() throws Exception {
|
||||
|
||||
@@ -683,10 +683,8 @@ internal object ConstraintSystemForks : ResolutionStage() {
|
||||
override suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext) {
|
||||
if (candidate.system.hasContradiction) return
|
||||
|
||||
candidate.system.processForkConstraints()
|
||||
|
||||
if (candidate.system.hasContradiction) {
|
||||
sink.yieldDiagnostic(candidate.system.errors.firstOrNull()?.let(::InferenceError) ?: InapplicableCandidate)
|
||||
candidate.system.checkIfForksMightBeSuccessfullyResolved()?.let { csError ->
|
||||
sink.yieldDiagnostic(InferenceError(csError))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+35
-1
@@ -11,7 +11,10 @@ import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzerC
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.*
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.components.*
|
||||
import org.jetbrains.kotlin.resolve.checkers.EmptyIntersectionTypeInfo
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.AbstractTypeApproximator
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
|
||||
import org.jetbrains.kotlin.types.isDefinitelyEmpty
|
||||
import org.jetbrains.kotlin.types.model.*
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import org.jetbrains.kotlin.utils.SmartSet
|
||||
@@ -372,6 +375,37 @@ class NewConstraintSystemImpl(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if current state of forked constraints is not contradictory.
|
||||
*
|
||||
* That function is expected to be pure, i.e. it should leave the system in the same state it was found before the call.
|
||||
*
|
||||
* @return null if for each fork we found a possible branch that doesn't contradict with all other constraints
|
||||
* @return non-nullable error if there's a contradiction we didn't manage to resolve
|
||||
*/
|
||||
fun checkIfForksMightBeSuccessfullyResolved(): ConstraintSystemError? {
|
||||
if (constraintsFromAllForkPoints.isEmpty()) return null
|
||||
|
||||
val allForkPointsData = constraintsFromAllForkPoints.toList()
|
||||
constraintsFromAllForkPoints.clear()
|
||||
|
||||
var result: ConstraintSystemError? = null
|
||||
runTransaction {
|
||||
for ((position, forkPointData) in allForkPointsData) {
|
||||
if (!processForkPointData(forkPointData, position)) {
|
||||
result = NoSuccessfulFork(position)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
constraintsFromAllForkPoints.addAll(allForkPointsData)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if there is a successful constraints set for the fork
|
||||
*/
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
// SKIP_TXT
|
||||
|
||||
interface Slice<V>
|
||||
|
||||
interface A
|
||||
interface B : A
|
||||
interface C : A
|
||||
|
||||
val SL0: Slice<A> = TODO()
|
||||
val SL1: Slice<B> = TODO()
|
||||
val SL2: Slice<C> = TODO()
|
||||
|
||||
fun <X> foo(s: Slice<X>): X? {
|
||||
if (s.hashCode() == 0) {
|
||||
return bar(s)
|
||||
}
|
||||
|
||||
if (s === SL0) {
|
||||
return bar(s)
|
||||
}
|
||||
|
||||
if (s === SL1 || s === SL2) {
|
||||
return bar(s)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun <Y> bar(w: Slice<Y>): Y? = null
|
||||
@@ -0,0 +1,28 @@
|
||||
// SKIP_TXT
|
||||
|
||||
interface Slice<V>
|
||||
|
||||
interface A
|
||||
interface B : A
|
||||
interface C : A
|
||||
|
||||
val SL0: Slice<A> = TODO()
|
||||
val SL1: Slice<B> = TODO()
|
||||
val SL2: Slice<C> = TODO()
|
||||
|
||||
fun <X> foo(s: Slice<X>): X? {
|
||||
if (s.hashCode() == 0) {
|
||||
return bar(s)
|
||||
}
|
||||
|
||||
if (s === SL0) {
|
||||
return <!TYPE_MISMATCH, TYPE_MISMATCH!>bar(<!DEBUG_INFO_SMARTCAST!>s<!>)<!>
|
||||
}
|
||||
|
||||
if (s === SL1 || s === SL2) {
|
||||
return bar(s)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun <Y> bar(w: Slice<Y>): Y? = null
|
||||
@@ -19,7 +19,7 @@ fun <X> InvBase<X>.myLastInv(): X = TODO()
|
||||
|
||||
fun <T> fooInv(x: InvBase<T>) {
|
||||
if (x is InvDerived<*>) {
|
||||
val l: T = <!INITIALIZER_TYPE_MISMATCH, TYPE_MISMATCH!>x.myLastInv()<!> // required T, found Cap(*). Only in NI
|
||||
val l: T = x.myLastInv() // required T, found Cap(*). Only in NI
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Generated
+6
@@ -14282,6 +14282,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
runTest("compiler/testData/diagnostics/tests/inference/smartCastFork.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("smartCastForkForExpectType.kt")
|
||||
public void testSmartCastForkForExpectType() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inference/smartCastForkForExpectType.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("specialCallsWithCallableReferences.kt")
|
||||
public void testSpecialCallsWithCallableReferences() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user