From 715a73c8fba806165d8661a58c9f0ea04f4f5415 Mon Sep 17 00:00:00 2001 From: "Denis.Zharkov" Date: Tue, 1 Nov 2022 19:21:16 +0100 Subject: [PATCH] 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 --- ...CompilerTestFE10TestdataTestGenerated.java | 6 ++++ ...irOldFrontendDiagnosticsTestGenerated.java | 6 ++++ ...DiagnosticsWithLightTreeTestGenerated.java | 6 ++++ .../fir/resolve/calls/ResolutionStages.kt | 6 ++-- .../model/NewConstraintSystemImpl.kt | 36 ++++++++++++++++++- .../smartCastForkForExpectType.fir.kt | 28 +++++++++++++++ .../inference/smartCastForkForExpectType.kt | 28 +++++++++++++++ .../tests/inference/tooEagerSmartcast.fir.kt | 2 +- .../test/runners/DiagnosticTestGenerated.java | 6 ++++ 9 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/inference/smartCastForkForExpectType.fir.kt create mode 100644 compiler/testData/diagnostics/tests/inference/smartCastForkForExpectType.kt 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 {