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
This commit is contained in:
Denis.Zharkov
2023-08-04 19:52:29 +02:00
committed by Space Team
parent cd5105c133
commit a02cb16fb2
53 changed files with 1183 additions and 381 deletions
@@ -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 {
@@ -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 {
@@ -144,7 +144,7 @@ digraph delegateWithAnonymousObject_kt {
}
55 [label="Postponed exit from lambda"];
56 [label="Function call: this@R|/IssuesListUserProfile|.R|/delegate|<R|IssuesListUserProfile|, R|IssuesListUserProfile|, R|IssueListView|>(...)" style="filled" fillcolor=yellow];
57 [label="Function call: this@R|/IssuesListUserProfile|.R|/delegate|<R|IssuesListUserProfile|, R|IssuesListUserProfile|, R|IssueListView|>(...).<Unresolved name: provideDelegate>#(...)" style="filled" fillcolor=yellow];
57 [label="Function call: this@R|/IssuesListUserProfile|.R|/delegate|<R|IssuesListUserProfile|, R|IssuesListUserProfile|, R|IssueListView|>(...).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];
}
@@ -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 {
@@ -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 {
@@ -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")
@@ -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")
@@ -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")
@@ -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
}
@@ -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()
}
}
@@ -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<TypeConstructorMarker>,
) {
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
@@ -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<Pair<FirStatement, Candidate>> = mutableListOf()
private var lambdaImplicitReceivers: MutableList<ImplicitExtensionReceiverValue> = mutableListOf()
override val currentConstraintStorage: ConstraintStorage
get() = ConstraintStorage.Empty
override fun hasSyntheticTypeVariables(): Boolean = false
override fun isSyntheticTypeVariable(typeVariable: TypeVariableMarker): Boolean {
return false
}
override fun <T> 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<ConeTypeVariableTypeConstructor, ConeKotlinType>
}
override fun createSyntheticStubTypes(system: NewConstraintSystemImpl): Map<TypeConstructorMarker, ConeStubType> = emptyMap()
private fun buildCommonSystem(initialStorage: ConstraintStorage): Pair<NewConstraintSystemImpl, Boolean> {
val commonSystem = components.session.inferenceComponents.createConstraintSystem()
val nonFixedToVariablesSubstitutor = createNonFixedTypeToVariableSubstitutor()
@@ -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
@@ -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 <R> 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 <T> shouldAvoidFullCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement =
call.isAnyOfDelegateOperators()
override fun <T> 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 <T> addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement {
partiallyResolvedCalls += call to candidate
if (candidate.isSuccessful) {
integrateResolvedCall(candidate.system.asReadOnlyStorage())
}
}
private fun <T> T.isProvideDelegate() where T : FirResolvable, T : FirStatement =
isAnyOfDelegateOperators() && (this as FirFunctionCall).calleeReference.name == OperatorNameConventions.PROVIDE_DELEGATE
override fun <T> addPartiallyResolvedCall(call: T) where T : FirResolvable, T : FirStatement {
super.addPartiallyResolvedCall(call)
if (call.candidate.isSuccessful) {
integrateResolvedCall(call.candidate.system.currentStorage())
}
}
override fun <T> 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> 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<ConeTypeVariableTypeConstructor, ConeKotlinType>? = null
private fun createNonFixedTypeToVariableSubstitutor(): NotFixedTypeToVariableSubstitutorForDelegateInference {
val typeContext = components.session.typeContext
val bindings = mutableMapOf<TypeVariableMarker, ConeKotlinType>()
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<TypeConstructorMarker, ConeKotlinType>()
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<FirResolvable> {
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 <T> addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement {}
private val stubTypesByTypeVariable: MutableMap<ConeTypeVariable, ConeStubType> = mutableMapOf()
private val syntheticTypeVariableByTypeVariable = mutableMapOf<TypeVariableMarker, ConeTypeVariable>()
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<TypeConstructorMarker, ConeStubType> {
val bindings = mutableMapOf<TypeConstructorMarker, ConeStubType>()
registerSyntheticVariables(system.currentStorage())
for (variable in system.postponedTypeVariables) {
variable as ConeTypeVariable
bindings[variable.typeConstructor] = stubTypesByTypeVariable[variable]!!
}
return bindings
}
override fun registerStubTypes(map: Map<TypeVariableMarker, StubTypeMarker>) {
// @Suppress("UNCHECKED_CAST")
// stubTypesByTypeVariable.putAll(map as Map<ConeTypeVariable, ConeStubType>)
}
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 <T> shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = true
}
@@ -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 <T> shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement
abstract val currentConstraintStorage: ConstraintStorage
abstract fun <T> 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 <T> shouldAvoidFullCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = false
abstract fun <T> processPartiallyResolvedCall(call: T, resolutionMode: ResolutionMode) where T : FirResolvable, T : FirStatement
abstract fun <T> addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement
abstract fun registerStubTypes(map: Map<TypeVariableMarker, StubTypeMarker>)
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<ConeTypeVariableTypeConstructor, ConeKotlinType>?
abstract fun clear()
abstract fun createSyntheticStubTypes(system: NewConstraintSystemImpl): Map<TypeConstructorMarker, ConeStubType>
open fun <R> onCandidatesResolution(call: FirFunctionCall, candidatesResolutionCallback: () -> R) = candidatesResolutionCallback()
}
abstract class FirStubInferenceSession : FirInferenceSession() {
override fun <T> shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = true
override val currentConstraintStorage: ConstraintStorage
get() = ConstraintStorage.Empty
override fun <T> addPartiallyResolvedCall(call: T) where T : FirResolvable, T : FirStatement {}
override fun <T> processPartiallyResolvedCall(call: T, resolutionMode: ResolutionMode) where T : FirResolvable, T : FirStatement {}
override fun <T> 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<TypeVariableMarker, StubTypeMarker>) {}
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<TypeConstructorMarker, ConeStubType> = emptyMap()
override fun clear() {
}
}
@@ -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<Pair<FirResolvable, Candidate>> = mutableListOf()
private val completedCalls: MutableSet<FirResolvable> = mutableSetOf()
@@ -39,7 +33,7 @@ abstract class FirInferenceSessionForChainedResolve(
// do nothing
}
override fun <T> addPartiallyResolvedCall(call: T) where T : FirResolvable, T : FirStatement {
override fun <T> processPartiallyResolvedCall(call: T, resolutionMode: ResolutionMode) where T : FirResolvable, T : FirStatement {
partiallyResolvedCalls += call to call.candidate
}
@@ -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)
}
@@ -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<FirFunctionSymbol<*>> = mutableSetOf()
var containingClassDeclarations: ArrayDeque<FirRegularClass> = ArrayDeque()
@@ -831,6 +844,21 @@ class BodyResolveContext(
}
}
@OptIn(PrivateForInline::class)
inline fun <T> 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 <T> withConstructor(constructor: FirConstructor, f: () -> T): T =
withContainer(constructor, f)
@@ -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> K.provideDelegate(receiver: Any?, property: kotlin.reflect.KProperty<*>): K = this
*
* Here, if delegate expression returns something like `Delegate<Tv>` where Tv is a variable and the `Delegate` class contains
* the member `getValue`, we need to fix `K` into `Delegate<Tv>`, 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<Tv> 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<TypeConstructorMarker, ConeKotlinType>? {
// 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<Tv>` 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<Y>
// 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))
}
}
@@ -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) {
@@ -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) {
@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.types.model.*
fun ConstraintStorage.buildCurrentSubstitutor(
context: TypeSystemInferenceExtensionContext,
additionalBindings: Map<TypeConstructorMarker, StubTypeMarker>
additionalBindings: Map<TypeConstructorMarker, KotlinTypeMarker>
): TypeSubstitutorMarker {
return context.typeSubstitutorByTypeConstructor(fixedTypeVariables.entries.associate { it.key to it.value } + additionalBindings)
}
@@ -21,6 +21,11 @@ abstract class ConstraintSystemCompletionContext : VariableFixationFinder.Contex
abstract override val fixedTypeVariables: Map<TypeConstructorMarker, KotlinTypeMarker>
abstract override val postponedTypeVariables: List<TypeVariableMarker>
/**
* 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
@@ -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
@@ -28,6 +28,17 @@ class VariableFixationFinder(
val postponedTypeVariables: List<TypeVariableMarker>
val constraintsFromAllForkPoints: MutableList<Pair<IncorporationConstraintPosition, ForkPointData>>
/**
* 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<TypeConstructorMarker>?
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
@@ -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()
@@ -48,6 +48,20 @@ interface ConstraintStorage {
val builtFunctionalTypesForPostponedArgumentsByExpectedTypeVariables: Map<TypeConstructorMarker, KotlinTypeMarker>
val constraintsFromAllForkPoints: List<Pair<IncorporationConstraintPosition, ForkPointData>>
/**
* 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<TypeConstructorMarker, TypeVariableMarker> get() = emptyMap()
override val notFixedTypeVariables: Map<TypeConstructorMarker, VariableWithConstraints> get() = emptyMap()
@@ -61,6 +75,8 @@ interface ConstraintStorage {
override val builtFunctionalTypesForPostponedArgumentsByTopLevelTypeVariables: Map<Pair<TypeConstructorMarker, List<Pair<TypeConstructorMarker, Int>>>, KotlinTypeMarker> = emptyMap()
override val builtFunctionalTypesForPostponedArgumentsByExpectedTypeVariables: Map<TypeConstructorMarker, KotlinTypeMarker> = emptyMap()
override val constraintsFromAllForkPoints: List<Pair<IncorporationConstraintPosition, ForkPointData>> = emptyList()
override val outerSystemVariablesPrefixSize: Int get() = 0
}
}
@@ -241,4 +241,6 @@ internal class MutableConstraintStorage : ConstraintStorage {
LinkedHashMap()
override val constraintsFromAllForkPoints: MutableList<Pair<IncorporationConstraintPosition, ForkPointData>> = SmartList()
override var outerSystemVariablesPrefixSize: Int = 0
}
@@ -42,10 +42,35 @@ class NewConstraintSystemImpl(
private val properTypesCache: MutableSet<KotlinTypeMarker> = SmartSet.create()
private val notProperTypesCache: MutableSet<KotlinTypeMarker> = SmartSet.create()
private val intersectionTypesCache: MutableMap<Collection<KotlinTypeMarker>, EmptyIntersectionTypeInfo?> = mutableMapOf()
override var typeVariablesThatAreNotCountedAsProperTypes: Set<TypeConstructorMarker>? = 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<TypeConstructorMarker>, 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<Pair<IncorporationConstraintPosition, ForkPointData>>
get() {
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
@@ -0,0 +1,17 @@
// TARGET_BACKEND: JVM_IR
// IGNORE_BACKEND_K1: JVM_IR
// ISSUE: KT-61633
class C<T3>(val p: T3)
class D<T4>(val p: T4)
val <X3> C<X3>.getValue: D<X3> get() = D(p)
operator fun <X4> D<X4>.invoke(x: Any?, y: Any?): X4 = p
fun foo(c: C<String>): String {
val y1 by c
return y1
}
fun box(): String = foo(C("OK"))
@@ -0,0 +1,23 @@
// FIR_IDENTICAL
// ISSUE: KT-61075
// WITH_REFLECT
import kotlin.properties.ReadWriteProperty
open class BaseState
interface CustomComparable<T>
annotation class XCollection
fun <E> treeSet(): ReadWriteProperty<Any?, E> where E: BaseState, E: CustomComparable<E> = TODO()
internal class VisibleTreeState {
internal class State: BaseState(), CustomComparable<State>
// 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()
}
@@ -0,0 +1,16 @@
// FIR_IDENTICAL
// WITH_STDLIB
import kotlin.reflect.KProperty
class Cached<T>(val f: () -> T) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T = TODO()
}
private val map: MutableMap<String, Cached<String>> = TODO()
fun foo() {
val bar by map.getOrPut("A") {
Cached { "B" }
}
}
@@ -0,0 +1,19 @@
// FIR_IDENTICAL
import kotlin.reflect.KProperty
fun <X> myEmptyList1(): List<X> = TODO()
fun <Y> myEmptyList2(): List<Y> = TODO()
var b: Boolean = false
val foo: List<String> by myLazy {
if (b) return@myLazy myEmptyList1()
myEmptyList2()
}
private fun <T> myLazy(initialValue: () -> T): ReadProperty<T> = TODO()
class ReadProperty<V>(val v: V) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): V = v
}
@@ -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
}
@@ -0,0 +1,17 @@
// FIR_IDENTICAL
interface A<T> {
operator fun getValue(x: Any?, y: Any?): T = TODO()
}
fun <T> foo(): A<T> = TODO()
fun bar1() {
val x: String by foo()
}
fun bar2() {
operator fun <F : A<E>, E> A<E>.provideDelegate(x: Any?, y: Any?): F = TODO()
val x: String by foo()
}
@@ -0,0 +1,19 @@
// ISSUE: KT-61747
interface A {
operator fun getValue(x: Any?, y: Any?): String = ""
}
fun <T> foo(x: (T) -> Unit): T = TODO()
fun bar1(a: A) {
val x by <!DELEGATE_SPECIAL_FUNCTION_MISSING!>foo { x: A -> }<!>
}
fun bar2(a: A) {
operator fun <F> F.provideDelegate(x: Any?, y: Any?): F {
return this
}
val x by <!DELEGATE_SPECIAL_FUNCTION_MISSING!>foo { x: A -> }<!>
}
@@ -0,0 +1,19 @@
// ISSUE: KT-61747
interface A {
operator fun getValue(x: Any?, y: Any?): String = ""
}
fun <T> foo(x: (T) -> Unit): T = TODO()
fun bar1(a: A) {
val x by <!DELEGATE_SPECIAL_FUNCTION_MISSING!>foo { x: A -> }<!>
}
fun bar2(a: A) {
operator fun <F> F.provideDelegate(x: Any?, y: Any?): F {
return this
}
val x by foo { x: A -> }
}
@@ -0,0 +1,11 @@
// ISSUE: KT-61077
val test: Int by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE, INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION!>materializeDelegate()<!>
fun <T: CharSequence> materializeDelegate(): Box<T> = TODO()
operator fun <K: Comparable<K>> Box<K>.provideDelegate(receiver: Any?, property: kotlin.reflect.KProperty<*>): K = TODO()
operator fun <Q: Comparable<Q>> Q.getValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>): Q = TODO()
class Box<V> {}
@@ -0,0 +1,11 @@
// ISSUE: KT-61077
val test: Int by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE!><!TYPE_MISMATCH!>materializeDelegate<!>()<!>
fun <T: CharSequence> materializeDelegate(): Box<T> = TODO()
operator fun <K: Comparable<K>> Box<K>.provideDelegate(receiver: Any?, property: kotlin.reflect.KProperty<*>): K = TODO()
operator fun <Q: Comparable<Q>> Q.getValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>): Q = TODO()
class Box<V> {}
@@ -0,0 +1,28 @@
// FIR_IDENTICAL
// ISSUE: KT-59529
import kotlin.reflect.KProperty
interface Out<out F> {
val x: F
}
interface Out2<out E> {
val x: E
}
interface Inv<U> {
val u: U
}
val outString: Out<String> = TODO()
val invOut2String by foo(outString)
operator fun <X> Inv<X>.getValue(
thisRef: Any?,
property: KProperty<*>
): Inv<X> = TODO()
fun <M> foo(other: Out<M>): Inv<Out2<M>> = TODO()
fun main() {
invOut2String.u.x.length
}
@@ -0,0 +1,37 @@
// FIR_IDENTICAL
// WITH_STDLIB
// !CHECK_TYPE
// ISSUE: KT-59529
import kotlin.reflect.KProperty
val ows: IgnoringParser = IgnoringParser()
class MyParser<T> {
private val booleanLiteral: Parser<String> = TODO()
val topLevel by ows + booleanLiteral
fun main() {
topLevel checkType { _<TransformParser<Pair<Unit, String>, String>>() }
}
operator fun <T, R> TransformParser<T, R>.provideDelegate(
thisRef: MyParser<*>,
property: KProperty<*>
): TransformParser<T, R> = TODO()
operator fun <T, R> TransformParser<T, R>.getValue(
thisRef: MyParser<*>,
property: KProperty<*>
): TransformParser<T, R> = TODO()
}
abstract class Parser<out T>
class TransformParser<T, R> : Parser<R>()
class IgnoringParser : Parser<Unit>()
operator fun <T> IgnoringParser.plus(other: Parser<T>): TransformParser<Pair<Unit, T>, T> =
TODO()
@@ -0,0 +1,10 @@
// FIR_IDENTICAL
val test: String by materializeDelegate()
fun <T> materializeDelegate(): Delegate<T> = Delegate()
operator fun <K> K.provideDelegate(receiver: Any?, property: kotlin.reflect.KProperty<*>): K = this
operator fun <X> Delegate<X>.getValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>): X = TODO()
class Delegate<V>
@@ -1,5 +1,5 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
// ISSUE: KT-61077
class Delegate<T>
operator fun Delegate<*>.getValue(receiver: Any?, p: Any): String = ""
@@ -10,7 +10,7 @@ operator fun <T> String.provideDelegate(receiver: Any?, p: Any) = Delegate<T>()
var test1: String by Delegate()
var test2: String by Delegate<String>()
var test3: String by <!DELEGATE_SPECIAL_FUNCTION_MISSING, DELEGATE_SPECIAL_FUNCTION_MISSING!>"OK"<!>
var test3: String by "OK"
var test4: String by "OK".provideDelegate(null, "")
var test5: String by "OK".provideDelegate<String>(null, "")
@@ -1,5 +1,5 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
// ISSUE: KT-61077
class Delegate<T>
operator fun Delegate<*>.getValue(receiver: Any?, p: Any): String = ""
@@ -0,0 +1,22 @@
// ISSUE: KT-61633
interface A<T1>
interface B<T2>
interface C<T3>
interface D<T4>
val <X1> A<X1>.provideDelegate: B<X1> get() = TODO()
operator fun <X2> B<X2>.invoke(x: Any?, y: Any?): C<X2> = TODO()
val <X3> C<X3>.getValue: D<X3> get() = TODO()
operator fun <X4> D<X4>.invoke(x: Any?, y: Any?): X4 = TODO()
fun foo(a: A<String>, c: C<String>) {
val x1 by a
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>x1<!>
x1.length
val y1 by c
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>y1<!>
y1.length
}
@@ -0,0 +1,22 @@
// ISSUE: KT-61633
interface A<T1>
interface B<T2>
interface C<T3>
interface D<T4>
val <X1> A<X1>.provideDelegate: B<X1> get() = TODO()
operator fun <X2> B<X2>.invoke(x: Any?, y: Any?): C<X2> = TODO()
val <X3> C<X3>.getValue: D<X3> get() = TODO()
operator fun <X4> D<X4>.invoke(x: Any?, y: Any?): X4 = TODO()
fun foo(a: A<String>, c: C<String>) {
val x1 by a
<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE, DEBUG_INFO_EXPRESSION_TYPE("[Error type: Error delegation type for a]")!>x1<!>
<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>x1<!>.<!DEBUG_INFO_MISSING_UNRESOLVED!>length<!>
val y1 by c
<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE, DEBUG_INFO_EXPRESSION_TYPE("[Error type: Error delegation type for c]")!>y1<!>
<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>y1<!>.<!DEBUG_INFO_MISSING_UNRESOLVED!>length<!>
}
@@ -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 {
@@ -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")
@@ -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")
@@ -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);
}
-4
View File
@@ -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
+142
View File
@@ -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 <T> materializeDelegate(): Delegate<T> = Delegate()
operator fun <K> K.provideDelegate(receiver: Any?, property: kotlin.reflect.KProperty<*>): K = this
class Delegate<V> {
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<T>` 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.
+12
View File
@@ -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