diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index 11802f5456f..26b7692b913 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -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 { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index 7f818c757e7..72997bd2a26 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -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 { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index f1617953378..465c6affc41 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -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 { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt index 7b4372db378..52f9e085bd3 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt @@ -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)) } } } diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt index 8c6f50b3840..e4b87c9c963 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt @@ -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 */ diff --git a/compiler/testData/diagnostics/tests/inference/smartCastForkForExpectType.fir.kt b/compiler/testData/diagnostics/tests/inference/smartCastForkForExpectType.fir.kt new file mode 100644 index 00000000000..fdafdd25251 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/smartCastForkForExpectType.fir.kt @@ -0,0 +1,28 @@ +// SKIP_TXT + +interface Slice + +interface A +interface B : A +interface C : A + +val SL0: Slice = TODO() +val SL1: Slice = TODO() +val SL2: Slice = TODO() + +fun foo(s: Slice): 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 bar(w: Slice): Y? = null diff --git a/compiler/testData/diagnostics/tests/inference/smartCastForkForExpectType.kt b/compiler/testData/diagnostics/tests/inference/smartCastForkForExpectType.kt new file mode 100644 index 00000000000..6ce581f5f98 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/smartCastForkForExpectType.kt @@ -0,0 +1,28 @@ +// SKIP_TXT + +interface Slice + +interface A +interface B : A +interface C : A + +val SL0: Slice = TODO() +val SL1: Slice = TODO() +val SL2: Slice = TODO() + +fun foo(s: Slice): 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 bar(w: Slice): Y? = null diff --git a/compiler/testData/diagnostics/tests/inference/tooEagerSmartcast.fir.kt b/compiler/testData/diagnostics/tests/inference/tooEagerSmartcast.fir.kt index 7abe51b59f4..65e32ca6a0b 100644 --- a/compiler/testData/diagnostics/tests/inference/tooEagerSmartcast.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/tooEagerSmartcast.fir.kt @@ -19,7 +19,7 @@ fun InvBase.myLastInv(): X = TODO() fun fooInv(x: InvBase) { if (x is InvDerived<*>) { - val l: T = x.myLastInv() // required T, found Cap(*). Only in NI + val l: T = x.myLastInv() // required T, found Cap(*). Only in NI } } 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 88880fe4b26..afeb692a1f5 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 @@ -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 {