From a02cb16fb26cea2cd66be411deb4d5a41c585afa Mon Sep 17 00:00:00 2001 From: "Denis.Zharkov" Date: Fri, 4 Aug 2023 19:52:29 +0200 Subject: [PATCH] K2: Rewrite delegate inference The main idea is getting rid of stub types and using just type variables See more detailed description at docs/fir/delegated_property_inference.md The problem with stub types is that they need really special treatment in many places, and on the other hand, there are no clear contracts on how they should work (that regularly leads to bugs like KT-59529) ^KT-61060 Fixed ^KT-61075 Fixed ^KT-61077 Fixed ^KT-59529 Fixed ^KT-61633 Related ^KT-61618 Related ^KT-61740 Related ^KT-59107 Related ^KT-61747 Related ^KT-61077 Related ^KT-61781 Related --- ...CompilerTestFE10TestdataTestGenerated.java | 66 ++++ ...sticCompilerFE10TestDataTestGenerated.java | 66 ++++ .../delegates/delegateWithAnonymousObject.dot | 2 +- ...eeOldFrontendDiagnosticsTestGenerated.java | 66 ++++ ...siOldFrontendDiagnosticsTestGenerated.java | 66 ++++ ...LightTreeBlackBoxCodegenTestGenerated.java | 6 + ...hIrFakeOverrideGeneratorTestGenerated.java | 12 + .../FirPsiBlackBoxCodegenTestGenerated.java | 6 + .../kotlin/fir/resolve/calls/Candidate.kt | 2 +- .../fir/resolve/calls/CandidateFactory.kt | 2 +- .../inference/ConstraintSystemCompleter.kt | 40 +-- .../inference/FirBuilderInferenceSession.kt | 14 +- .../fir/resolve/inference/FirCallCompleter.kt | 24 +- .../FirDelegatedPropertyInferenceSession.kt | 284 ++++-------------- .../resolve/inference/FirInferenceSession.kt | 39 +-- .../FirInferenceSessionForChainedResolve.kt | 10 +- ...rCallCompletionResultsWriterTransformer.kt | 26 +- .../body/resolve/BodyResolveContext.kt | 30 +- .../FirDeclarationsResolveTransformer.kt | 159 +++++++--- .../FirExpressionsResolveTransformer.kt | 6 +- .../DiagnosticReporterByTrackingStrategy.kt | 4 +- .../resolve/calls/inference/InferenceUtils.kt | 2 +- .../ConstraintSystemCompletionContext.kt | 5 + .../components/ResultTypeResolver.kt | 2 +- .../components/VariableFixationFinder.kt | 19 +- .../model/ConstraintPositionAndErrors.kt | 2 + .../inference/model/ConstraintStorage.kt | 16 + .../model/MutableConstraintStorage.kt | 2 + .../model/NewConstraintSystemImpl.kt | 46 +++ .../box/delegatedProperty/withInvoke.kt | 17 ++ .../inference/annotatedAccessor.kt | 23 ++ .../fullyCompleteDelegateWhenPossible.kt | 16 + .../inference/lambdaWithMultipleReturns.kt | 19 ++ .../inference/nothingTypeThisRef.kt | 22 ++ ...ixationResultContainsOtherInnerVariable.kt | 17 ++ ...eDelegateReturningOuterTypeVariable.fir.kt | 19 ++ ...ovideDelegateReturningOuterTypeVariable.kt | 19 ++ ...ulProvideDelegateLeadsToRedGetValue.fir.kt | 11 + ...essfulProvideDelegateLeadsToRedGetValue.kt | 11 + .../inference/typeVariablesFlow.kt | 28 ++ .../inference/typeVariablesFlowComplex.kt | 37 +++ ...lutionWithStubTypesAndExtensionGetValue.kt | 10 + .../provideDelegate/setValue.fir.kt | 4 +- .../provideDelegate/setValue.kt | 2 +- .../delegatedProperty/withInvokes.fir.kt | 22 ++ .../tests/delegatedProperty/withInvokes.kt | 22 ++ .../test/runners/DiagnosticTestGenerated.java | 66 ++++ .../IrBlackBoxCodegenTestGenerated.java | 6 + ...kBoxCodegenWithIrInlinerTestGenerated.java | 6 + .../LightAnalysisModeTestGenerated.java | 5 + docs/fir/builder_inference.md | 4 - docs/fir/delegated_property_inference.md | 142 +++++++++ docs/fir/inference.md | 12 + 53 files changed, 1183 insertions(+), 381 deletions(-) create mode 100644 compiler/testData/codegen/box/delegatedProperty/withInvoke.kt create mode 100644 compiler/testData/diagnostics/tests/delegatedProperty/inference/annotatedAccessor.kt create mode 100644 compiler/testData/diagnostics/tests/delegatedProperty/inference/fullyCompleteDelegateWhenPossible.kt create mode 100644 compiler/testData/diagnostics/tests/delegatedProperty/inference/lambdaWithMultipleReturns.kt create mode 100644 compiler/testData/diagnostics/tests/delegatedProperty/inference/nothingTypeThisRef.kt create mode 100644 compiler/testData/diagnostics/tests/delegatedProperty/inference/provideDelegateFixationResultContainsOtherInnerVariable.kt create mode 100644 compiler/testData/diagnostics/tests/delegatedProperty/inference/provideDelegateReturningOuterTypeVariable.fir.kt create mode 100644 compiler/testData/diagnostics/tests/delegatedProperty/inference/provideDelegateReturningOuterTypeVariable.kt create mode 100644 compiler/testData/diagnostics/tests/delegatedProperty/inference/successfulProvideDelegateLeadsToRedGetValue.fir.kt create mode 100644 compiler/testData/diagnostics/tests/delegatedProperty/inference/successfulProvideDelegateLeadsToRedGetValue.kt create mode 100644 compiler/testData/diagnostics/tests/delegatedProperty/inference/typeVariablesFlow.kt create mode 100644 compiler/testData/diagnostics/tests/delegatedProperty/inference/typeVariablesFlowComplex.kt create mode 100644 compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/provideDelegateResolutionWithStubTypesAndExtensionGetValue.kt create mode 100644 compiler/testData/diagnostics/tests/delegatedProperty/withInvokes.fir.kt create mode 100644 compiler/testData/diagnostics/tests/delegatedProperty/withInvokes.kt create mode 100644 docs/fir/delegated_property_inference.md create mode 100644 docs/fir/inference.md 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 244b33e1f0c..20d5ae4f56f 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 @@ -9550,6 +9550,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia runTest("compiler/testData/diagnostics/tests/delegatedProperty/useTypeParameterOfExtensionProperty_Disabled.kt"); } + @Test + @TestMetadata("withInvokes.kt") + public void testWithInvokes() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/withInvokes.kt"); + } + @Test @TestMetadata("wrongCountOfParametersInGet.kt") public void testWrongCountOfParametersInGet() throws Exception { @@ -9577,6 +9583,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/delegatedProperty/inference"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true, "multiplatform"); } + @Test + @TestMetadata("annotatedAccessor.kt") + public void testAnnotatedAccessor() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/annotatedAccessor.kt"); + } + @Test @TestMetadata("callableReferenceArgumentInDelegatedExpression.kt") public void testCallableReferenceArgumentInDelegatedExpression() throws Exception { @@ -9625,6 +9637,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/extensionProperty.kt"); } + @Test + @TestMetadata("fullyCompleteDelegateWhenPossible.kt") + public void testFullyCompleteDelegateWhenPossible() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/fullyCompleteDelegateWhenPossible.kt"); + } + @Test @TestMetadata("genericMethodInGenericClass.kt") public void testGenericMethodInGenericClass() throws Exception { @@ -9655,6 +9673,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/labeledDelegatedExpression.kt"); } + @Test + @TestMetadata("lambdaWithMultipleReturns.kt") + public void testLambdaWithMultipleReturns() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/lambdaWithMultipleReturns.kt"); + } + @Test @TestMetadata("manyIncompleteCandidates.kt") public void testManyIncompleteCandidates() throws Exception { @@ -9691,12 +9715,36 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/notNullAssertionInLocalDelegated.kt"); } + @Test + @TestMetadata("nothingTypeThisRef.kt") + public void testNothingTypeThisRef() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/nothingTypeThisRef.kt"); + } + + @Test + @TestMetadata("provideDelegateFixationResultContainsOtherInnerVariable.kt") + public void testProvideDelegateFixationResultContainsOtherInnerVariable() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/provideDelegateFixationResultContainsOtherInnerVariable.kt"); + } + + @Test + @TestMetadata("provideDelegateReturningOuterTypeVariable.kt") + public void testProvideDelegateReturningOuterTypeVariable() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/provideDelegateReturningOuterTypeVariable.kt"); + } + @Test @TestMetadata("resultTypeOfLambdaForConventionMethods.kt") public void testResultTypeOfLambdaForConventionMethods() throws Exception { runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/resultTypeOfLambdaForConventionMethods.kt"); } + @Test + @TestMetadata("successfulProvideDelegateLeadsToRedGetValue.kt") + public void testSuccessfulProvideDelegateLeadsToRedGetValue() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/successfulProvideDelegateLeadsToRedGetValue.kt"); + } + @Test @TestMetadata("tryInGenerated.kt") public void testTryInGenerated() throws Exception { @@ -9709,6 +9757,18 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/typeOfLazyDelegatedPropertyWithObject.kt"); } + @Test + @TestMetadata("typeVariablesFlow.kt") + public void testTypeVariablesFlow() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/typeVariablesFlow.kt"); + } + + @Test + @TestMetadata("typeVariablesFlowComplex.kt") + public void testTypeVariablesFlowComplex() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/typeVariablesFlowComplex.kt"); + } + @Test @TestMetadata("useCompleterWithoutExpectedType.kt") public void testUseCompleterWithoutExpectedType() throws Exception { @@ -9833,6 +9893,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia runTest("compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/provideDelegateResolutionWithStubTypes.kt"); } + @Test + @TestMetadata("provideDelegateResolutionWithStubTypesAndExtensionGetValue.kt") + public void testProvideDelegateResolutionWithStubTypesAndExtensionGetValue() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/provideDelegateResolutionWithStubTypesAndExtensionGetValue.kt"); + } + @Test @TestMetadata("setValue.kt") public void testSetValue() 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 820d966675b..092a158504f 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 @@ -9550,6 +9550,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated runTest("compiler/testData/diagnostics/tests/delegatedProperty/useTypeParameterOfExtensionProperty_Disabled.kt"); } + @Test + @TestMetadata("withInvokes.kt") + public void testWithInvokes() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/withInvokes.kt"); + } + @Test @TestMetadata("wrongCountOfParametersInGet.kt") public void testWrongCountOfParametersInGet() throws Exception { @@ -9577,6 +9583,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/delegatedProperty/inference"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true, "multiplatform"); } + @Test + @TestMetadata("annotatedAccessor.kt") + public void testAnnotatedAccessor() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/annotatedAccessor.kt"); + } + @Test @TestMetadata("callableReferenceArgumentInDelegatedExpression.kt") public void testCallableReferenceArgumentInDelegatedExpression() throws Exception { @@ -9625,6 +9637,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/extensionProperty.kt"); } + @Test + @TestMetadata("fullyCompleteDelegateWhenPossible.kt") + public void testFullyCompleteDelegateWhenPossible() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/fullyCompleteDelegateWhenPossible.kt"); + } + @Test @TestMetadata("genericMethodInGenericClass.kt") public void testGenericMethodInGenericClass() throws Exception { @@ -9655,6 +9673,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/labeledDelegatedExpression.kt"); } + @Test + @TestMetadata("lambdaWithMultipleReturns.kt") + public void testLambdaWithMultipleReturns() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/lambdaWithMultipleReturns.kt"); + } + @Test @TestMetadata("manyIncompleteCandidates.kt") public void testManyIncompleteCandidates() throws Exception { @@ -9691,12 +9715,36 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/notNullAssertionInLocalDelegated.kt"); } + @Test + @TestMetadata("nothingTypeThisRef.kt") + public void testNothingTypeThisRef() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/nothingTypeThisRef.kt"); + } + + @Test + @TestMetadata("provideDelegateFixationResultContainsOtherInnerVariable.kt") + public void testProvideDelegateFixationResultContainsOtherInnerVariable() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/provideDelegateFixationResultContainsOtherInnerVariable.kt"); + } + + @Test + @TestMetadata("provideDelegateReturningOuterTypeVariable.kt") + public void testProvideDelegateReturningOuterTypeVariable() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/provideDelegateReturningOuterTypeVariable.kt"); + } + @Test @TestMetadata("resultTypeOfLambdaForConventionMethods.kt") public void testResultTypeOfLambdaForConventionMethods() throws Exception { runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/resultTypeOfLambdaForConventionMethods.kt"); } + @Test + @TestMetadata("successfulProvideDelegateLeadsToRedGetValue.kt") + public void testSuccessfulProvideDelegateLeadsToRedGetValue() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/successfulProvideDelegateLeadsToRedGetValue.kt"); + } + @Test @TestMetadata("tryInGenerated.kt") public void testTryInGenerated() throws Exception { @@ -9709,6 +9757,18 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/typeOfLazyDelegatedPropertyWithObject.kt"); } + @Test + @TestMetadata("typeVariablesFlow.kt") + public void testTypeVariablesFlow() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/typeVariablesFlow.kt"); + } + + @Test + @TestMetadata("typeVariablesFlowComplex.kt") + public void testTypeVariablesFlowComplex() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/typeVariablesFlowComplex.kt"); + } + @Test @TestMetadata("useCompleterWithoutExpectedType.kt") public void testUseCompleterWithoutExpectedType() throws Exception { @@ -9833,6 +9893,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated runTest("compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/provideDelegateResolutionWithStubTypes.kt"); } + @Test + @TestMetadata("provideDelegateResolutionWithStubTypesAndExtensionGetValue.kt") + public void testProvideDelegateResolutionWithStubTypesAndExtensionGetValue() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/provideDelegateResolutionWithStubTypesAndExtensionGetValue.kt"); + } + @Test @TestMetadata("setValue.kt") public void testSetValue() throws Exception { diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/delegates/delegateWithAnonymousObject.dot b/compiler/fir/analysis-tests/testData/resolveWithStdlib/delegates/delegateWithAnonymousObject.dot index e9fbb1152e8..5ccaab568c0 100644 --- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/delegates/delegateWithAnonymousObject.dot +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/delegates/delegateWithAnonymousObject.dot @@ -144,7 +144,7 @@ digraph delegateWithAnonymousObject_kt { } 55 [label="Postponed exit from lambda"]; 56 [label="Function call: this@R|/IssuesListUserProfile|.R|/delegate|(...)" style="filled" fillcolor=yellow]; - 57 [label="Function call: this@R|/IssuesListUserProfile|.R|/delegate|(...).#(...)" style="filled" fillcolor=yellow]; + 57 [label="Function call: this@R|/IssuesListUserProfile|.R|/delegate|(...).R?C|special/error|(...)" style="filled" fillcolor=yellow]; 58 [label="Exit property delegate" style="filled" fillcolor=yellow]; 59 [label="Exit property" style="filled" fillcolor=red]; } 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 2b1924fb6c3..ea537207826 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 @@ -9550,6 +9550,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir runTest("compiler/testData/diagnostics/tests/delegatedProperty/useTypeParameterOfExtensionProperty_Disabled.kt"); } + @Test + @TestMetadata("withInvokes.kt") + public void testWithInvokes() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/withInvokes.kt"); + } + @Test @TestMetadata("wrongCountOfParametersInGet.kt") public void testWrongCountOfParametersInGet() throws Exception { @@ -9577,6 +9583,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/delegatedProperty/inference"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true, "multiplatform"); } + @Test + @TestMetadata("annotatedAccessor.kt") + public void testAnnotatedAccessor() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/annotatedAccessor.kt"); + } + @Test @TestMetadata("callableReferenceArgumentInDelegatedExpression.kt") public void testCallableReferenceArgumentInDelegatedExpression() throws Exception { @@ -9625,6 +9637,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/extensionProperty.kt"); } + @Test + @TestMetadata("fullyCompleteDelegateWhenPossible.kt") + public void testFullyCompleteDelegateWhenPossible() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/fullyCompleteDelegateWhenPossible.kt"); + } + @Test @TestMetadata("genericMethodInGenericClass.kt") public void testGenericMethodInGenericClass() throws Exception { @@ -9655,6 +9673,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/labeledDelegatedExpression.kt"); } + @Test + @TestMetadata("lambdaWithMultipleReturns.kt") + public void testLambdaWithMultipleReturns() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/lambdaWithMultipleReturns.kt"); + } + @Test @TestMetadata("manyIncompleteCandidates.kt") public void testManyIncompleteCandidates() throws Exception { @@ -9691,12 +9715,36 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/notNullAssertionInLocalDelegated.kt"); } + @Test + @TestMetadata("nothingTypeThisRef.kt") + public void testNothingTypeThisRef() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/nothingTypeThisRef.kt"); + } + + @Test + @TestMetadata("provideDelegateFixationResultContainsOtherInnerVariable.kt") + public void testProvideDelegateFixationResultContainsOtherInnerVariable() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/provideDelegateFixationResultContainsOtherInnerVariable.kt"); + } + + @Test + @TestMetadata("provideDelegateReturningOuterTypeVariable.kt") + public void testProvideDelegateReturningOuterTypeVariable() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/provideDelegateReturningOuterTypeVariable.kt"); + } + @Test @TestMetadata("resultTypeOfLambdaForConventionMethods.kt") public void testResultTypeOfLambdaForConventionMethods() throws Exception { runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/resultTypeOfLambdaForConventionMethods.kt"); } + @Test + @TestMetadata("successfulProvideDelegateLeadsToRedGetValue.kt") + public void testSuccessfulProvideDelegateLeadsToRedGetValue() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/successfulProvideDelegateLeadsToRedGetValue.kt"); + } + @Test @TestMetadata("tryInGenerated.kt") public void testTryInGenerated() throws Exception { @@ -9709,6 +9757,18 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/typeOfLazyDelegatedPropertyWithObject.kt"); } + @Test + @TestMetadata("typeVariablesFlow.kt") + public void testTypeVariablesFlow() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/typeVariablesFlow.kt"); + } + + @Test + @TestMetadata("typeVariablesFlowComplex.kt") + public void testTypeVariablesFlowComplex() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/typeVariablesFlowComplex.kt"); + } + @Test @TestMetadata("useCompleterWithoutExpectedType.kt") public void testUseCompleterWithoutExpectedType() throws Exception { @@ -9833,6 +9893,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir runTest("compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/provideDelegateResolutionWithStubTypes.kt"); } + @Test + @TestMetadata("provideDelegateResolutionWithStubTypesAndExtensionGetValue.kt") + public void testProvideDelegateResolutionWithStubTypesAndExtensionGetValue() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/provideDelegateResolutionWithStubTypesAndExtensionGetValue.kt"); + } + @Test @TestMetadata("setValue.kt") public void testSetValue() 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 5412bfb9597..b492a392710 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 @@ -9556,6 +9556,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia runTest("compiler/testData/diagnostics/tests/delegatedProperty/useTypeParameterOfExtensionProperty_Disabled.kt"); } + @Test + @TestMetadata("withInvokes.kt") + public void testWithInvokes() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/withInvokes.kt"); + } + @Test @TestMetadata("wrongCountOfParametersInGet.kt") public void testWrongCountOfParametersInGet() throws Exception { @@ -9583,6 +9589,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/delegatedProperty/inference"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true, "multiplatform"); } + @Test + @TestMetadata("annotatedAccessor.kt") + public void testAnnotatedAccessor() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/annotatedAccessor.kt"); + } + @Test @TestMetadata("callableReferenceArgumentInDelegatedExpression.kt") public void testCallableReferenceArgumentInDelegatedExpression() throws Exception { @@ -9631,6 +9643,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/extensionProperty.kt"); } + @Test + @TestMetadata("fullyCompleteDelegateWhenPossible.kt") + public void testFullyCompleteDelegateWhenPossible() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/fullyCompleteDelegateWhenPossible.kt"); + } + @Test @TestMetadata("genericMethodInGenericClass.kt") public void testGenericMethodInGenericClass() throws Exception { @@ -9661,6 +9679,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/labeledDelegatedExpression.kt"); } + @Test + @TestMetadata("lambdaWithMultipleReturns.kt") + public void testLambdaWithMultipleReturns() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/lambdaWithMultipleReturns.kt"); + } + @Test @TestMetadata("manyIncompleteCandidates.kt") public void testManyIncompleteCandidates() throws Exception { @@ -9697,12 +9721,36 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/notNullAssertionInLocalDelegated.kt"); } + @Test + @TestMetadata("nothingTypeThisRef.kt") + public void testNothingTypeThisRef() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/nothingTypeThisRef.kt"); + } + + @Test + @TestMetadata("provideDelegateFixationResultContainsOtherInnerVariable.kt") + public void testProvideDelegateFixationResultContainsOtherInnerVariable() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/provideDelegateFixationResultContainsOtherInnerVariable.kt"); + } + + @Test + @TestMetadata("provideDelegateReturningOuterTypeVariable.kt") + public void testProvideDelegateReturningOuterTypeVariable() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/provideDelegateReturningOuterTypeVariable.kt"); + } + @Test @TestMetadata("resultTypeOfLambdaForConventionMethods.kt") public void testResultTypeOfLambdaForConventionMethods() throws Exception { runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/resultTypeOfLambdaForConventionMethods.kt"); } + @Test + @TestMetadata("successfulProvideDelegateLeadsToRedGetValue.kt") + public void testSuccessfulProvideDelegateLeadsToRedGetValue() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/successfulProvideDelegateLeadsToRedGetValue.kt"); + } + @Test @TestMetadata("tryInGenerated.kt") public void testTryInGenerated() throws Exception { @@ -9715,6 +9763,18 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/typeOfLazyDelegatedPropertyWithObject.kt"); } + @Test + @TestMetadata("typeVariablesFlow.kt") + public void testTypeVariablesFlow() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/typeVariablesFlow.kt"); + } + + @Test + @TestMetadata("typeVariablesFlowComplex.kt") + public void testTypeVariablesFlowComplex() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/typeVariablesFlowComplex.kt"); + } + @Test @TestMetadata("useCompleterWithoutExpectedType.kt") public void testUseCompleterWithoutExpectedType() throws Exception { @@ -9839,6 +9899,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia runTest("compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/provideDelegateResolutionWithStubTypes.kt"); } + @Test + @TestMetadata("provideDelegateResolutionWithStubTypesAndExtensionGetValue.kt") + public void testProvideDelegateResolutionWithStubTypesAndExtensionGetValue() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/provideDelegateResolutionWithStubTypesAndExtensionGetValue.kt"); + } + @Test @TestMetadata("setValue.kt") public void testSetValue() throws Exception { diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java index 166ab76e2a6..043a257b35d 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java @@ -15513,6 +15513,12 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr runTest("compiler/testData/codegen/box/delegatedProperty/varInInnerClass.kt"); } + @Test + @TestMetadata("withInvoke.kt") + public void testWithInvoke() throws Exception { + runTest("compiler/testData/codegen/box/delegatedProperty/withInvoke.kt"); + } + @Nested @TestMetadata("compiler/testData/codegen/box/delegatedProperty/delegateToAnother") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenWithIrFakeOverrideGeneratorTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenWithIrFakeOverrideGeneratorTestGenerated.java index d604ba3d095..338b0542e9e 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenWithIrFakeOverrideGeneratorTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenWithIrFakeOverrideGeneratorTestGenerated.java @@ -15405,6 +15405,12 @@ public class FirLightTreeBlackBoxCodegenWithIrFakeOverrideGeneratorTestGenerated runTest("compiler/testData/codegen/box/delegatedProperty/kt9712.kt"); } + @Test + @TestMetadata("noTypeVariablesLeft.kt") + public void testNoTypeVariablesLeft() throws Exception { + runTest("compiler/testData/codegen/box/delegatedProperty/noTypeVariablesLeft.kt"); + } + @Test @TestMetadata("privateInSubClass.kt") public void testPrivateInSubClass() throws Exception { @@ -15513,6 +15519,12 @@ public class FirLightTreeBlackBoxCodegenWithIrFakeOverrideGeneratorTestGenerated runTest("compiler/testData/codegen/box/delegatedProperty/varInInnerClass.kt"); } + @Test + @TestMetadata("withInvoke.kt") + public void testWithInvoke() throws Exception { + runTest("compiler/testData/codegen/box/delegatedProperty/withInvoke.kt"); + } + @Nested @TestMetadata("compiler/testData/codegen/box/delegatedProperty/delegateToAnother") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java index 8d0777092c1..c81cca3b6c1 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java @@ -15513,6 +15513,12 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo runTest("compiler/testData/codegen/box/delegatedProperty/varInInnerClass.kt"); } + @Test + @TestMetadata("withInvoke.kt") + public void testWithInvoke() throws Exception { + runTest("compiler/testData/codegen/box/delegatedProperty/withInvoke.kt"); + } + @Nested @TestMetadata("compiler/testData/codegen/box/delegatedProperty/delegateToAnother") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt index f970ea5a745..96008604668 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt @@ -52,7 +52,7 @@ class Candidate( private var systemInitialized: Boolean = false val system: NewConstraintSystemImpl by lazy(LazyThreadSafetyMode.NONE) { val system = constraintSystemFactory.createConstraintSystem() - system.addOtherSystem(baseSystem) + system.setBaseSystem(baseSystem) systemInitialized = true system } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt index 5b4982d1422..2c0b6628442 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt @@ -35,10 +35,10 @@ class CandidateFactory private constructor( companion object { private fun buildBaseSystem(context: ResolutionContext, callInfo: CallInfo): ConstraintStorage { val system = context.inferenceComponents.createConstraintSystem() + system.addOuterSystem(context.bodyResolveContext.outerConstraintStorage) callInfo.arguments.forEach { system.addSubsystemFromExpression(it) } - system.addOtherSystem(context.bodyResolveContext.inferenceSession.currentConstraintStorage) return system.asReadOnlyStorage() } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt index ca16682037a..2159c7dbbe8 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt @@ -256,17 +256,7 @@ class ConstraintSystemCompleter(components: BodyResolveComponents, private val c ) ?: return false val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable) - if (!variableForFixation.hasProperConstraint) { - if (context.inferenceSession.isSyntheticTypeVariable(variableWithConstraints.typeVariable)) { - context.inferenceSession.fixSyntheticTypeVariableWithNotEnoughInformation( - variableWithConstraints.typeVariable as ConeTypeVariable, - this - ) - return true - } - - return false - } + if (!variableForFixation.hasProperConstraint) return false fixVariable(this, variableWithConstraints) @@ -379,18 +369,30 @@ class ConstraintSystemCompleter(components: BodyResolveComponents, private val c topLevelAtom.collectAllTypeVariables() } - if (context.inferenceSession.hasSyntheticTypeVariables()) { - result.addAll(notFixedTypeVariables.filter { context.inferenceSession.isSyntheticTypeVariable(it.value.typeVariable) }.keys.asIterable()) - } - - require(result.size == notFixedTypeVariables.size) { - val notFoundTypeVariables = notFixedTypeVariables.keys.toMutableSet().apply { removeAll(result) } - "Not all type variables found: $notFoundTypeVariables" - } + checkNotFixedTypeVariablesCountConsistency(result) return result.toList() } + private fun ConstraintSystemCompletionContext.checkNotFixedTypeVariablesCountConsistency( + result: LinkedHashSet, + ) { + val notFixedTypeVariablesToUse = + when (outerSystemVariablesPrefixSize) { + 0 -> notFixedTypeVariables.keys + else -> notFixedTypeVariables.keys.toMutableSet().apply { + removeAll(allTypeVariables.keys.take(outerSystemVariablesPrefixSize).toSet()) + } + } + + require(result.size == notFixedTypeVariablesToUse.size) { + val notFoundTypeVariables = notFixedTypeVariablesToUse.toMutableSet().apply { + removeAll(result) + } + "Not all type variables found: $notFoundTypeVariables" + } + } + private fun fixVariable( c: ConstraintSystemCompletionContext, variableWithConstraints: VariableWithConstraints diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirBuilderInferenceSession.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirBuilderInferenceSession.kt index 50c3a0d34d4..818065189d8 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirBuilderInferenceSession.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirBuilderInferenceSession.kt @@ -10,6 +10,8 @@ import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction import org.jetbrains.kotlin.fir.declarations.FirDeclaration import org.jetbrains.kotlin.fir.declarations.hasAnnotation +import org.jetbrains.kotlin.fir.expressions.FirResolvable +import org.jetbrains.kotlin.fir.expressions.FirStatement import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.resolve.calls.Candidate import org.jetbrains.kotlin.fir.resolve.calls.ImplicitExtensionReceiverValue @@ -31,7 +33,6 @@ import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImp import org.jetbrains.kotlin.resolve.calls.inference.registerTypeVariableIfNotPresent import org.jetbrains.kotlin.resolve.descriptorUtil.BUILDER_INFERENCE_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.types.model.TypeConstructorMarker -import org.jetbrains.kotlin.types.model.TypeVariableMarker /** * General documentation for builder inference algorithm is located at `/docs/fir/builder_inference.md` @@ -45,15 +46,6 @@ class FirBuilderInferenceSession( private val commonCalls: MutableList> = mutableListOf() private var lambdaImplicitReceivers: MutableList = mutableListOf() - override val currentConstraintStorage: ConstraintStorage - get() = ConstraintStorage.Empty - - override fun hasSyntheticTypeVariables(): Boolean = false - - override fun isSyntheticTypeVariable(typeVariable: TypeVariableMarker): Boolean { - return false - } - override fun shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement { val candidate = call.candidate val system = candidate.system @@ -154,8 +146,6 @@ class FirBuilderInferenceSession( return commonSystem.fixedTypeVariables as Map } - override fun createSyntheticStubTypes(system: NewConstraintSystemImpl): Map = emptyMap() - private fun buildCommonSystem(initialStorage: ConstraintStorage): Pair { val commonSystem = components.session.inferenceComponents.createConstraintSystem() val nonFixedToVariablesSubstitutor = createNonFixedTypeToVariableSubstitutor() diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt index b16e720a8ae..e8a3b6172f7 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt @@ -77,7 +77,20 @@ class FirCallCompleter( val completionMode = candidate.computeCompletionMode( session.inferenceComponents, resolutionMode, initialType - ) + ).let { + // The difference between `shouldAvoidFullCompletion` and `!shouldRunCompletion` is very subtle: + // we don't run even partial completion for `!inferenceSession.shouldRunCompletion(call)`, while actually + // do that for `shouldAvoidFullCompletion` + // + // As for implementations, `shouldRunCompletion` only works for Builder inference, while `shouldAvoidFullCompletion` is for + // delegate inference where it's assumed to have partially completed intermediate calls. + // + // Ideally, we should get rid of `shouldRunCompletion` once Builder inference is rewritten (see KT-61041 for tracking) + if (it == ConstraintSystemCompletionMode.FULL && inferenceSession.shouldAvoidFullCompletion(call)) + ConstraintSystemCompletionMode.PARTIAL + else + it + } val analyzer = createPostponedArgumentsAnalyzer(transformer.resolutionContext) if (call is FirFunctionCall) { @@ -105,18 +118,15 @@ class FirCallCompleter( inferenceSession.addCompletedCall(completedCall, candidate) completedCall } else { - inferenceSession.addPartiallyResolvedCall(call) + inferenceSession.processPartiallyResolvedCall(call, resolutionMode) call } } ConstraintSystemCompletionMode.PARTIAL -> { runCompletionForCall(candidate, completionMode, call, initialType, analyzer) - - // Add top-level delegate call as partially resolved to inference session - if (resolutionMode is ResolutionMode.ContextDependent.Delegate) { - require(inferenceSession is FirDelegatedPropertyInferenceSession) - inferenceSession.addPartiallyResolvedCall(call) + if (inferenceSession is FirDelegatedPropertyInferenceSession) { + inferenceSession.processPartiallyResolvedCall(call, resolutionMode) } call diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirDelegatedPropertyInferenceSession.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirDelegatedPropertyInferenceSession.kt index e7b2389d42b..8c20a0f06b6 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirDelegatedPropertyInferenceSession.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirDelegatedPropertyInferenceSession.kt @@ -6,25 +6,25 @@ package org.jetbrains.kotlin.fir.resolve.inference import org.jetbrains.kotlin.fir.declarations.FirProperty +import org.jetbrains.kotlin.fir.expressions.FirFunctionCall +import org.jetbrains.kotlin.fir.expressions.FirFunctionCallOrigin import org.jetbrains.kotlin.fir.expressions.FirResolvable import org.jetbrains.kotlin.fir.expressions.FirStatement -import org.jetbrains.kotlin.fir.resolve.calls.* -import org.jetbrains.kotlin.fir.resolve.inference.model.ConeFixVariableConstraintPosition -import org.jetbrains.kotlin.fir.resolve.substitution.ChainedSubstitutor +import org.jetbrains.kotlin.fir.resolve.ResolutionMode +import org.jetbrains.kotlin.fir.resolve.calls.Candidate +import org.jetbrains.kotlin.fir.resolve.calls.InferenceError +import org.jetbrains.kotlin.fir.resolve.calls.ResolutionContext +import org.jetbrains.kotlin.fir.resolve.calls.candidate import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor -import org.jetbrains.kotlin.fir.resolve.substitution.NotFixedTypeToVariableSubstitutorForDelegateInference -import org.jetbrains.kotlin.fir.resolve.substitution.replaceStubsAndTypeVariablesToErrors -import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.fir.types.ConeClassLikeType +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.ConeTypeVariableTypeConstructor +import org.jetbrains.kotlin.fir.types.typeContext import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder -import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem import org.jetbrains.kotlin.resolve.calls.inference.buildAbstractResultingSubstitutor -import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionContext import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode -import org.jetbrains.kotlin.resolve.calls.inference.model.BuilderInferencePosition import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage -import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl -import org.jetbrains.kotlin.resolve.calls.inference.registerTypeVariableIfNotPresent -import org.jetbrains.kotlin.types.model.* +import org.jetbrains.kotlin.util.OperatorNameConventions class FirDelegatedPropertyInferenceSession( val property: FirProperty, @@ -32,80 +32,48 @@ class FirDelegatedPropertyInferenceSession( private val postponedArgumentsAnalyzer: PostponedArgumentsAnalyzer, ) : FirInferenceSessionForChainedResolve(resolutionContext) { - private val currentConstraintSystem = components.session.inferenceComponents.createConstraintSystem() - override val currentConstraintStorage: ConstraintStorage get() = currentConstraintSystem.currentStorage() + private var currentConstraintSystem = components.session.inferenceComponents.createConstraintSystem() + val currentConstraintStorage: ConstraintStorage get() = currentConstraintSystem.currentStorage() private val unitType: ConeClassLikeType = components.session.builtinTypes.unitType.type - private lateinit var resultingConstraintSystem: NewConstraintSystem - private fun ConeKotlinType.containsStubType(): Boolean { - return this.contains { - it is ConeStubTypeForChainInference - } + // TODO after PCLA (KT-59107): + // Outer system seems to be a property of a concrete call resolution and probably should be applied to concrete call resolution + override fun onCandidatesResolution(call: FirFunctionCall, candidatesResolutionCallback: () -> R): R { + return if (!call.isAnyOfDelegateOperators()) + candidatesResolutionCallback() + else + resolutionContext.bodyResolveContext.withOuterConstraintStorage( + currentConstraintSystem.currentStorage(), + candidatesResolutionCallback + ) } - private fun integrateResolvedCall(storage: ConstraintStorage) { - registerSyntheticVariables(storage) - val stubToTypeVariableSubstitutor = createToSyntheticTypeVariableSubstitutor() - integrateConstraints( - currentConstraintSystem, - storage, - stubToTypeVariableSubstitutor, - false - ) + override fun shouldAvoidFullCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = + call.isAnyOfDelegateOperators() + + override fun processPartiallyResolvedCall(call: T, resolutionMode: ResolutionMode) where T : FirResolvable, T : FirStatement { + if (resolutionMode != ResolutionMode.ContextDependent.Delegate && !call.isAnyOfDelegateOperators()) return + + val candidate = call.candidate + + // Ignore unsuccessful `provideDelegate` candidates + // This behavior is aligned with a relevant part at FirDeclarationsResolveTransformer.transformWrappedDelegateExpression + if (call.isProvideDelegate() && !candidate.isSuccessful) return + + val candidateSystem = candidate.system + + partiallyResolvedCalls.add(call to candidate) + currentConstraintSystem = candidateSystem } - override fun addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement { - partiallyResolvedCalls += call to candidate - if (candidate.isSuccessful) { - integrateResolvedCall(candidate.system.asReadOnlyStorage()) - } - } + private fun T.isProvideDelegate() where T : FirResolvable, T : FirStatement = + isAnyOfDelegateOperators() && (this as FirFunctionCall).calleeReference.name == OperatorNameConventions.PROVIDE_DELEGATE - override fun addPartiallyResolvedCall(call: T) where T : FirResolvable, T : FirStatement { - super.addPartiallyResolvedCall(call) - if (call.candidate.isSuccessful) { - integrateResolvedCall(call.candidate.system.currentStorage()) - } - } - - override fun shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement { - val callee = call.calleeReference as? FirNamedReferenceWithCandidate ?: return true - - if (callee.candidate.system.hasContradiction) return true - - val hasStubType = - callee.candidate.chosenExtensionReceiver?.resolvedType?.containsStubType() ?: false - || callee.candidate.dispatchReceiver?.resolvedType?.containsStubType() ?: false - - if (!hasStubType) { - return true - } - - val system = call.candidate.system - - val storage = system.getBuilder().currentStorage() - - if (call.hasPostponed()) return true - - return storage.notFixedTypeVariables.keys.all { - val variable = storage.allTypeVariables[it] - val isPostponed = variable != null && variable in storage.postponedTypeVariables - isPostponed || isSyntheticTypeVariable(variable!!) || - components.callCompleter.completer.variableFixationFinder.isTypeVariableHasProperConstraint(system, it) - } - } - - private fun FirStatement.hasPostponed(): Boolean { - var result = false - processAllContainingCallCandidates(processBlocks = false) { - result = result || it.hasPostponed() - } - return result - } - - private fun Candidate.hasPostponed(): Boolean { - return postponedAtoms.any { !it.analyzed } + private fun T.isAnyOfDelegateOperators(): Boolean where T : FirResolvable { + if (this !is FirFunctionCall || origin != FirFunctionCallOrigin.Operator) return false + val name = calleeReference.name + return name == OperatorNameConventions.PROVIDE_DELEGATE || name == OperatorNameConventions.GET_VALUE || name == OperatorNameConventions.SET_VALUE } override fun inferPostponedVariables( @@ -114,59 +82,8 @@ class FirDelegatedPropertyInferenceSession( completionMode: ConstraintSystemCompletionMode ): Map? = null - private fun createNonFixedTypeToVariableSubstitutor(): NotFixedTypeToVariableSubstitutorForDelegateInference { - val typeContext = components.session.typeContext - - val bindings = mutableMapOf() - for ((variable, synthetic) in syntheticTypeVariableByTypeVariable) { - bindings[synthetic] = variable.defaultType(typeContext) as ConeKotlinType - } - - return NotFixedTypeToVariableSubstitutorForDelegateInference(bindings, typeContext) - } - - - /* - * This creates Stub-preserving substitution to synthetic type variables - * Stub(R) => Stub(_R) - * R => _R - */ - private fun createToSyntheticTypeVariableSubstitutor(): ConeSubstitutor { - - val typeContext = components.session.typeContext - val bindings = mutableMapOf() - for ((variable, syntheticVariable) in syntheticTypeVariableByTypeVariable) { - bindings[variable.freshTypeConstructor(typeContext)] = syntheticVariable.defaultType - } - - return typeContext.typeSubstitutorByTypeConstructor(bindings) - } - - override fun hasSyntheticTypeVariables(): Boolean { - return syntheticTypeVariableByTypeVariable.isNotEmpty() - } - - override fun isSyntheticTypeVariable(typeVariable: TypeVariableMarker): Boolean { - return typeVariable in syntheticTypeVariableByTypeVariable.values - } - - override fun fixSyntheticTypeVariableWithNotEnoughInformation( - typeVariable: TypeVariableMarker, - completionContext: ConstraintSystemCompletionContext - ) { - typeVariable as ConeTypeVariable - completionContext.fixVariable( - typeVariable, - ConeStubTypeForSyntheticFixation( - ConeStubTypeConstructor(typeVariable, isTypeVariableInSubtyping = false, isForFixation = true), - ConeNullability.create(typeVariable.defaultType.isMarkedNullable) - ), - ConeFixVariableConstraintPosition(typeVariable) - ) - } - fun completeCandidates(): List { - val commonSystem = components.session.inferenceComponents.createConstraintSystem() + val commonSystem = currentConstraintSystem.apply { prepareForGlobalCompletion() } val notCompletedCalls = partiallyResolvedCalls.mapNotNull { partiallyResolvedCall -> partiallyResolvedCall.first.takeIf { resolvable -> @@ -174,20 +91,6 @@ class FirDelegatedPropertyInferenceSession( } } - val stubToTypeVariableSubstitutor = createNonFixedTypeToVariableSubstitutor() - - for ((call, candidate) in partiallyResolvedCalls) { - if (candidate.isSuccessful) { - integrateConstraints( - commonSystem, - candidate.system.asReadOnlyStorage(), - stubToTypeVariableSubstitutor, - call.candidate() != null - ) - } - } - - resolutionContext.bodyResolveContext.withInferenceSession(DEFAULT) { @Suppress("UNCHECKED_CAST") components.callCompleter.completer.complete( @@ -221,97 +124,14 @@ class FirDelegatedPropertyInferenceSession( } } - resultingConstraintSystem = commonSystem return notCompletedCalls } - fun createFinalSubstitutor(): ConeSubstitutor { - val stubTypeSubstitutor = createNonFixedTypeToVariableSubstitutor() + fun createFinalSubstitutor(): ConeSubstitutor = + currentConstraintSystem.asReadOnlyStorage() + .buildAbstractResultingSubstitutor(components.session.typeContext) as ConeSubstitutor - val typeContext = components.session.typeContext - val resultSubstitutor = resultingConstraintSystem.asReadOnlyStorage() - .buildAbstractResultingSubstitutor(typeContext) as ConeSubstitutor - return ChainedSubstitutor(stubTypeSubstitutor, resultSubstitutor) - .replaceStubsAndTypeVariablesToErrors(typeContext, stubTypesByTypeVariable.values.map { it.constructor }) - } + override fun addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement {} - private val stubTypesByTypeVariable: MutableMap = mutableMapOf() - private val syntheticTypeVariableByTypeVariable = mutableMapOf() - - private fun registerSyntheticVariables(storage: ConstraintStorage) { - for (variableWithConstraints in storage.notFixedTypeVariables.values) { - val variable = variableWithConstraints.typeVariable as ConeTypeVariable - - val syntheticVariable = syntheticTypeVariableByTypeVariable.getOrPut(variable) { - ConeTypeVariable("+" + variable.typeConstructor.name).also { - currentConstraintSystem.registerVariable(it) - } - } - - stubTypesByTypeVariable.getOrPut(variable) { - ConeStubTypeForChainInference( - syntheticVariable, - ConeNullability.create(syntheticVariable.defaultType.isMarkedNullable) - ) - } - } - } - - override fun createSyntheticStubTypes(system: NewConstraintSystemImpl): Map { - - val bindings = mutableMapOf() - registerSyntheticVariables(system.currentStorage()) - for (variable in system.postponedTypeVariables) { - variable as ConeTypeVariable - - bindings[variable.typeConstructor] = stubTypesByTypeVariable[variable]!! - } - - return bindings - } - - override fun registerStubTypes(map: Map) { -// @Suppress("UNCHECKED_CAST") -// stubTypesByTypeVariable.putAll(map as Map) - } - - - private fun integrateConstraints( - commonSystem: NewConstraintSystemImpl, - storage: ConstraintStorage, - nonFixedToVariablesSubstitutor: ConeSubstitutor, - shouldIntegrateAllConstraints: Boolean - ) { - if (shouldIntegrateAllConstraints) { - storage.notFixedTypeVariables.values.forEach { - if (isSyntheticTypeVariable(it.typeVariable)) return@forEach - commonSystem.registerTypeVariableIfNotPresent(it.typeVariable) - } - } - /* - * storage can contain the following substitutions: - * TypeVariable(A) -> ProperType - * TypeVariable(B) -> Special-Non-Fixed-Type - * - * while substitutor from parameter map non-fixed types to the original type variable - * */ - val callSubstitutor = - storage.buildAbstractResultingSubstitutor(commonSystem, transformTypeVariablesToErrorTypes = false) as ConeSubstitutor - - for (initialConstraint in storage.initialConstraints) { - integrateConstraintToSystem( - commonSystem, initialConstraint, callSubstitutor, nonFixedToVariablesSubstitutor, storage.fixedTypeVariables - ) - } - - if (shouldIntegrateAllConstraints) { - for ((variableConstructor, type) in storage.fixedTypeVariables) { - val typeVariable = storage.allTypeVariables.getValue(variableConstructor) - if (isSyntheticTypeVariable(typeVariable)) continue - - commonSystem.registerTypeVariableIfNotPresent(typeVariable) - commonSystem.addEqualityConstraint((typeVariable as ConeTypeVariable).defaultType, type, BuilderInferencePosition) - } - } - } + override fun shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = true } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt index 3e7f83d52f7..fdfd6b04039 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt @@ -5,19 +5,16 @@ package org.jetbrains.kotlin.fir.resolve.inference +import org.jetbrains.kotlin.fir.expressions.FirFunctionCall import org.jetbrains.kotlin.fir.expressions.FirResolvable import org.jetbrains.kotlin.fir.expressions.FirStatement +import org.jetbrains.kotlin.fir.resolve.ResolutionMode import org.jetbrains.kotlin.fir.resolve.calls.Candidate import org.jetbrains.kotlin.fir.types.ConeKotlinType -import org.jetbrains.kotlin.fir.types.ConeStubType import org.jetbrains.kotlin.fir.types.ConeTypeVariableTypeConstructor import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder -import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionContext import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode -import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage -import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl import org.jetbrains.kotlin.types.model.StubTypeMarker -import org.jetbrains.kotlin.types.model.TypeConstructorMarker import org.jetbrains.kotlin.types.model.TypeVariableMarker abstract class FirInferenceSession { @@ -26,20 +23,18 @@ abstract class FirInferenceSession { } abstract fun shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement - abstract val currentConstraintStorage: ConstraintStorage - abstract fun addPartiallyResolvedCall(call: T) where T : FirResolvable, T : FirStatement + /** + * In some cases (like getValue/setValue resolution of property delegation convention), it might be necessary to postpone full completion + * even with the ContextIndependent mode (which is used for delegated accessors bodies) + */ + open fun shouldAvoidFullCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = false + + abstract fun processPartiallyResolvedCall(call: T, resolutionMode: ResolutionMode) where T : FirResolvable, T : FirStatement abstract fun addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement abstract fun registerStubTypes(map: Map) - abstract fun hasSyntheticTypeVariables(): Boolean - abstract fun isSyntheticTypeVariable(typeVariable: TypeVariableMarker): Boolean - abstract fun fixSyntheticTypeVariableWithNotEnoughInformation( - typeVariable: TypeVariableMarker, - completionContext: ConstraintSystemCompletionContext - ) - abstract fun inferPostponedVariables( lambda: ResolvedLambdaAtom, constraintSystemBuilder: ConstraintSystemBuilder, @@ -48,16 +43,13 @@ abstract class FirInferenceSession { ): Map? abstract fun clear() - abstract fun createSyntheticStubTypes(system: NewConstraintSystemImpl): Map + open fun onCandidatesResolution(call: FirFunctionCall, candidatesResolutionCallback: () -> R) = candidatesResolutionCallback() } abstract class FirStubInferenceSession : FirInferenceSession() { override fun shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = true - override val currentConstraintStorage: ConstraintStorage - get() = ConstraintStorage.Empty - - override fun addPartiallyResolvedCall(call: T) where T : FirResolvable, T : FirStatement {} + override fun processPartiallyResolvedCall(call: T, resolutionMode: ResolutionMode) where T : FirResolvable, T : FirStatement {} override fun addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement {} override fun inferPostponedVariables( @@ -68,15 +60,6 @@ abstract class FirStubInferenceSession : FirInferenceSession() { override fun registerStubTypes(map: Map) {} - override fun hasSyntheticTypeVariables(): Boolean = false - override fun isSyntheticTypeVariable(typeVariable: TypeVariableMarker): Boolean = false - override fun fixSyntheticTypeVariableWithNotEnoughInformation( - typeVariable: TypeVariableMarker, - completionContext: ConstraintSystemCompletionContext - ) {} - - override fun createSyntheticStubTypes(system: NewConstraintSystemImpl): Map = emptyMap() - override fun clear() { } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSessionForChainedResolve.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSessionForChainedResolve.kt index a5dfb432a8d..25a9a9eb729 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSessionForChainedResolve.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSessionForChainedResolve.kt @@ -8,13 +8,13 @@ package org.jetbrains.kotlin.fir.resolve.inference import org.jetbrains.kotlin.fir.expressions.FirResolvable import org.jetbrains.kotlin.fir.expressions.FirStatement import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents +import org.jetbrains.kotlin.fir.resolve.ResolutionMode import org.jetbrains.kotlin.fir.resolve.calls.Candidate import org.jetbrains.kotlin.fir.resolve.calls.ResolutionContext import org.jetbrains.kotlin.fir.resolve.calls.candidate import org.jetbrains.kotlin.fir.resolve.inference.model.ConeBuilderInferenceSubstitutionConstraintPosition import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.types.* -import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionContext import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind import org.jetbrains.kotlin.resolve.calls.inference.model.InitialConstraint import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl @@ -23,12 +23,6 @@ import org.jetbrains.kotlin.types.model.* abstract class FirInferenceSessionForChainedResolve( protected val resolutionContext: ResolutionContext ) : FirInferenceSession() { - override fun fixSyntheticTypeVariableWithNotEnoughInformation( - typeVariable: TypeVariableMarker, - completionContext: ConstraintSystemCompletionContext - ) { - } - protected val partiallyResolvedCalls: MutableList> = mutableListOf() private val completedCalls: MutableSet = mutableSetOf() @@ -39,7 +33,7 @@ abstract class FirInferenceSessionForChainedResolve( // do nothing } - override fun addPartiallyResolvedCall(call: T) where T : FirResolvable, T : FirStatement { + override fun processPartiallyResolvedCall(call: T, resolutionMode: ResolutionMode) where T : FirResolvable, T : FirStatement { partiallyResolvedCalls += call to call.candidate } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt index 23c13ee60bf..bfb326fdad2 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt @@ -94,6 +94,7 @@ class FirCallCompletionResultsWriterTransformer( qualifiedAccessExpression: T, calleeReference: FirNamedReferenceWithCandidate, ): T { val subCandidate = calleeReference.candidate + val declaration = subCandidate.symbol.fir val typeArguments = computeTypeArguments(qualifiedAccessExpression, subCandidate) val type = if (declaration is FirCallableDeclaration) { @@ -117,6 +118,12 @@ class FirCallCompletionResultsWriterTransformer( ) } + if (mode == Mode.DelegatedPropertyCompletion) { + // Update type for `$delegateField` in `$$delegateField.get/setValue()` calls inside accessors + val typeUpdater = TypeUpdaterForDelegateArguments() + qualifiedAccessExpression.transformExplicitReceiver(typeUpdater, null) + } + var dispatchReceiver = subCandidate.dispatchReceiverExpression() var extensionReceiver = subCandidate.chosenExtensionReceiverExpression() if (!declaration.isWrappedIntegerOperator()) { @@ -157,13 +164,7 @@ class FirCallCompletionResultsWriterTransformer( data: ExpectedArgumentType?, ): FirStatement { val calleeReference = qualifiedAccessExpression.calleeReference as? FirNamedReferenceWithCandidate - ?: return run { - if (mode == Mode.DelegatedPropertyCompletion) { - val typeUpdater = TypeUpdaterForDelegateArguments() - qualifiedAccessExpression.transformSingle(typeUpdater, null) - } - qualifiedAccessExpression - } + ?: return qualifiedAccessExpression val result = prepareQualifiedTransform(qualifiedAccessExpression, calleeReference) val subCandidate = calleeReference.candidate @@ -172,11 +173,6 @@ class FirCallCompletionResultsWriterTransformer( result.replaceConeTypeOrNull(resultType) session.lookupTracker?.recordTypeResolveAsLookup(resultType, qualifiedAccessExpression.source, context.file.source) - if (mode == Mode.DelegatedPropertyCompletion) { - val typeUpdater = TypeUpdaterForDelegateArguments() - result.transformExplicitReceiver(typeUpdater, null) - } - if (calleeReference.candidate.doesResolutionResultOverrideOtherToPreserveCompatibility()) { result.addNonFatalDiagnostic(ConeResolutionResultOverridesOtherToPreserveCompatibility) } @@ -227,12 +223,6 @@ class FirCallCompletionResultsWriterTransformer( result.replaceConeTypeOrNull(resultType) session.lookupTracker?.recordTypeResolveAsLookup(resultType, functionCall.source, context.file.source) - if (mode == Mode.DelegatedPropertyCompletion) { - val typeUpdater = TypeUpdaterForDelegateArguments() - result.argumentList.transformArguments(typeUpdater, null) - result.transformExplicitReceiver(typeUpdater, null) - } - if (enableArrayOfCallTransformation) { return arrayOfCallTransformer.transformFunctionCall(result, session) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt index 210fd672a83..3f66dbfe79e 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt @@ -7,13 +7,15 @@ package org.jetbrains.kotlin.fir.resolve.transformers.body.resolve import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.fir.* +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.correspondingProperty import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor import org.jetbrains.kotlin.fir.declarations.utils.isCompanion import org.jetbrains.kotlin.fir.declarations.utils.isInner import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess import org.jetbrains.kotlin.fir.expressions.FirWhenExpression +import org.jetbrains.kotlin.fir.languageVersionSettings import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.calls.ImplicitExtensionReceiverValue import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue @@ -38,6 +40,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.SpecialNames.UNDERSCORE_FOR_UNUSED_VAR +import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage import org.jetbrains.kotlin.util.PrivateForInline class BodyResolveContext( @@ -87,6 +90,16 @@ class BodyResolveContext( @set:PrivateForInline var inferenceSession: FirInferenceSession = FirInferenceSession.DEFAULT + /** + * CS for an outer type system if it's relevant, for example, in the case of `val x by myGenericDelegateCall()`, + * relevant `getValue` call is expected to be resolved in the context of an outer CS built from `myGenericDelegateCall()` and + * probably using its type variables inside `getValue` candidates resolution. + * + * Note that it's not assumed to modify the storage, but only use it as a base system content for the candidate. + */ + @set:PrivateForInline + var outerConstraintStorage: ConstraintStorage = ConstraintStorage.Empty + val anonymousFunctionsAnalyzedInDependentContext: MutableSet> = mutableSetOf() var containingClassDeclarations: ArrayDeque = ArrayDeque() @@ -831,6 +844,21 @@ class BodyResolveContext( } } + @OptIn(PrivateForInline::class) + inline fun withOuterConstraintStorage( + storage: ConstraintStorage, + f: () -> T + ): T { + val oldStorage = this.outerConstraintStorage + this.outerConstraintStorage = storage + return try { + f() + } finally { + this.outerConstraintStorage = oldStorage + } + } + + @OptIn(PrivateForInline::class) inline fun withConstructor(constructor: FirConstructor, f: () -> T): T = withContainer(constructor, f) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt index d94538eaa1e..a65f658d6c4 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt @@ -28,14 +28,15 @@ import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.references.FirResolvedErrorReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.resolve.* -import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate +import org.jetbrains.kotlin.fir.resolve.calls.Candidate import org.jetbrains.kotlin.fir.resolve.calls.candidate import org.jetbrains.kotlin.fir.resolve.dfa.FirControlFlowGraphReferenceImpl import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeLocalVariableNoTypeOrInitializer -import org.jetbrains.kotlin.fir.resolve.inference.FirStubTypeTransformer +import org.jetbrains.kotlin.fir.resolve.inference.FirDelegatedPropertyInferenceSession import org.jetbrains.kotlin.fir.resolve.inference.ResolvedLambdaAtom import org.jetbrains.kotlin.fir.resolve.inference.extractLambdaInfoFromFunctionType -import org.jetbrains.kotlin.fir.resolve.substitution.createTypeSubstitutorByTypeConstructor +import org.jetbrains.kotlin.fir.resolve.substitution.ChainedSubstitutor +import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.transformers.FirCallCompletionResultsWriterTransformer import org.jetbrains.kotlin.fir.resolve.transformers.FirStatusResolver import org.jetbrains.kotlin.fir.resolve.transformers.contracts.runContractResolveForFunction @@ -47,6 +48,10 @@ import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef import org.jetbrains.kotlin.fir.types.impl.FirImplicitTypeRefImplWithoutSource import org.jetbrains.kotlin.fir.visitors.FirTransformer import org.jetbrains.kotlin.fir.visitors.transformSingle +import org.jetbrains.kotlin.resolve.calls.inference.buildCurrentSubstitutor +import org.jetbrains.kotlin.resolve.calls.inference.components.TypeVariableDirectionCalculator +import org.jetbrains.kotlin.resolve.calls.inference.model.ProvideDelegateFixationPosition +import org.jetbrains.kotlin.types.model.TypeConstructorMarker open class FirDeclarationsResolveTransformer( transformer: FirAbstractBodyResolveTransformerDispatcher @@ -288,8 +293,11 @@ open class FirDeclarationsResolveTransformer( val finalSubstitutor = createFinalSubstitutor() - val stubTypeCompletionResultsWriter = FirStubTypeTransformer(finalSubstitutor) - property.transformSingle(stubTypeCompletionResultsWriter, null) + finalSubstitutor.substituteOrNull(property.returnTypeRef.coneType)?.let { substitutedType -> + property.replaceReturnTypeRef(property.returnTypeRef.withReplacedConeType(substitutedType)) + } + property.getter?.transformTypeWithPropertyType(property.returnTypeRef, forceUpdateForNonImplicitTypes = true) + property.setter?.transformTypeWithPropertyType(property.returnTypeRef, forceUpdateForNonImplicitTypes = true) property.replaceReturnTypeRef( property.returnTypeRef.approximateDeclarationType( session, @@ -326,29 +334,10 @@ open class FirDeclarationsResolveTransformer( data: ResolutionMode, ): FirStatement { // First, resolve delegate expression in dependent context, and add potentially partially resolved call to inference session - // (that is why we use ContextDependent.Delegate instead of plain ContextDependent) + // (that is why we use ResolutionMode.ContextDependent.Delegate instead of plain ContextDependent) val delegateExpression = wrappedDelegateExpression.expression.transformSingle(transformer, ResolutionMode.ContextDependent.Delegate) .transformSingle(components.integerLiteralAndOperatorApproximationTransformer, null) - // Second, replace result type of delegate expression with stub type if delegate not yet resolved - if (delegateExpression is FirResolvable) { - val calleeReference = delegateExpression.calleeReference - if (calleeReference is FirNamedReferenceWithCandidate) { - val system = calleeReference.candidate.system - system.notFixedTypeVariables.forEach { - system.markPostponedVariable(it.value.typeVariable) - } - val typeVariableTypeToStubType = context.inferenceSession.createSyntheticStubTypes(system) - - val substitutor = createTypeSubstitutorByTypeConstructor( - typeVariableTypeToStubType, session.typeContext, approximateIntegerLiterals = true - ) - val delegateExpressionType = delegateExpression.resolvedType - val stubTypeSubstituted = substitutor.substituteOrNull(delegateExpressionType) - delegateExpression.replaceConeTypeOrNull(stubTypeSubstituted) - } - } - val provideDelegateCall = wrappedDelegateExpression.delegateProvider as FirFunctionCall provideDelegateCall.replaceExplicitReceiver(delegateExpression) @@ -356,28 +345,25 @@ open class FirDeclarationsResolveTransformer( // TODO: this generates some nodes in the control flow graph which we don't want if we // end up not selecting this option, KT-59684 transformer.expressionsTransformer?.transformFunctionCallInternal( - provideDelegateCall, ResolutionMode.ContextIndependent, provideDelegate = true + provideDelegateCall, ResolutionMode.ContextDependent, provideDelegate = true ) // If we got successful candidate for provideDelegate, let's select it val provideDelegateCandidate = provideDelegateCall.candidate() if (provideDelegateCandidate != null && provideDelegateCandidate.isSuccessful) { - val system = provideDelegateCandidate.system - system.notFixedTypeVariables.forEach { - system.markPostponedVariable(it.value.typeVariable) - } - val typeVariableTypeToStubType = context.inferenceSession.createSyntheticStubTypes(system) - val substitutor = createTypeSubstitutorByTypeConstructor( - typeVariableTypeToStubType, session.typeContext, approximateIntegerLiterals = true + val additionalBinding = findResultTypeForInnerVariableIfNeeded(provideDelegateCall, provideDelegateCandidate) + + val substitutor = ChainedSubstitutor( + provideDelegateCandidate.substitutor, + (context.inferenceSession as FirDelegatedPropertyInferenceSession).currentConstraintStorage.buildCurrentSubstitutor( + session.typeContext, additionalBinding?.let(::mapOf) ?: emptyMap() + ) as ConeSubstitutor ) - val stubTypeSubstituted = substitutor.substituteOrSelf( - provideDelegateCandidate.substitutor.substituteOrSelf( - components.typeFromCallee(provideDelegateCall).type - ) - ) + val toTypeVariableSubstituted = + substitutor.substituteOrSelf(components.typeFromCallee(provideDelegateCall).type) - provideDelegateCall.replaceConeTypeOrNull(stubTypeSubstituted) + provideDelegateCall.replaceConeTypeOrNull(toTypeVariableSubstituted) return provideDelegateCall } @@ -390,6 +376,92 @@ open class FirDeclarationsResolveTransformer( return delegateExpression } + /** + * For supporting the case when `provideDelegate` has a signature with type variable as a return type, like + * fun K.provideDelegate(receiver: Any?, property: kotlin.reflect.KProperty<*>): K = this + * + * Here, if delegate expression returns something like `Delegate` where Tv is a variable and the `Delegate` class contains + * the member `getValue`, we need to fix `K` into `Delegate`, so that resulting `provideDelegate()` expression would have the type, + * so we could look into its member scope (as we can't look into the member scope of `K` type variable). + * + * On another hand, we can't just actually fix `K` variable (or just run FULL completion there) as the current result might refer + * other not fixed yet type variables, and we would break the contract that fixation results should not contain other type variables. + * + * Thus, to support exactly the case when we had to look into the member scope of `K`, we just pretend like we fixing it + * + * @see compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/provideDelegateResolutionWithStubTypes.kt + * + * In K1, it was working because we used stub types that are not counted as actual type variables, and we've been completing + * `provideDelegate` FULLy in the context where outer type variables were stubs (thus counted as proper types). + * + * But in K2, we decided to get rid of the stub type concept and just stick to the type variables. + * + * @return K to Delegate or null in case return type of `provideDelegate` is not a type variable. + * + * TODO: reconsider the place where the function belong and it necessity after PCLA is implemented (KT-61740 for tracking) + */ + private fun findResultTypeForInnerVariableIfNeeded( + provideDelegate: FirFunctionCall, + candidate: Candidate + ): Pair? { + // We're only interested in the case when `provideDelegate` candidate returns a type variable + // because in other cases we could look into the member scope of the type. + val returnTypeBasedOnVariable = + components.typeFromCallee(provideDelegate).type + // Substitut type parameter to type variable + .let(candidate.substitutor::substituteOrSelf) + .unwrapTopLevelVariableType() ?: return null + val typeVariable = returnTypeBasedOnVariable.lookupTag + + val candidateSystem = candidate.system + val candidateStorage = candidateSystem.currentStorage() + val allTypeVariables = candidateStorage.allTypeVariables.keys.toList() + // Subset of type variables obtained from the `provideDelegate` call + val typeVariablesRelatedToProvideDelegate = + allTypeVariables.subList(candidateStorage.outerSystemVariablesPrefixSize, allTypeVariables.size).toSet() + + check(typeVariable in typeVariablesRelatedToProvideDelegate) { + "Return type of provideDelegate is expected to be one of the type variables of a candidate, but $typeVariable was found" + } + + val variableWithConstraints = + candidateSystem.notFixedTypeVariables[typeVariable] ?: error("Not found type variable $typeVariable") + + var resultType: ConeKotlinType? = null + + // Temporary declare all the "outer" variables as proper (i.e., all inner variables as improper) + // Without that, all variables (both inner and outer ones) would be considered as improper, + // while we want to fix to assume `Delegate` as proper because `Tv` belongs to the outer system + candidateSystem.withTypeVariablesThatAreNotCountedAsProperTypes(typeVariablesRelatedToProvideDelegate) { + // TODO: reconsider the approach here (KT-61781 for tracking) + // Actually, this code might fail with an exception in some rare cases (see KT-61781) + // The problem is that in the issue example, when fixing T type variable, it has two upper bounds: X and Delegate + // So, when ResultTypeResolver builds result type, it tries to intersect them and fails with an exception, + // because both TypeIntersector and CommonSuperTypeCalculator are not ready to the situation + // where the part of an input is a type variable. + // + // Just for the inspiration, take a look at ResultTypeResolver.Context.buildNotFixedVariablesToStubTypesSubstitutor usages: + // it seems like they do something relevant. + resultType = inferenceComponents.resultTypeResolver.findResultTypeOrNull( + candidateSystem, variableWithConstraints, TypeVariableDirectionCalculator.ResolveDirection.UNKNOWN + ) as? ConeKotlinType ?: return@withTypeVariablesThatAreNotCountedAsProperTypes + + + check(!candidateStorage.hasContradiction) { "We only should try fixing variables on successful provideDelegate candidate" } + // We don't actually fix it, but add an equality constraint as approximation + candidateSystem.addEqualityConstraint(returnTypeBasedOnVariable, resultType!!, ProvideDelegateFixationPosition) + + check(!candidateStorage.hasContradiction) { + "Currently, we see no cases when contradiction might happen after adding equality constraint like that." + + "But if you see the message, please report your case to https://youtrack.jetbrains.com/newIssue?project=KT" + } + } + + return resultType?.let { + typeVariable to it + } + } + private fun transformLocalVariable(variable: FirProperty): FirProperty = whileAnalysing(session, variable) { assert(variable.isLocal) val delegate = variable.delegate @@ -445,6 +517,13 @@ open class FirDeclarationsResolveTransformer( } } + private fun ConeKotlinType.unwrapTopLevelVariableType(): ConeTypeVariableType? = when { + this is ConeTypeVariableType -> this + this is ConeFlexibleType -> lowerBound.unwrapTopLevelVariableType() + this is ConeDefinitelyNotNullType -> original.unwrapTopLevelVariableType() + else -> null + } + private fun FirProperty.resolveSetter( mayResolveSetterBody: Boolean, ) { @@ -469,7 +548,7 @@ open class FirDeclarationsResolveTransformer( } isSetter -> { val valueParameter = valueParameters.firstOrNull() ?: return - if (valueParameter.returnTypeRef is FirImplicitTypeRef) { + if (valueParameter.returnTypeRef is FirImplicitTypeRef || forceUpdateForNonImplicitTypes) { valueParameter.replaceReturnTypeRef(propertyTypeRef.copyWithNewSource(returnTypeRef.source)) } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt index afa6b674716..e94d11f013f 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt @@ -427,7 +427,11 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT } else { functionCall } - val resultExpression = callResolver.resolveCallAndSelectCandidate(withTransformedArguments) + + val resultExpression = context.inferenceSession.onCandidatesResolution(withTransformedArguments) { + callResolver.resolveCallAndSelectCandidate(withTransformedArguments) + } + val completeInference = callCompleter.completeCall(resultExpression, data) val result = completeInference.transformToIntegerOperatorCallOrApproximateItIfNeeded(data) if (!resolvingAugmentedAssignment) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt index 23a5911e8c1..a3c5527ba3a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt @@ -599,8 +599,8 @@ class DiagnosticReporterByTrackingStrategy( is CallableReferenceConstraintPosition<*>, is IncorporationConstraintPosition, is InjectedAnotherStubTypeConstraintPosition<*>, - is LHSArgumentConstraintPosition<*, *>, - SimpleConstraintSystemConstraintPosition -> { + is LHSArgumentConstraintPosition<*, *>, SimpleConstraintSystemConstraintPosition, ProvideDelegateFixationPosition + -> { if (AbstractTypeChecker.RUN_SLOW_ASSERTIONS) { throw AssertionError("Constraint error in unexpected position: $position") } else if (reportAdditionalErrors) { diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/InferenceUtils.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/InferenceUtils.kt index be95656652d..8bdfdb14b8f 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/InferenceUtils.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/InferenceUtils.kt @@ -11,7 +11,7 @@ import org.jetbrains.kotlin.types.model.* fun ConstraintStorage.buildCurrentSubstitutor( context: TypeSystemInferenceExtensionContext, - additionalBindings: Map + additionalBindings: Map ): TypeSubstitutorMarker { return context.typeSubstitutorByTypeConstructor(fixedTypeVariables.entries.associate { it.key to it.value } + additionalBindings) } diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt index 0fda2b69957..82761384d72 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt @@ -21,6 +21,11 @@ abstract class ConstraintSystemCompletionContext : VariableFixationFinder.Contex abstract override val fixedTypeVariables: Map abstract override val postponedTypeVariables: List + /** + * See [org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage.outerSystemVariablesPrefixSize] + */ + abstract val outerSystemVariablesPrefixSize: Int + abstract fun getBuilder(): ConstraintSystemBuilder // type can be proper if it not contains not fixed type variables diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt index 97e7ef3644a..de12ac5c1c4 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt @@ -63,7 +63,7 @@ class ResultTypeResolver( return c.getDefaultType(direction, variableWithConstraints.constraints, variableWithConstraints.typeVariable) } - private fun findResultTypeOrNull( + fun findResultTypeOrNull( c: Context, variableWithConstraints: VariableWithConstraints, direction: ResolveDirection diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt index 4f725e3ada8..f9c51d3b820 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt @@ -28,6 +28,17 @@ class VariableFixationFinder( val postponedTypeVariables: List val constraintsFromAllForkPoints: MutableList> + /** + * If not null, that property means that we should assume temporary + * `allTypeVariables.keys.minus(typeVariablesThatAreNotCountedAsProperTypes)` as proper types when fixating some variables. + * + * By default, if that property is null, we assume all `allTypeVariables` as not proper. + * + * Currently, that is only used for `provideDelegate` resolution, see + * [org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.fixInnerVariablesForProvideDelegateIfNeeded] + */ + val typeVariablesThatAreNotCountedAsProperTypes: Set? + fun isReified(variable: TypeVariableMarker): Boolean } @@ -182,7 +193,13 @@ class VariableFixationFinder( && !c.isNullabilityConstraint private fun Context.isProperType(type: KotlinTypeMarker): Boolean = - isProperTypeForFixation(type) { t -> !t.contains { notFixedTypeVariables.containsKey(it.typeConstructor()) } } + isProperTypeForFixation(type) { t -> !t.contains { isNotFixedRelevantVariable(it) } } + + private fun Context.isNotFixedRelevantVariable(it: KotlinTypeMarker): Boolean { + if (!notFixedTypeVariables.containsKey(it.typeConstructor())) return false + if (typeVariablesThatAreNotCountedAsProperTypes == null) return true + return typeVariablesThatAreNotCountedAsProperTypes!!.contains(it.typeConstructor()) + } private fun Context.isReified(variable: TypeConstructorMarker): Boolean = notFixedTypeVariables[variable]?.typeVariable?.let { isReified(it) } ?: false diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintPositionAndErrors.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintPositionAndErrors.kt index f6bda3f5942..d1254ef6cd0 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintPositionAndErrors.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintPositionAndErrors.kt @@ -93,6 +93,8 @@ object BuilderInferencePosition : ConstraintPosition() { override fun toString(): String = "For builder inference call" } +data object ProvideDelegateFixationPosition : ConstraintPosition() + // TODO: should be used only in SimpleConstraintSystemImpl, KT-59675 object SimpleConstraintSystemConstraintPosition : ConstraintPosition() diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt index be56d0df2ad..9f8fb0ff878 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt @@ -48,6 +48,20 @@ interface ConstraintStorage { val builtFunctionalTypesForPostponedArgumentsByExpectedTypeVariables: Map val constraintsFromAllForkPoints: List> + /** + * In case some candidate's CS is built in the context of some outer CS, first [outerSystemVariablesPrefixSize] in the list + * of [allTypeVariables] belong to the outer CS. + * + * That information is very limitedly used in a couple of cases when we need to separate those kinds of variables + * - When completing `provideDelegate` calls, we assume outer variables as proper types + * (see fixInnerVariablesForProvideDelegateIfNeeded). + * - When checking consistency of collected variables for the inner candidate + * (see checkNotFixedTypeVariablesCountConsistency). + * + * Also, see docs/fir/delegated_property_inference.md + */ + val outerSystemVariablesPrefixSize: Int + object Empty : ConstraintStorage { override val allTypeVariables: Map get() = emptyMap() override val notFixedTypeVariables: Map get() = emptyMap() @@ -61,6 +75,8 @@ interface ConstraintStorage { override val builtFunctionalTypesForPostponedArgumentsByTopLevelTypeVariables: Map>>, KotlinTypeMarker> = emptyMap() override val builtFunctionalTypesForPostponedArgumentsByExpectedTypeVariables: Map = emptyMap() override val constraintsFromAllForkPoints: List> = emptyList() + + override val outerSystemVariablesPrefixSize: Int get() = 0 } } diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt index a1706f5b3d4..5b85be1293c 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt @@ -241,4 +241,6 @@ internal class MutableConstraintStorage : ConstraintStorage { LinkedHashMap() override val constraintsFromAllForkPoints: MutableList> = SmartList() + + override var outerSystemVariablesPrefixSize: Int = 0 } 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 a47f80ad758..85935fa1f5c 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 @@ -42,10 +42,35 @@ class NewConstraintSystemImpl( private val properTypesCache: MutableSet = SmartSet.create() private val notProperTypesCache: MutableSet = SmartSet.create() private val intersectionTypesCache: MutableMap, EmptyIntersectionTypeInfo?> = mutableMapOf() + override var typeVariablesThatAreNotCountedAsProperTypes: Set? = null + private var couldBeResolvedWithUnrestrictedBuilderInference: Boolean = false override var atCompletionState: Boolean = false + /** + * @see [org.jetbrains.kotlin.resolve.calls.inference.components.VariableFixationFinder.Context.typeVariablesThatAreNotCountedAsProperTypes] + * @see [org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.fixInnerVariablesForProvideDelegateIfNeeded] + */ + fun withTypeVariablesThatAreNotCountedAsProperTypes(typeVariables: Set, block: () -> Unit) { + checkState(State.BUILDING) + // Cleaning cache is necessary because temporarily we change the meaning of what does "proper type" mean + properTypesCache.clear() + notProperTypesCache.clear() + + require(typeVariablesThatAreNotCountedAsProperTypes == null) { + "Currently there should be no nested withDisallowingOnlyThisTypeVariablesForProperTypes calls" + } + + typeVariablesThatAreNotCountedAsProperTypes = typeVariables + + block() + + typeVariablesThatAreNotCountedAsProperTypes = null + properTypesCache.clear() + notProperTypesCache.clear() + } + private enum class State { BUILDING, TRANSACTION, @@ -276,6 +301,21 @@ class NewConstraintSystemImpl( ) } + fun addOuterSystem(outerSystem: ConstraintStorage) { + addOtherSystem(outerSystem) + storage.outerSystemVariablesPrefixSize = outerSystem.allTypeVariables.size + } + + fun setBaseSystem(outerSystem: ConstraintStorage) { + addOtherSystem(outerSystem) + storage.outerSystemVariablesPrefixSize = outerSystem.outerSystemVariablesPrefixSize + } + + fun prepareForGlobalCompletion() { + // There's no more separation of outer/inner variables once global completion starts + storage.outerSystemVariablesPrefixSize = 0 + } + override fun addOtherSystem(otherSystem: ConstraintStorage) { if (otherSystem.allTypeVariables.isNotEmpty()) { otherSystem.allTypeVariables.forEach { @@ -317,6 +357,9 @@ class NewConstraintSystemImpl( it if (typeToCheck == null) return@contains false + if (typeVariablesThatAreNotCountedAsProperTypes != null) { + return@contains typeVariablesThatAreNotCountedAsProperTypes!!.contains(typeToCheck.typeConstructor()) + } storage.allTypeVariables.containsKey(typeToCheck.typeConstructor()) } @@ -369,6 +412,9 @@ class NewConstraintSystemImpl( return storage.postponedTypeVariables } + override val outerSystemVariablesPrefixSize: Int + get() = storage.outerSystemVariablesPrefixSize + override val constraintsFromAllForkPoints: MutableList> get() { checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION) diff --git a/compiler/testData/codegen/box/delegatedProperty/withInvoke.kt b/compiler/testData/codegen/box/delegatedProperty/withInvoke.kt new file mode 100644 index 00000000000..166c952024e --- /dev/null +++ b/compiler/testData/codegen/box/delegatedProperty/withInvoke.kt @@ -0,0 +1,17 @@ +// TARGET_BACKEND: JVM_IR +// IGNORE_BACKEND_K1: JVM_IR +// ISSUE: KT-61633 + +class C(val p: T3) +class D(val p: T4) + +val C.getValue: D get() = D(p) +operator fun D.invoke(x: Any?, y: Any?): X4 = p + +fun foo(c: C): String { + val y1 by c + + return y1 +} + +fun box(): String = foo(C("OK")) \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/inference/annotatedAccessor.kt b/compiler/testData/diagnostics/tests/delegatedProperty/inference/annotatedAccessor.kt new file mode 100644 index 00000000000..5dab5bec0dd --- /dev/null +++ b/compiler/testData/diagnostics/tests/delegatedProperty/inference/annotatedAccessor.kt @@ -0,0 +1,23 @@ +// FIR_IDENTICAL +// ISSUE: KT-61075 +// WITH_REFLECT + +import kotlin.properties.ReadWriteProperty + +open class BaseState +interface CustomComparable +annotation class XCollection + +fun treeSet(): ReadWriteProperty where E: BaseState, E: CustomComparable = TODO() + +internal class VisibleTreeState { + internal class State: BaseState(), CustomComparable + + // K1: ok + // K2: NEW_INFERENCE_ERROR + @get:XCollection var expandedNodes: State by treeSet() + + // K1: ok + // K2: NEW_INFERENCE_ERROR + @set:XCollection var selectedNodes: State by treeSet() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/inference/fullyCompleteDelegateWhenPossible.kt b/compiler/testData/diagnostics/tests/delegatedProperty/inference/fullyCompleteDelegateWhenPossible.kt new file mode 100644 index 00000000000..12b417db35b --- /dev/null +++ b/compiler/testData/diagnostics/tests/delegatedProperty/inference/fullyCompleteDelegateWhenPossible.kt @@ -0,0 +1,16 @@ +// FIR_IDENTICAL +// WITH_STDLIB + +import kotlin.reflect.KProperty + +class Cached(val f: () -> T) { + operator fun getValue(thisRef: Any?, property: KProperty<*>): T = TODO() +} + +private val map: MutableMap> = TODO() + +fun foo() { + val bar by map.getOrPut("A") { + Cached { "B" } + } +} diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/inference/lambdaWithMultipleReturns.kt b/compiler/testData/diagnostics/tests/delegatedProperty/inference/lambdaWithMultipleReturns.kt new file mode 100644 index 00000000000..9fe191297f6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/delegatedProperty/inference/lambdaWithMultipleReturns.kt @@ -0,0 +1,19 @@ +// FIR_IDENTICAL +import kotlin.reflect.KProperty + +fun myEmptyList1(): List = TODO() +fun myEmptyList2(): List = TODO() + +var b: Boolean = false + +val foo: List by myLazy { + if (b) return@myLazy myEmptyList1() + + myEmptyList2() +} + +private fun myLazy(initialValue: () -> T): ReadProperty = TODO() + +class ReadProperty(val v: V) { + operator fun getValue(thisRef: Any?, property: KProperty<*>): V = v +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/inference/nothingTypeThisRef.kt b/compiler/testData/diagnostics/tests/delegatedProperty/inference/nothingTypeThisRef.kt new file mode 100644 index 00000000000..7e8b7b362dc --- /dev/null +++ b/compiler/testData/diagnostics/tests/delegatedProperty/inference/nothingTypeThisRef.kt @@ -0,0 +1,22 @@ +// FIR_IDENTICAL +// WITH_STDLIB + +import kotlin.reflect.KProperty + +class DelegateProvider { + operator fun provideDelegate( + thisRef: Nothing?, + prop: KProperty<*> + ): RDelegate = TODO() +} + +class RDelegate { + operator fun getValue(thisRef: Nothing?, property: KProperty<*>): String = "" +} + +fun builder(name: String? = null, block: () -> Unit): DelegateProvider = TODO() +val x by builder {} + +fun main() { + x.length +} diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/inference/provideDelegateFixationResultContainsOtherInnerVariable.kt b/compiler/testData/diagnostics/tests/delegatedProperty/inference/provideDelegateFixationResultContainsOtherInnerVariable.kt new file mode 100644 index 00000000000..fc722388e40 --- /dev/null +++ b/compiler/testData/diagnostics/tests/delegatedProperty/inference/provideDelegateFixationResultContainsOtherInnerVariable.kt @@ -0,0 +1,17 @@ +// FIR_IDENTICAL + +interface A { + operator fun getValue(x: Any?, y: Any?): T = TODO() +} + +fun foo(): A = TODO() + +fun bar1() { + val x: String by foo() +} + +fun bar2() { + operator fun , E> A.provideDelegate(x: Any?, y: Any?): F = TODO() + + val x: String by foo() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/inference/provideDelegateReturningOuterTypeVariable.fir.kt b/compiler/testData/diagnostics/tests/delegatedProperty/inference/provideDelegateReturningOuterTypeVariable.fir.kt new file mode 100644 index 00000000000..1d362f02859 --- /dev/null +++ b/compiler/testData/diagnostics/tests/delegatedProperty/inference/provideDelegateReturningOuterTypeVariable.fir.kt @@ -0,0 +1,19 @@ +// ISSUE: KT-61747 + +interface A { + operator fun getValue(x: Any?, y: Any?): String = "" +} + +fun foo(x: (T) -> Unit): T = TODO() + +fun bar1(a: A) { + val x by foo { x: A -> } +} + +fun bar2(a: A) { + operator fun F.provideDelegate(x: Any?, y: Any?): F { + return this + } + + val x by foo { x: A -> } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/inference/provideDelegateReturningOuterTypeVariable.kt b/compiler/testData/diagnostics/tests/delegatedProperty/inference/provideDelegateReturningOuterTypeVariable.kt new file mode 100644 index 00000000000..63bd577fa31 --- /dev/null +++ b/compiler/testData/diagnostics/tests/delegatedProperty/inference/provideDelegateReturningOuterTypeVariable.kt @@ -0,0 +1,19 @@ +// ISSUE: KT-61747 + +interface A { + operator fun getValue(x: Any?, y: Any?): String = "" +} + +fun foo(x: (T) -> Unit): T = TODO() + +fun bar1(a: A) { + val x by foo { x: A -> } +} + +fun bar2(a: A) { + operator fun F.provideDelegate(x: Any?, y: Any?): F { + return this + } + + val x by foo { x: A -> } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/inference/successfulProvideDelegateLeadsToRedGetValue.fir.kt b/compiler/testData/diagnostics/tests/delegatedProperty/inference/successfulProvideDelegateLeadsToRedGetValue.fir.kt new file mode 100644 index 00000000000..df7d206548f --- /dev/null +++ b/compiler/testData/diagnostics/tests/delegatedProperty/inference/successfulProvideDelegateLeadsToRedGetValue.fir.kt @@ -0,0 +1,11 @@ +// ISSUE: KT-61077 + +val test: Int by materializeDelegate() + +fun materializeDelegate(): Box = TODO() + +operator fun > Box.provideDelegate(receiver: Any?, property: kotlin.reflect.KProperty<*>): K = TODO() + +operator fun > Q.getValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>): Q = TODO() + +class Box {} diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/inference/successfulProvideDelegateLeadsToRedGetValue.kt b/compiler/testData/diagnostics/tests/delegatedProperty/inference/successfulProvideDelegateLeadsToRedGetValue.kt new file mode 100644 index 00000000000..1b900f230cf --- /dev/null +++ b/compiler/testData/diagnostics/tests/delegatedProperty/inference/successfulProvideDelegateLeadsToRedGetValue.kt @@ -0,0 +1,11 @@ +// ISSUE: KT-61077 + +val test: Int by materializeDelegate() + +fun materializeDelegate(): Box = TODO() + +operator fun > Box.provideDelegate(receiver: Any?, property: kotlin.reflect.KProperty<*>): K = TODO() + +operator fun > Q.getValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>): Q = TODO() + +class Box {} diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/inference/typeVariablesFlow.kt b/compiler/testData/diagnostics/tests/delegatedProperty/inference/typeVariablesFlow.kt new file mode 100644 index 00000000000..b1826362b94 --- /dev/null +++ b/compiler/testData/diagnostics/tests/delegatedProperty/inference/typeVariablesFlow.kt @@ -0,0 +1,28 @@ +// FIR_IDENTICAL +// ISSUE: KT-59529 +import kotlin.reflect.KProperty + +interface Out { + val x: F +} +interface Out2 { + val x: E +} +interface Inv { + val u: U +} + +val outString: Out = TODO() + +val invOut2String by foo(outString) + +operator fun Inv.getValue( + thisRef: Any?, + property: KProperty<*> +): Inv = TODO() + +fun foo(other: Out): Inv> = TODO() + +fun main() { + invOut2String.u.x.length +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/inference/typeVariablesFlowComplex.kt b/compiler/testData/diagnostics/tests/delegatedProperty/inference/typeVariablesFlowComplex.kt new file mode 100644 index 00000000000..e309cedc45e --- /dev/null +++ b/compiler/testData/diagnostics/tests/delegatedProperty/inference/typeVariablesFlowComplex.kt @@ -0,0 +1,37 @@ +// FIR_IDENTICAL +// WITH_STDLIB +// !CHECK_TYPE +// ISSUE: KT-59529 + +import kotlin.reflect.KProperty + +val ows: IgnoringParser = IgnoringParser() + +class MyParser { + + private val booleanLiteral: Parser = TODO() + + val topLevel by ows + booleanLiteral + + fun main() { + topLevel checkType { _, String>>() } + } + + operator fun TransformParser.provideDelegate( + thisRef: MyParser<*>, + property: KProperty<*> + ): TransformParser = TODO() + + operator fun TransformParser.getValue( + thisRef: MyParser<*>, + property: KProperty<*> + ): TransformParser = TODO() +} + +abstract class Parser + +class TransformParser : Parser() +class IgnoringParser : Parser() + +operator fun IgnoringParser.plus(other: Parser): TransformParser, T> = + TODO() diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/provideDelegateResolutionWithStubTypesAndExtensionGetValue.kt b/compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/provideDelegateResolutionWithStubTypesAndExtensionGetValue.kt new file mode 100644 index 00000000000..74241580279 --- /dev/null +++ b/compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/provideDelegateResolutionWithStubTypesAndExtensionGetValue.kt @@ -0,0 +1,10 @@ +// FIR_IDENTICAL +val test: String by materializeDelegate() + +fun materializeDelegate(): Delegate = Delegate() + +operator fun K.provideDelegate(receiver: Any?, property: kotlin.reflect.KProperty<*>): K = this + +operator fun Delegate.getValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>): X = TODO() + +class Delegate diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/setValue.fir.kt b/compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/setValue.fir.kt index 923ce3053a9..ee4dba500bf 100644 --- a/compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/setValue.fir.kt +++ b/compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/setValue.fir.kt @@ -1,5 +1,5 @@ // !DIAGNOSTICS: -UNUSED_PARAMETER - +// ISSUE: KT-61077 class Delegate operator fun Delegate<*>.getValue(receiver: Any?, p: Any): String = "" @@ -10,7 +10,7 @@ operator fun String.provideDelegate(receiver: Any?, p: Any) = Delegate() var test1: String by Delegate() var test2: String by Delegate() -var test3: String by "OK" +var test3: String by "OK" var test4: String by "OK".provideDelegate(null, "") var test5: String by "OK".provideDelegate(null, "") diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/setValue.kt b/compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/setValue.kt index c0c565e46d8..69772c14a12 100644 --- a/compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/setValue.kt +++ b/compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/setValue.kt @@ -1,5 +1,5 @@ // !DIAGNOSTICS: -UNUSED_PARAMETER - +// ISSUE: KT-61077 class Delegate operator fun Delegate<*>.getValue(receiver: Any?, p: Any): String = "" diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/withInvokes.fir.kt b/compiler/testData/diagnostics/tests/delegatedProperty/withInvokes.fir.kt new file mode 100644 index 00000000000..8830e1ce58f --- /dev/null +++ b/compiler/testData/diagnostics/tests/delegatedProperty/withInvokes.fir.kt @@ -0,0 +1,22 @@ +// ISSUE: KT-61633 + +interface A +interface B +interface C +interface D + +val A.provideDelegate: B get() = TODO() +operator fun B.invoke(x: Any?, y: Any?): C = TODO() + +val C.getValue: D get() = TODO() +operator fun D.invoke(x: Any?, y: Any?): X4 = TODO() + +fun foo(a: A, c: C) { + val x1 by a + x1 + x1.length + + val y1 by c + y1 + y1.length +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/withInvokes.kt b/compiler/testData/diagnostics/tests/delegatedProperty/withInvokes.kt new file mode 100644 index 00000000000..8df750227c2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/delegatedProperty/withInvokes.kt @@ -0,0 +1,22 @@ +// ISSUE: KT-61633 + +interface A +interface B +interface C +interface D + +val A.provideDelegate: B get() = TODO() +operator fun B.invoke(x: Any?, y: Any?): C = TODO() + +val C.getValue: D get() = TODO() +operator fun D.invoke(x: Any?, y: Any?): X4 = TODO() + +fun foo(a: A, c: C) { + val x1 by a + x1 + x1.length + + val y1 by c + y1 + y1.length +} \ No newline at end of file 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 3c57ffe6c9d..6c57afb2ca6 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 @@ -9556,6 +9556,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/delegatedProperty/useTypeParameterOfExtensionProperty_Disabled.kt"); } + @Test + @TestMetadata("withInvokes.kt") + public void testWithInvokes() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/withInvokes.kt"); + } + @Test @TestMetadata("wrongCountOfParametersInGet.kt") public void testWrongCountOfParametersInGet() throws Exception { @@ -9583,6 +9589,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/delegatedProperty/inference"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true); } + @Test + @TestMetadata("annotatedAccessor.kt") + public void testAnnotatedAccessor() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/annotatedAccessor.kt"); + } + @Test @TestMetadata("callableReferenceArgumentInDelegatedExpression.kt") public void testCallableReferenceArgumentInDelegatedExpression() throws Exception { @@ -9631,6 +9643,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/extensionProperty.kt"); } + @Test + @TestMetadata("fullyCompleteDelegateWhenPossible.kt") + public void testFullyCompleteDelegateWhenPossible() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/fullyCompleteDelegateWhenPossible.kt"); + } + @Test @TestMetadata("genericMethodInGenericClass.kt") public void testGenericMethodInGenericClass() throws Exception { @@ -9661,6 +9679,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/labeledDelegatedExpression.kt"); } + @Test + @TestMetadata("lambdaWithMultipleReturns.kt") + public void testLambdaWithMultipleReturns() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/lambdaWithMultipleReturns.kt"); + } + @Test @TestMetadata("manyIncompleteCandidates.kt") public void testManyIncompleteCandidates() throws Exception { @@ -9697,12 +9721,36 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/notNullAssertionInLocalDelegated.kt"); } + @Test + @TestMetadata("nothingTypeThisRef.kt") + public void testNothingTypeThisRef() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/nothingTypeThisRef.kt"); + } + + @Test + @TestMetadata("provideDelegateFixationResultContainsOtherInnerVariable.kt") + public void testProvideDelegateFixationResultContainsOtherInnerVariable() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/provideDelegateFixationResultContainsOtherInnerVariable.kt"); + } + + @Test + @TestMetadata("provideDelegateReturningOuterTypeVariable.kt") + public void testProvideDelegateReturningOuterTypeVariable() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/provideDelegateReturningOuterTypeVariable.kt"); + } + @Test @TestMetadata("resultTypeOfLambdaForConventionMethods.kt") public void testResultTypeOfLambdaForConventionMethods() throws Exception { runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/resultTypeOfLambdaForConventionMethods.kt"); } + @Test + @TestMetadata("successfulProvideDelegateLeadsToRedGetValue.kt") + public void testSuccessfulProvideDelegateLeadsToRedGetValue() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/successfulProvideDelegateLeadsToRedGetValue.kt"); + } + @Test @TestMetadata("tryInGenerated.kt") public void testTryInGenerated() throws Exception { @@ -9715,6 +9763,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/typeOfLazyDelegatedPropertyWithObject.kt"); } + @Test + @TestMetadata("typeVariablesFlow.kt") + public void testTypeVariablesFlow() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/typeVariablesFlow.kt"); + } + + @Test + @TestMetadata("typeVariablesFlowComplex.kt") + public void testTypeVariablesFlowComplex() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/inference/typeVariablesFlowComplex.kt"); + } + @Test @TestMetadata("useCompleterWithoutExpectedType.kt") public void testUseCompleterWithoutExpectedType() throws Exception { @@ -9839,6 +9899,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/provideDelegateResolutionWithStubTypes.kt"); } + @Test + @TestMetadata("provideDelegateResolutionWithStubTypesAndExtensionGetValue.kt") + public void testProvideDelegateResolutionWithStubTypesAndExtensionGetValue() throws Exception { + runTest("compiler/testData/diagnostics/tests/delegatedProperty/provideDelegate/provideDelegateResolutionWithStubTypesAndExtensionGetValue.kt"); + } + @Test @TestMetadata("setValue.kt") public void testSetValue() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index 77b953b6740..7a633a947cd 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -15513,6 +15513,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/delegatedProperty/varInInnerClass.kt"); } + @Test + @TestMetadata("withInvoke.kt") + public void testWithInvoke() throws Exception { + runTest("compiler/testData/codegen/box/delegatedProperty/withInvoke.kt"); + } + @Nested @TestMetadata("compiler/testData/codegen/box/delegatedProperty/delegateToAnother") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenWithIrInlinerTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenWithIrInlinerTestGenerated.java index b7d944f3fdf..4a10ae8128c 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenWithIrInlinerTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenWithIrInlinerTestGenerated.java @@ -15513,6 +15513,12 @@ public class IrBlackBoxCodegenWithIrInlinerTestGenerated extends AbstractIrBlack runTest("compiler/testData/codegen/box/delegatedProperty/varInInnerClass.kt"); } + @Test + @TestMetadata("withInvoke.kt") + public void testWithInvoke() throws Exception { + runTest("compiler/testData/codegen/box/delegatedProperty/withInvoke.kt"); + } + @Nested @TestMetadata("compiler/testData/codegen/box/delegatedProperty/delegateToAnother") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 6717dbaf9d0..e12751ff9ed 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -12455,6 +12455,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/delegatedProperty/genericDelegateUncheckedCast2.kt"); } + @TestMetadata("withInvoke.kt") + public void ignoreWithInvoke() throws Exception { + runTest("compiler/testData/codegen/box/delegatedProperty/withInvoke.kt"); + } + private void runTest(String testDataFilePath) throws Exception { KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath); } diff --git a/docs/fir/builder_inference.md b/docs/fir/builder_inference.md index b90593e900e..6e286c74297 100644 --- a/docs/fir/builder_inference.md +++ b/docs/fir/builder_inference.md @@ -2,10 +2,6 @@ See also: [Kotlin Spec: Builder-style type inference](https://kotlinlang.org/spec/type-inference.html#builder-style-type-inference) ### Glossary -#### CS = Constraint system -An instance of `org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl` -#### Call-tree -A tree of calls, in which constraint systems are joined and solved(completed) together #### Postponed type variable Type-variable, that was used to build a stub type on, and fixation of which is postponed until the end of lambda analysis. Selection of type variables to postpone happens during [Initiation](#initiation) phase diff --git a/docs/fir/delegated_property_inference.md b/docs/fir/delegated_property_inference.md new file mode 100644 index 00000000000..febd5f510a0 --- /dev/null +++ b/docs/fir/delegated_property_inference.md @@ -0,0 +1,142 @@ +## FIR/Delegated property inference +See also: [Kotlin Spec: Delegated property declaration](https://kotlinlang.org/spec/declarations.html#delegated-property-declaration) and some [Common inference terms definition](inference.md) + +### Glossary +#### Delegation related operator calls +Synthetically generated by Raw FIR builder calls to `getValue`, `setValue` or `provideDelegate` functions. + +*NB:* They might be differentiated from regular calls by comparing origin to `FirFunctionCallOrigin.Operator` + +#### Outer CS +A constraint system that defines type variables with their constraints not brought by the call itself or its arguments. +The general idea is that before running resolution of a specific "inner" candidate, we copy all the outer variables to its own CS in the very +beginning, and after some successful candidate is chosen, we apply the "relevant" changes to the outer CS. + +By "apply" operation currently we mean literally replacement of the old CS with the new one. + +### Desugaring at Raw FIR building stage +The code like this +```kotlin +var prop by delegateExpression() +``` +is being desugared to something similar to this (see `org.jetbrains.kotlin.fir.builder.ConversionUtilsKt.generateAccessorsByDelegate`) +``` +val prop = propertyNode { + delegate = delegate { + expression = "delegateExpression()" + delegateProvider = "expression.provideDelegate()" + } + + get() = delegate$field.getValue($thisRef, ::prop) + set(value) { + delegate$field.setValue($thisRef, ::prop, value) + } +} +``` + +Even in case the property type is specified, all the content types are set as implicit. + +### Delegated property inference algorithm +#### Initiation +At first, we set up a delegate inference session that is necessary to preserve the common CS and partially completed calls that need to be +fully completed in the end. + +For reference, see `org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.BodyResolveContext.forPropertyDelegateAccessors`. + +#### Delegate expression inference +At first, we resolve delegate expression with `ContextDependent.Delegate` resolution mode that behaves just like the regular `ContextDependent` +but it makes DelegateInferenceSession to remember resulting incomplete CS from the call. + +*NB:* If the delegate expression is simple enough, i.e., it does not contain type parameters, or they might be inferred from the call itself, +we just run FULL completion on it as for regular `ContextDependent` and don't store its CS. + +For reference, see `org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.transformWrappedDelegateExpression`. + +#### provideDelegate resolution +After delegate expression is resolved, we start regular resolution of `provideDelegate()` call stored at `FirWrappedDelegateExpression::delegateProvider`. + +Note that as explicit receiver it uses just the same instance of `delegateExpression` we've just resolved on the previous step. +As the receiver still might contain some type variables, we use CS of it as [Outer CS](#outer-cs) for all the `provideDelegate` candidates. + +If there is no most specific successful candidate, then we just drop and forget the call. + +Otherwise, after the resulting candidate is chosen, it has some state of CS that contains both "inner" type variables of the candidate itself and some +global ones brought by Outer CS. + +Note that we don't force FULL completion (unlike K1 did), thus potentially leaving some of the type variables not fixed. +The most problematic part with that approach is that in some cases the return type of `provideDelegate` is a type variable _and_ we +might need to look into its member scope to find `getValue` call there. + +```kotlin +val test: String by materializeDelegate() + +fun materializeDelegate(): Delegate = Delegate() + +operator fun K.provideDelegate(receiver: Any?, property: kotlin.reflect.KProperty<*>): K = this + +class Delegate { + operator fun getValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>): V = TODO() +} +``` + +But the problem is that member scope is not defined for variables, thus we emulate K1 full completion behavior by + +- Looking if `provideDelegate` actually returns a type variable (`K`) +- Finding the current result type that might be used for fixation, but under the assumption that all outer type variables +(`[T]` in the example above) are considered proper, so could be used inside a fixation result +- Add equality constraint `K = FoundFixationResult` (`K = Delegate(Tv)` in the example above) +- When creating a final substitutor for `provideDelegate` expression, take that constraint into account + +See `org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.findResultTypeForInnerVariableIfNeeded`. + +If `provideDelegate` is completed without contradictions, we effectively replace `delegateExpression` with `delegateExpression.provideDelegate()`. + +#### getValue/setValue analysis + +If a property type is set explicitly, it's being propagated to the accessors' signatures at `FirDeclarationsResolveTransformer::transformAccessors` +(return type of getter and parameter type of setter). +Otherwise, the types remain implicit. + +Then, we effectively call `transformFunctionWithGivenSignature` on getter and _only_ if the property type was explicit, +call `transformFunctionWithGivenSignature` on setter, too. + +We do that under the same delegate inference session, so we've got callbacks for `FirInferenceSession.onCandidatesResolution` thus +exactly for `getValue`/`setValue`/`provideDelegate` calls, we set outer CS from the session. + +Note that there's no need to do that for the delegate expression or nested arguments (they should be resolved as usual in `ContextDependent` resolution mode). + +Also, while accessors have a shape like `delegate$field.getValue($thisRef, ::prop)`, `delegate$field` is a special reference which type +is being set to the current type of `delegateExpression` at `FirExpressionsResolveTransformer.transformQualifiedAccessExpression`. + +For those delegation operator calls, if they're successfully resolved, we preserve their CSs and use them as the main outer ones, +also we collect the calls as ones that need to be completed later (partially completed). + +Note that `transformFunctionWithGivenSignature` if a return type is implicit propagates one from the body of the function, thus +after getter resolution its return type (and the property one) might contain some type variables. + +See `org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.transformPropertyAccessorsWithDelegate`. + +#### Partial calls completion + +At that stage, we've got all those delegation operators calls resolved effectively under the same constraint system. +So, what we need to do further is solving that CS, thus find the result types for all the type variables from those calls. + +To achieve that, we run completion for all incomplete calls altogether as a list of `topLevelAtoms`, so it works mostly as regular FULL completion. + +See `org.jetbrains.kotlin.fir.resolve.inference.FirDelegatedPropertyInferenceSession.completeCandidates`. + +#### Completion results writing + +In case of successful completion, we get a final substitutor that we may apply to property return type and accessor signatures, too. +Thus, getting rid of potentially left type variables there. + +After that we run `FirCallCompletionResultsWriterTransformer` on each of the freshly completed calls, with a special mode +`DelegatedPropertyCompletion` that is only different in a meaning that for each qualified access it also, recursively ran on the explicit +receiver (that is necessary to update `delegate$field` references types for `getValue`/`setValue` calls). + +Another nasty tweak that is needed to be made is updating substituted member after completion. +Some of the final candidates might be obtained from member scopes of types with type arguments containing variables +(like `Delegate` from the example above), but after body transformation they should be resolved to the corrected symbols from the +scopes with substituted type arguments. + +See `FirCallCompletionResultsWriterTransformer.updateSubstitutedMemberIfReceiverContainsTypeVariable` for details. diff --git a/docs/fir/inference.md b/docs/fir/inference.md new file mode 100644 index 00000000000..6b626e8a97c --- /dev/null +++ b/docs/fir/inference.md @@ -0,0 +1,12 @@ +## Inference + +Currently, this document contains some basic terms that are common for different specific inference types. +Lately, it might be extended to include some basic description of how inference works. + +### Glossary +#### CS = Constraint system +An instance of `org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl` +#### Call-tree +A tree of calls, in which constraint systems are joined and solved(completed) together +#### Proper constraint +A constraint that doesn't reference any type variables \ No newline at end of file