From 260683c20ef7ea5e51a11ecb8dae7c64793c8720 Mon Sep 17 00:00:00 2001 From: Victor Petukhov Date: Sat, 28 Mar 2020 20:40:29 +0300 Subject: [PATCH] NI: Improve postponed arguments analysis Introduce seven stages: 1) Analyze postponed arguments with fixed parameter types 2) Collect parameter types from constraints and lambda parameters' declaration 3) Fix not postponed variables for parameter types of all postponed arguments 4) Create atoms with revised expected types if needed 5) Analyze the first ready postponed argument and rerun stages if it has been analyzed 6) Force fixation remaining type variables: fix if possible or report not enough information 7) Force analysis remaining not analyzed postponed arguments and rerun stages if there are ^KT-37952 Fixed ^KT-32156 Fixed ^KT-37249 Fixed ^KT-37341 Fixed --- ...irOldFrontendDiagnosticsTestGenerated.java | 38 ++ ...endDiagnosticsTestWithStdlibGenerated.java | 76 +++ .../kotlin/fir/types/ConeInferenceContext.kt | 2 + .../components/PostponeArgumentsChecks.kt | 2 +- .../components/PostponedArgumentsAnalyzer.kt | 5 +- .../calls/inference/NewConstraintSystem.kt | 2 + .../KotlinConstraintSystemCompleter.kt | 552 +++++++++--------- .../PostponedArgumentInputTypesResolver.kt | 473 +++++++++++++++ ...peVariableDependencyInformationProvider.kt | 52 +- .../inference/model/ConstraintStorage.kt | 6 + .../model/NewConstraintSystemImpl.kt | 4 + .../calls/inference/model/TypeVariable.kt | 18 +- .../resolve/calls/model/ResolutionAtoms.kt | 25 +- .../generic/nestedCallWithOverload.fir.kt | 2 - .../generic/nestedCallWithOverload.kt | 2 - .../controlFlowAnalysis/elvisNotProcessed.kt | 4 +- .../postponedArgumentsAnalysis/basic.fir.fail | 11 + .../postponedArgumentsAnalysis/basic.fir.kt | 195 +++++++ .../postponedArgumentsAnalysis/basic.kt | 223 +++++++ .../postponedArgumentsAnalysis/basic.txt | 94 +++ ...bleReferenceLambdaCombinationInsideCall.kt | 10 + ...leReferenceLambdaCombinationInsideCall.txt | 6 + .../callableReferences.fir.kt | 12 + .../callableReferences.kt | 12 + .../callableReferences.txt | 19 + .../lackOfDeepIncorporation.fir.kt | 59 ++ .../lackOfDeepIncorporation.kt | 59 ++ .../lackOfDeepIncorporation.txt | 23 + .../lambdasInTryCatch.fir.kt | 41 ++ .../lambdasInTryCatch.kt | 41 ++ .../lambdasInTryCatch.txt | 5 + .../inference/extensionLambdasAndArrow.fir.kt | 2 + .../inference/extensionLambdasAndArrow.kt | 2 + ...solveWithSpecifiedFunctionLiteralWithId.kt | 10 +- .../callableReferences.fir.fail | 8 + .../callableReferences.fir.kt | 57 ++ .../callableReferences.kt | 57 ++ .../callableReferences.txt | 36 ++ ...mplexInterdependentInputOutputTypes.fir.kt | 41 ++ .../complexInterdependentInputOutputTypes.kt | 41 ++ .../complexInterdependentInputOutputTypes.txt | 61 ++ .../postponedArgumentsAnalysis/deepLambdas.kt | 14 + .../deepLambdas.txt | 25 + .../fixIndependentVariables.fir.kt | 11 + .../fixIndependentVariables.kt | 11 + .../fixIndependentVariables.txt | 12 + .../fixInputTypeToMoreSpecificType.kt | 10 + .../fixInputTypeToMoreSpecificType.txt | 4 + .../fixReceiverToMoreSpecificType.kt | 9 + .../fixReceiverToMoreSpecificType.txt | 4 + .../moreSpecificOutputType.kt | 6 + .../moreSpecificOutputType.txt | 14 + .../rerunStagesAfterFixationInFullMode.kt | 12 + .../rerunStagesAfterFixationInFullMode.txt | 14 + .../rerunStagesAfterFixationInPartialMode.kt | 27 + .../rerunStagesAfterFixationInPartialMode.txt | 21 + .../suspendFunctions.fir.fail | 8 + .../suspendFunctions.fir.kt | 25 + .../suspendFunctions.kt | 25 + .../suspendFunctions.txt | 7 + .../when-expression/p-2/pos/1.1.kt | 7 +- .../when-expression/p-5/pos/1.1.kt | 3 +- .../checkers/DiagnosticsTestGenerated.java | 38 ++ .../DiagnosticsTestWithStdLibGenerated.java | 76 +++ ...ticsTestWithStdLibUsingJavacGenerated.java | 76 +++ .../DiagnosticsUsingJavacTestGenerated.java | 38 ++ .../kotlin/builtins/KotlinBuiltIns.java | 16 + .../kotlin/builtins/functionTypes.kt | 62 +- .../types/checker/ClassicTypeSystemContext.kt | 6 + .../kotlin/types/model/TypeSystemContext.kt | 2 + 70 files changed, 2619 insertions(+), 352 deletions(-) create mode 100644 compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/PostponedArgumentInputTypesResolver.kt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.fir.fail create mode 100644 compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.fir.kt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.kt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.txt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferenceLambdaCombinationInsideCall.kt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferenceLambdaCombinationInsideCall.txt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferences.fir.kt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferences.kt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferences.txt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lackOfDeepIncorporation.fir.kt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lackOfDeepIncorporation.kt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lackOfDeepIncorporation.txt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lambdasInTryCatch.fir.kt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lambdasInTryCatch.kt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lambdasInTryCatch.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/callableReferences.fir.fail create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/callableReferences.fir.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/callableReferences.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/callableReferences.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/complexInterdependentInputOutputTypes.fir.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/complexInterdependentInputOutputTypes.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/complexInterdependentInputOutputTypes.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/deepLambdas.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/deepLambdas.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixIndependentVariables.fir.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixIndependentVariables.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixIndependentVariables.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixInputTypeToMoreSpecificType.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixInputTypeToMoreSpecificType.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixReceiverToMoreSpecificType.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixReceiverToMoreSpecificType.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/moreSpecificOutputType.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/moreSpecificOutputType.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/rerunStagesAfterFixationInFullMode.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/rerunStagesAfterFixationInFullMode.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/rerunStagesAfterFixationInPartialMode.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/rerunStagesAfterFixationInPartialMode.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/suspendFunctions.fir.fail create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/suspendFunctions.fir.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/suspendFunctions.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/suspendFunctions.txt diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java index a12be7e9920..2ef8a32b41d 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java @@ -10741,6 +10741,44 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte public void testWithExact() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/completion/withExact.kt"); } + + @TestMetadata("compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class PostponedArgumentsAnalysis extends AbstractFirOldFrontendDiagnosticsTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInPostponedArgumentsAnalysis() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("basic.kt") + public void testBasic() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.kt"); + } + + @TestMetadata("callableReferenceLambdaCombinationInsideCall.kt") + public void testCallableReferenceLambdaCombinationInsideCall() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferenceLambdaCombinationInsideCall.kt"); + } + + @TestMetadata("callableReferences.kt") + public void testCallableReferences() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferences.kt"); + } + + @TestMetadata("lackOfDeepIncorporation.kt") + public void testLackOfDeepIncorporation() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lackOfDeepIncorporation.kt"); + } + + @TestMetadata("lambdasInTryCatch.kt") + public void testLambdasInTryCatch() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lambdasInTryCatch.kt"); + } + } } @TestMetadata("compiler/testData/diagnostics/tests/inference/constraints") diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestWithStdlibGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestWithStdlibGenerated.java index c4e4ca48d69..5d9b819ede9 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestWithStdlibGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestWithStdlibGenerated.java @@ -2059,6 +2059,82 @@ public class FirOldFrontendDiagnosticsTestWithStdlibGenerated extends AbstractFi } } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/inference/completion") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Completion extends AbstractFirOldFrontendDiagnosticsTestWithStdlib { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInCompletion() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/inference/completion"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class PostponedArgumentsAnalysis extends AbstractFirOldFrontendDiagnosticsTestWithStdlib { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInPostponedArgumentsAnalysis() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("callableReferences.kt") + public void testCallableReferences() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/callableReferences.kt"); + } + + @TestMetadata("complexInterdependentInputOutputTypes.kt") + public void testComplexInterdependentInputOutputTypes() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/complexInterdependentInputOutputTypes.kt"); + } + + @TestMetadata("deepLambdas.kt") + public void testDeepLambdas() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/deepLambdas.kt"); + } + + @TestMetadata("fixIndependentVariables.kt") + public void testFixIndependentVariables() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixIndependentVariables.kt"); + } + + @TestMetadata("fixInputTypeToMoreSpecificType.kt") + public void testFixInputTypeToMoreSpecificType() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixInputTypeToMoreSpecificType.kt"); + } + + @TestMetadata("fixReceiverToMoreSpecificType.kt") + public void testFixReceiverToMoreSpecificType() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixReceiverToMoreSpecificType.kt"); + } + + @TestMetadata("moreSpecificOutputType.kt") + public void testMoreSpecificOutputType() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/moreSpecificOutputType.kt"); + } + + @TestMetadata("rerunStagesAfterFixationInFullMode.kt") + public void testRerunStagesAfterFixationInFullMode() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/rerunStagesAfterFixationInFullMode.kt"); + } + + @TestMetadata("rerunStagesAfterFixationInPartialMode.kt") + public void testRerunStagesAfterFixationInPartialMode() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/rerunStagesAfterFixationInPartialMode.kt"); + } + + @TestMetadata("suspendFunctions.kt") + public void testSuspendFunctions() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/suspendFunctions.kt"); + } + } + } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/inference/delegates") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt index 032e13f41c9..173198ae998 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt @@ -204,6 +204,8 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo return this.typeConstructor().isUnitTypeConstructor() && !this.isNullable } + override fun KotlinTypeMarker.isBuiltinFunctionalTypeOrSubtype() = TODO() + override fun KotlinTypeMarker.withNullability(nullable: Boolean): KotlinTypeMarker { require(this is ConeKotlinType) return this.withNullability(ConeNullability.create(nullable), this@ConeInferenceContext) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponeArgumentsChecks.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponeArgumentsChecks.kt index e3e0f446623..ec6f82b9ec0 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponeArgumentsChecks.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponeArgumentsChecks.kt @@ -93,7 +93,7 @@ private fun extraLambdaInfo( val isFunctionSupertype = expectedType != null && KotlinBuiltIns.isNotNullOrNullableFunctionSupertype(expectedType) val argumentAsFunctionExpression = argument.safeAs() - val typeVariable = TypeVariableForLambdaReturnType(argument, builtIns, "_L") + val typeVariable = TypeVariableForLambdaReturnType(builtIns, "_L") val receiverType = argumentAsFunctionExpression?.receiverType val returnType = diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzer.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzer.kt index 2901f4b0454..17d8ebde48f 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzer.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzer.kt @@ -150,10 +150,7 @@ class PostponedArgumentsAnalyzer( val subResolvedKtPrimitives = allReturnArguments.map { resolveKtPrimitive( c.getBuilder(), it, lambda.returnType.let(::substitute), diagnosticHolder, ReceiverInfo.notReceiver, convertedType = null - ).apply { - if (this is LambdaWithTypeVariableAsExpectedTypeAtom) - isReturnArgumentOfAnotherLambda = true - } + ) } if (!returnArgumentsInfo.returnArgumentsExist) { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/NewConstraintSystem.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/NewConstraintSystem.kt index 4dae8193500..39e153045c9 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/NewConstraintSystem.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/NewConstraintSystem.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.resolve.calls.components.KotlinCallCompleter import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzer import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter +import org.jetbrains.kotlin.resolve.calls.inference.components.PostponedArgumentInputTypesResolver import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic @@ -35,4 +36,5 @@ interface NewConstraintSystem { fun asConstraintSystemCompleterContext(): KotlinConstraintSystemCompleter.Context fun asPostponedArgumentsAnalyzerContext(): PostponedArgumentsAnalyzer.Context + fun asPostponedArgumentInputTypesResolverContext(): PostponedArgumentInputTypesResolver.Context } \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt index 8a1b9c79658..a53eee72608 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt @@ -5,28 +5,23 @@ package org.jetbrains.kotlin.resolve.calls.inference.components -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType -import org.jetbrains.kotlin.builtins.isBuiltinFunctionalTypeOrSubtype -import org.jetbrains.kotlin.builtins.isExtensionFunctionType -import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus -import org.jetbrains.kotlin.resolve.calls.components.transformToResolvedLambda import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder -import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem import org.jetbrains.kotlin.resolve.calls.inference.model.* import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.model.TypeConstructorMarker import org.jetbrains.kotlin.types.model.TypeVariableMarker -import org.jetbrains.kotlin.types.typeUtil.asTypeProjection import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addToStdlib.safeAs +import kotlin.collections.LinkedHashSet class KotlinConstraintSystemCompleter( private val resultTypeResolver: ResultTypeResolver, val variableFixationFinder: VariableFixationFinder, ) { + private val postponedArgumentInputTypesResolver = PostponedArgumentInputTypesResolver(resultTypeResolver, variableFixationFinder) + enum class ConstraintSystemCompletionMode { FULL, PARTIAL @@ -37,6 +32,8 @@ class KotlinConstraintSystemCompleter( override val notFixedTypeVariables: Map override val postponedTypeVariables: List + fun getBuilder(): ConstraintSystemBuilder + // type can be proper if it not contains not fixed type variables fun canBeProper(type: KotlinTypeMarker): Boolean @@ -46,6 +43,8 @@ class KotlinConstraintSystemCompleter( fun addError(error: KotlinCallDiagnostic) fun fixVariable(variable: TypeVariableMarker, resultType: KotlinTypeMarker, atom: ResolvedAtom?) + + fun asPostponedArgumentInputTypesResolverContext(): PostponedArgumentInputTypesResolver.Context } fun runCompletion( @@ -56,8 +55,7 @@ class KotlinConstraintSystemCompleter( diagnosticsHolder: KotlinDiagnosticsHolder, analyze: (PostponedResolvedAtom) -> Unit ) { - runCompletion( - c, + c.runCompletion( completionMode, topLevelAtoms, topLevelType, @@ -73,8 +71,7 @@ class KotlinConstraintSystemCompleter( topLevelAtoms: List, diagnosticsHolder: KotlinDiagnosticsHolder ) { - runCompletion( - c, + c.runCompletion( ConstraintSystemCompletionMode.FULL, topLevelAtoms, topLevelType, @@ -85,8 +82,8 @@ class KotlinConstraintSystemCompleter( } } - private fun runCompletion( - c: Context, + // TODO: investigate type variables fixation order and incorporation depth (see `diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lackOfDeepIncorporation.kt`) + private fun Context.runCompletion( completionMode: ConstraintSystemCompletionMode, topLevelAtoms: List, topLevelType: UnwrappedType, @@ -94,241 +91,242 @@ class KotlinConstraintSystemCompleter( collectVariablesFromContext: Boolean, analyze: (PostponedResolvedAtom) -> Unit ) { - while (true) { - if (analyzePostponeArgumentIfPossible(c, topLevelAtoms, analyze)) continue + completion@ while (true) { + val postponedArguments = getOrderedNotAnalyzedPostponedArguments(topLevelAtoms) - val allTypeVariables = getOrderedAllTypeVariables(c, collectVariablesFromContext, topLevelAtoms) - val postponedKtPrimitives = getOrderedNotAnalyzedPostponedArguments(topLevelAtoms) - val variableForFixation = - variableFixationFinder.findFirstVariableForFixation( - c, allTypeVariables, postponedKtPrimitives, completionMode, topLevelType - ) ?: break - - if ( - completionMode == ConstraintSystemCompletionMode.FULL && - c.resolveLambdaByAdditionalConditions( - variableForFixation, - topLevelAtoms, - diagnosticsHolder, - analyze, - variableFixationFinder - ) - ) { + // Stage 1: analyze postponed arguments with fixed parameter types + if (analyzeArgumentWithFixedParameterTypes(postponedArguments, analyze)) continue + + val isThereAnyReadyForFixationVariable = isThereAnyReadyForFixationVariable( + completionMode, topLevelAtoms, topLevelType, collectVariablesFromContext, postponedArguments + ) + + // If there aren't any postponed arguments and ready for fixation variables, then completion isn't needed: nothing to do + if (postponedArguments.isEmpty() && !isThereAnyReadyForFixationVariable) + break + + val postponedArgumentsWithRevisableType = postponedArguments.filterIsInstance() + val dependencyProvider = + TypeVariableDependencyInformationProvider(notFixedTypeVariables, postponedArguments, topLevelType, this) + + // Stage 2: collect parameter types for postponed arguments + postponedArgumentInputTypesResolver.collectParameterTypesAndBuildNewExpectedTypes( + asPostponedArgumentInputTypesResolverContext(), postponedArgumentsWithRevisableType, completionMode, dependencyProvider + ) + + if (completionMode == ConstraintSystemCompletionMode.FULL) { + // Stage 3: fix variables for parameter types of all postponed arguments + for (argument in postponedArguments) { + val wasFixedSomeVariable = postponedArgumentInputTypesResolver.fixNextReadyVariableForParameterTypeIfNeeded( + asPostponedArgumentInputTypesResolverContext(), + argument, + postponedArguments, + topLevelType, + topLevelAtoms, + dependencyProvider + ) + + if (wasFixedSomeVariable) + continue@completion + } + + // Stage 4: create atoms with revised expected types if needed + for (argument in postponedArgumentsWithRevisableType) { + postponedArgumentInputTypesResolver.transformToAtomWithNewFunctionalExpectedType( + asPostponedArgumentInputTypesResolverContext(), argument, diagnosticsHolder + ) + } } - if (variableForFixation.hasProperConstraint || completionMode == ConstraintSystemCompletionMode.FULL) { - val variableWithConstraints = c.notFixedTypeVariables.getValue(variableForFixation.variable) - - if (variableForFixation.hasProperConstraint) - fixVariable(c, variableWithConstraints, topLevelAtoms) - else - processVariableWhenNotEnoughInformation(c, variableWithConstraints, topLevelAtoms) + /* + * We should get not analyzed postponed arguments again because they can be changed by + * the stage of fixation type variables for parameters or analysing postponed arguments with fixed parameter types (see stage #1 and #4) + */ + val revisedPostponedArguments = getOrderedNotAnalyzedPostponedArguments(topLevelAtoms) + // Stage 5: analyze the next ready postponed argument + if (analyzeNextReadyPostponedArgument(revisedPostponedArguments, completionMode, analyze)) continue + + // Stage 6: fix type variables – fix if possible or report not enough information (if completion mode is full) + val wasFixedSomeVariable = fixVariablesOrReportNotEnoughInformation( + completionMode, topLevelAtoms, topLevelType, collectVariablesFromContext, revisedPostponedArguments, diagnosticsHolder + ) + if (wasFixedSomeVariable) + continue + + // Stage 7: force analysis of remaining not analyzed postponed arguments and rerun stages if there are + if (completionMode == ConstraintSystemCompletionMode.FULL) { + if (analyzeRemainingNotAnalyzedPostponedArgument(revisedPostponedArguments, analyze)) + continue } break } - - if (completionMode == ConstraintSystemCompletionMode.FULL) { - // force resolution for all not-analyzed argument's - getOrderedNotAnalyzedPostponedArguments(topLevelAtoms).forEach(analyze) - - if (c.notFixedTypeVariables.isNotEmpty() && c.postponedTypeVariables.isEmpty()) { - runCompletion(c, completionMode, topLevelAtoms, topLevelType, diagnosticsHolder, analyze) - } - } } - /* - * returns true -> analyzed - */ - private fun Context.resolveLambdaByAdditionalConditions( - variableForFixation: VariableFixationFinder.VariableForFixation, - topLevelAtoms: List, - diagnosticsHolder: KotlinDiagnosticsHolder, - analyze: (PostponedResolvedAtom) -> Unit, - fixationFinder: VariableFixationFinder - ): Boolean { - val postponedArguments = getOrderedNotAnalyzedPostponedArguments(topLevelAtoms) - - if (postponedArguments.isEmpty()) - return false - - val groupedPostponedArguments = postponedArguments.filterIsInstance() - .ifEmpty { postponedArguments.filter { it !is LambdaWithTypeVariableAsExpectedTypeAtom } } - - if (groupedPostponedArguments.isEmpty()) - return false - - fun shouldAnalyzeLambdaWithTypeVariableAsExpectedType(argument: PostponedResolvedAtom): Boolean { - val hasProperAtom = when (argument) { - is LambdaWithTypeVariableAsExpectedTypeAtom, is PostponedCallableReferenceAtom -> - argument.expectedType?.constructor == variableForFixation.variable - else -> false - } - - return hasProperAtom || !variableForFixation.hasProperConstraint || variableForFixation.hasOnlyTrivialProperConstraint - } - - fun shouldAnalyzeLambdaWhichIsReturnArgument(argument: PostponedResolvedAtom): Boolean { - val isReturnArgumentOfAnotherLambda = - argument is LambdaWithTypeVariableAsExpectedTypeAtom && argument.isReturnArgumentOfAnotherLambda - val atomExpectedType = argument.expectedType - - return isReturnArgumentOfAnotherLambda && atomExpectedType != null && - with(fixationFinder) { variableHasTrivialOrNonProperConstraints(atomExpectedType.constructor) } - } - - return resolveLambdaByCondition( - variableForFixation, groupedPostponedArguments, diagnosticsHolder, analyze, ::shouldAnalyzeLambdaWithTypeVariableAsExpectedType - ) || resolveLambdaByCondition( - variableForFixation, groupedPostponedArguments, diagnosticsHolder, analyze, ::shouldAnalyzeLambdaWhichIsReturnArgument - ) - } - - /* - * returns true -> analyzed - */ - private fun Context.resolveLambdaByCondition( - variableForFixation: VariableFixationFinder.VariableForFixation, + private fun Context.analyzeArgumentWithFixedParameterTypes( postponedArguments: List, - diagnosticsHolder: KotlinDiagnosticsHolder, - analyze: (PostponedResolvedAtom) -> Unit, - shouldAnalyze: (PostponedResolvedAtom) -> Boolean - ): Boolean { - val variable = variableForFixation.variable as? TypeConstructor ?: return false - - return postponedArguments.all { argument -> - if (!shouldAnalyze(argument)) - return@all false - - val expectedTypeVariable = argument.expectedType?.constructor?.takeIf { it in allTypeVariables } ?: variable - - val preparedAtom = - preparePostponedAtom(expectedTypeVariable, argument, expectedTypeVariable.builtIns, diagnosticsHolder) - ?: return@all false - - analyze(preparedAtom) - - true - } - } - - private fun Context.preparePostponedAtom( - expectedTypeVariable: TypeConstructor, - postponedAtom: PostponedResolvedAtom, - builtIns: KotlinBuiltIns, - diagnosticsHolder: KotlinDiagnosticsHolder - ): PostponedResolvedAtom? { - val csBuilder = (this as? NewConstraintSystem)?.getBuilder() ?: return null - - return when (postponedAtom) { - is PostponedCallableReferenceAtom -> postponedAtom.preparePostponedAtomWithTypeVariableAsExpectedType( - this, csBuilder, expectedTypeVariable, - parameterTypes = null, - isSuitable = KotlinType::isBuiltinFunctionalTypeOrSubtype, - typeVariableCreator = { TypeVariableForCallableReferenceReturnType(builtIns, "_Q") }, - newAtomCreator = { returnVariable, expectedType -> - CallableReferenceWithTypeVariableAsExpectedTypeAtom(postponedAtom.atom, expectedType, returnVariable).also { - postponedAtom.setAnalyzedResults(null, listOf(it)) - } - } - ) - is LambdaWithTypeVariableAsExpectedTypeAtom -> postponedAtom.preparePostponedAtomWithTypeVariableAsExpectedType( - this, csBuilder, expectedTypeVariable, - parameterTypes = postponedAtom.atom.parametersTypes, - isSuitable = KotlinType::isBuiltinFunctionalType, - typeVariableCreator = { TypeVariableForLambdaReturnType(postponedAtom.atom, builtIns, "_R") }, - newAtomCreator = { returnVariable, expectedType -> - postponedAtom.transformToResolvedLambda(csBuilder, diagnosticsHolder, expectedType, returnVariable) - } - ) - else -> null - } - } - - private inline fun T.preparePostponedAtomWithTypeVariableAsExpectedType( - c: Context, - csBuilder: ConstraintSystemBuilder, - variable: TypeConstructor, - parameterTypes: Array?, - isSuitable: KotlinType.() -> Boolean, - typeVariableCreator: () -> V, - newAtomCreator: (V, SimpleType) -> PostponedResolvedAtom - ): PostponedResolvedAtom { - val functionalType = resultTypeResolver.findResultType( - c, - c.notFixedTypeVariables.getValue(variable), - TypeVariableDirectionCalculator.ResolveDirection.TO_SUPERTYPE - ) as KotlinType - - val isExtensionFunction = functionalType.isExtensionFunctionType - val isExtensionFunctionWithReceiverAsDeclaredParameter = - isExtensionFunction && functionalType.arguments.size - 1 == parameterTypes?.count { it != null } - if (parameterTypes?.all { it != null } == true && (!isExtensionFunction || isExtensionFunctionWithReceiverAsDeclaredParameter)) return this - - if (!functionalType.isSuitable()) return this - val returnVariable = typeVariableCreator() - csBuilder.registerVariable(returnVariable) - val expectedType = KotlinTypeFactory.simpleType( - functionalType.annotations, - functionalType.constructor, - functionalType.arguments.dropLast(1) + returnVariable.defaultType.asTypeProjection(), - functionalType.isMarkedNullable - ) - csBuilder.addSubtypeConstraint( - expectedType, - variable.typeForTypeVariable(), - ArgumentConstraintPosition(atom as KotlinCallArgument) - ) - return newAtomCreator(returnVariable, expectedType) - } - - // true if we do analyze - private fun analyzePostponeArgumentIfPossible( - c: Context, - topLevelAtoms: List, analyze: (PostponedResolvedAtom) -> Unit ): Boolean { - for (argument in getOrderedNotAnalyzedPostponedArguments(topLevelAtoms)) { - ProgressIndicatorAndCompilationCanceledStatus.checkCanceled() - if (canWeAnalyzeIt(c, argument)) { - analyze(argument) - return true - } + val argumentWithFixedOrPostponedInputTypes = findPostponedArgumentWithFixedOrPostponedInputTypes(postponedArguments) + + if (argumentWithFixedOrPostponedInputTypes != null) { + analyze(argumentWithFixedOrPostponedInputTypes) + return true } + return false } - private fun getOrderedAllTypeVariables( + private fun Context.analyzeNextReadyPostponedArgument( + postponedArguments: List, + completionMode: ConstraintSystemCompletionMode, + analyze: (PostponedResolvedAtom) -> Unit + ): Boolean { + if (completionMode == ConstraintSystemCompletionMode.FULL) { + val argumentWithTypeVariableAsExpectedType = findPostponedArgumentWithRevisableExpectedType(postponedArguments) + + if (argumentWithTypeVariableAsExpectedType != null) { + analyze(argumentWithTypeVariableAsExpectedType) + return true + } + } + + return analyzeArgumentWithFixedParameterTypes(postponedArguments, analyze) + } + + private fun analyzeRemainingNotAnalyzedPostponedArgument( + postponedArguments: List, + analyze: (PostponedResolvedAtom) -> Unit + ): Boolean { + val remainingNotAnalyzedPostponedArgument = postponedArguments.firstOrNull { !it.analyzed } + + if (remainingNotAnalyzedPostponedArgument != null) { + analyze(remainingNotAnalyzedPostponedArgument) + return true + } + + return false + } + + private fun findPostponedArgumentWithRevisableExpectedType(postponedArguments: List) = + postponedArguments.firstOrNull { argument -> argument is PostponedAtomWithRevisableExpectedType } + + private fun Context.findPostponedArgumentWithFixedOrPostponedInputTypes(postponedArguments: List) = + postponedArguments.firstOrNull { argument -> argument.inputTypes.all { containsOnlyFixedOrPostponedVariables(it) } } + + private fun fixVariable( c: Context, + variableWithConstraints: VariableWithConstraints, + topLevelAtoms: List + ) { + fixVariable(c, variableWithConstraints, TypeVariableDirectionCalculator.ResolveDirection.UNKNOWN, topLevelAtoms) + } + + private fun fixVariable( + c: Context, + variableWithConstraints: VariableWithConstraints, + direction: TypeVariableDirectionCalculator.ResolveDirection, + topLevelAtoms: List + ) { + val resultType = resultTypeResolver.findResultType(c, variableWithConstraints, direction) + val resolvedAtom = findResolvedAtomBy(variableWithConstraints.typeVariable, topLevelAtoms) ?: topLevelAtoms.firstOrNull() + c.fixVariable(variableWithConstraints.typeVariable, resultType, resolvedAtom) + } + + private fun Context.fixVariablesOrReportNotEnoughInformation( + completionMode: ConstraintSystemCompletionMode, + topLevelAtoms: List, + topLevelType: UnwrappedType, + collectVariablesFromContext: Boolean, + postponedArguments: List, + diagnosticsHolder: KotlinDiagnosticsHolder + ): Boolean { + while (true) { + val variableForFixation = getVariableReadyForFixation( + completionMode, + topLevelAtoms, + topLevelType, + collectVariablesFromContext, + postponedArguments + ) ?: break + + if (!variableForFixation.hasProperConstraint && completionMode == ConstraintSystemCompletionMode.PARTIAL) + break + + val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable) + + if (variableForFixation.hasProperConstraint) { + fixVariable(this, variableWithConstraints, topLevelAtoms) + return true + } else { + processVariableWhenNotEnoughInformation(this, variableWithConstraints, topLevelAtoms, diagnosticsHolder) + } + } + + return false + } + + private fun processVariableWhenNotEnoughInformation( + c: Context, + variableWithConstraints: VariableWithConstraints, + topLevelAtoms: List, + diagnosticsHolder: KotlinDiagnosticsHolder + ) { + val typeVariable = variableWithConstraints.typeVariable + val resolvedAtom = findResolvedAtomBy(typeVariable, topLevelAtoms) ?: topLevelAtoms.firstOrNull() + + if (resolvedAtom != null) { + c.addError(NotEnoughInformationForTypeParameter(typeVariable, resolvedAtom)) + } + + val resultErrorType = when { + typeVariable is TypeVariableFromCallableDescriptor -> + ErrorUtils.createUninferredParameterType(typeVariable.originalTypeParameter) + typeVariable is TypeVariableForLambdaParameterType && typeVariable.atom is LambdaKotlinCallArgument -> { + diagnosticsHolder.addDiagnostic( + NotEnoughInformationForLambdaParameter(typeVariable.atom, typeVariable.index) + ) + ErrorUtils.createErrorType("Cannot infer lambda parameter type") + } + else -> ErrorUtils.createErrorType("Cannot infer type variable $typeVariable") + } + + c.fixVariable(typeVariable, resultErrorType, resolvedAtom) + } + + private fun Context.getOrderedAllTypeVariables( collectVariablesFromContext: Boolean, topLevelAtoms: List ): List { - if (collectVariablesFromContext) return c.notFixedTypeVariables.keys.toList() + if (collectVariablesFromContext) + return notFixedTypeVariables.keys.toList() + + fun getVariablesFromRevisedExpectedType(revisedExpectedType: KotlinType?) = + revisedExpectedType?.arguments?.map { it.type.constructor }?.filterIsInstance() fun ResolvedAtom.process(to: LinkedHashSet) { val typeVariables = when (this) { - is ResolvedCallAtom -> freshVariablesSubstitutor.freshVariables - is CallableReferenceWithTypeVariableAsExpectedTypeAtom -> mutableListOf().apply { - addIfNotNull(typeVariableForReturnType) - addAll(candidate?.freshSubstitutor?.freshVariables.orEmpty()) - } - is ResolvedCallableReferenceAtom -> candidate?.freshSubstitutor?.freshVariables.orEmpty() - is ResolvedLambdaAtom -> listOfNotNull(typeVariableForLambdaReturnType) + is LambdaWithTypeVariableAsExpectedTypeAtom -> getVariablesFromRevisedExpectedType(revisedExpectedType).orEmpty() + is ResolvedCallAtom -> freshVariablesSubstitutor.freshVariables.map { it.freshTypeConstructor } + is PostponedCallableReferenceAtom -> + getVariablesFromRevisedExpectedType(revisedExpectedType).orEmpty() + + candidate?.freshSubstitutor?.freshVariables?.map { it.freshTypeConstructor }.orEmpty() + is ResolvedCallableReferenceAtom -> candidate?.freshSubstitutor?.freshVariables?.map { it.freshTypeConstructor }.orEmpty() + is ResolvedLambdaAtom -> listOfNotNull(typeVariableForLambdaReturnType?.freshTypeConstructor) else -> emptyList() } + typeVariables.mapNotNullTo(to) { - val typeConstructor = it.freshTypeConstructor - typeConstructor.takeIf { c.notFixedTypeVariables.containsKey(typeConstructor) } + it.takeIf { notFixedTypeVariables.containsKey(it) } } /* * Hack for completing error candidates in delegate resolve */ - if (this is StubResolvedAtom && typeVariable in c.notFixedTypeVariables) { + if (this is StubResolvedAtom && typeVariable in notFixedTypeVariables) { to += typeVariable } @@ -343,86 +341,35 @@ class KotlinConstraintSystemCompleter( primitive.process(result) } - assert(result.size == c.notFixedTypeVariables.size) { - val notFoundTypeVariables = c.notFixedTypeVariables.keys.toMutableSet().apply { removeAll(result) } + assert(result.size == notFixedTypeVariables.size) { + val notFoundTypeVariables = notFixedTypeVariables.keys.toMutableSet().apply { removeAll(result) } "Not all type variables found: $notFoundTypeVariables" } return result.toList() } + private fun Context.getVariableReadyForFixation( + completionMode: ConstraintSystemCompletionMode, + topLevelAtoms: List, + topLevelType: UnwrappedType, + collectVariablesFromContext: Boolean, + postponedArguments: List + ) = variableFixationFinder.findFirstVariableForFixation( + this, + getOrderedAllTypeVariables(collectVariablesFromContext, topLevelAtoms), + postponedArguments, + completionMode, + topLevelType + ) - private fun canWeAnalyzeIt(c: Context, argument: PostponedResolvedAtom): Boolean { - if (argument.analyzed) return false - - return argument.inputTypes.all { c.containsOnlyFixedOrPostponedVariables(it) } - } - - private fun fixVariable( - c: Context, - variableWithConstraints: VariableWithConstraints, - topLevelAtoms: List - ) { - fixVariable(c, variableWithConstraints, TypeVariableDirectionCalculator.ResolveDirection.UNKNOWN, topLevelAtoms) - } - - fun fixVariable( - c: Context, - variableWithConstraints: VariableWithConstraints, - direction: TypeVariableDirectionCalculator.ResolveDirection, - topLevelAtoms: List - ) { - val resultType = resultTypeResolver.findResultType(c, variableWithConstraints, direction) - val resolvedAtom = findResolvedAtomBy(variableWithConstraints.typeVariable, topLevelAtoms) ?: topLevelAtoms.firstOrNull() - c.fixVariable(variableWithConstraints.typeVariable, resultType, resolvedAtom) - } - - private fun processVariableWhenNotEnoughInformation( - c: Context, - variableWithConstraints: VariableWithConstraints, - topLevelAtoms: List - ) { - val typeVariable = variableWithConstraints.typeVariable - - val resolvedAtom = findResolvedAtomBy(typeVariable, topLevelAtoms) ?: topLevelAtoms.firstOrNull() - if (resolvedAtom != null) { - c.addError(NotEnoughInformationForTypeParameter(typeVariable, resolvedAtom)) - } - - val resultErrorType = if (typeVariable is TypeVariableFromCallableDescriptor) - ErrorUtils.createUninferredParameterType(typeVariable.originalTypeParameter) - else - ErrorUtils.createErrorType("Cannot infer type variable $typeVariable") - - c.fixVariable(typeVariable, resultErrorType, resolvedAtom) - } - - private fun findResolvedAtomBy(typeVariable: TypeVariableMarker, topLevelAtoms: List): ResolvedAtom? { - fun ResolvedAtom.check(): ResolvedAtom? { - val suitableCall = when (this) { - is ResolvedCallAtom -> typeVariable in freshVariablesSubstitutor.freshVariables - is ResolvedCallableReferenceAtom -> candidate?.freshSubstitutor?.freshVariables?.let { typeVariable in it } ?: false - is ResolvedLambdaAtom -> typeVariable == typeVariableForLambdaReturnType - else -> false - } - - if (suitableCall) { - return this - } - - subResolvedAtoms?.forEach { subResolvedAtom -> - subResolvedAtom.check()?.let { result -> return@check result } - } - - return null - } - - for (topLevelAtom in topLevelAtoms) { - topLevelAtom.check()?.let { return it } - } - - return null - } + private fun Context.isThereAnyReadyForFixationVariable( + completionMode: ConstraintSystemCompletionMode, + topLevelAtoms: List, + topLevelType: UnwrappedType, + collectVariablesFromContext: Boolean, + postponedArguments: List + ) = getVariableReadyForFixation(completionMode, topLevelAtoms, topLevelType, collectVariablesFromContext, postponedArguments) != null companion object { fun getOrderedNotAnalyzedPostponedArguments(topLevelAtoms: List): List { @@ -441,5 +388,32 @@ class KotlinConstraintSystemCompleter( return notAnalyzedArguments } + + fun findResolvedAtomBy(typeVariable: TypeVariableMarker, topLevelAtoms: List): ResolvedAtom? { + fun ResolvedAtom.check(): ResolvedAtom? { + val suitableCall = when (this) { + is ResolvedCallAtom -> typeVariable in freshVariablesSubstitutor.freshVariables + is ResolvedCallableReferenceAtom -> candidate?.freshSubstitutor?.freshVariables?.let { typeVariable in it } ?: false + is ResolvedLambdaAtom -> typeVariable == typeVariableForLambdaReturnType + else -> false + } + + if (suitableCall) { + return this + } + + subResolvedAtoms?.forEach { subResolvedAtom -> + subResolvedAtom.check()?.let { result -> return@check result } + } + + return null + } + + for (topLevelAtom in topLevelAtoms) { + topLevelAtom.check()?.let { return it } + } + + return null + } } -} \ No newline at end of file +} diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/PostponedArgumentInputTypesResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/PostponedArgumentInputTypesResolver.kt new file mode 100644 index 00000000000..01566a569b6 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/PostponedArgumentInputTypesResolver.kt @@ -0,0 +1,473 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.resolve.calls.inference.components + +import org.jetbrains.kotlin.builtins.* +import org.jetbrains.kotlin.resolve.calls.components.transformToResolvedLambda +import org.jetbrains.kotlin.resolve.calls.inference.model.* +import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.typeUtil.asTypeProjection +import org.jetbrains.kotlin.types.typeUtil.builtIns +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.utils.addToStdlib.safeAs + +class PostponedArgumentInputTypesResolver( + private val resultTypeResolver: ResultTypeResolver, + private val variableFixationFinder: VariableFixationFinder +) { + interface Context : KotlinConstraintSystemCompleter.Context + + data class ParameterTypesInfo( + val parametersFromDeclaration: List?, + val parametersFromDeclarationOfRelatedLambdas: Set>?, + val parametersFromConstraints: Set>?, + val annotations: Annotations, + val isSuspend: Boolean, + val isNullable: Boolean + ) + + data class TypeWithKind( + val type: KotlinType, + val direction: ConstraintKind = ConstraintKind.UPPER + ) + + private fun Context.findFunctionalTypesInConstraints( + variable: VariableWithConstraints, + variableDependencyProvider: TypeVariableDependencyInformationProvider + ): List? { + fun List.extractFunctionalTypes() = mapNotNull { constraint -> + val type = constraint.type as? KotlinType ?: return@mapNotNull null + TypeWithKind(type.extractFunctionalTypeFromSupertypes(), constraint.kind) + } + + val typeVariableTypeConstructor = variable.typeVariable.freshTypeConstructor() as? TypeVariableTypeConstructor ?: return null + val dependentVariables = + variableDependencyProvider.getShallowlyDependentVariables(typeVariableTypeConstructor).orEmpty() + typeVariableTypeConstructor + + return dependentVariables.mapNotNull { type -> + val constraints = notFixedTypeVariables[type]?.constraints ?: return@mapNotNull null + val constraintsWithFunctionalType = constraints.filter { (it.type as? KotlinType)?.isBuiltinFunctionalTypeOrSubtype == true } + constraintsWithFunctionalType.extractFunctionalTypes() + }.flatten() + } + + private fun extractParameterTypesFromDeclaration(atom: ResolutionAtom) = + when (atom) { + is FunctionExpression -> { + val receiverType = atom.receiverType + if (receiverType != null) listOf(receiverType) + atom.parametersTypes else atom.parametersTypes.toList() + } + is LambdaKotlinCallArgument -> atom.parametersTypes?.toList() + else -> null + } + + private fun Context.extractParameterTypesInfo( + argument: PostponedAtomWithRevisableExpectedType, + postponedArguments: List, + variableDependencyProvider: TypeVariableDependencyInformationProvider + ): ParameterTypesInfo? { + val expectedType = argument.expectedType ?: return null + val variableWithConstraints = notFixedTypeVariables[expectedType.constructor] ?: return null + + // We shouldn't collect functional types from constraints for anonymous functions as they have fully explicit declaration form + val functionalTypesFromConstraints = if (!isAnonymousFunction(argument)) { + findFunctionalTypesInConstraints(variableWithConstraints, variableDependencyProvider) + } else null + + // Don't create functional expected type for further error reporting about a different number of arguments + if (functionalTypesFromConstraints != null && functionalTypesFromConstraints.distinctBy { it.type.argumentsCount() }.size > 1) + return null + + val parameterTypesFromDeclaration = + if (argument is LambdaWithTypeVariableAsExpectedTypeAtom) argument.parameterTypesFromDeclaration else null + + val parameterTypesFromConstraints = functionalTypesFromConstraints?.map { typeWithKind -> + typeWithKind.type.getPureArgumentsForFunctionalTypeOrSubtype().map { + // We should use opposite kind as lambda's parameters are contravariant + TypeWithKind(it, typeWithKind.direction.opposite()) + } + }?.toSet() + + val annotations = functionalTypesFromConstraints?.run { + Annotations.create(map { it.type.annotations }.flatten()) + } + + val parameterTypesFromDeclarationOfRelatedLambdas = + getDeclaredParametersFromRelatedLambdas(argument, postponedArguments, variableDependencyProvider) + + return ParameterTypesInfo( + parameterTypesFromDeclaration, + parameterTypesFromDeclarationOfRelatedLambdas, + parameterTypesFromConstraints, + annotations ?: Annotations.EMPTY, + isSuspend = !functionalTypesFromConstraints.isNullOrEmpty() && functionalTypesFromConstraints.any { it.type.isSuspendFunctionTypeOrSubtype }, + isNullable = !functionalTypesFromConstraints.isNullOrEmpty() && functionalTypesFromConstraints.all { it.type.isMarkedNullable } + ) + } + + private fun Context.getDeclaredParametersFromRelatedLambdas( + argument: PostponedAtomWithRevisableExpectedType, + postponedArguments: List, + dependencyProvider: TypeVariableDependencyInformationProvider + ): Set>? { + fun PostponedAtomWithRevisableExpectedType.getExpectedTypeConstructor() = expectedType?.typeConstructor() + + val parameterTypesFromDeclarationOfRelatedLambdas = postponedArguments + .filterIsInstance() + .filter { it.parameterTypesFromDeclaration != null && it != argument } + .mapNotNull { anotherArgument -> + val argumentExpectedTypeConstructor = argument.getExpectedTypeConstructor() ?: return@mapNotNull null + val anotherArgumentExpectedTypeConstructor = anotherArgument.getExpectedTypeConstructor() ?: return@mapNotNull null + val areTypeVariablesRelated = dependencyProvider.areVariablesDependentShallowly( + argumentExpectedTypeConstructor, + anotherArgumentExpectedTypeConstructor + ) + + if (areTypeVariablesRelated) anotherArgument.parameterTypesFromDeclaration else null + } + + return parameterTypesFromDeclarationOfRelatedLambdas.toSet().takeIf { it.isNotEmpty() } + } + + private fun Context.createTypeVariableForReturnType(argument: PostponedAtomWithRevisableExpectedType): NewTypeVariable { + val expectedType = argument.expectedType + ?: throw IllegalStateException("Postponed argument's expected type must not be null") + + return when (argument) { + is LambdaWithTypeVariableAsExpectedTypeAtom -> TypeVariableForLambdaReturnType( + expectedType.builtIns, + TYPE_VARIABLE_NAME_FOR_LAMBDA_RETURN_TYPE + ) + is PostponedCallableReferenceAtom -> TypeVariableForCallableReferenceReturnType( + expectedType.builtIns, + TYPE_VARIABLE_NAME_FOR_CR_RETURN_TYPE + ) + else -> throw IllegalStateException("Unsupported postponed argument type of $argument") + }.also { getBuilder().registerVariable(it) } + } + + private fun Context.createTypeVariableForParameterType( + argument: PostponedAtomWithRevisableExpectedType, + index: Int + ): NewTypeVariable { + val expectedType = argument.expectedType + ?: throw IllegalStateException("Postponed argument's expected type must not be null") + + return when (argument) { + is LambdaWithTypeVariableAsExpectedTypeAtom -> TypeVariableForLambdaParameterType( + argument.atom, + index, + expectedType.builtIns, + TYPE_VARIABLE_NAME_PREFIX_FOR_LAMBDA_PARAMETER_TYPE + (index + 1) + ) + is PostponedCallableReferenceAtom -> TypeVariableForCallableReferenceParameterType( + expectedType.builtIns, + TYPE_VARIABLE_NAME_PREFIX_FOR_CR_PARAMETER_TYPE + (index + 1) + ) + else -> throw IllegalStateException("Unsupported postponed argument type of $argument") + }.also { getBuilder().registerVariable(it) } + } + + private fun Context.createTypeVariablesForParameters( + argument: PostponedAtomWithRevisableExpectedType, + parameterTypes: List> + ): List { + val atom = argument.atom + val csBuilder = getBuilder() + val allGroupedParameterTypes = parameterTypes.first().indices.map { i -> parameterTypes.map { it.getOrNull(i) } } + + return allGroupedParameterTypes.mapIndexed { index, types -> + val parameterTypeVariable = createTypeVariableForParameterType(argument, index) + + for (typeWithKind in types.filterNotNull()) { + when (typeWithKind.direction) { + ConstraintKind.EQUALITY -> csBuilder.addEqualityConstraint( + parameterTypeVariable.defaultType, typeWithKind.type, ArgumentConstraintPosition(atom) + ) + ConstraintKind.UPPER -> csBuilder.addSubtypeConstraint( + parameterTypeVariable.defaultType, typeWithKind.type, ArgumentConstraintPosition(atom) + ) + ConstraintKind.LOWER -> csBuilder.addSubtypeConstraint( + typeWithKind.type, parameterTypeVariable.defaultType, ArgumentConstraintPosition(atom) + ) + } + } + + parameterTypeVariable.defaultType.asTypeProjection() + } + } + + private fun Context.computeResultingFunctionalConstructor( + argument: PostponedAtomWithRevisableExpectedType, + parametersNumber: Int, + isSuspend: Boolean, + resultTypeResolver: ResultTypeResolver + ): TypeConstructor { + val expectedType = argument.expectedType + ?: throw IllegalStateException("Postponed argument's expected type must not be null") + + val expectedTypeConstructor = expectedType.constructor + + return when (argument) { + is LambdaWithTypeVariableAsExpectedTypeAtom -> + getFunctionDescriptor(expectedTypeConstructor.builtIns, parametersNumber, isSuspend).typeConstructor + is PostponedCallableReferenceAtom -> { + val computedResultType = resultTypeResolver.findResultType( + this, + notFixedTypeVariables.getValue(expectedTypeConstructor), + TypeVariableDirectionCalculator.ResolveDirection.TO_SUPERTYPE + ) + + // Avoid KFunction<...>/Function<...> types + if (computedResultType.isBuiltinFunctionalTypeOrSubtype() && computedResultType.argumentsCount() > 1) { + computedResultType.typeConstructor() as TypeConstructor + } else { + getKFunctionDescriptor(expectedTypeConstructor.builtIns, parametersNumber, isSuspend).typeConstructor + } + } + else -> throw IllegalStateException("Unsupported postponed argument type of $argument") + } + } + + private fun Context.buildNewFunctionalExpectedType( + argument: PostponedAtomWithRevisableExpectedType, + parameterTypesInfo: ParameterTypesInfo + ): UnwrappedType? { + val expectedType = argument.expectedType + + if (expectedType == null || expectedType.constructor !in notFixedTypeVariables) + return null + + val atom = argument.atom + val parametersFromConstraints = parameterTypesInfo.parametersFromConstraints + val parametersFromDeclaration = getDeclaredParametersConsideringExtensionFunctionsPresence(parameterTypesInfo) + val areAllParameterTypesSpecified = !parametersFromDeclaration.isNullOrEmpty() && parametersFromDeclaration.all { it != null } + val isExtensionFunction = parameterTypesInfo.annotations.hasExtensionFunctionAnnotation() + val parametersFromDeclarations = parameterTypesInfo.parametersFromDeclarationOfRelatedLambdas.orEmpty() + parametersFromDeclaration + + /* + * We shouldn't create synthetic functional type if all lambda's parameter types are specified explicitly + * + * TODO: regarding anonymous functions: see info about need for analysis in partial mode in `collectParameterTypesAndBuildNewExpectedTypes` + */ + if (areAllParameterTypesSpecified && !isExtensionFunction && !isAnonymousFunction(argument)) + return null + + val allParameterTypes = + (parametersFromConstraints.orEmpty() + parametersFromDeclarations.map { parameters -> parameters?.map { it.wrapToTypeWithKind() } }).filterNotNull() + + if (allParameterTypes.isEmpty()) + return null + + val variablesForParameterTypes = createTypeVariablesForParameters(argument, allParameterTypes) + val variableForReturnType = createTypeVariableForReturnType(argument) + val functionalConstructor = computeResultingFunctionalConstructor( + argument, + variablesForParameterTypes.size, + parameterTypesInfo.isSuspend, + resultTypeResolver + ) + + val isExtensionFunctionType = parameterTypesInfo.annotations.hasExtensionFunctionAnnotation() + val areParametersNumberInDeclarationAndConstraintsEqual = + !parametersFromDeclaration.isNullOrEmpty() && !parametersFromConstraints.isNullOrEmpty() + && parametersFromDeclaration.size == parametersFromConstraints.first().size + + /* + * We need to exclude further considering a postponed argument as an extension function + * to support cases with explicitly specified receiver as a value parameter (only if all parameter types are specified) + * + * Example: `val x: String.() -> Int = id { x: String -> 42 }` + */ + val shouldDiscriminateExtensionFunctionAnnotation = + isExtensionFunctionType && areAllParameterTypesSpecified && areParametersNumberInDeclarationAndConstraintsEqual + + /* + * We need to add an extension function annotation for anonymous functions with an explicitly specified receiver + * + * Example: `val x = id(fun String.() = this)` + */ + val shouldAddExtensionFunctionAnnotation = atom is FunctionExpression && atom.receiverType != null + + val annotations = when { + shouldDiscriminateExtensionFunctionAnnotation -> + parameterTypesInfo.annotations.withoutExtensionFunctionAnnotation() + shouldAddExtensionFunctionAnnotation -> + parameterTypesInfo.annotations.withExtensionFunctionAnnotation(expectedType.builtIns) + else -> parameterTypesInfo.annotations + } + + val nexExpectedType = KotlinTypeFactory.simpleType( + annotations, + functionalConstructor, + variablesForParameterTypes + variableForReturnType.defaultType.asTypeProjection(), + parameterTypesInfo.isNullable + ) + + getBuilder().addSubtypeConstraint( + nexExpectedType, + expectedType, + ArgumentConstraintPosition(argument.atom) + ) + + return nexExpectedType + } + + fun collectParameterTypesAndBuildNewExpectedTypes( + c: Context, + postponedArguments: List, + completionMode: KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode, + dependencyProvider: TypeVariableDependencyInformationProvider + ) { + // We can collect parameter types from declaration in any mode, they can't change during completion. + val postponedArgumentsToCollectTypesFromDeclaredParameters = postponedArguments + .filterIsInstance() + .filter { it.parameterTypesFromDeclaration == null } + + for (argument in postponedArgumentsToCollectTypesFromDeclaredParameters) { + argument.parameterTypesFromDeclaration = extractParameterTypesFromDeclaration(argument.atom) + } + + /* + * We can build new functional expected types in partial mode only for anonymous functions, + * because more exact type can't appear from constraints in full mode (anonymous functions have fully explicit declaration). + * It can be so for lambdas: for instance, an extension function type can appear in full mode (it may not be known in partial mode). + * + * TODO: investigate why we can't do it for anonymous functions in full mode always (see `diagnostics/tests/resolve/resolveWithSpecifiedFunctionLiteralWithId.kt`) + */ + val postponedArgumentsToCollectParameterTypesAndBuildNewExpectedType = + if (completionMode == KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.PARTIAL) { + postponedArguments.filter(::isAnonymousFunction) + } else { + postponedArguments + } + + do { + val wasTransformedSomePostponedArgument = + postponedArgumentsToCollectParameterTypesAndBuildNewExpectedType.filter { it.revisedExpectedType == null }.any { argument -> + val parameterTypesInfo = + c.extractParameterTypesInfo(argument, postponedArguments, dependencyProvider) ?: return@any false + val newExpectedType = + c.buildNewFunctionalExpectedType(argument, parameterTypesInfo) ?: return@any false + + argument.revisedExpectedType = newExpectedType + + true + } + } while (wasTransformedSomePostponedArgument) + } + + fun transformToAtomWithNewFunctionalExpectedType( + c: Context, + argument: PostponedAtomWithRevisableExpectedType, + diagnosticsHolder: KotlinDiagnosticsHolder + ) { + val revisedExpectedType = argument.revisedExpectedType?.takeIf { it.isFunctionOrKFunctionTypeWithAnySuspendability } ?: return + + when (argument) { + is PostponedCallableReferenceAtom -> + CallableReferenceWithRevisedExpectedTypeAtom(argument.atom, revisedExpectedType).also { + argument.setAnalyzedResults(null, listOf(it)) + } + is LambdaWithTypeVariableAsExpectedTypeAtom -> + argument.transformToResolvedLambda(c.getBuilder(), diagnosticsHolder, revisedExpectedType) + else -> throw IllegalStateException("Unsupported postponed argument type of $argument") + } + } + + private fun getAllDeeplyRelatedTypeVariables( + type: KotlinType, + variableDependencyProvider: TypeVariableDependencyInformationProvider + ): List { + val typeConstructor = type.constructor + + return when { + typeConstructor is TypeVariableTypeConstructor -> { + val relatedVariables = variableDependencyProvider.getDeeplyDependentVariables(typeConstructor).orEmpty() + listOf(typeConstructor) + relatedVariables.filterIsInstance() + } + type.arguments.isNotEmpty() -> { + type.arguments.map { getAllDeeplyRelatedTypeVariables(it.type, variableDependencyProvider) }.flatten() + } + else -> listOf() + } + } + + private fun getDeclaredParametersConsideringExtensionFunctionsPresence(parameterTypesInfo: ParameterTypesInfo): List? { + val (parametersFromDeclaration, _, parametersFromConstraints, annotations) = parameterTypesInfo + + if (parametersFromConstraints.isNullOrEmpty() || parametersFromDeclaration.isNullOrEmpty()) + return parametersFromDeclaration + + val oneLessParameterInDeclarationThanInConstraints = parametersFromConstraints.first().size == parametersFromDeclaration.size + 1 + + return if (oneLessParameterInDeclarationThanInConstraints && annotations.hasExtensionFunctionAnnotation()) { + listOf(null) + parametersFromDeclaration + } else { + parametersFromDeclaration + } + } + + fun fixNextReadyVariableForParameterTypeIfNeeded( + c: Context, + argument: PostponedResolvedAtom, + postponedArguments: List, + topLevelType: UnwrappedType, + topLevelAtoms: List, + dependencyProvider: TypeVariableDependencyInformationProvider + ): Boolean { + val expectedType = argument.run { safeAs()?.revisedExpectedType ?: expectedType } + + if (expectedType != null && expectedType.isFunctionOrKFunctionTypeWithAnySuspendability) { + val wasFixedSomeVariable = + c.fixNextReadyVariableForParameterType(expectedType, postponedArguments, topLevelType, topLevelAtoms, dependencyProvider) + + if (wasFixedSomeVariable) + return true + } + + return false + } + + private fun Context.fixNextReadyVariableForParameterType( + type: KotlinType, + postponedArguments: List, + topLevelType: UnwrappedType, + topLevelAtoms: List, + dependencyProvider: TypeVariableDependencyInformationProvider + ): Boolean { + val relatedVariables = type.getPureArgumentsForFunctionalTypeOrSubtype() + .map { getAllDeeplyRelatedTypeVariables(it, dependencyProvider) }.flatten() + val variableForFixation = variableFixationFinder.findFirstVariableForFixation( + this, relatedVariables, postponedArguments, KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.FULL, topLevelType + ) + + if (variableForFixation == null || !variableForFixation.hasProperConstraint) + return false + + val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable) + val resultType = + resultTypeResolver.findResultType(this, variableWithConstraints, TypeVariableDirectionCalculator.ResolveDirection.UNKNOWN) + val resolvedAtom = KotlinConstraintSystemCompleter.findResolvedAtomBy(variableWithConstraints.typeVariable, topLevelAtoms) + ?: topLevelAtoms.firstOrNull() + + fixVariable(variableWithConstraints.typeVariable, resultType, resolvedAtom) + + return true + } + + private fun KotlinType?.wrapToTypeWithKind() = this?.let { TypeWithKind(it) } + + private fun isAnonymousFunction(argument: PostponedAtomWithRevisableExpectedType) = argument.atom is FunctionExpression + + companion object { + private const val TYPE_VARIABLE_NAME_PREFIX_FOR_LAMBDA_PARAMETER_TYPE = "_RP" + private const val TYPE_VARIABLE_NAME_FOR_LAMBDA_RETURN_TYPE = "_R" + private const val TYPE_VARIABLE_NAME_PREFIX_FOR_CR_PARAMETER_TYPE = "_QP" + private const val TYPE_VARIABLE_NAME_FOR_CR_RETURN_TYPE = "_Q" + } +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDependencyInformationProvider.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDependencyInformationProvider.kt index 75c6318c7f3..2bd6a0b15ba 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDependencyInformationProvider.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeVariableDependencyInformationProvider.kt @@ -18,10 +18,7 @@ package org.jetbrains.kotlin.resolve.calls.inference.components import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker -import org.jetbrains.kotlin.types.model.KotlinTypeMarker -import org.jetbrains.kotlin.types.model.TypeConstructorMarker -import org.jetbrains.kotlin.types.model.TypeSystemInferenceExtensionContext -import org.jetbrains.kotlin.types.model.freshTypeConstructor +import org.jetbrains.kotlin.types.model.* import org.jetbrains.kotlin.utils.SmartSet class TypeVariableDependencyInformationProvider( @@ -30,10 +27,19 @@ class TypeVariableDependencyInformationProvider( private val topLevelType: KotlinTypeMarker?, private val typeSystemContext: TypeSystemInferenceExtensionContext ) { - // not oriented edges - private val constrainEdges: MutableMap> = hashMapOf() + /* + * Not oriented edges + * TypeVariable(A) has UPPER(Function1) => A and B are related deeply + */ + private val deepTypeVariableDependencies: MutableMap> = hashMapOf() - // oriented edges + /* + * Not oriented edges + * TypeVariable(A) has UPPER(TypeVariable(B)) => A and B are related shallowly + */ + private val shallowTypeVariableDependencies: MutableMap> = hashMapOf() + + // Oriented edges private val postponeArgumentsEdges: MutableMap> = hashMapOf() private val relatedToAllOutputTypes: MutableSet = hashSetOf() @@ -49,21 +55,43 @@ class TypeVariableDependencyInformationProvider( fun isVariableRelatedToTopLevelType(variable: TypeConstructorMarker) = relatedToTopLevelType.contains(variable) fun isVariableRelatedToAnyOutputType(variable: TypeConstructorMarker) = relatedToAllOutputTypes.contains(variable) + fun getDeeplyDependentVariables(variable: TypeConstructorMarker) = deepTypeVariableDependencies[variable] + fun getShallowlyDependentVariables(variable: TypeConstructorMarker) = shallowTypeVariableDependencies[variable] + + fun areVariablesDependentShallowly(a: TypeConstructorMarker, b: TypeConstructorMarker): Boolean { + if (a == b) return true + + val shallowDependencies = shallowTypeVariableDependencies[a] ?: return false + + return shallowDependencies.any { it == b } || + shallowTypeVariableDependencies.values.any { dependencies -> a in dependencies && b in dependencies } + } + private fun computeConstraintEdges() { - fun addConstraintEdge(from: TypeConstructorMarker, to: TypeConstructorMarker) { - constrainEdges.getOrPut(from) { hashSetOf() }.add(to) - constrainEdges.getOrPut(to) { hashSetOf() }.add(from) + fun addConstraintEdgeForDeepDependency(from: TypeConstructorMarker, to: TypeConstructorMarker) { + deepTypeVariableDependencies.getOrPut(from) { linkedSetOf() }.add(to) + deepTypeVariableDependencies.getOrPut(to) { linkedSetOf() }.add(from) + } + + fun addConstraintEdgeForShallowDependency(from: TypeConstructorMarker, to: TypeConstructorMarker) { + shallowTypeVariableDependencies.getOrPut(from) { linkedSetOf() }.add(to) + shallowTypeVariableDependencies.getOrPut(to) { linkedSetOf() }.add(from) } for (variableWithConstraints in notFixedTypeVariables.values) { val from = variableWithConstraints.typeVariable.freshTypeConstructor(typeSystemContext) for (constraint in variableWithConstraints.constraints) { + val constraintTypeConstructor = constraint.type.typeConstructor(typeSystemContext) + constraint.type.forAllMyTypeVariables { if (isMyTypeVariable(it)) { - addConstraintEdge(from, it) + addConstraintEdgeForDeepDependency(from, it) } } + if (isMyTypeVariable(constraintTypeConstructor)) { + addConstraintEdgeForShallowDependency(from, constraintTypeConstructor) + } } } } @@ -118,7 +146,7 @@ class TypeVariableDependencyInformationProvider( } - private fun getConstraintEdges(from: TypeConstructorMarker): Set = constrainEdges[from] ?: emptySet() + private fun getConstraintEdges(from: TypeConstructorMarker): Set = deepTypeVariableDependencies[from] ?: emptySet() private fun getPostponeEdges(from: TypeConstructorMarker): Set = postponeArgumentsEdges[from] ?: emptySet() private fun addAllRelatedNodes(to: MutableSet, node: TypeConstructorMarker, includePostponedEdges: Boolean) { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt index 161f3bd2da2..f11ac24434d 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt @@ -61,6 +61,12 @@ enum class ConstraintKind { fun isLower(): Boolean = this == LOWER fun isUpper(): Boolean = this == UPPER fun isEqual(): Boolean = this == EQUALITY + + fun opposite() = when (this) { + LOWER -> UPPER + UPPER -> LOWER + EQUALITY -> EQUALITY + } } class Constraint( diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt index d5380cc6b4f..90f897cfc91 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzer import org.jetbrains.kotlin.resolve.calls.inference.* import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintInjector import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter +import org.jetbrains.kotlin.resolve.calls.inference.components.PostponedArgumentInputTypesResolver import org.jetbrains.kotlin.resolve.calls.inference.components.ResultTypeResolver import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic import org.jetbrains.kotlin.resolve.calls.model.OnlyInputTypesDiagnostic @@ -31,6 +32,7 @@ class NewConstraintSystemImpl( ConstraintInjector.Context, ResultTypeResolver.Context, KotlinConstraintSystemCompleter.Context, + PostponedArgumentInputTypesResolver.Context, PostponedArgumentsAnalyzer.Context { private val storage = MutableConstraintStorage() private var state = State.BUILDING @@ -87,6 +89,8 @@ class NewConstraintSystemImpl( override fun asPostponedArgumentsAnalyzerContext() = apply { checkState(State.BUILDING) } + override fun asPostponedArgumentInputTypesResolverContext() = apply { checkState(State.BUILDING) } + // ConstraintSystemOperation override fun registerVariable(variable: TypeVariableMarker) { checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/TypeVariable.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/TypeVariable.kt index ef22b96113e..f1c4a2e5107 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/TypeVariable.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/TypeVariable.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.resolve.calls.model.CallableReferenceKotlinCallArgument import org.jetbrains.kotlin.resolve.calls.model.LambdaKotlinCallArgument +import org.jetbrains.kotlin.resolve.calls.model.PostponableKotlinCallArgument import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.descriptorUtil.hasOnlyInputTypesAnnotation import org.jetbrains.kotlin.types.KotlinType @@ -79,7 +80,15 @@ class TypeVariableFromCallableDescriptor( } class TypeVariableForLambdaReturnType( - val lambdaArgument: LambdaKotlinCallArgument, + builtIns: KotlinBuiltIns, + name: String +) : NewTypeVariable(builtIns, name) { + override fun hasOnlyInputTypesAnnotation(): Boolean = false +} + +class TypeVariableForLambdaParameterType( + val atom: PostponableKotlinCallArgument, + val index: Int, builtIns: KotlinBuiltIns, name: String ) : NewTypeVariable(builtIns, name) { @@ -92,3 +101,10 @@ class TypeVariableForCallableReferenceReturnType( ) : NewTypeVariable(builtIns, name) { override fun hasOnlyInputTypesAnnotation(): Boolean = false } + +class TypeVariableForCallableReferenceParameterType( + builtIns: KotlinBuiltIns, + name: String +) : NewTypeVariable(builtIns, name) { + override fun hasOnlyInputTypesAnnotation(): Boolean = false +} diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionAtoms.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionAtoms.kt index 9fb88d69baa..3129ec2dc60 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionAtoms.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionAtoms.kt @@ -13,10 +13,7 @@ import org.jetbrains.kotlin.resolve.calls.components.TypeArgumentsToParametersMa import org.jetbrains.kotlin.resolve.calls.components.extractInputOutputTypesFromCallableReferenceExpectedType import org.jetbrains.kotlin.resolve.calls.inference.components.FreshVariableNewTypeSubstitutor import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutor -import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage -import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintError -import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableForCallableReferenceReturnType -import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableForLambdaReturnType +import org.jetbrains.kotlin.resolve.calls.inference.model.* import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstant import org.jetbrains.kotlin.types.KotlinType @@ -98,6 +95,12 @@ interface PostponedResolvedAtomMarker { val analyzed: Boolean } +interface PostponedAtomWithRevisableExpectedType { + var revisedExpectedType: UnwrappedType? + val expectedType: UnwrappedType? + val atom: PostponableKotlinCallArgument +} + sealed class PostponedResolvedAtom : ResolvedAtom(), PostponedResolvedAtomMarker { abstract override val inputTypes: Collection abstract override val outputType: UnwrappedType? @@ -107,10 +110,13 @@ sealed class PostponedResolvedAtom : ResolvedAtom(), PostponedResolvedAtomMarker class LambdaWithTypeVariableAsExpectedTypeAtom( override val atom: LambdaKotlinCallArgument, override val expectedType: UnwrappedType -) : PostponedResolvedAtom() { +) : PostponedResolvedAtom(), PostponedAtomWithRevisableExpectedType { override val inputTypes: Collection get() = listOf(expectedType) override val outputType: UnwrappedType? get() = null - var isReturnArgumentOfAnotherLambda: Boolean = false + + override var revisedExpectedType: UnwrappedType? = null + + var parameterTypesFromDeclaration: List? = null fun setAnalyzed(resolvedLambdaAtom: ResolvedLambdaAtom) { setAnalyzedResults(listOf(resolvedLambdaAtom)) @@ -180,15 +186,16 @@ sealed class AbstractPostponedCallableReferenceAtom( get() = extractInputOutputTypesFromCallableReferenceExpectedType(expectedType)?.outputType } -class CallableReferenceWithTypeVariableAsExpectedTypeAtom( +class CallableReferenceWithRevisedExpectedTypeAtom( atom: CallableReferenceKotlinCallArgument, expectedType: UnwrappedType?, - val typeVariableForReturnType: TypeVariableForCallableReferenceReturnType? ) : AbstractPostponedCallableReferenceAtom(atom, expectedType) class PostponedCallableReferenceAtom( eagerCallableReferenceAtom: EagerCallableReferenceAtom -) : AbstractPostponedCallableReferenceAtom(eagerCallableReferenceAtom.atom, eagerCallableReferenceAtom.expectedType) +) : AbstractPostponedCallableReferenceAtom(eagerCallableReferenceAtom.atom, eagerCallableReferenceAtom.expectedType), PostponedAtomWithRevisableExpectedType { + override var revisedExpectedType: UnwrappedType? = null +} class ResolvedCollectionLiteralAtom( override val atom: CollectionLiteralKotlinCallArgument, diff --git a/compiler/testData/diagnostics/tests/callableReference/generic/nestedCallWithOverload.fir.kt b/compiler/testData/diagnostics/tests/callableReference/generic/nestedCallWithOverload.fir.kt index 74317571e4d..af8867fde95 100644 --- a/compiler/testData/diagnostics/tests/callableReference/generic/nestedCallWithOverload.fir.kt +++ b/compiler/testData/diagnostics/tests/callableReference/generic/nestedCallWithOverload.fir.kt @@ -19,6 +19,4 @@ fun test() { baz<(Int) -> Unit>(id(::foo), id(id(::foo))) baz(id(::foo), id(id<(Int) -> Unit>(::foo))) baz(id(::foo), id<(Int) -> Unit>(id(::foo))) - - baz(id { it.inv() }, id<(Int) -> Unit> { }) } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/callableReference/generic/nestedCallWithOverload.kt b/compiler/testData/diagnostics/tests/callableReference/generic/nestedCallWithOverload.kt index 477ab16ce32..4dcb4810326 100644 --- a/compiler/testData/diagnostics/tests/callableReference/generic/nestedCallWithOverload.kt +++ b/compiler/testData/diagnostics/tests/callableReference/generic/nestedCallWithOverload.kt @@ -19,6 +19,4 @@ fun test() { baz<(Int) -> Unit>(id(::foo), id(id(::foo))) baz(id(::foo), id(id<(Int) -> Unit>(::foo))) baz(id(::foo), id<(Int) -> Unit>(id(::foo))) - - baz(id { it.inv() }, id<(Int) -> Unit> { }) } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/elvisNotProcessed.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/elvisNotProcessed.kt index 83a43ec4c73..285fee4684d 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/elvisNotProcessed.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/elvisNotProcessed.kt @@ -34,6 +34,6 @@ val bbb = null ?: ( l() ?: null) val bbbb = ( l() ?: null) ?: ( l() ?: null) fun f(x : Long?): Long { - var a = x ?: (fun() {} ?: fun() {}) - return a + var a = x ?: (fun() {} ?: fun() {}) + return a } diff --git a/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.fir.fail b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.fir.fail new file mode 100644 index 00000000000..6de8440105e --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.fir.fail @@ -0,0 +1,11 @@ +Failures detected in FirBodyResolveTransformerAdapter, file: /basic.fir.kt +Cause: java.lang.RuntimeException: While resolving call R?C|/selectFloat|(R?C|/id|( = id@fun .(): { + ^@id Unit +} +), R?C|/id|( = id@fun .(x: ): { + ^@id Unit +} +), R?C|/id|( = id@fun .(): { + it# +} +)) diff --git a/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.fir.kt b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.fir.kt new file mode 100644 index 00000000000..27c25ec6bf0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.fir.kt @@ -0,0 +1,195 @@ +import kotlin.reflect.KFunction1 + +// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_ANONYMOUS_PARAMETER + +fun foo(i: Int) {} +fun foo(s: String) {} +fun foo2(i: Int) {} +fun foo3(i: Number) {} +fun id(x: K): K = x +fun id1(x: K): K = x +fun id2(x: L): L = x +fun baz1(x: T, y: T): T = TODO() +fun baz2(x: T, y: Inv): T = TODO() +fun select(vararg x: T) = x[0] + +fun takeInterdependentLambdas(x: (T) -> R, y: (R) -> T) {} + +fun takeDependentLambdas(x: (T) -> Int, y: (Int) -> T) {} + +class Inv(val x: T) + +fun test1() { + val x1: (Int) -> Unit = id(id(::foo)) + val x2: (Int) -> Unit = baz1(id(::foo), ::foo) + val x3: (Int) -> Unit = baz1(id(::foo), id(id(::foo))) + val x4: (String) -> Unit = baz1(id(::foo), id(id(::foo))) + + id<(Int) -> Unit>(id(id(::foo))) + id(id<(Int) -> Unit>(::foo)) + baz1<(Int) -> Unit>(id(::foo), id(id(::foo))) + baz1(id(::foo), id(id<(Int) -> Unit>(::foo))) + baz1(id(::foo), id<(Int) -> Unit>(id(::foo))) + + baz1(id { it.inv() }, id<(Int) -> Unit> { }) + baz1(id1 { x -> x.inv() }, id2 { x: Int -> }) + baz1(id1 { it.inv() }, id2 { x: Int -> }) + + baz2(id1 { it.inv() }, id2(Inv { x: Int -> })) + + select(id1 { it.inv() }, id1 { x: Number -> TODO() }, id1(id2 { x: Int -> x })) + + select(id1 { it.inv() }, id1 { x: Number -> TODO() }, id1(id2(::foo2))) + select(id1 { x: Inv -> TODO() }, id1 { ")!>it.x.inv() }, id1 { x: Inv -> TODO() }) + + select(id1 { & Inv}")!>it }, id1 { x: Inv -> TODO() }, id1 { x: Inv -> TODO() }) + + ")!>select(id1(::foo), id(::foo3), id1(id2(::foo2))) + + select({ it }, { x: Int -> TODO() }) + + // Interdependent postponed arguments are unsupported + takeInterdependentLambdas({}, {}) + takeInterdependentLambdas({ it }, { 10 }) + takeInterdependentLambdas({ 10 }, { it }) + takeInterdependentLambdas({ 10 }, { x -> x }) + takeInterdependentLambdas({ x -> 10 }, { it }) + takeInterdependentLambdas({ it }, { x -> 10 }) + + takeDependentLambdas({ it }, { it }) + takeDependentLambdas({ it.length }, { "it" }) + takeDependentLambdas({ it; 10 }, { }) + + select({ it }, fun(x: Int) = 1) + + val x5: (Int) -> Unit = select({ it }, { x: Number -> Unit }) + val x6 = select(id { it }, id(id<(Int) -> Unit> { x: Number -> Unit })) + + select(id(id2 { it.inv() }), id(id { x: Int -> x })) + + val x7: (Int) -> Unit = selectNumber(id {}, id {}, id {}) + val x8: (Int) -> Unit = selectNumber(id { x -> x }, id { x -> }, id { x -> }) + val x9: (Int) -> Unit = selectNumber(id { }, id { x -> }, id { it }) + + val x10: (Int) -> Unit = selectFloat(id { }, id { x -> }, id { & Number}")!>it }) + + val x11: (B) -> Unit = selectC(id { }, id { x -> x }, id { it }) + + /* + * Upper constraint is less specific than lower (it's error): + * K <: (A) -> Unit -> TypeVariable(_RP1) >: A + * K >: (C) -> TypeVariable(_R) -> TypeVariable(_RP1) <: C + */ + val x12 = selectC(id { it }, id { x: B -> }) + val x13 = selectA(id { it }, id { x: C -> }) + + // one upper constraint and one lower + val x14 = selectC(id { it }, id { x: A -> }, { x -> x }) + val x15 = selectC(id { it }, { x: A -> }, id { x -> x }) + + /* + * Two upper constraints and one lower + * K <: (C) -> Unit -> TypeVariable(_RP1) >: C + * K <: (B) -> Unit -> TypeVariable(_RP1) >: B + * K >: (A) -> TypeVariable(_R) -> TypeVariable(_RP1) <: A + * K == intersect(CST(C, B), A) == A + */ + val x16: (C) -> Unit = selectB(id { it }, { x -> }, id { x: A -> x }) + + /* + * two upper constraints and one equality (it's error) + * K <: (C) -> Unit -> TypeVariable(_RP1) >: C + * K == (B) -> Unit -> TypeVariable(_RP1) == B + */ + val x17: (C) -> Unit = selectB(id { it }, id { it }, id<(B) -> Unit> { x -> x }) + val x18: (C) -> Unit = select(id { it }, { it }, id<(B) -> Unit> { x -> x }) + + val x19: String.() -> Unit = select( kotlin.Unit")!>id {}, kotlin.Unit")!>id(fun(x: String) {})) + val x20: String.() -> Unit = select( kotlin.Unit")!>{}, kotlin.Unit")!>(fun(x: String) {})) + val x21: String.() -> Unit = select( kotlin.Unit")!>id(fun(x: String) {}), kotlin.Unit")!>id(fun(x: String) {})) + val x22 = select(id Unit>(fun(x: String) {}), kotlin.Unit")!>id(fun(x: String) {})) + val x23 = select( kotlin.Unit")!>id(fun String.(x: String) {}), kotlin.Unit")!>id(fun(x: String, y: String) {})) + val x24 = select(id(fun String.(x: String) {}), id(fun(x: String, y: String) { }), { x: String -> this }) + val x25 = select(id(fun String.(x: String) {}), id(fun(x: String, y: String) { }), { x: String, y: String -> x }) + + // It isn't related with posponed arguments, see KT-38439 + val x26: Int.(String) -> Int = fun (x: String) = 10 + val x27: Int.(String) -> Int = id(fun (x: String) = 10) + + val x28 = select(id { x, y -> x.inv() + y.toByte() }, { x: Int, y -> y.toByte() }, { x, y: Number -> x.inv() }) + val x29 = select(id { x, y -> x.inv() + y.toByte() }, id { x: Int, y -> y.toByte() }, id { x, y: Number -> x.inv() }) + val x30 = select({ x, y -> x.inv() + y.toByte() }, id { x: Int, y -> y.toByte() }, id { x, y: Number -> x.inv() }) + + val x31 = select( + id { x, y -> x.inv() + y.toByte() }, + id<(Int, Number) -> Int> { x, y -> x.inv() }, + {} as (Number, Number) -> Int + ) + + val x32 = selectPosponedArgument({ it }, { x: Int -> }, { x: Nothing -> x }) + val x33 = selectPosponedArgument({ it }, { } as (Int) -> Unit, { x: Nothing -> x }) + val x34 = selectPosponedArgument({ it }, { } as (Nothing) -> Unit, { x: Int -> x }) + val x35 = selectPosponedArgument({ it }, { } as (Int) -> Unit, { } as (Nothing) -> Unit) + + val x36 = selectPosponedArgument3({ it }, { it }, { x: Int -> x }) + val x37 = selectPosponedArgument3({ it }, { x: Number -> x }, { x: Int -> x }) + val x38 = selectPosponedArgument3Revert({ it }, { it }, { x: Int -> x }) + val x39 = selectPosponedArgument3Revert({ it }, { x: Number -> x }, { x: Int -> x }) + + val x40 = select(id Unit> {}, { x: Int, y: String -> x }) + + val x41 = select(A2(), { a, b, c -> a; b; c }) + val x42 = select(A3(), { it }, { a -> a }) + val x43 = select(A3(), ")!>A3::foo1) + val x44 = select(A3(), ")!>A3::foo1, { a -> a }, { it -> it }) + + val x45 = select(A4(), { x: Number -> "" }) + val x46 = select(A5(), { x: Number, y: Int -> "" }) + + val x47 = select(A2(), id { a, b, c -> a; b; c }) + val x48 = select(id(A3()), { it }, { a -> a }) + val x49 = select(A3(), id(")!>A3::foo1)) + val x50 = select(A3(), ")!>A3::foo1, { a -> a }, { it -> it }) + + val x51 = select(A4(), id { x: Number -> x }) + val x52 = select(id(A5()), id { x: Number, y: Int -> x;y }) + val x53 = select(id(A5()), id { x, y -> x;y }) + val x54 = select(id(")!>A5()), id { x: Number, y: Int -> x;y }) + val x55: Function2 = select(id(A5()), id { x, y -> x;y; 1f }) +} + +fun Unit> selectNumber(arg1: T, arg2: T, arg3: T) = arg1 + +fun Unit> selectFloat(arg1: T, arg2: T, arg3: T) = arg2 + +fun Unit> selectA(arg1: T, arg2: T, arg3: T) = arg2 +fun Unit> selectB(arg1: T, arg2: T, arg3: T) = arg2 +fun Unit> selectC(arg1: T, arg2: T, arg3: T) = arg2 + +fun selectPosponedArgument(vararg x: (T) -> Unit) = x[0] +fun selectPosponedArgument3(x: (T) -> Unit, y: (R) -> Unit, z: (L) -> Unit) = x +fun selectPosponedArgument3Revert(x: (T) -> Unit, y: (R) -> Unit, z: (L) -> Unit) = x + +interface A +class B: A +class C: A + +class A2: Function3 { + override fun invoke(p1: Int, p2: String, p3: Float): Float = 4f +} + +class A3: KFunction1 { + override fun invoke(p1: Number): String = TODO() + override val name: String = TODO() + + fun foo1(x: Int) {} + fun foo1(x: Any?) {} +} + +class A4: Function1 { + override fun invoke(p1: Int): Float = TODO() +} + +class A5: Function2 { + override fun invoke(p1: K, p2: Q): Float = 5f +} diff --git a/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.kt b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.kt new file mode 100644 index 00000000000..8a948df4410 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.kt @@ -0,0 +1,223 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_ANONYMOUS_PARAMETER -UNCHECKED_CAST + +import kotlin.reflect.KFunction1 + +fun withOverload(i: Int) {} +fun withOverload(s: String) {} + +fun takeInt(i: Int) {} +fun takeNumber(i: Number) {} + +fun id(x: K): K = x +fun id1(x: K): K = x +fun id2(x: L): L = x + +fun selectWithInv(x: T, y: Inv): T = TODO() +fun select(vararg x: T) = x[0] + +fun takeLambdas(vararg x: (T) -> Unit) = x[0] +fun takeLambdasWithDirectlyDependentTypeParameters(x: (T) -> Unit, y: (R) -> Unit, z: (L) -> Unit) = x +fun takeLambdasWithInverselyDependentTypeParameters(x: (T) -> Unit, y: (R) -> Unit, z: (L) -> Unit) = x + +fun takeInterdependentLambdas(x: (T) -> R, y: (R) -> T) {} + +fun takeDependentLambdas(x: (T) -> Int, y: (Int) -> T) {} + +class Inv(val x: T) + +fun Unit> selectNumber(arg1: T, arg2: T, arg3: T) = arg1 + +fun Unit> selectFloat(arg1: T, arg2: T, arg3: T) = arg2 + +fun Unit> selectA(arg1: T, arg2: T, arg3: T) = arg2 +fun Unit> selectB(arg1: T, arg2: T, arg3: T) = arg2 +fun Unit> selectC(arg1: T, arg2: T, arg3: T) = arg2 + +interface A +class B: A +class C: A + +class A2: Function3 { + override fun invoke(p1: Int, p2: String, p3: Float): Float = 4f +} + +class A3: KFunction1 { + override fun invoke(p1: Number): String = TODO() + override val name: String = TODO() + + fun foo1(x: Int) {} + fun foo1(x: Any?) {} + + companion object { + fun foo2(x: Int) {} + fun foo2(x: Any?) {} + } +} + +class A4: Function1 { + override fun invoke(p1: Int): Float = TODO() +} + +class A5: Function2 { + override fun invoke(p1: K, p2: Q): Float = 5f +} + + +fun test1() { + // Inferring lambda parameter types by other lambda explicit parameters; expected type is type variable + select(id1 { x -> x.inv() }, id2 { x: Int -> }) + select(id1 { it.inv() }, id2 { x: Int -> }) + selectWithInv(id1 { it.inv() }, id2(Inv { x: Int -> })) + select(id1 { it.inv() }, id1 { x: Number -> TODO() }, id1(id2 { x: Int -> x })) + select(id1 { it.inv() }, id1 { x: Number -> TODO() }, id1(id2(::takeInt))) + select(id1 { x: Inv -> TODO() }, id1 { ")!>it.x.inv() }, id1 { x: Inv -> TODO() }) + select(id1 { & Inv}")!>it }, id1 { x: Inv -> TODO() }, id1 { x: Inv -> TODO() }) + select(id(id2 { it.inv() }), id(id { x: Int -> x })) + + // Disambiguating callable references by other callable references without overloads + ")!>select(id(::withOverload), id(::takeInt), id(id(::takeNumber))) + + // Interdependent lambdas by input-output types aren't supported + takeInterdependentLambdas({}, {}) + takeInterdependentLambdas({ it }, { 10 }) + takeInterdependentLambdas({ 10 }, { it }) + takeInterdependentLambdas({ 10 }, { x -> x }) + takeInterdependentLambdas({ x -> 10 }, { it }) + takeInterdependentLambdas({ it }, { x -> 10 }) + + // Dependent lambdas by input-output types + takeDependentLambdas({ it }, { it }) + takeDependentLambdas({ it.length }, { "it" }) + takeDependentLambdas({ it; 10 }, { }) + + // Inferring lambda parameter types by anonymous function parameters + select({ it }, fun(x: Int) = 1) + + // Inferring lambda parameter types by other lambda explicit parameters (lower constraints) and expected type (upper constraints) + val x5: (Int) -> Unit = select({ it }, { x: Number -> Unit }) + + // Inferring lambda parameter types by other lambda explicit parameters (lower constraints) and specified type arguments (equality constraints) + select(id { it }, id(id<(Int) -> Unit> { x: Number -> Unit })) + + // Inferring lambda parameter types by specified type arguments (equality constraints) of other lambdas + select(id { it.inv() }, id<(Int) -> Unit> { }) + select( + id { x, y -> x.inv() + y.toByte() }, + id<(Int, Number) -> Int> { x, y -> x.inv() }, + {} as (Number, Number) -> Int + ) + + // Inferring lambda parameter types by a few expected types (a few upper constraints) + val x7: (Int) -> Unit = kotlin.Unit")!>selectNumber(id {}, id {}, id {}) + val x8: (Int) -> Unit = selectNumber(id { x -> x }, id { x -> }, id { x -> }) + val x9: (Int) -> Unit = selectNumber(id { }, id { x -> }, id { it }) + val x10: (Int) -> Unit = selectFloat(id { }, id { x -> }, id { & Number}")!>it }) + val x11: (B) -> Unit = selectC(id { }, id { x -> x }, id { it }) + + // Inferring lambda parameter types by expected types (upper constraints) and other lambda explicit parameters (lower constraints) + /* + * Upper constraint is less specific than lower (it's error): + * K <: (A) -> Unit -> TypeVariable(_RP1) >: A + * K >: (C) -> TypeVariable(_R) -> TypeVariable(_RP1) <: C + */ + val x12 = selectC(id { it }, id { x: B -> }) + val x13 = selectA(id { it }, id { x: C -> }) + val x14 = selectC(id { it }, id { x: A -> }, { x -> x }) + val x15 = selectC(id { it }, { x: A -> }, id { x -> x }) + /* + * Two upper constraints and one lower + * K <: (C) -> Unit -> TypeVariable(_RP1) >: C + * K <: (B) -> Unit -> TypeVariable(_RP1) >: B + * K >: (A) -> TypeVariable(_R) -> TypeVariable(_RP1) <: A + * K == intersect(CST(C, B), A) == A + */ + val x16: (C) -> Unit = selectB(id { it }, { x -> }, id { x: A -> x }) + + // Inferring lambda parameter types by expected types (upper constraints) and specified type arguments (equality constraints) of other lambdas + /* + * two upper constraints and one equality (it's error) + * K <: (C) -> Unit -> TypeVariable(_RP1) >: C + * K == (B) -> Unit -> TypeVariable(_RP1) == B + */ + val x17: (C) -> Unit = selectB(id { it }, id { it }, id<(B) -> Unit> { x -> x }) + val x18: (C) -> Unit = select(id { it }, { it }, id<(B) -> Unit> { x -> x }) + + // Resolution of extension/non-extension functions combination + val x19: String.() -> Unit = select( kotlin.Unit")!>id {}, kotlin.Unit")!>id(fun(x: String) {})) + val x20: String.() -> Unit = select( kotlin.Unit")!>{}, kotlin.Unit")!>(fun(x: String) {})) + val x21: String.() -> Unit = select( kotlin.Unit")!>id(fun(x: String) {}), kotlin.Unit")!>id(fun(x: String) {})) + select(id Unit>(fun(x: String) {}), kotlin.Unit")!>id(fun(x: String) {})) + select( kotlin.Unit")!>id(fun String.(x: String) {}), kotlin.Unit")!>id(fun(x: String, y: String) {})) + select(id(fun String.(x: String) {}), id(fun(x: String, y: String) { }), { x: String -> this }) + select(id(fun String.(x: String) {}), id(fun(x: String, y: String) { }), { x -> this }) + select(id(fun String.(x: String) {}), id(fun(x: String, y: String) { }), { x: String, y: String -> x }) + // Convert to extension lambda is impossible because the lambda parameter types aren't specified explicitly + select(id(fun String.(x: String) {}), id(fun(x: String, y: String) { }), { x, y -> x }) + select(id(id(fun(x: String, y: String) { }), fun String.(x: String) {}), { x, y -> x }) + val x26: Int.(String) -> Int = fun (x: String) = 10 // it must be error, see KT-38439 + // Receiver must be specified in anonymous function declaration + val x27: Int.(String) -> Int = id(fun (x: String) = 10) + select(id Unit> {}, { x: Int, y: String -> x }) + + // Inferring lambda parameter types by partially specified parameter types of other lambdas + select(id { x, y -> x.inv() + y.toByte() }, { x: Int, y -> y.toByte() }, { x, y: Number -> x.inv() }) + select(id { x, y -> x.inv() + y.toByte() }, id { x: Int, y -> y.toByte() }, id { x, y: Number -> x.inv() }) + select({ x, y -> x.inv() + y.toByte() }, id { x: Int, y -> y.toByte() }, id { x, y: Number -> x.inv() }) + + // Inferring lambda parameter types by other specified lambda parameters; expected type is a functional type with type variables in parameter types + takeLambdas({ it }, { x: Int -> }, { x: Nothing -> x }) + takeLambdas({ it }, { } as (Int) -> Unit, { x: Nothing -> x }) + takeLambdas({ it }, { } as (Nothing) -> Unit, { x: Int -> x }) + takeLambdas({ it }, { } as (Int) -> Unit, { } as (Nothing) -> Unit) + + // Inferring lambda parameter types by other specified lambda parameters; expected type is a functional type with type variables in parameter types; dependent type parameters + takeLambdasWithDirectlyDependentTypeParameters({ it }, { it }, { x: Int -> x }) + takeLambdasWithDirectlyDependentTypeParameters({ it }, { x: Number -> x }, { x: Int -> x }) + takeLambdasWithInverselyDependentTypeParameters({ it }, { it }, { x: Int -> x }) + /* + * Interesting test case: variable can be fixed to different types randomly (`Int` or `Number`; it depends on variable fixation order) + * if in `TypeVariableDependencyInformationProvider` `hashSet` instead of `linkedSet` for `deepTypeVariableDependencies` and `shallowTypeVariableDependencies` will be used + */ + takeLambdasWithInverselyDependentTypeParameters({ it }, { x: Number -> x }, { x: Int -> x }) + + // Inferring lambda parameter types by subtypes of functional type + kotlin.Float")!>select(A2(), { a, b, c -> a; b; c }) + java.io.Serializable")!>select(A3(), { it }, { a -> a }) + ")!>select(A3(), ")!>A3::foo1) + // Should be error as `A3::foo1` is `KFunction2`, but the remaining arguments are `KFuncion1` or `Function1` + ")!>select(A3(), ")!>A3::foo1, { a -> ]")!>a }, { it -> ]")!>it }) + // It's OK because `A3::foo2` is from companion of `A3` + kotlin.Any")!>select(A3(), ")!>A3::foo2, { a -> a }, { it -> it }) + {Comparable<*> & java.io.Serializable}")!>select(A4(), { x: Number -> "" }) + {Comparable<*> & java.io.Serializable}")!>select(A5(), { x: Number, y: Int -> "" }) + kotlin.Float")!>select(A2(), id { a, b, c -> a; b; c }) + java.io.Serializable")!>select(id(A3()), { it }, { a -> a }) + ")!>select(A3(), id(")!>A3::foo1)) + ")!>select(A3(), ")!>A3::foo1, id { a -> ]")!>a }, { it -> ]")!>it }) + ")!>select(A3(), ")!>A3::foo1, { a -> ]")!>a }, id { it -> ]")!>it }) + ")!>select(id(A3()), id(")!>A3::foo1), { a -> ]")!>a }, { it -> ]")!>it }) + ")!>select(id(A3()), id(")!>A3::foo1), { a -> ]")!>a }, id { it -> ]")!>it }) + // If lambdas' parameters are specified explicitly, we don't report an error, because there is proper CST – Function + ")!>select(id(A3()), id(")!>A3::foo1), { a: Number -> a }) + ")!>select(id(A3()), id(")!>A3::foo1), id { a: Number -> a }) + kotlin.Number")!>select(A4(), id { x: Number -> x }) + {Comparable<*> & Number}")!>select(id(A5()), id { x: Number, y: Int -> x;y }) + {Comparable<*> & Number}")!>select(id(A5()), id { x, y -> x;y }) + {Comparable<*> & Number}")!>select(id(")!>A5()), id { x: Number, y: Int -> x;y }) + val x55: Function2 = select(id(A5()), id { x, y -> x;y; 1f }) + + // Diffrerent lambda's parameters with proper CST + ")!>select({ x: Int -> }, { x: String -> }) + ")!>select({ x: Int -> }, { x: Int, y: Number -> }) + ")!>select(id { x: Int -> }, { x: String -> }) + ")!>select({ x: Int -> }, id { x: Int, y: Number -> }) + ")!>select(id { x: Int -> }, id { x: String -> }) + ")!>select(id { x: Int -> }, id { x: Int, y: Number -> }) + & java.io.Serializable}>")!>select({ x: Int -> 1 }, { x: String -> "" }) + & Number}>")!>select({ x: Int -> 1 }, { x: Int, y: Number -> 1f }) + & java.io.Serializable}>>")!>select(id { x: Int -> Inv(10) }, { x: String -> Inv("") }) + ")!>select({ x: Int -> TODO() }, id { x: Int, y: Number -> Any() }) + ")!>select(id { x: Int -> null }, id { x: String -> "" }) + ")!>select(id { x: Int -> 10 }, id { x: Int, y: Number -> TODO() }) + val x68: String.(String) -> String = select(id { x: String, y: String -> "10" }, id { x: String, y: String -> "TODO()" }) +} diff --git a/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.txt b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.txt new file mode 100644 index 00000000000..3766c363583 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.txt @@ -0,0 +1,94 @@ +package + +public fun id(/*0*/ x: K): K +public fun id1(/*0*/ x: K): K +public fun id2(/*0*/ x: L): L +public fun select(/*0*/ vararg x: T /*kotlin.Array*/): T +public fun kotlin.Unit> selectA(/*0*/ arg1: T, /*1*/ arg2: T, /*2*/ arg3: T): T +public fun kotlin.Unit> selectB(/*0*/ arg1: T, /*1*/ arg2: T, /*2*/ arg3: T): T +public fun kotlin.Unit> selectC(/*0*/ arg1: T, /*1*/ arg2: T, /*2*/ arg3: T): T +public fun kotlin.Unit> selectFloat(/*0*/ arg1: T, /*1*/ arg2: T, /*2*/ arg3: T): T +public fun kotlin.Unit> selectNumber(/*0*/ arg1: T, /*1*/ arg2: T, /*2*/ arg3: T): T +public fun selectWithInv(/*0*/ x: T, /*1*/ y: Inv): T +public fun takeDependentLambdas(/*0*/ x: (T) -> kotlin.Int, /*1*/ y: (kotlin.Int) -> T): kotlin.Unit +public fun takeInt(/*0*/ i: kotlin.Int): kotlin.Unit +public fun takeInterdependentLambdas(/*0*/ x: (T) -> R, /*1*/ y: (R) -> T): kotlin.Unit +public fun takeLambdas(/*0*/ vararg x: (T) -> kotlin.Unit /*kotlin.Array kotlin.Unit>*/): (T) -> kotlin.Unit +public fun takeLambdasWithDirectlyDependentTypeParameters(/*0*/ x: (T) -> kotlin.Unit, /*1*/ y: (R) -> kotlin.Unit, /*2*/ z: (L) -> kotlin.Unit): (T) -> kotlin.Unit +public fun takeLambdasWithInverselyDependentTypeParameters(/*0*/ x: (T) -> kotlin.Unit, /*1*/ y: (R) -> kotlin.Unit, /*2*/ z: (L) -> kotlin.Unit): (T) -> kotlin.Unit +public fun takeNumber(/*0*/ i: kotlin.Number): kotlin.Unit +public fun test1(): kotlin.Unit +public fun withOverload(/*0*/ i: kotlin.Int): kotlin.Unit +public fun withOverload(/*0*/ s: kotlin.String): kotlin.Unit + +public interface A { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class A2 : (kotlin.Int, kotlin.String, kotlin.Float) -> kotlin.Float { + public constructor A2() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ fun invoke(/*0*/ p1: kotlin.Int, /*1*/ p2: kotlin.String, /*2*/ p3: kotlin.Float): kotlin.Float + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class A3 : kotlin.reflect.KFunction1 { + public constructor A3() + public open override /*1*/ val name: kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo1(/*0*/ x: kotlin.Any?): kotlin.Unit + public final fun foo1(/*0*/ x: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ fun invoke(/*0*/ p1: kotlin.Number): kotlin.String + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public companion object Companion { + private constructor Companion() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo2(/*0*/ x: kotlin.Any?): kotlin.Unit + public final fun foo2(/*0*/ x: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} + +public final class A4 : (kotlin.Int) -> kotlin.Float { + public constructor A4() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ fun invoke(/*0*/ p1: kotlin.Int): kotlin.Float + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class A5 : (K, Q) -> kotlin.Float { + public constructor A5() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ fun invoke(/*0*/ p1: K, /*1*/ p2: Q): kotlin.Float + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class B : A { + public constructor B() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class C : A { + public constructor C() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Inv { + public constructor Inv(/*0*/ x: T) + public final val x: T + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferenceLambdaCombinationInsideCall.kt b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferenceLambdaCombinationInsideCall.kt new file mode 100644 index 00000000000..b6f9434a539 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferenceLambdaCombinationInsideCall.kt @@ -0,0 +1,10 @@ +// FIR_IDENTICAL + +fun select(vararg x: T) = x[0] +fun id1(x: T): T = x +fun id2(x: T): T = x + +fun test() { + fun foo() {} + select(id1(::foo), id2 { }) +} diff --git a/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferenceLambdaCombinationInsideCall.txt b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferenceLambdaCombinationInsideCall.txt new file mode 100644 index 00000000000..4f36635523e --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferenceLambdaCombinationInsideCall.txt @@ -0,0 +1,6 @@ +package + +public fun id1(/*0*/ x: T): T +public fun id2(/*0*/ x: T): T +public fun select(/*0*/ vararg x: T /*kotlin.Array*/): T +public fun test(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferences.fir.kt b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferences.fir.kt new file mode 100644 index 00000000000..7e425beda63 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferences.fir.kt @@ -0,0 +1,12 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -CAST_NEVER_SUCCEEDS -UNUSED_VARIABLE + +class Foo +class P(x: K, y: T) + +val Foo.bar: Foo get() = this + +fun Foo.bar(x: String) = null as Foo + +fun main() { + val x: P.() -> Foo> = P("", Foo::bar) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferences.kt b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferences.kt new file mode 100644 index 00000000000..59af82211ba --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferences.kt @@ -0,0 +1,12 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -CAST_NEVER_SUCCEEDS -UNUSED_VARIABLE + +class Foo +class P(x: K, y: T) + +val Foo.bar: Foo get() = this + +fun Foo.bar(x: String) = null as Foo + +fun main() { + val x: P.() -> Foo> = P("", , Foo>")!>Foo::bar) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferences.txt b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferences.txt new file mode 100644 index 00000000000..1d5c63b4f43 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferences.txt @@ -0,0 +1,19 @@ +package + +public val Foo.bar: Foo +public fun main(): kotlin.Unit +public fun Foo.bar(/*0*/ x: kotlin.String): Foo + +public final class Foo { + public constructor Foo() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class P { + public constructor P(/*0*/ x: K, /*1*/ y: T) + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lackOfDeepIncorporation.fir.kt b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lackOfDeepIncorporation.fir.kt new file mode 100644 index 00000000000..d04749b5711 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lackOfDeepIncorporation.fir.kt @@ -0,0 +1,59 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_EXPRESSION -UNCHECKED_CAST -UNUSED_PARAMETER -UNUSED_ANONYMOUS_PARAMETER + +fun materialize(): T = null as T + +class Foo { + fun product(other: Foo<(A) -> B>) = materialize>() + + fun foo1(other1: Foo, function: (A, B) -> R) { + val x = product( + other1.product( + bar { b -> { a -> function(a, b) } } + ) + ) + x + } + + fun foo2(other1: Foo, other2: Foo, function: (A, B, C) -> R) { + val x = product( + other1.product( + other2.product( + bar { c -> { b -> { a -> function(a, b, c) } } } + ) + ) + ) + x + } + + fun foo3(other1: Foo, other2: Foo, other3: Foo, other4: Foo, function: (A, B, C, D) -> R) { + val x = product( + other1.product( + other2.product( + other3.product( + bar { d -> { c -> { b -> { a -> function(a, b, c, d) } } } } + ) + ) + ) + ) + x + } + + fun foo4(other1: Foo, other2: Foo, other3: Foo, other4: Foo, function: (A, B, C, D, E) -> R) { + val x = product( + other1.product( + other2.product( + other3.product( + other4.product( + bar { e -> { d -> { c -> { b -> { a -> function(a, b, c, d, e) } } } } } + ) + ) + ) + ) + ) + x + } + + companion object { + fun bar(x: A) = materialize>() + } +} diff --git a/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lackOfDeepIncorporation.kt b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lackOfDeepIncorporation.kt new file mode 100644 index 00000000000..b7ca2e10762 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lackOfDeepIncorporation.kt @@ -0,0 +1,59 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_EXPRESSION -UNCHECKED_CAST -UNUSED_PARAMETER -UNUSED_ANONYMOUS_PARAMETER + +fun materialize(): T = null as T + +class Foo { + fun product(other: Foo<(A) -> B>) = materialize>() + + fun foo1(other1: Foo, function: (A, B) -> R) { + val x = product( + other1.product( + bar { b -> { a -> function(a, b) } } + ) + ) + ")!>x + } + + fun foo2(other1: Foo, other2: Foo, function: (A, B, C) -> R) { + val x = product( + other1.product( + other2.product( + bar { c -> { b -> { a -> function(a, b, c) } } } + ) + ) + ) + ")!>x + } + + fun foo3(other1: Foo, other2: Foo, other3: Foo, other4: Foo, function: (A, B, C, D) -> R) { + val x = product( + other1.product( + other2.product( + other3.product( + bar { d -> { c -> { b -> { a -> function(a, b, c, d) } } } } + ) + ) + ) + ) + ")!>x + } + + fun foo4(other1: Foo, other2: Foo, other3: Foo, other4: Foo, function: (A, B, C, D, E) -> R) { + val x = product( + other1.product( + other2.product( + other3.product( + other4.product( + bar { e -> { d -> { c -> { b -> { a -> function(a, b, c, d, e) } } } } } + ) + ) + ) + ) + ) + ")!>x + } + + companion object { + fun bar(x: A) = materialize>() + } +} diff --git a/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lackOfDeepIncorporation.txt b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lackOfDeepIncorporation.txt new file mode 100644 index 00000000000..1c3f452d17d --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lackOfDeepIncorporation.txt @@ -0,0 +1,23 @@ +package + +public fun materialize(): T + +public final class Foo { + public constructor Foo() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo1(/*0*/ other1: Foo, /*1*/ function: (A, B) -> R): kotlin.Unit + public final fun foo2(/*0*/ other1: Foo, /*1*/ other2: Foo, /*2*/ function: (A, B, C) -> R): kotlin.Unit + public final fun foo3(/*0*/ other1: Foo, /*1*/ other2: Foo, /*2*/ other3: Foo, /*3*/ other4: Foo, /*4*/ function: (A, B, C, D) -> R): kotlin.Unit + public final fun foo4(/*0*/ other1: Foo, /*1*/ other2: Foo, /*2*/ other3: Foo, /*3*/ other4: Foo, /*4*/ function: (A, B, C, D, E) -> R): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final fun product(/*0*/ other: Foo<(A) -> B>): Foo + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public companion object Companion { + private constructor Companion() + public final fun bar(/*0*/ x: A): Foo + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} diff --git a/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lambdasInTryCatch.fir.kt b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lambdasInTryCatch.fir.kt new file mode 100644 index 00000000000..5a9e4dcb4c6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lambdasInTryCatch.fir.kt @@ -0,0 +1,41 @@ +// !DIAGNOSTICS: -UNUSED_LAMBDA_EXPRESSION, -UNUSED_EXPRESSION + +fun case1(x: Any){ + when (x){ + 1 -> try { {"1"}; ""; 1} catch (e: Exception) { { }} + "1" -> try { 1 } catch (e: Exception) { { }} + else -> try { 1 } catch (e: Exception) { {1 }} + } + + when (x){ + 1 -> try { {"1"}; ""} catch (e: Exception) { { }} + "1" -> try { 1 } catch (e: Exception) { { }} + else -> try { 1 } catch (e: Exception) { {1 }} + } +} + +fun case2(x: Any){ + when (x){ + 1 -> try { {"1"}; ""; TODO()} catch (e: Exception) { { }} + "1" -> try { 1 } catch (e: Exception) { { }} + else -> try { 1 } catch (e: Exception) { {1 }} + } + when (x){ + 1 -> try { {"1"}; ""; TODO(); ""} catch (e: Exception) { { }} + "1" -> try { 1 } catch (e: Exception) { { }} + else -> try { 1 } catch (e: Exception) { {1 }} + } + when (x){ + 1 -> try { {"1"}; ""; TODO()} catch (e: Exception) { { }} + "1" -> try { 1; "" } catch (e: Exception) { { }} + else -> try { 1 } catch (e: Exception) { {1 }} + } +} + +fun case3(x: Any){ + when (x){ + 1 -> try { {"1"}} catch (e: Exception) { { }} + "1" -> try { 1 } catch (e: Exception) { { }} + else -> try { 1 } catch (e: Exception) { {1 }} + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lambdasInTryCatch.kt b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lambdasInTryCatch.kt new file mode 100644 index 00000000000..c13aaabc66c --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lambdasInTryCatch.kt @@ -0,0 +1,41 @@ +// !DIAGNOSTICS: -UNUSED_LAMBDA_EXPRESSION, -UNUSED_EXPRESSION + +fun case1(x: Any){ + when (x){ + 1 -> try { {"1"}; ""; 1} catch (e: Exception) { { }} + "1" -> try { 1 } catch (e: Exception) { { }} + else -> try { 1 } catch (e: Exception) { {1 }} + } + + when (x){ + 1 -> try { {"1"}; ""} catch (e: Exception) { { }} + "1" -> try { 1 } catch (e: Exception) { { }} + else -> try { 1 } catch (e: Exception) { {1 }} + } +} + +fun case2(x: Any){ + when (x){ + 1 -> try { {"1"}; ""; TODO()} catch (e: Exception) { { }} + "1" -> try { 1 } catch (e: Exception) { { }} + else -> try { 1 } catch (e: Exception) { {1 }} + } + when (x){ + 1 -> try { {"1"}; ""; TODO(); ""} catch (e: Exception) { { }} + "1" -> try { 1 } catch (e: Exception) { { }} + else -> try { 1 } catch (e: Exception) { {1 }} + } + when (x){ + 1 -> try { {"1"}; ""; TODO()} catch (e: Exception) { { }} + "1" -> try { 1; "" } catch (e: Exception) { { }} + else -> try { 1 } catch (e: Exception) { {1 }} + } +} + +fun case3(x: Any){ + when (x){ + 1 -> try { {"1"}} catch (e: Exception) { { }} + "1" -> try { 1 } catch (e: Exception) { { }} + else -> try { 1 } catch (e: Exception) { {1 }} + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lambdasInTryCatch.txt b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lambdasInTryCatch.txt new file mode 100644 index 00000000000..874653577c5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lambdasInTryCatch.txt @@ -0,0 +1,5 @@ +package + +public fun case1(/*0*/ x: kotlin.Any): kotlin.Unit +public fun case2(/*0*/ x: kotlin.Any): kotlin.Unit +public fun case3(/*0*/ x: kotlin.Any): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/inference/extensionLambdasAndArrow.fir.kt b/compiler/testData/diagnostics/tests/inference/extensionLambdasAndArrow.fir.kt index 1c13bf01c69..1e63cd4b0d3 100644 --- a/compiler/testData/diagnostics/tests/inference/extensionLambdasAndArrow.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/extensionLambdasAndArrow.fir.kt @@ -7,6 +7,8 @@ fun main() { val x2: String.() -> String = if (true) {{ -> this }} else {{ -> this }} val x3: () -> String = if (true) {{ -> "this" }} else {{ -> "this" }} val x4: String.() -> String = if (true) {{ str: String -> "this" }} else {{ str: String -> "this" }} + val x41: String.(String) -> String = if (true) {{ str: String, str2: String -> "this" }} else {{ str: String, str2: String -> "this" }} + val x42: String.(String) -> String = if (true) {{ str, str2 -> "this" }} else {{ str, str2 -> "this" }} val x5: String.() -> String = if (true) {{ str -> "this" }} else {{ str -> "this" }} val x6: String.() -> String = if (true) {{ str -> "this" }} else {{ "this" }} val x7: String.() -> String = select({ -> this }, { -> this }) diff --git a/compiler/testData/diagnostics/tests/inference/extensionLambdasAndArrow.kt b/compiler/testData/diagnostics/tests/inference/extensionLambdasAndArrow.kt index 1f8163060a5..1325bf3cbed 100644 --- a/compiler/testData/diagnostics/tests/inference/extensionLambdasAndArrow.kt +++ b/compiler/testData/diagnostics/tests/inference/extensionLambdasAndArrow.kt @@ -7,6 +7,8 @@ fun main() { val x2: String.() -> String = if (true) {{ -> this }} else {{ -> this }} val x3: () -> String = if (true) {{ -> "this" }} else {{ -> "this" }} val x4: String.() -> String = if (true) {{ str: String -> "this" }} else {{ str: String -> "this" }} + val x41: String.(String) -> String = if (true) {{ str: String, str2: String -> "this" }} else {{ str: String, str2: String -> "this" }} + val x42: String.(String) -> String = if (true) {{ str, str2 -> "this" }} else {{ str, str2 -> "this" }} val x5: String.() -> String = if (true) {{ str -> "this" }} else {{ str -> "this" }} val x6: String.() -> String = if (true) {{ str -> "this" }} else {{ "this" }} val x7: String.() -> String = select({ -> this }, { -> this }) diff --git a/compiler/testData/diagnostics/tests/resolve/resolveWithSpecifiedFunctionLiteralWithId.kt b/compiler/testData/diagnostics/tests/resolve/resolveWithSpecifiedFunctionLiteralWithId.kt index 626182c5195..67b4400b312 100644 --- a/compiler/testData/diagnostics/tests/resolve/resolveWithSpecifiedFunctionLiteralWithId.kt +++ b/compiler/testData/diagnostics/tests/resolve/resolveWithSpecifiedFunctionLiteralWithId.kt @@ -22,11 +22,11 @@ fun foo(i: Int, f: (Int) -> Int) = f(i) fun id(t: T) = t fun test() { - foo(1, id(fun(x1: Int) = - foo(2, id(fun(x2: Int) = - foo(3, id(fun(x3: Int) = - foo(4, id(fun(x4: Int) = - foo(5, id(fun(x5: Int) = + foo(1, id(fun(x1: Int) = + foo(2, id(fun(x2: Int) = + foo(3, id(fun(x3: Int) = + foo(4, id(fun(x4: Int) = + foo(5, id(fun(x5: Int) = x1 + x2 + x3 + x4 + x5 + A.iii )) )) diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/callableReferences.fir.fail b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/callableReferences.fir.fail new file mode 100644 index 00000000000..1f87de94e93 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/callableReferences.fir.fail @@ -0,0 +1,8 @@ +Failures detected in FirBodyResolveTransformerAdapter, file: /callableReferences.fir.kt +Cause: java.lang.RuntimeException: While resolving call R?C|/selectNumber|(R?C|/id|(::R?C|/foo6|), R?C|/id|( = id@fun .(x: ): { + x# +} +), R?C|/id|( = id@fun .(): { + it# +} +)) diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/callableReferences.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/callableReferences.fir.kt new file mode 100644 index 00000000000..96b52e45bb3 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/callableReferences.fir.kt @@ -0,0 +1,57 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_ANONYMOUS_PARAMETER + +import kotlin.reflect.* + +fun id(x: T) = x + +fun select(vararg x: T) = x[0] + +fun foo(x: Int) {} +fun foo2(x: Number) {} + +fun foo3(x: Int) {} +fun foo3(x: Number) {} + +interface A +interface B + +fun foo4(x: A) {} +fun foo4(x: B) {} + +interface C: A, B + +fun foo5(x: A) {} +fun foo5(x: B) {} +fun foo5(x: C) {} + +fun Unit> selectNumber(vararg x: T) = x[0] + +fun foo6(x: Int) {} +fun foo6(x: Float) {} +fun foo6(x: Number) {} + +fun main() { + select(::foo, { it }) + select(id(::foo), { x: Number -> }, { it }) + + val x1 = select(")!>id(::foo), kotlin.Number")!>id { x: Number -> x }) + kotlin.Any")!>x1 + + val x11 = select(id(::foo), id { x: Number -> }, id { it }) + kotlin.Any")!>x11 + + select(id(::foo2), id { x: Int -> }, id { it }) + + select(id(")!>::foo3), id { x: Int -> }, id { it }) + select(id(")!>::foo3), id { x: Number -> }, id { it }) + select(id(")!>::foo3), id { x: Number -> }, id { x: Int -> }, id { it }) + + select(id(")!>::foo4), id { x: A -> }, id { it }) + select(id(")!>::foo4), id { x: B -> }, id { it }) + // Expected ambiguity + select(id(::foo4), id { x: A -> }, id { x: B -> }, id { it }) + + select(id(::foo5), id { x: A -> }, id { x: B -> }, id { it }) + + val x2: (Int) -> Unit = selectNumber(id(")!>::foo6), id { x -> & Number}")!>x }, id { & Number}")!>it }) +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/callableReferences.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/callableReferences.kt new file mode 100644 index 00000000000..96b52e45bb3 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/callableReferences.kt @@ -0,0 +1,57 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_ANONYMOUS_PARAMETER + +import kotlin.reflect.* + +fun id(x: T) = x + +fun select(vararg x: T) = x[0] + +fun foo(x: Int) {} +fun foo2(x: Number) {} + +fun foo3(x: Int) {} +fun foo3(x: Number) {} + +interface A +interface B + +fun foo4(x: A) {} +fun foo4(x: B) {} + +interface C: A, B + +fun foo5(x: A) {} +fun foo5(x: B) {} +fun foo5(x: C) {} + +fun Unit> selectNumber(vararg x: T) = x[0] + +fun foo6(x: Int) {} +fun foo6(x: Float) {} +fun foo6(x: Number) {} + +fun main() { + select(::foo, { it }) + select(id(::foo), { x: Number -> }, { it }) + + val x1 = select(")!>id(::foo), kotlin.Number")!>id { x: Number -> x }) + kotlin.Any")!>x1 + + val x11 = select(id(::foo), id { x: Number -> }, id { it }) + kotlin.Any")!>x11 + + select(id(::foo2), id { x: Int -> }, id { it }) + + select(id(")!>::foo3), id { x: Int -> }, id { it }) + select(id(")!>::foo3), id { x: Number -> }, id { it }) + select(id(")!>::foo3), id { x: Number -> }, id { x: Int -> }, id { it }) + + select(id(")!>::foo4), id { x: A -> }, id { it }) + select(id(")!>::foo4), id { x: B -> }, id { it }) + // Expected ambiguity + select(id(::foo4), id { x: A -> }, id { x: B -> }, id { it }) + + select(id(::foo5), id { x: A -> }, id { x: B -> }, id { it }) + + val x2: (Int) -> Unit = selectNumber(id(")!>::foo6), id { x -> & Number}")!>x }, id { & Number}")!>it }) +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/callableReferences.txt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/callableReferences.txt new file mode 100644 index 00000000000..ef2fa367a5a --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/callableReferences.txt @@ -0,0 +1,36 @@ +package + +public fun foo(/*0*/ x: kotlin.Int): kotlin.Unit +public fun foo2(/*0*/ x: kotlin.Number): kotlin.Unit +public fun foo3(/*0*/ x: kotlin.Int): kotlin.Unit +public fun foo3(/*0*/ x: kotlin.Number): kotlin.Unit +public fun foo4(/*0*/ x: A): kotlin.Unit +public fun foo4(/*0*/ x: B): kotlin.Unit +public fun foo5(/*0*/ x: A): kotlin.Unit +public fun foo5(/*0*/ x: B): kotlin.Unit +public fun foo5(/*0*/ x: C): kotlin.Unit +public fun foo6(/*0*/ x: kotlin.Float): kotlin.Unit +public fun foo6(/*0*/ x: kotlin.Int): kotlin.Unit +public fun foo6(/*0*/ x: kotlin.Number): kotlin.Unit +public fun id(/*0*/ x: T): T +public fun main(): kotlin.Unit +public fun select(/*0*/ vararg x: T /*kotlin.Array*/): T +public fun kotlin.Unit> selectNumber(/*0*/ vararg x: T /*kotlin.Array*/): T + +public interface A { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface B { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface C : A, B { + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/complexInterdependentInputOutputTypes.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/complexInterdependentInputOutputTypes.fir.kt new file mode 100644 index 00000000000..f7f948f907a --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/complexInterdependentInputOutputTypes.fir.kt @@ -0,0 +1,41 @@ +// !DIAGNOSTICS: -CAST_NEVER_SUCCEEDS -UNUSED_PARAMETER + +interface AssertionPlant +interface AssertionPlantNullable: BaseAssertionPlant> +interface BaseAssertionPlant> + +interface BaseCollectingAssertionPlant, out C : BaseCollectingAssertionPlant> : BaseAssertionPlant + +interface CreatorLike, C : BaseCollectingAssertionPlant> + +interface ParameterObjectOption { + fun withParameterObjectNullable( + parameterObject: ParameterObject + ) = null as CreatorNullable +} + +class ParameterObject +interface CollectingAssertionPlantNullable : AssertionPlantNullable, + BaseCollectingAssertionPlant, CollectingAssertionPlantNullable> +interface CreatorNullable: CreatorLike, CollectingAssertionPlantNullable> + +fun , C : BaseCollectingAssertionPlant> contains( + pairs: List>, + parameterObjectOption: (ParameterObjectOption, K) -> CreatorLike, V, A, C>, + assertionCreator: C.(M) -> Unit +) {} + +private fun createGetParameterObject( + plant: AssertionPlant>, + key: K +) = null as ParameterObject, V> + +private fun containsNullable( + plant: AssertionPlant>, + pairs: List>, + assertionCreator: AssertionPlantNullable.(M) -> Unit +) = contains( + pairs, + { option, key -> option.withParameterObjectNullable(createGetParameterObject(plant, key)) }, + assertionCreator +) \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/complexInterdependentInputOutputTypes.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/complexInterdependentInputOutputTypes.kt new file mode 100644 index 00000000000..bd1544a44e4 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/complexInterdependentInputOutputTypes.kt @@ -0,0 +1,41 @@ +// !DIAGNOSTICS: -CAST_NEVER_SUCCEEDS -UNUSED_PARAMETER + +interface AssertionPlant +interface AssertionPlantNullable: BaseAssertionPlant> +interface BaseAssertionPlant> + +interface BaseCollectingAssertionPlant, out C : BaseCollectingAssertionPlant> : BaseAssertionPlant + +interface CreatorLike, C : BaseCollectingAssertionPlant> + +interface ParameterObjectOption { + fun withParameterObjectNullable( + parameterObject: ParameterObject + ) = null as CreatorNullable +} + +class ParameterObject +interface CollectingAssertionPlantNullable : AssertionPlantNullable, + BaseCollectingAssertionPlant, CollectingAssertionPlantNullable> +interface CreatorNullable: CreatorLike, CollectingAssertionPlantNullable> + +fun , C : BaseCollectingAssertionPlant> contains( + pairs: List>, + parameterObjectOption: (ParameterObjectOption, K) -> CreatorLike, V, A, C>, + assertionCreator: C.(M) -> Unit +) {} + +private fun createGetParameterObject( + plant: AssertionPlant>, + key: K +) = null as ParameterObject, V> + +private fun containsNullable( + plant: AssertionPlant>, + pairs: List>, + assertionCreator: AssertionPlantNullable.(M) -> Unit +) = contains( + pairs, + { option, key -> , V?>")!>option.withParameterObjectNullable(createGetParameterObject(plant, key)) }, + assertionCreator +) diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/complexInterdependentInputOutputTypes.txt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/complexInterdependentInputOutputTypes.txt new file mode 100644 index 00000000000..d510e40341d --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/complexInterdependentInputOutputTypes.txt @@ -0,0 +1,61 @@ +package + +public fun , /*4*/ C : BaseCollectingAssertionPlant> contains(/*0*/ pairs: kotlin.collections.List>, /*1*/ parameterObjectOption: (ParameterObjectOption, K) -> CreatorLike, V, A, C>, /*2*/ assertionCreator: C.(M) -> kotlin.Unit): kotlin.Unit +private fun containsNullable(/*0*/ plant: AssertionPlant>, /*1*/ pairs: kotlin.collections.List>, /*2*/ assertionCreator: AssertionPlantNullable.(M) -> kotlin.Unit): kotlin.Unit +private fun createGetParameterObject(/*0*/ plant: AssertionPlant>, /*1*/ key: K): ParameterObject, V> + +public interface AssertionPlant { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface AssertionPlantNullable : BaseAssertionPlant> { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface BaseAssertionPlant> { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface BaseCollectingAssertionPlant, /*2*/ out C : BaseCollectingAssertionPlant> : BaseAssertionPlant { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface CollectingAssertionPlantNullable : AssertionPlantNullable, BaseCollectingAssertionPlant, CollectingAssertionPlantNullable> { + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface CreatorLike, /*3*/ C : BaseCollectingAssertionPlant> { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface CreatorNullable : CreatorLike, CollectingAssertionPlantNullable> { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class ParameterObject { + public constructor ParameterObject() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface ParameterObjectOption { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + public open fun withParameterObjectNullable(/*0*/ parameterObject: ParameterObject): CreatorNullable +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/deepLambdas.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/deepLambdas.kt new file mode 100644 index 00000000000..63bcaf24468 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/deepLambdas.kt @@ -0,0 +1,14 @@ +// FIR_IDENTICAL +// !DIAGNOSTICS: -UNUSED_PARAMETER + +fun useList(list: List) { } +fun useMap(map: Map) { } + +enum class Color {BLUE} + +fun Color.grayValue() {} + +fun test() { + useList(listOf({ x: Color -> x.grayValue() }, Color.BLUE)) + useMap(mapOf("a" to { x: Color -> x.grayValue() }, "b" to Color.BLUE)) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/deepLambdas.txt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/deepLambdas.txt new file mode 100644 index 00000000000..6711ef7dbab --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/deepLambdas.txt @@ -0,0 +1,25 @@ +package + +public fun test(): kotlin.Unit +public fun useList(/*0*/ list: kotlin.collections.List): kotlin.Unit +public fun useMap(/*0*/ map: kotlin.collections.Map): kotlin.Unit +public fun Color.grayValue(): kotlin.Unit + +public final enum class Color : kotlin.Enum { + enum entry BLUE + + private constructor Color() + public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: Color): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected/*protected and package*/ final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun finalize(): kotlin.Unit + public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class! + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): Color + public final /*synthesized*/ fun values(): kotlin.Array +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixIndependentVariables.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixIndependentVariables.fir.kt new file mode 100644 index 00000000000..404707f49d8 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixIndependentVariables.fir.kt @@ -0,0 +1,11 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNCHECKED_CAST + +fun foo(items: List, handler: (T) -> Unit) {} + +class Foo(x: T) + +fun materialize(): T = null as T + +fun main(x: List?) { + foo(x?.map { Foo(it) } ?: listOf(materialize>())) {} +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixIndependentVariables.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixIndependentVariables.kt new file mode 100644 index 00000000000..09f17724e5b --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixIndependentVariables.kt @@ -0,0 +1,11 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNCHECKED_CAST + +fun foo(items: List, handler: (T) -> Unit) {} + +class Foo(x: T) + +fun materialize(): T = null as T + +fun main(x: List?) { + foo(x?.map { Foo(it) } ?: listOf(materialize>())) {} +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixIndependentVariables.txt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixIndependentVariables.txt new file mode 100644 index 00000000000..12903f8bc8f --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixIndependentVariables.txt @@ -0,0 +1,12 @@ +package + +public fun foo(/*0*/ items: kotlin.collections.List, /*1*/ handler: (T) -> kotlin.Unit): kotlin.Unit +public fun main(/*0*/ x: kotlin.collections.List?): kotlin.Unit +public fun materialize(): T + +public final class Foo { + public constructor Foo(/*0*/ x: T) + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixInputTypeToMoreSpecificType.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixInputTypeToMoreSpecificType.kt new file mode 100644 index 00000000000..e1b3d9f6847 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixInputTypeToMoreSpecificType.kt @@ -0,0 +1,10 @@ +// FIR_IDENTICAL +// !DIAGNOSTICS: -CAST_NEVER_SUCCEEDS -UNUSED_PARAMETER -UNCHECKED_CAST + +fun materialize() = null as T + +val x: Map = materialize>>().fold(mutableMapOf()) { m, x -> + val (s, action) = x.entries.first() + m[s] = action + m +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixInputTypeToMoreSpecificType.txt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixInputTypeToMoreSpecificType.txt new file mode 100644 index 00000000000..6b8be614736 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixInputTypeToMoreSpecificType.txt @@ -0,0 +1,4 @@ +package + +public val x: kotlin.collections.Map +public fun materialize(): T diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixReceiverToMoreSpecificType.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixReceiverToMoreSpecificType.kt new file mode 100644 index 00000000000..4f8d5dfe6bc --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixReceiverToMoreSpecificType.kt @@ -0,0 +1,9 @@ +// FIR_IDENTICAL +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_ANONYMOUS_PARAMETER + +fun xSelectButton2(items: (matcher: String) -> List, handler: T.() -> Unit) {} + +fun main() { + val x: List? = listOf() + xSelectButton2({ matcher -> x ?: emptyList() }) { this.inv() } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixReceiverToMoreSpecificType.txt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixReceiverToMoreSpecificType.txt new file mode 100644 index 00000000000..5e1946ce58e --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixReceiverToMoreSpecificType.txt @@ -0,0 +1,4 @@ +package + +public fun main(): kotlin.Unit +public fun xSelectButton2(/*0*/ items: (matcher: kotlin.String) -> kotlin.collections.List, /*1*/ handler: T.() -> kotlin.Unit): kotlin.Unit diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/moreSpecificOutputType.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/moreSpecificOutputType.kt new file mode 100644 index 00000000000..3a1dca5dc0b --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/moreSpecificOutputType.kt @@ -0,0 +1,6 @@ +// FIR_IDENTICAL +interface ObservableSet : Set {} + +fun test(x: List>) { + x.reduce { acc: Set, set: Set -> acc + set } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/moreSpecificOutputType.txt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/moreSpecificOutputType.txt new file mode 100644 index 00000000000..4480615899f --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/moreSpecificOutputType.txt @@ -0,0 +1,14 @@ +package + +public fun test(/*0*/ x: kotlin.collections.List>): kotlin.Unit + +public interface ObservableSet : kotlin.collections.Set { + public abstract override /*1*/ /*fake_override*/ val size: kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun contains(/*0*/ element: T): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun containsAll(/*0*/ elements: kotlin.collections.Collection): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun isEmpty(): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun iterator(): kotlin.collections.Iterator + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/rerunStagesAfterFixationInFullMode.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/rerunStagesAfterFixationInFullMode.kt new file mode 100644 index 00000000000..dc288332212 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/rerunStagesAfterFixationInFullMode.kt @@ -0,0 +1,12 @@ +// FIR_IDENTICAL +// !DIAGNOSTICS: -UNUSED_PARAMETER + +class ArraySortedChecker(val array: A, val comparator: Comparator) { + fun checkSorted(sorted: A.() -> R, sortedDescending: A.() -> R, iterator: R.() -> Iterator) {} +} + +fun > arrayData(vararg values: T, toArray: Array.() -> A) = ArraySortedChecker(values.toArray(), naturalOrder()) + +fun main() { + with (arrayData("ac", "aD", "aba") { toList().toTypedArray() }) {} +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/rerunStagesAfterFixationInFullMode.txt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/rerunStagesAfterFixationInFullMode.txt new file mode 100644 index 00000000000..627692d7763 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/rerunStagesAfterFixationInFullMode.txt @@ -0,0 +1,14 @@ +package + +public fun > arrayData(/*0*/ vararg values: T /*kotlin.Array*/, /*1*/ toArray: kotlin.Array.() -> A): ArraySortedChecker +public fun main(): kotlin.Unit + +public final class ArraySortedChecker { + public constructor ArraySortedChecker(/*0*/ array: A, /*1*/ comparator: kotlin.Comparator /* = java.util.Comparator */) + public final val array: A + public final val comparator: kotlin.Comparator /* = java.util.Comparator */ + public final fun checkSorted(/*0*/ sorted: A.() -> R, /*1*/ sortedDescending: A.() -> R, /*2*/ iterator: R.() -> kotlin.collections.Iterator): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/rerunStagesAfterFixationInPartialMode.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/rerunStagesAfterFixationInPartialMode.kt new file mode 100644 index 00000000000..64d4b84132b --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/rerunStagesAfterFixationInPartialMode.kt @@ -0,0 +1,27 @@ +// FIR_IDENTICAL +// !DIAGNOSTICS: -UNUSED_PARAMETER + +class Wrapper (val x: T) + +inline fun Wrapper.unwrap(validator: (T) -> R): R = validator(x) + +fun select(x: T) {} + +class Foo(y: Wrapper>) { + fun MutableCollection.foo(x: T) {} + fun MutableCollection.foo(x: Iterable) {} + + init { + /* + * Before the fix, `foo` can't be disambiguated because the lambda is alanyzed in full mode, + * not in partial as before the inroducing new posponed arguments analysis. + * + * It happens due to the lack of rerun stages after fixation variables + * (stage 5 – see `fixVariablesOrReportNotEnoughInformation` in `KotlinConstraintSystemCompleter.kt`). + * Rerun is need as fixation of variables can be make lambda available for analysis. + * + * TODO: add tests with lambdas, which can't be analyzed in partial mode, but if can, the code will be successfully resolved. + */ + ArrayList().foo(y.unwrap { it }) + } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/rerunStagesAfterFixationInPartialMode.txt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/rerunStagesAfterFixationInPartialMode.txt new file mode 100644 index 00000000000..6c43da90cf9 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/rerunStagesAfterFixationInPartialMode.txt @@ -0,0 +1,21 @@ +package + +public fun select(/*0*/ x: T): kotlin.Unit +public inline fun Wrapper.unwrap(/*0*/ validator: (T) -> R): R + +public final class Foo { + public constructor Foo(/*0*/ y: Wrapper>) + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + public final fun kotlin.collections.MutableCollection.foo(/*0*/ x: T): kotlin.Unit + public final fun kotlin.collections.MutableCollection.foo(/*0*/ x: kotlin.collections.Iterable): kotlin.Unit +} + +public final class Wrapper { + public constructor Wrapper(/*0*/ x: T) + public final val x: T + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/suspendFunctions.fir.fail b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/suspendFunctions.fir.fail new file mode 100644 index 00000000000..5ff560136a0 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/suspendFunctions.fir.fail @@ -0,0 +1,8 @@ +Failures detected in FirBodyResolveTransformerAdapter, file: /suspendFunctions.fir.kt +Cause: java.lang.RuntimeException: While resolving call R?C|/takeSuspend|(R?C|/id|( = id@fun .(): { + it# +} +), takeSuspend@fun (x: R|kotlin/Int|): R|kotlin/Unit| { + ^ R|/x| +} +) diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/suspendFunctions.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/suspendFunctions.fir.kt new file mode 100644 index 00000000000..770c150974a --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/suspendFunctions.fir.kt @@ -0,0 +1,25 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_ANONYMOUS_PARAMETER + +fun select(vararg x: T) = x[0] + +fun id(x: K): K = x + +fun Unit, K: T> takeSuspend(x: T, y: K) = x +fun Unit, K: T> takeSimpleFunction(x: T, y: K) = x + +fun main() { + select(suspend {}, kotlin.Unit")!>{}) + select( kotlin.Unit")!>{}, suspend {}) + select( kotlin.Unit")!>id {}, suspend {}) + select( kotlin.Unit")!>id {}, id(suspend {})) + select( kotlin.Unit")!>id {}, id Unit> {}) + + takeSuspend( kotlin.Unit")!>id { it }, kotlin.Unit")!>{ x -> x }) + + val x1: suspend (Int) -> Unit = takeSuspend( kotlin.Unit")!>id { it }, kotlin.Unit")!>{ x -> x }) + + // Here, the error should be + val x2: (Int) -> Unit = takeSuspend( kotlin.Unit")!>id { it }, kotlin.Unit"), TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH!>{ x -> x }) + val x3: suspend (Int) -> Unit = takeSimpleFunction( kotlin.Unit")!>id { it }, kotlin.Unit"), TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH!>{ x -> x }) + val x4: (Int) -> Unit = takeSimpleFunction(id Unit> {}, kotlin.Unit"), TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH!>{}) +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/suspendFunctions.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/suspendFunctions.kt new file mode 100644 index 00000000000..770c150974a --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/suspendFunctions.kt @@ -0,0 +1,25 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_ANONYMOUS_PARAMETER + +fun select(vararg x: T) = x[0] + +fun id(x: K): K = x + +fun Unit, K: T> takeSuspend(x: T, y: K) = x +fun Unit, K: T> takeSimpleFunction(x: T, y: K) = x + +fun main() { + select(suspend {}, kotlin.Unit")!>{}) + select( kotlin.Unit")!>{}, suspend {}) + select( kotlin.Unit")!>id {}, suspend {}) + select( kotlin.Unit")!>id {}, id(suspend {})) + select( kotlin.Unit")!>id {}, id Unit> {}) + + takeSuspend( kotlin.Unit")!>id { it }, kotlin.Unit")!>{ x -> x }) + + val x1: suspend (Int) -> Unit = takeSuspend( kotlin.Unit")!>id { it }, kotlin.Unit")!>{ x -> x }) + + // Here, the error should be + val x2: (Int) -> Unit = takeSuspend( kotlin.Unit")!>id { it }, kotlin.Unit"), TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH!>{ x -> x }) + val x3: suspend (Int) -> Unit = takeSimpleFunction( kotlin.Unit")!>id { it }, kotlin.Unit"), TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH!>{ x -> x }) + val x4: (Int) -> Unit = takeSimpleFunction(id Unit> {}, kotlin.Unit"), TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH!>{}) +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/suspendFunctions.txt b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/suspendFunctions.txt new file mode 100644 index 00000000000..99fb856af18 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/suspendFunctions.txt @@ -0,0 +1,7 @@ +package + +public fun id(/*0*/ x: K): K +public fun main(): kotlin.Unit +public fun select(/*0*/ vararg x: T /*kotlin.Array*/): T +public fun kotlin.Unit, /*1*/ K : T> takeSimpleFunction(/*0*/ x: T, /*1*/ y: K): T +public fun kotlin.Unit, /*1*/ K : T> takeSuspend(/*0*/ x: T, /*1*/ y: K): T diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-2/pos/1.1.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-2/pos/1.1.kt index e9e07e0684e..0dbda849b22 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-2/pos/1.1.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-2/pos/1.1.kt @@ -118,13 +118,12 @@ fun case_8(value_1: Int, value_2: Int) = when { /* * TESTCASE NUMBER: 9 - * UNEXPECTED BEHAVIOUR * ISSUES: KT-37249 */ fun case_9(value_1: Int, value_2: String, value_3: String) = when { - value_1 == 1 -> try { 4 } catch (e: Exception) { 5 } - value_1 == 2 -> try { throw Exception() } catch (e: Exception) { value_2 } - else -> try { throw Exception() } catch (e: Exception) { {value_3} } finally { } + value_1 == 1 -> try { 4 } catch (e: Exception) { 5 } + value_1 == 2 -> try { throw Exception() } catch (e: Exception) { value_2 } + else -> try { throw Exception() } catch (e: Exception) { {value_3} } finally { } } // TESTCASE NUMBER: 10 diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-5/pos/1.1.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-5/pos/1.1.kt index 8b4c81115e1..61840798d07 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-5/pos/1.1.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-5/pos/1.1.kt @@ -119,14 +119,13 @@ fun case_8(value_1: Int, value_2: Int) = when (value_1) { /* * TESTCASE NUMBER: 9 - * UNEXPECTED BEHAVIOUR * ISSUES: KT-37249 */ fun case_9(value_1: Int, value_2: String, value_3: String): Any { return when (value_1) { 1 -> try { 4 } catch (e: Exception) { 5 } 2 -> try { throw Exception() } catch (e: Exception) { value_2 } - else -> try { throw Exception() } catch (e: Exception) { {value_3} } finally { } + else -> try { throw Exception() } catch (e: Exception) { {value_3} } finally { } } } diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 0e30e57079d..94fe68ce5e0 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -10748,6 +10748,44 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTestWithFirVali public void testWithExact() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/completion/withExact.kt"); } + + @TestMetadata("compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class PostponedArgumentsAnalysis extends AbstractDiagnosticsTestWithFirValidation { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInPostponedArgumentsAnalysis() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("basic.kt") + public void testBasic() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.kt"); + } + + @TestMetadata("callableReferenceLambdaCombinationInsideCall.kt") + public void testCallableReferenceLambdaCombinationInsideCall() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferenceLambdaCombinationInsideCall.kt"); + } + + @TestMetadata("callableReferences.kt") + public void testCallableReferences() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferences.kt"); + } + + @TestMetadata("lackOfDeepIncorporation.kt") + public void testLackOfDeepIncorporation() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lackOfDeepIncorporation.kt"); + } + + @TestMetadata("lambdasInTryCatch.kt") + public void testLambdasInTryCatch() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lambdasInTryCatch.kt"); + } + } } @TestMetadata("compiler/testData/diagnostics/tests/inference/constraints") diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index 11de66aa3f4..ca2455fc8d8 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -3069,6 +3069,82 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW } } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/inference/completion") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Completion extends AbstractDiagnosticsTestWithStdLib { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInCompletion() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/inference/completion"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class PostponedArgumentsAnalysis extends AbstractDiagnosticsTestWithStdLib { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInPostponedArgumentsAnalysis() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("callableReferences.kt") + public void testCallableReferences() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/callableReferences.kt"); + } + + @TestMetadata("complexInterdependentInputOutputTypes.kt") + public void testComplexInterdependentInputOutputTypes() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/complexInterdependentInputOutputTypes.kt"); + } + + @TestMetadata("deepLambdas.kt") + public void testDeepLambdas() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/deepLambdas.kt"); + } + + @TestMetadata("fixIndependentVariables.kt") + public void testFixIndependentVariables() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixIndependentVariables.kt"); + } + + @TestMetadata("fixInputTypeToMoreSpecificType.kt") + public void testFixInputTypeToMoreSpecificType() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixInputTypeToMoreSpecificType.kt"); + } + + @TestMetadata("fixReceiverToMoreSpecificType.kt") + public void testFixReceiverToMoreSpecificType() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixReceiverToMoreSpecificType.kt"); + } + + @TestMetadata("moreSpecificOutputType.kt") + public void testMoreSpecificOutputType() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/moreSpecificOutputType.kt"); + } + + @TestMetadata("rerunStagesAfterFixationInFullMode.kt") + public void testRerunStagesAfterFixationInFullMode() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/rerunStagesAfterFixationInFullMode.kt"); + } + + @TestMetadata("rerunStagesAfterFixationInPartialMode.kt") + public void testRerunStagesAfterFixationInPartialMode() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/rerunStagesAfterFixationInPartialMode.kt"); + } + + @TestMetadata("suspendFunctions.kt") + public void testSuspendFunctions() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/suspendFunctions.kt"); + } + } + } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/inference/delegates") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java index 5742ead30a2..e8000fe3c24 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java @@ -3069,6 +3069,82 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno } } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/inference/completion") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Completion extends AbstractDiagnosticsTestWithStdLibUsingJavac { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInCompletion() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/inference/completion"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class PostponedArgumentsAnalysis extends AbstractDiagnosticsTestWithStdLibUsingJavac { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInPostponedArgumentsAnalysis() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("callableReferences.kt") + public void testCallableReferences() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/callableReferences.kt"); + } + + @TestMetadata("complexInterdependentInputOutputTypes.kt") + public void testComplexInterdependentInputOutputTypes() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/complexInterdependentInputOutputTypes.kt"); + } + + @TestMetadata("deepLambdas.kt") + public void testDeepLambdas() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/deepLambdas.kt"); + } + + @TestMetadata("fixIndependentVariables.kt") + public void testFixIndependentVariables() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixIndependentVariables.kt"); + } + + @TestMetadata("fixInputTypeToMoreSpecificType.kt") + public void testFixInputTypeToMoreSpecificType() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixInputTypeToMoreSpecificType.kt"); + } + + @TestMetadata("fixReceiverToMoreSpecificType.kt") + public void testFixReceiverToMoreSpecificType() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/fixReceiverToMoreSpecificType.kt"); + } + + @TestMetadata("moreSpecificOutputType.kt") + public void testMoreSpecificOutputType() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/moreSpecificOutputType.kt"); + } + + @TestMetadata("rerunStagesAfterFixationInFullMode.kt") + public void testRerunStagesAfterFixationInFullMode() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/rerunStagesAfterFixationInFullMode.kt"); + } + + @TestMetadata("rerunStagesAfterFixationInPartialMode.kt") + public void testRerunStagesAfterFixationInPartialMode() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/rerunStagesAfterFixationInPartialMode.kt"); + } + + @TestMetadata("suspendFunctions.kt") + public void testSuspendFunctions() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/completion/postponedArgumentsAnalysis/suspendFunctions.kt"); + } + } + } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/inference/delegates") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 70cc96ecf19..5fd00971eb5 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -10743,6 +10743,44 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing public void testWithExact() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/completion/withExact.kt"); } + + @TestMetadata("compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class PostponedArgumentsAnalysis extends AbstractDiagnosticsUsingJavacTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInPostponedArgumentsAnalysis() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("basic.kt") + public void testBasic() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/basic.kt"); + } + + @TestMetadata("callableReferenceLambdaCombinationInsideCall.kt") + public void testCallableReferenceLambdaCombinationInsideCall() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferenceLambdaCombinationInsideCall.kt"); + } + + @TestMetadata("callableReferences.kt") + public void testCallableReferences() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/callableReferences.kt"); + } + + @TestMetadata("lackOfDeepIncorporation.kt") + public void testLackOfDeepIncorporation() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lackOfDeepIncorporation.kt"); + } + + @TestMetadata("lambdasInTryCatch.kt") + public void testLambdasInTryCatch() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/postponedArgumentsAnalysis/lambdasInTryCatch.kt"); + } + } } @TestMetadata("compiler/testData/diagnostics/tests/inference/constraints") diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java index 7d5553ed7d0..870f412722e 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java @@ -442,6 +442,11 @@ public abstract class KotlinBuiltIns { return "Function" + parameterCount; } + @NotNull + private static FqNameUnsafe getKFunctionFqName(int parameterCount) { + return FqNames.reflect(FunctionClassDescriptor.Kind.KFunction.getClassNamePrefix() + parameterCount); + } + @NotNull public static ClassId getFunctionClassId(int parameterCount) { return new ClassId(BUILT_INS_PACKAGE_FQ_NAME, Name.identifier(getFunctionName(parameterCount))); @@ -467,6 +472,17 @@ public abstract class KotlinBuiltIns { return getBuiltInClassByFqName(COROUTINES_PACKAGE_FQ_NAME_RELEASE.child(Name.identifier(getSuspendFunctionName(parameterCount)))); } + @NotNull + public ClassDescriptor getKFunction(int parameterCount) { + return getBuiltInClassByFqName(getKFunctionFqName(parameterCount).toSafe()); + } + + @NotNull + public ClassDescriptor getKSuspendFunction(int parameterCount) { + Name name = Name.identifier(FunctionClassDescriptor.Kind.KSuspendFunction.getClassNamePrefix() + parameterCount); + return getBuiltInClassByFqName(COROUTINES_PACKAGE_FQ_NAME_RELEASE.child(name)); + } + @NotNull public ClassDescriptor getThrowable() { return getBuiltInClassByName("Throwable"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/functionTypes.kt b/core/descriptors/src/org/jetbrains/kotlin/builtins/functionTypes.kt index d0d763391ed..fa6e67519b0 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/functionTypes.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/functionTypes.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.annotations.BuiltInAnnotationDescriptor +import org.jetbrains.kotlin.descriptors.annotations.FilteredAnnotations import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.name.Name @@ -19,6 +20,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.typeUtil.asTypeProjection import org.jetbrains.kotlin.types.typeUtil.replaceAnnotations +import org.jetbrains.kotlin.types.typeUtil.supertypes import org.jetbrains.kotlin.utils.DFS import org.jetbrains.kotlin.utils.addIfNotNull import java.util.* @@ -67,6 +69,9 @@ val KotlinType.isKSuspendFunctionType: Boolean val KotlinType.isFunctionOrSuspendFunctionType: Boolean get() = isFunctionType || isSuspendFunctionType +val KotlinType.isFunctionOrKFunctionTypeWithAnySuspendability: Boolean + get() = isFunctionType || isSuspendFunctionType || isKFunctionType || isKSuspendFunctionType + val KotlinType.isBuiltinFunctionalType: Boolean get() = constructor.declarationDescriptor?.isBuiltinFunctionalClassDescriptor == true @@ -154,6 +159,16 @@ fun KotlinType.getValueParameterTypesFromCallableReflectionType(isCallableTypeWi return arguments.subList(first, last) } +fun KotlinType.extractFunctionalTypeFromSupertypes(): KotlinType { + assert(isBuiltinFunctionalTypeOrSubtype) { "Not a function type or subtype: $this" } + return if (isBuiltinFunctionalType) this else supertypes().first { it.isBuiltinFunctionalType } +} + +fun KotlinType.getPureArgumentsForFunctionalTypeOrSubtype(): List { + assert(isBuiltinFunctionalTypeOrSubtype) { "Not a function type or subtype: $this" } + return extractFunctionalTypeFromSupertypes().arguments.dropLast(1).map { it.type } +} + fun KotlinType.extractParameterNameFromFunctionTypeArgument(): Name? { val annotation = annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.parameterName) ?: return null val name = (annotation.allValueArguments.values.singleOrNull() as? StringValue) @@ -197,29 +212,38 @@ fun getFunctionTypeArgumentProjections( @JvmOverloads fun createFunctionType( - builtIns: KotlinBuiltIns, - annotations: Annotations, - receiverType: KotlinType?, - parameterTypes: List, - parameterNames: List?, - returnType: KotlinType, - suspendFunction: Boolean = false + builtIns: KotlinBuiltIns, + annotations: Annotations, + receiverType: KotlinType?, + parameterTypes: List, + parameterNames: List?, + returnType: KotlinType, + suspendFunction: Boolean = false ): SimpleType { val arguments = getFunctionTypeArgumentProjections(receiverType, parameterTypes, parameterNames, returnType, builtIns) - val size = parameterTypes.size - val parameterCount = if (receiverType == null) size else size + 1 - val classDescriptor = if (suspendFunction) builtIns.getSuspendFunction(parameterCount) else builtIns.getFunction(parameterCount) + val parameterCount = if (receiverType == null) parameterTypes.size else parameterTypes.size + 1 + val classDescriptor = getFunctionDescriptor(builtIns, parameterCount, suspendFunction) // TODO: preserve laziness of given annotations - val typeAnnotations = - if (receiverType == null || annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.extensionFunctionType) != null) { - annotations - } - else { - Annotations.create( - annotations + BuiltInAnnotationDescriptor(builtIns, KotlinBuiltIns.FQ_NAMES.extensionFunctionType, emptyMap()) - ) - } + val typeAnnotations = if (receiverType != null) annotations.withExtensionFunctionAnnotation(builtIns) else annotations return KotlinTypeFactory.simpleNotNullType(typeAnnotations, classDescriptor, arguments) } + +fun Annotations.hasExtensionFunctionAnnotation() = hasAnnotation(KotlinBuiltIns.FQ_NAMES.extensionFunctionType) + +fun Annotations.withoutExtensionFunctionAnnotation() = + FilteredAnnotations(this, true) { it != KotlinBuiltIns.FQ_NAMES.extensionFunctionType } + +fun Annotations.withExtensionFunctionAnnotation(builtIns: KotlinBuiltIns) = + if (hasAnnotation(KotlinBuiltIns.FQ_NAMES.extensionFunctionType)) { + this + } else { + Annotations.create(this + BuiltInAnnotationDescriptor(builtIns, KotlinBuiltIns.FQ_NAMES.extensionFunctionType, emptyMap())) + } + +fun getFunctionDescriptor(builtIns: KotlinBuiltIns, parameterCount: Int, isSuspendFunction: Boolean) = + if (isSuspendFunction) builtIns.getSuspendFunction(parameterCount) else builtIns.getFunction(parameterCount) + +fun getKFunctionDescriptor(builtIns: KotlinBuiltIns, parameterCount: Int, isSuspendFunction: Boolean) = + if (isSuspendFunction) builtIns.getKSuspendFunction(parameterCount) else builtIns.getKFunction(parameterCount) diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt index 241a93ae444..fb45b2c32f8 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.types.checker import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns.FQ_NAMES import org.jetbrains.kotlin.builtins.PrimitiveType +import org.jetbrains.kotlin.builtins.isBuiltinFunctionalTypeOrSubtype import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.annotations.Annotations @@ -318,6 +319,11 @@ interface ClassicTypeSystemContext : TypeSystemInferenceExtensionContext, TypeSy return KotlinBuiltIns.isUnit(this) } + override fun KotlinTypeMarker.isBuiltinFunctionalTypeOrSubtype(): Boolean { + require(this is UnwrappedType, this::errorMessage) + return isBuiltinFunctionalTypeOrSubtype + } + override fun createFlexibleType(lowerBound: SimpleTypeMarker, upperBound: SimpleTypeMarker): KotlinTypeMarker { require(lowerBound is SimpleType, this::errorMessage) require(upperBound is SimpleType, this::errorMessage) diff --git a/core/type-system/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt b/core/type-system/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt index 5b2ede931f7..d64711b1103 100644 --- a/core/type-system/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt +++ b/core/type-system/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt @@ -132,6 +132,8 @@ interface TypeSystemInferenceExtensionContext : TypeSystemContext, TypeSystemBui fun KotlinTypeMarker.isUnit(): Boolean + fun KotlinTypeMarker.isBuiltinFunctionalTypeOrSubtype(): Boolean + fun KotlinTypeMarker.withNullability(nullable: Boolean): KotlinTypeMarker