From f31c48017bdd69b15d3e4f267be7ae820a0bc930 Mon Sep 17 00:00:00 2001 From: Mikhail Zarechenskiy Date: Tue, 22 Aug 2017 14:35:23 +0300 Subject: [PATCH] [NI] Don't process lambda until expected type will be fixed --- .../components/PostponeArgumentsChecks.kt | 90 ++++++++++++------- .../components/PostponedArgumentsAnalyzer.kt | 1 + .../inference/ConstraintSystemBuilder.kt | 1 + .../KotlinConstraintSystemCompleter.kt | 18 ++-- .../model/NewConstraintSystemImpl.kt | 5 ++ .../resolve/calls/model/ResolutionAtoms.kt | 12 +++ .../regressions/lambdaPostponeConstruction.kt | 14 +++ .../box/regressions/lambdaWrongReturnType.kt | 7 ++ .../ir/IrBlackBoxCodegenTestGenerated.java | 12 +++ .../codegen/BlackBoxCodegenTestGenerated.java | 12 +++ .../LightAnalysisModeTestGenerated.java | 12 +++ .../semantics/JsCodegenBoxTestGenerated.java | 12 +++ 12 files changed, 157 insertions(+), 39 deletions(-) create mode 100644 compiler/testData/codegen/box/regressions/lambdaPostponeConstruction.kt create mode 100644 compiler/testData/codegen/box/regressions/lambdaWrongReturnType.kt 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 0240a0e0778..58c78bafe33 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 @@ -45,52 +45,80 @@ fun resolveKtPrimitive( private fun preprocessLambdaArgument( csBuilder: ConstraintSystemBuilder, argument: LambdaKotlinCallArgument, - expectedType: UnwrappedType? + expectedType: UnwrappedType?, + forceResolution: Boolean = false ): ResolvedAtom { + if (expectedType != null && !forceResolution && csBuilder.isTypeVariable(expectedType)) { + return LambdaWithTypeVariableAsExpectedTypeAtom(argument, expectedType) + } + + val resolvedArgument = extractLambdaInfoFromFunctionalType(expectedType, argument) ?: extraLambdaInfo(expectedType, argument, csBuilder) + + if (expectedType != null) { + val lambdaType = createFunctionType(csBuilder.builtIns, Annotations.EMPTY, resolvedArgument.receiver, + resolvedArgument.parameters, null, resolvedArgument.returnType, resolvedArgument.isSuspend) + csBuilder.addSubtypeConstraint(lambdaType, expectedType, ArgumentConstraintPosition(argument)) + } + + return resolvedArgument +} + +private fun extraLambdaInfo( + expectedType: UnwrappedType?, + argument: LambdaKotlinCallArgument, + csBuilder: ConstraintSystemBuilder +): ResolvedLambdaAtom { val builtIns = csBuilder.builtIns val isSuspend = expectedType?.isSuspendFunctionType ?: false - val receiverType: UnwrappedType? // null means that there is no receiver - val parameters: List - val returnType: UnwrappedType + val isFunctionSupertype = expectedType != null && KotlinBuiltIns.isNotNullOrNullableFunctionSupertype(expectedType) + val argumentAsFunctionExpression = argument.safeAs() + val typeVariable = TypeVariableForLambdaReturnType(argument, builtIns, "_L") - if (expectedType?.isBuiltinFunctionalType == true) { - receiverType = if (argument is FunctionExpression) argument.receiverType else expectedType.getReceiverTypeFromFunctionType()?.unwrap() - - val expectedParameters = expectedType.getValueParameterTypesFromFunctionType() - parameters = if (argument.parametersTypes != null) { - argument.parametersTypes!!.mapIndexed { - index, type -> - type ?: expectedParameters.getOrNull(index)?.type?.unwrap() ?: builtIns.nullableAnyType - } - } - else { - // lambda without explicit parameters: { } - expectedParameters.map { it.type.unwrap() } - } - returnType = argument.safeAs()?.returnType ?: expectedType.getReturnTypeFromFunctionType().unwrap() - } - else { - val isFunctionSupertype = expectedType != null && KotlinBuiltIns.isNotNullOrNullableFunctionSupertype(expectedType) - receiverType = argument.safeAs()?.receiverType - parameters = argument.parametersTypes?.map { it ?: builtIns.nothingType } ?: emptyList() - returnType = argument.safeAs()?.returnType ?: + val receiverType = argumentAsFunctionExpression?.receiverType + val returnType = argumentAsFunctionExpression?.returnType ?: expectedType?.arguments?.singleOrNull()?.type?.unwrap()?.takeIf { isFunctionSupertype } ?: typeVariable.defaultType - // what about case where expected type is type variable? In old TY such cases was not supported. => do nothing for now. todo design - } + val parameters = argument.parametersTypes?.map { it ?: builtIns.nothingType } ?: emptyList() val newTypeVariableUsed = returnType == typeVariable.defaultType if (newTypeVariableUsed) csBuilder.registerVariable(typeVariable) - if (expectedType != null) { - val lambdaType = createFunctionType(returnType.builtIns, Annotations.EMPTY, receiverType, parameters, null, returnType, isSuspend) - csBuilder.addSubtypeConstraint(lambdaType, expectedType, ArgumentConstraintPosition(argument)) + return ResolvedLambdaAtom(argument, isSuspend, receiverType, parameters, returnType, typeVariable.takeIf { newTypeVariableUsed }) +} + +private fun extractLambdaInfoFromFunctionalType(expectedType: UnwrappedType?, argument: LambdaKotlinCallArgument): ResolvedLambdaAtom? { + if (expectedType == null || !expectedType.isBuiltinFunctionalType) return null + val parameters = extractLambdaParameters(expectedType, argument) + + val argumentAsFunctionExpression = argument.safeAs() + val receiverType = argumentAsFunctionExpression?.receiverType ?: expectedType.getReceiverTypeFromFunctionType()?.unwrap() + val returnType = argumentAsFunctionExpression?.returnType ?: expectedType.getReturnTypeFromFunctionType().unwrap() + + return ResolvedLambdaAtom(argument, expectedType.isSuspendFunctionType, receiverType, parameters, returnType, typeVariableForLambdaReturnType = null) +} + +private fun extractLambdaParameters(expectedType: UnwrappedType, argument: LambdaKotlinCallArgument): List { + val parametersTypes = argument.parametersTypes + val expectedParameters = expectedType.getValueParameterTypesFromFunctionType() + if (parametersTypes == null) { + return expectedParameters.map { it.type.unwrap() } } - return ResolvedLambdaAtom(argument, isSuspend, receiverType, parameters, returnType, typeVariable.takeIf { newTypeVariableUsed }) + return parametersTypes.mapIndexed { index, type -> + type ?: expectedParameters.getOrNull(index)?.type?.unwrap() ?: expectedType.builtIns.nullableAnyType + } +} + +fun LambdaWithTypeVariableAsExpectedTypeAtom.transformToResolvedLambda(csBuilder: ConstraintSystemBuilder): ResolvedLambdaAtom { + val fixedExpectedType = csBuilder.buildCurrentSubstitutor().safeSubstitute(expectedType) + val resolvedLambdaAtom = preprocessLambdaArgument(csBuilder, atom, fixedExpectedType, forceResolution = true) as ResolvedLambdaAtom + + setAnalyzed(resolvedLambdaAtom) + + return resolvedLambdaAtom } private fun preprocessCallableReference( 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 027a0b6ae5f..19c2fca3a3b 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 @@ -42,6 +42,7 @@ class PostponedArgumentsAnalyzer( fun analyze(c: Context, resolutionCallbacks: KotlinResolutionCallbacks, argument: ResolvedAtom) { when (argument) { is ResolvedLambdaAtom -> analyzeLambda(c, resolutionCallbacks, argument) + is LambdaWithTypeVariableAsExpectedTypeAtom -> analyzeLambda(c, resolutionCallbacks, argument.transformToResolvedLambda(c.getBuilder())) is ResolvedCallableReferenceAtom -> callableReferenceResolver.processCallableReferenceArgument(c.getBuilder(), argument) is ResolvedCollectionLiteralAtom -> TODO("Not supported") else -> error("Unexpected resolved primitive: ${argument.javaClass.canonicalName}") diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilder.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilder.kt index bfea007cbde..6bf5171c851 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilder.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilder.kt @@ -37,6 +37,7 @@ interface ConstraintSystemOperation { fun addEqualityConstraint(a: UnwrappedType, b: UnwrappedType, position: ConstraintPosition) fun isProperType(type: UnwrappedType): Boolean + fun isTypeVariable(type: UnwrappedType): Boolean fun getProperSuperTypeConstructors(type: UnwrappedType): List } 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 05f61bf878b..f2a0d95a11a 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 @@ -61,8 +61,9 @@ class KotlinConstraintSystemCompleter( val variableForFixation = variableFixationFinder.findFirstVariableForFixation( c, allTypeVariables, postponedKtPrimitives, completionMode, topLevelType) - if (shouldWeForceCallableReferenceResolution(completionMode, variableForFixation)) { - if (forceCallableReferenceResolution(topLevelPrimitive, analyze)) continue + if (shouldForceCallableReferenceOrLambdaResolution(completionMode, variableForFixation)) { + if (forcePostponedAtomResolution(topLevelPrimitive, analyze)) continue + if (forcePostponedAtomResolution(topLevelPrimitive, analyze)) continue } if (variableForFixation != null) { @@ -86,7 +87,7 @@ class KotlinConstraintSystemCompleter( } } - private fun shouldWeForceCallableReferenceResolution( + private fun shouldForceCallableReferenceOrLambdaResolution( completionMode: ConstraintSystemCompletionMode, variableForFixation: VariableFixationFinder.VariableForFixation? ): Boolean { @@ -108,11 +109,12 @@ class KotlinConstraintSystemCompleter( } // true if we find some callable reference and run resolution for it. Note that such resolution can be unsuccessful - private fun forceCallableReferenceResolution(topLevelPrimitive: ResolvedAtom, analyze: (PostponedResolvedAtom) -> Unit): Boolean { - val callableReferenceArgument = getOrderedNotAnalyzedPostponedArguments(topLevelPrimitive). - firstIsInstanceOrNull() ?: return false - - analyze(callableReferenceArgument) + private inline fun forcePostponedAtomResolution( + topLevelPrimitive: ResolvedAtom, + analyze: (PostponedResolvedAtom) -> Unit + ): Boolean { + val postponedArgument = getOrderedNotAnalyzedPostponedArguments(topLevelPrimitive).firstIsInstanceOrNull() ?: return false + analyze(postponedArgument) return true } 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 867d7730abd..b568c16783d 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 @@ -174,6 +174,11 @@ class NewConstraintSystemImpl( } } + override fun isTypeVariable(type: UnwrappedType): Boolean { + checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION) + return notFixedTypeVariables.containsKey(type.constructor) + } + // ConstraintInjector.Context override val allTypeVariables: Map get() { checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION) 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 f1951998827..abfeb957acc 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 @@ -82,6 +82,18 @@ sealed class PostponedResolvedAtom : ResolvedAtom() { abstract val outputType: UnwrappedType? } +class LambdaWithTypeVariableAsExpectedTypeAtom( + override val atom: LambdaKotlinCallArgument, + val expectedType: UnwrappedType +) : PostponedResolvedAtom() { + override val inputTypes: Collection get() = listOf(expectedType) + override val outputType: UnwrappedType? get() = null + + fun setAnalyzed(resolvedLambdaAtom: ResolvedLambdaAtom) { + setAnalyzedResults(listOf(resolvedLambdaAtom), listOf()) + } +} + class ResolvedLambdaAtom( override val atom: LambdaKotlinCallArgument, val isSuspend: Boolean, diff --git a/compiler/testData/codegen/box/regressions/lambdaPostponeConstruction.kt b/compiler/testData/codegen/box/regressions/lambdaPostponeConstruction.kt new file mode 100644 index 00000000000..8da1e337a0c --- /dev/null +++ b/compiler/testData/codegen/box/regressions/lambdaPostponeConstruction.kt @@ -0,0 +1,14 @@ +class MyList + +operator fun MyList.plusAssign(element: T) {} + +val listOfFunctions = MyList<(Int) -> Int>() + +fun foo() { + listOfFunctions.plusAssign({ it -> it }) + listOfFunctions += { it -> it } +} + +fun box(): String { + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/regressions/lambdaWrongReturnType.kt b/compiler/testData/codegen/box/regressions/lambdaWrongReturnType.kt new file mode 100644 index 00000000000..b60e314e0fb --- /dev/null +++ b/compiler/testData/codegen/box/regressions/lambdaWrongReturnType.kt @@ -0,0 +1,7 @@ +fun test() = foo({ line: String -> line }) + +fun foo(x: T): T = TODO() + +fun box(): String { + return "OK" +} \ No newline at end of file diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 858e66e1264..35bf985d648 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -17018,6 +17018,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("lambdaPostponeConstruction.kt") + public void testLambdaPostponeConstruction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/lambdaPostponeConstruction.kt"); + doTest(fileName); + } + + @TestMetadata("lambdaWrongReturnType.kt") + public void testLambdaWrongReturnType() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/lambdaWrongReturnType.kt"); + doTest(fileName); + } + @TestMetadata("nestedIntersection.kt") public void testNestedIntersection() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/nestedIntersection.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index e787d0ad547..a902bdac98b 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -17018,6 +17018,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("lambdaPostponeConstruction.kt") + public void testLambdaPostponeConstruction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/lambdaPostponeConstruction.kt"); + doTest(fileName); + } + + @TestMetadata("lambdaWrongReturnType.kt") + public void testLambdaWrongReturnType() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/lambdaWrongReturnType.kt"); + doTest(fileName); + } + @TestMetadata("nestedIntersection.kt") public void testNestedIntersection() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/nestedIntersection.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 8e6577da8dd..58fff15da44 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -17018,6 +17018,18 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } + @TestMetadata("lambdaPostponeConstruction.kt") + public void testLambdaPostponeConstruction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/lambdaPostponeConstruction.kt"); + doTest(fileName); + } + + @TestMetadata("lambdaWrongReturnType.kt") + public void testLambdaWrongReturnType() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/lambdaWrongReturnType.kt"); + doTest(fileName); + } + @TestMetadata("nestedIntersection.kt") public void testNestedIntersection() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/nestedIntersection.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 65596a14544..ce6eb2f3deb 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -20798,6 +20798,18 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("lambdaPostponeConstruction.kt") + public void testLambdaPostponeConstruction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/lambdaPostponeConstruction.kt"); + doTest(fileName); + } + + @TestMetadata("lambdaWrongReturnType.kt") + public void testLambdaWrongReturnType() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/lambdaWrongReturnType.kt"); + doTest(fileName); + } + @TestMetadata("nestedIntersection.kt") public void testNestedIntersection() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/nestedIntersection.kt");