From 865ddac07a6915c63c63acd28b27466e7861a26a Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Wed, 22 Apr 2020 13:15:49 +0300 Subject: [PATCH] [NI] Add feature for choosing candidate by lambda return type --- ...endDiagnosticsTestWithStdlibGenerated.java | 23 +++++ .../fir/resolve/inference/FirCallCompleter.kt | 2 + .../tower/KotlinResolutionCallbacksImpl.kt | 8 +- .../resolve/calls/KotlinCallResolver.kt | 15 ++-- .../calls/components/ExternalComponents.kt | 1 + .../calls/components/KotlinCallCompleter.kt | 84 +++++++++++++++++++ .../components/PostponeArgumentsChecks.kt | 18 ++++ .../components/PostponedArgumentsAnalyzer.kt | 32 +++---- .../KotlinConstraintSystemCompleter.kt | 37 +++++++- ...overloadByLambdaReturnType_disabled.fir.kt | 36 ++++++++ .../overloadByLambdaReturnType_disabled.kt | 36 ++++++++ .../overloadByLambdaReturnType_disabled.txt | 13 +++ .../overloadByLambdaReturnType_enabled.fir.kt | 36 ++++++++ .../overloadByLambdaReturnType_enabled.kt | 36 ++++++++ .../overloadByLambdaReturnType_enabled.txt | 13 +++ .../DiagnosticsTestWithStdLibGenerated.java | 23 +++++ ...ticsTestWithStdLibUsingJavacGenerated.java | 23 +++++ .../kotlin/config/LanguageVersionSettings.kt | 1 + .../org/jetbrains/kotlin/utils/addToStdlib.kt | 15 +++- 19 files changed, 429 insertions(+), 23 deletions(-) create mode 100644 compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_disabled.fir.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_disabled.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_disabled.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_enabled.fir.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_enabled.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_enabled.txt 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 4fab81cd597..124f0f2f4b6 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 @@ -1788,6 +1788,29 @@ public class FirOldFrontendDiagnosticsTestWithStdlibGenerated extends AbstractFi } } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/factoryPattern") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class FactoryPattern extends AbstractFirOldFrontendDiagnosticsTestWithStdlib { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInFactoryPattern() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/factoryPattern"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("overloadByLambdaReturnType_disabled.kt") + public void testOverloadByLambdaReturnType_disabled() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_disabled.kt"); + } + + @TestMetadata("overloadByLambdaReturnType_enabled.kt") + public void testOverloadByLambdaReturnType_enabled() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_enabled.kt"); + } + } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/forInArrayLoop") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt index 9232b155f82..2e583e058e2 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt @@ -119,6 +119,8 @@ class FirCallCompleter( inferenceSession.addPartiallyResolvedCall(approximatedCall) CompletionResult(approximatedCall, false) } + + ConstraintSystemCompletionMode.UNTIL_FIRST_LAMBDA -> throw IllegalStateException() } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt index 6bde43de217..b4068034a6f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns.isPrimitiveTypeOrNullablePri import org.jetbrains.kotlin.builtins.KotlinBuiltIns.isUnderKotlinPackage import org.jetbrains.kotlin.builtins.UnsignedTypes import org.jetbrains.kotlin.builtins.createFunctionType +import org.jetbrains.kotlin.builtins.isFunctionTypeOrSubtype import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor @@ -196,8 +197,10 @@ class KotlinResolutionCallbacksImpl( val functionTypeInfo = expressionTypingServices.getTypeInfo(psiCallArgument.expression, actualContext) (temporaryTrace ?: trace).record(BindingContext.NEW_INFERENCE_LAMBDA_INFO, psiCallArgument.ktFunction, LambdaInfo.STUB_EMPTY) + val inferedReturnType = functionTypeInfo.type?.arguments?.last()?.type?.takeIf { functionTypeInfo.type?.isFunctionTypeOrSubtype == true } + if (coroutineSession?.hasInapplicableCall() == true) { - return ReturnArgumentsAnalysisResult(ReturnArgumentsInfo.empty, coroutineSession, hasInapplicableCallForBuilderInference = true) + return ReturnArgumentsAnalysisResult(ReturnArgumentsInfo.empty, coroutineSession, inferedReturnType, hasInapplicableCallForBuilderInference = true) } else { temporaryTrace?.commit() } @@ -244,7 +247,8 @@ class KotlinResolutionCallbacksImpl( lastExpressionCoercedToUnit, returnArgumentFound ), - coroutineSession + coroutineSession, + inferedReturnType ) } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/KotlinCallResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/KotlinCallResolver.kt index 66264909045..f1dc80ee7ee 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/KotlinCallResolver.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/KotlinCallResolver.kt @@ -18,15 +18,14 @@ package org.jetbrains.kotlin.resolve.calls import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus -import org.jetbrains.kotlin.resolve.calls.components.CallableReferenceResolver -import org.jetbrains.kotlin.resolve.calls.components.KotlinCallCompleter -import org.jetbrains.kotlin.resolve.calls.components.KotlinResolutionCallbacks -import org.jetbrains.kotlin.resolve.calls.components.NewOverloadingConflictResolver +import org.jetbrains.kotlin.resolve.calls.components.* import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.tower.* import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.model.safeSubstitute +import org.jetbrains.kotlin.utils.addToStdlib.same import java.lang.UnsupportedOperationException @@ -142,13 +141,19 @@ class KotlinCallResolver( } } - val maximallySpecificCandidates = overloadingConflictResolver.chooseMaximallySpecificCandidates( + var maximallySpecificCandidates = overloadingConflictResolver.chooseMaximallySpecificCandidates( refinedCandidates, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS, discriminateGenerics = true // todo ) + if (maximallySpecificCandidates.size > 1 && callComponents.languageVersionSettings.supportsFeature(LanguageFeature.FactoryPatternResolution)) { + maximallySpecificCandidates = kotlinCallCompleter.chooseCandidateRegardingFactoryPatternResolution(maximallySpecificCandidates, resolutionCallbacks) + } + return kotlinCallCompleter.runCompletion(candidateFactory, maximallySpecificCandidates, expectedType, resolutionCallbacks) } + + } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ExternalComponents.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ExternalComponents.kt index 3a14b07539f..58db66cb149 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ExternalComponents.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ExternalComponents.kt @@ -59,6 +59,7 @@ data class ReturnArgumentsInfo( data class ReturnArgumentsAnalysisResult( val returnArgumentsInfo: ReturnArgumentsInfo, val inferenceSession: InferenceSession?, + val lambdaReturnType: KotlinType? = null, val hasInapplicableCallForBuilderInference: Boolean = false ) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt index d2dd01d3c8c..a2ff8d5cdba 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.resolve.calls.components +import org.jetbrains.kotlin.builtins.isFunctionTypeOrSubtype import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.synthetic.SyntheticMemberDescriptor @@ -15,11 +16,15 @@ import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintS import org.jetbrains.kotlin.resolve.calls.inference.components.TrivialConstraintTypeInferenceOracle import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage.Empty.hasContradiction import org.jetbrains.kotlin.resolve.calls.inference.model.ExpectedTypeConstraintPosition +import org.jetbrains.kotlin.resolve.calls.inference.model.LambdaArgumentConstraintPosition +import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableTypeConstructor import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.tower.forceResolution import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.model.safeSubstitute +import org.jetbrains.kotlin.utils.addToStdlib.same class KotlinCallCompleter( private val postponedArgumentsAnalyzer: PostponedArgumentsAnalyzer, @@ -66,10 +71,89 @@ class KotlinCallCompleter( candidate.runCompletion(completionMode, diagnosticHolder, resolutionCallbacks) candidate.asCallResolutionResult(completionMode, diagnosticHolder) } + ConstraintSystemCompletionMode.UNTIL_FIRST_LAMBDA -> throw IllegalStateException("Should not be here") } } + fun chooseCandidateRegardingFactoryPatternResolution( + candidates: Set, + resolutionCallbacks: KotlinResolutionCallbacks + ): Set { + val lambdas = candidates.flatMap { candidate -> + candidate.getSubResolvedAtoms() + .filter { it is ResolvedLambdaAtom && !it.analyzed } + .map { candidate to it as ResolvedLambdaAtom } + }.groupBy { (_, atom) -> atom.atom } + .values + .singleOrNull() + ?.toMap() ?: return candidates + + if (!lambdas.values.same { it.parameters.size }) return candidates + if (!lambdas.values.all { it.expectedType?.isFunctionTypeOrSubtype == true }) return candidates + + for (candidate in lambdas.keys) { + candidate.runCompletion( + ConstraintSystemCompletionMode.UNTIL_FIRST_LAMBDA, + candidate, + resolutionCallbacks + ) + } + if (!lambdas.entries.same { (candidate, atom) -> candidate.getInputTypesOfLambdaAtom(atom) }) { + return candidates + } + + val newAtoms = mutableMapOf() + for ((candidate, atom) in lambdas.entries) { + newAtoms[candidate] = kotlinConstraintSystemCompleter.prepareLambdaAtomForFactoryPattern(atom, candidate, candidate) + } + + val diagnosticHolderForLambda = KotlinDiagnosticsHolder.SimpleHolder() + val iterator = newAtoms.entries.iterator() + val (firstCandidate, firstAtom) = iterator.next() + val results = postponedArgumentsAnalyzer.analyzeLambda( + firstCandidate.getSystem().asPostponedArgumentsAnalyzerContext(), + resolutionCallbacks, + firstAtom, + diagnosticHolderForLambda + ) + lambdas.getValue(firstCandidate).setAnalyzedResults(results.returnArgumentsInfo, listOf(firstAtom)) + + val lambdaReturnType = results.lambdaReturnType ?: return candidates + while (iterator.hasNext()) { + val (candidate, atom) = iterator.next() + atom.setAnalyzedResults(results.returnArgumentsInfo, firstAtom.subResolvedAtoms!!) + lambdas.getValue(candidate).setAnalyzedResults(results.returnArgumentsInfo, listOf(atom)) + candidate.csBuilder.addSubtypeConstraint(lambdaReturnType, atom.returnType, LambdaArgumentConstraintPosition(atom)) + } + + val errorCandidates = mutableSetOf() + val successfulCandidates = mutableSetOf() + + for (candidate in candidates) { + if (candidate.isSuccessful) { + successfulCandidates += candidate + } else { + errorCandidates += candidate + } + } + return when { + successfulCandidates.isNotEmpty() -> successfulCandidates + else -> errorCandidates + } + } + + private fun KotlinResolutionCandidate.getInputTypesOfLambdaAtom(atom: ResolvedLambdaAtom): List { + val result = mutableListOf() + val substitutor = csBuilder.buildCurrentSubstitutor() + val ctx = getSystem().asConstraintSystemCompleterContext() + for (inputType in atom.inputTypes) { + result += substitutor.safeSubstitute(ctx, inputType) as UnwrappedType + } + return result + } + + private fun KotlinResolutionCandidate.checkSamWithVararg(diagnosticHolder: KotlinDiagnosticsHolder.SimpleHolder) { val samConversionPerArgumentWithWarningsForVarargAfterSam = callComponents.languageVersionSettings.supportsFeature(LanguageFeature.SamConversionPerArgument) && 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 bc7a885ee9a..b101634ce91 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 @@ -220,6 +220,24 @@ fun LambdaWithTypeVariableAsExpectedTypeAtom.transformToResolvedLambda( return resolvedLambdaAtom } +fun ResolvedLambdaAtom.transformToResolvedLambda( + csBuilder: ConstraintSystemBuilder, + diagnosticsHolder: KotlinDiagnosticsHolder, + expectedType: UnwrappedType, + returnTypeVariable: TypeVariableForLambdaReturnType? = null +): ResolvedLambdaAtom { + val resolvedLambdaAtom = preprocessLambdaArgument( + csBuilder, + atom, + expectedType, + diagnosticsHolder, + forceResolution = true, + returnTypeVariable = returnTypeVariable + ) as ResolvedLambdaAtom + + return resolvedLambdaAtom +} + private fun preprocessCallableReference( csBuilder: ConstraintSystemBuilder, argument: CallableReferenceKotlinCallArgument, 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 dbbcd6abb96..86dc004cdf3 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 @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage import org.jetbrains.kotlin.resolve.calls.inference.model.CoroutinePosition import org.jetbrains.kotlin.resolve.calls.inference.model.LambdaArgumentConstraintPosition import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.UnwrappedType import org.jetbrains.kotlin.types.model.* import org.jetbrains.kotlin.types.typeUtil.builtIns @@ -65,12 +66,12 @@ class PostponedArgumentsAnalyzer( } } - private fun analyzeLambda( + fun analyzeLambda( c: Context, resolutionCallbacks: KotlinResolutionCallbacks, lambda: ResolvedLambdaAtom, diagnosticHolder: KotlinDiagnosticsHolder - ) { + ): ReturnArgumentsAnalysisResult { val stubsForPostponedVariables = c.bindingStubsForPostponedVariables() val currentSubstitutor = c.buildCurrentSubstitutor(stubsForPostponedVariables.mapKeys { it.key.freshTypeConstructor(c) }) @@ -120,20 +121,21 @@ class PostponedArgumentsAnalyzer( else FilteredAnnotations(annotations, true) { it != KotlinBuiltIns.FQ_NAMES.extensionFunctionType } } - val (returnArgumentsInfo, inferenceSession, hasInapplicableCallForBuilderInference) = - resolutionCallbacks.analyzeAndGetLambdaReturnArguments( - lambda.atom, - lambda.isSuspend, - receiver, - parameters, - expectedTypeForReturnArguments, - convertedAnnotations ?: Annotations.EMPTY, - stubsForPostponedVariables.cast() - ) + val returnArgumentsAnalysisResult = resolutionCallbacks.analyzeAndGetLambdaReturnArguments( + lambda.atom, + lambda.isSuspend, + receiver, + parameters, + expectedTypeForReturnArguments, + convertedAnnotations ?: Annotations.EMPTY, + stubsForPostponedVariables.cast() + ) + val (returnArgumentsInfo, inferenceSession, inferedReturnType, hasInapplicableCallForBuilderInference) = + returnArgumentsAnalysisResult if (hasInapplicableCallForBuilderInference) { c.getBuilder().removePostponedVariables() - return + return returnArgumentsAnalysisResult } val returnArguments = returnArgumentsInfo.nonErrorArguments @@ -169,7 +171,7 @@ class PostponedArgumentsAnalyzer( val postponedVariables = inferenceSession.inferPostponedVariables(lambda, storageSnapshot, diagnosticHolder) if (postponedVariables == null) { c.getBuilder().removePostponedVariables() - return + return returnArgumentsAnalysisResult } for ((constructor, resultType) in postponedVariables) { @@ -180,6 +182,8 @@ class PostponedArgumentsAnalyzer( c.getBuilder().addEqualityConstraint(variable.defaultType(c), resultType, CoroutinePosition()) } } + + return returnArgumentsAnalysisResult } private fun UnwrappedType?.receiver(): UnwrappedType? { 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 7985bf41fc5..ab7956d688b 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,6 +5,7 @@ package org.jetbrains.kotlin.resolve.calls.inference.components +import org.jetbrains.kotlin.resolve.calls.components.transformToResolvedLambda import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder import org.jetbrains.kotlin.resolve.calls.inference.model.* import org.jetbrains.kotlin.resolve.calls.model.* @@ -12,6 +13,8 @@ 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.model.safeSubstitute +import org.jetbrains.kotlin.types.typeUtil.asTypeProjection import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addToStdlib.safeAs import kotlin.collections.LinkedHashSet @@ -24,7 +27,8 @@ class KotlinConstraintSystemCompleter( enum class ConstraintSystemCompletionMode { FULL, - PARTIAL + PARTIAL, + UNTIL_FIRST_LAMBDA } interface Context : VariableFixationFinder.Context, ResultTypeResolver.Context { @@ -91,7 +95,9 @@ class KotlinConstraintSystemCompleter( analyze: (PostponedResolvedAtom) -> Unit ) { completion@ while (true) { + // TODO val postponedArguments = getOrderedNotAnalyzedPostponedArguments(topLevelAtoms) + if (completionMode == ConstraintSystemCompletionMode.UNTIL_FIRST_LAMBDA && hasLambdaToAnalyze(postponedArguments)) return // Stage 1: analyze postponed arguments with fixed parameter types if (analyzeArgumentWithFixedParameterTypes(postponedArguments, analyze)) @@ -210,6 +216,35 @@ class KotlinConstraintSystemCompleter( return false } + fun prepareLambdaAtomForFactoryPattern( + atom: ResolvedLambdaAtom, + candidate: KotlinResolutionCandidate, + diagnosticsHolder: KotlinDiagnosticsHolder, + ): ResolvedLambdaAtom { + val returnVariable = TypeVariableForLambdaReturnType(candidate.callComponents.builtIns, "_R") + val csBuilder = candidate.csBuilder + csBuilder.registerVariable(returnVariable) + val functionalType: KotlinType = csBuilder.buildCurrentSubstitutor().safeSubstitute(candidate.getSystem().asConstraintSystemCompleterContext(), atom.expectedType!!) as KotlinType + val expectedType = KotlinTypeFactory.simpleType( + functionalType.annotations, + functionalType.constructor, + functionalType.arguments.dropLast(1) + returnVariable.defaultType.asTypeProjection(), + functionalType.isMarkedNullable + ) + csBuilder.addSubtypeConstraint( + expectedType, + functionalType, + ArgumentConstraintPosition(atom.atom) + ) + return atom.transformToResolvedLambda(csBuilder, diagnosticsHolder, expectedType, returnVariable) + } + + private fun Context.hasLambdaToAnalyze( + postponedArguments: List + ): Boolean { + return analyzeArgumentWithFixedParameterTypes(postponedArguments) {} + } + private fun findPostponedArgumentWithRevisableExpectedType(postponedArguments: List) = postponedArguments.firstOrNull { argument -> argument is PostponedAtomWithRevisableExpectedType } diff --git a/compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_disabled.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_disabled.fir.kt new file mode 100644 index 00000000000..177b751e59b --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_disabled.fir.kt @@ -0,0 +1,36 @@ +// !LANGUAGE: +NewInference -FactoryPatternResolution +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -UNUSED_EXPRESSION +// ISSUE: KT-11265 + +fun create(f: (Int) -> Int): Int = 1 +fun create(f: (Int) -> String): String = "" + +fun takeString(s: String) {} +fun takeInt(s: Int) {} + +fun test_1() { + val x = create { "" } + takeString(x) +} + +fun test_2() { + val x = create { 1 } + takeInt(x) +} + +fun test_3() { + val x = create { 1.0 } +} + +fun create(x: K, f: (K) -> Int): Int = 1 +fun create(x: T, f: (T) -> String): String = "" + +fun test_4() { + val x = create("") { "" } + takeString(x) +} + +fun test_5() { + val x = create("") { 1 } + takeInt(x) +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_disabled.kt b/compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_disabled.kt new file mode 100644 index 00000000000..0ae9029bbd7 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_disabled.kt @@ -0,0 +1,36 @@ +// !LANGUAGE: +NewInference -FactoryPatternResolution +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -UNUSED_EXPRESSION +// ISSUE: KT-11265 + +fun create(f: (Int) -> Int): Int = 1 +fun create(f: (Int) -> String): String = "" + +fun takeString(s: String) {} +fun takeInt(s: Int) {} + +fun test_1() { + val x = create { "" } + takeString(x) +} + +fun test_2() { + val x = create { 1 } + takeInt(x) +} + +fun test_3() { + val x = create { 1.0 } +} + +fun create(x: K, f: (K) -> Int): Int = 1 +fun create(x: T, f: (T) -> String): String = "" + +fun test_4() { + val x = create("") { "" } + takeString(x) +} + +fun test_5() { + val x = create("") { 1 } + takeInt(x) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_disabled.txt b/compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_disabled.txt new file mode 100644 index 00000000000..0581aa1b7c8 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_disabled.txt @@ -0,0 +1,13 @@ +package + +public fun create(/*0*/ f: (kotlin.Int) -> kotlin.Int): kotlin.Int +public fun create(/*0*/ f: (kotlin.Int) -> kotlin.String): kotlin.String +public fun create(/*0*/ x: K, /*1*/ f: (K) -> kotlin.Int): kotlin.Int +public fun create(/*0*/ x: T, /*1*/ f: (T) -> kotlin.String): kotlin.String +public fun takeInt(/*0*/ s: kotlin.Int): kotlin.Unit +public fun takeString(/*0*/ s: kotlin.String): kotlin.Unit +public fun test_1(): kotlin.Unit +public fun test_2(): kotlin.Unit +public fun test_3(): kotlin.Unit +public fun test_4(): kotlin.Unit +public fun test_5(): kotlin.Unit diff --git a/compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_enabled.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_enabled.fir.kt new file mode 100644 index 00000000000..e3cde147844 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_enabled.fir.kt @@ -0,0 +1,36 @@ +// !LANGUAGE: +NewInference +FactoryPatternResolution +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -UNUSED_EXPRESSION +// ISSUE: KT-11265 + +fun create(f: (Int) -> Int): Int = 1 +fun create(f: (Int) -> String): String = "" + +fun takeString(s: String) {} +fun takeInt(s: Int) {} + +fun test_1() { + val x = create { "" } + takeString(x) +} + +fun test_2() { + val x = create { 1 } + takeInt(x) +} + +fun test_3() { + val x = create { 1.0 } +} + +fun create(x: K, f: (K) -> Int): Int = 1 +fun create(x: T, f: (T) -> String): String = "" + +fun test_4() { + val x = create("") { "" } + takeString(x) +} + +fun test_5() { + val x = create("") { 1 } + takeInt(x) +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_enabled.kt b/compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_enabled.kt new file mode 100644 index 00000000000..0d13c22999e --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_enabled.kt @@ -0,0 +1,36 @@ +// !LANGUAGE: +NewInference +FactoryPatternResolution +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -UNUSED_EXPRESSION +// ISSUE: KT-11265 + +fun create(f: (Int) -> Int): Int = 1 +fun create(f: (Int) -> String): String = "" + +fun takeString(s: String) {} +fun takeInt(s: Int) {} + +fun test_1() { + val x = create { "" } + takeString(x) +} + +fun test_2() { + val x = create { 1 } + takeInt(x) +} + +fun test_3() { + val x = create { 1.0 } +} + +fun create(x: K, f: (K) -> Int): Int = 1 +fun create(x: T, f: (T) -> String): String = "" + +fun test_4() { + val x = create("") { "" } + takeString(x) +} + +fun test_5() { + val x = create("") { 1 } + takeInt(x) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_enabled.txt b/compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_enabled.txt new file mode 100644 index 00000000000..0581aa1b7c8 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_enabled.txt @@ -0,0 +1,13 @@ +package + +public fun create(/*0*/ f: (kotlin.Int) -> kotlin.Int): kotlin.Int +public fun create(/*0*/ f: (kotlin.Int) -> kotlin.String): kotlin.String +public fun create(/*0*/ x: K, /*1*/ f: (K) -> kotlin.Int): kotlin.Int +public fun create(/*0*/ x: T, /*1*/ f: (T) -> kotlin.String): kotlin.String +public fun takeInt(/*0*/ s: kotlin.Int): kotlin.Unit +public fun takeString(/*0*/ s: kotlin.String): kotlin.Unit +public fun test_1(): kotlin.Unit +public fun test_2(): kotlin.Unit +public fun test_3(): kotlin.Unit +public fun test_4(): kotlin.Unit +public fun test_5(): kotlin.Unit diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index 8e0b75a4602..19287d3aeea 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -2803,6 +2803,29 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW } } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/factoryPattern") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class FactoryPattern extends AbstractDiagnosticsTestWithStdLib { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInFactoryPattern() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/factoryPattern"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("overloadByLambdaReturnType_disabled.kt") + public void testOverloadByLambdaReturnType_disabled() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_disabled.kt"); + } + + @TestMetadata("overloadByLambdaReturnType_enabled.kt") + public void testOverloadByLambdaReturnType_enabled() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_enabled.kt"); + } + } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/forInArrayLoop") @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 720d93b59b1..3e0458b772c 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java @@ -2803,6 +2803,29 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno } } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/factoryPattern") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class FactoryPattern extends AbstractDiagnosticsTestWithStdLibUsingJavac { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInFactoryPattern() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/factoryPattern"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @TestMetadata("overloadByLambdaReturnType_disabled.kt") + public void testOverloadByLambdaReturnType_disabled() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_disabled.kt"); + } + + @TestMetadata("overloadByLambdaReturnType_enabled.kt") + public void testOverloadByLambdaReturnType_enabled() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/factoryPattern/overloadByLambdaReturnType_enabled.kt"); + } + } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/forInArrayLoop") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index aee09b0ae43..9170a99e9be 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -153,6 +153,7 @@ enum class LanguageFeature( FunctionReferenceWithDefaultValueAsOtherType(sinceVersion = KOTLIN_1_4), NonStrictOnlyInputTypesChecks(sinceVersion = KOTLIN_1_4), SuspendConversion(sinceVersion = KOTLIN_1_4, defaultState = State.DISABLED), + FactoryPatternResolution(sinceVersion = KOTLIN_1_4), BooleanElvisBoundSmartCasts(sinceVersion = KOTLIN_1_3, defaultState = State.DISABLED), // see KT-26357 for details NewDataFlowForTryExpressions(sinceVersion = KOTLIN_1_4, defaultState = State.DISABLED), diff --git a/core/util.runtime/src/org/jetbrains/kotlin/utils/addToStdlib.kt b/core/util.runtime/src/org/jetbrains/kotlin/utils/addToStdlib.kt index 6f5c160e83b..9e4cb3636be 100644 --- a/core/util.runtime/src/org/jetbrains/kotlin/utils/addToStdlib.kt +++ b/core/util.runtime/src/org/jetbrains/kotlin/utils/addToStdlib.kt @@ -148,4 +148,17 @@ inline fun > Iterable.flatMapToNullable(des } fun > min(a: E, b: E): E = if (a < b) a else b -fun > min(a: E, b: E): E = if (a < b) a else b \ No newline at end of file +fun > min(a: E, b: E): E = if (a < b) a else b + +inline fun Iterable.same(extractor: (T) -> R): Boolean { + val iterator = iterator() + val firstValue = extractor(iterator.next()) + while (iterator.hasNext()) { + val item = iterator.next() + val value = extractor(item) + if (value != firstValue) { + return false + } + } + return true +} \ No newline at end of file