From 3a98c84105f52dabe1fadce28712d9f3bf31fea0 Mon Sep 17 00:00:00 2001 From: Pavel Kirpichenkov Date: Mon, 20 Jan 2020 13:47:43 +0300 Subject: [PATCH] [NI] Make behaviour of anonymous functions consistent with lambdas Fix completion of anonymous functions with expression body without expected type. Premature completion led to losing type info from outer calls. Also report type mismatches on empty lambda expressions. KT-34729 In progress --- ...irOldFrontendDiagnosticsTestGenerated.java | 10 +++++ .../DiagnosticReporterByTrackingStrategy.kt | 1 + .../ControlStructureTypingVisitor.java | 12 +----- .../expressions/ExpressionTypingServices.java | 42 +++++++++++++++++++ .../components/PostponedArgumentsAnalyzer.kt | 1 - .../InferenceParametersTypes.kt | 2 +- .../diagnostics/tests/generics/kt34729.fir.kt | 19 +++++++++ .../diagnostics/tests/generics/kt34729.kt | 19 +++++++++ .../diagnostics/tests/generics/kt34729.txt | 20 +++++++++ .../completion/anonymousFunction.fir.kt | 22 ++++++++++ .../inference/completion/anonymousFunction.kt | 22 ++++++++++ .../completion/anonymousFunction.txt | 8 ++++ .../checkers/DiagnosticsTestGenerated.java | 10 +++++ .../DiagnosticsUsingJavacTestGenerated.java | 10 +++++ 14 files changed, 185 insertions(+), 13 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/generics/kt34729.fir.kt create mode 100644 compiler/testData/diagnostics/tests/generics/kt34729.kt create mode 100644 compiler/testData/diagnostics/tests/generics/kt34729.txt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/anonymousFunction.fir.kt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/anonymousFunction.kt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/anonymousFunction.txt diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java index d140450e211..fc77efafad8 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java @@ -8469,6 +8469,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte runTest("compiler/testData/diagnostics/tests/generics/kt30590.kt"); } + @TestMetadata("kt34729.kt") + public void testKt34729() throws Exception { + runTest("compiler/testData/diagnostics/tests/generics/kt34729.kt"); + } + @TestMetadata("kt5508.kt") public void testKt5508() throws Exception { runTest("compiler/testData/diagnostics/tests/generics/kt5508.kt"); @@ -10537,6 +10542,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/inference/completion"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @TestMetadata("anonymousFunction.kt") + public void testAnonymousFunction() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/anonymousFunction.kt"); + } + @TestMetadata("basic.kt") public void testBasic() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/completion/basic.kt"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt index c16fa091a8d..c636e8483c0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt @@ -273,6 +273,7 @@ class DiagnosticReporterByTrackingStrategy( is ArgumentConstraintPosition -> position.argument is ReceiverConstraintPosition -> position.argument is LHSArgumentConstraintPosition -> position.argument + is LambdaArgumentConstraintPosition -> position.lambda.atom else -> null } argument?.let { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java index 86dfae0c942..4e5b8bbf692 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java @@ -56,6 +56,7 @@ import static org.jetbrains.kotlin.resolve.BindingContext.*; import static org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT; import static org.jetbrains.kotlin.types.TypeUtils.*; import static org.jetbrains.kotlin.types.expressions.ControlStructureTypingUtils.*; +import static org.jetbrains.kotlin.types.expressions.ExpressionTypingServices.getNewInferenceLambdaInfo; import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.*; public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { @@ -945,17 +946,6 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { replaceJumpOutPossible(true); } - @Nullable - private static KotlinResolutionCallbacksImpl.LambdaInfo getNewInferenceLambdaInfo( - @NotNull ExpressionTypingContext context, - @NotNull KtElement function - ) { - if (function instanceof KtFunction) { - return context.trace.get(BindingContext.NEW_INFERENCE_LAMBDA_INFO, (KtFunction) function); - } - return null; - } - @NotNull private static KotlinType getFunctionExpectedReturnType( @NotNull FunctionDescriptor descriptor, diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingServices.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingServices.java index 59b2e7c575f..1d2e92af8bb 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingServices.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingServices.java @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext; import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo; import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue; import org.jetbrains.kotlin.resolve.calls.tower.KotlinResolutionCallbacksImpl; +import org.jetbrains.kotlin.resolve.calls.tower.LambdaContextInfo; import org.jetbrains.kotlin.resolve.scopes.*; import org.jetbrains.kotlin.types.ErrorUtils; import org.jetbrains.kotlin.types.KotlinType; @@ -224,7 +225,11 @@ public class ExpressionTypingServices { trace, functionInnerScope, dataFlowInfo, NO_EXPECTED_TYPE, getLanguageVersionSettings(), expressionTypingComponents.dataFlowValueFactory ); + + KotlinResolutionCallbacksImpl.LambdaInfo lambdaInfo = getNewInferenceLambdaInfo(context, function); + context = updateContextFromNILambdaInfo(lambdaInfo, context); KotlinTypeInfo typeInfo = expressionTypingFacade.getTypeInfo(bodyExpression, context, function.hasBlockBody()); + updateLambdaContextInfoForAnonymousFunction(lambdaInfo, typeInfo, context); KotlinType type = typeInfo.getType(); if (type != null) { @@ -235,6 +240,43 @@ public class ExpressionTypingServices { } } + private static void updateLambdaContextInfoForAnonymousFunction( + @Nullable KotlinResolutionCallbacksImpl.LambdaInfo lambdaInfo, + @NotNull KotlinTypeInfo bodyExpressionTypeInfo, + @NotNull ExpressionTypingContext context + ) { + if (lambdaInfo == null) return; + + LambdaContextInfo contextInfo = lambdaInfo.getLastExpressionInfo(); + contextInfo.setTypeInfo(bodyExpressionTypeInfo); + contextInfo.setDataFlowInfoAfter(null); + contextInfo.setLexicalScope(context.scope); + contextInfo.setTrace(context.trace); + } + + private static ExpressionTypingContext updateContextFromNILambdaInfo( + @Nullable KotlinResolutionCallbacksImpl.LambdaInfo lambdaInfo, + @NotNull ExpressionTypingContext context + ) { + if (lambdaInfo != null) { + context = context + .replaceContextDependency(lambdaInfo.getContextDependency()) + .replaceExpectedType(lambdaInfo.getExpectedType()); + } + return context; + } + + @Nullable + public static KotlinResolutionCallbacksImpl.LambdaInfo getNewInferenceLambdaInfo( + @NotNull ExpressionTypingContext context, + @NotNull KtElement function + ) { + if (function instanceof KtFunction) { + return context.trace.get(BindingContext.NEW_INFERENCE_LAMBDA_INFO, (KtFunction) function); + } + return null; + } + /** * Visits block statements propagating data flow information from the first to the last. * Determines block returned type and data flow information at the end of the block AND 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 d5ab883d0ce..1729daabf11 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 @@ -139,7 +139,6 @@ class PostponedArgumentsAnalyzer( if (returnArguments.isEmpty()) { val unitType = lambda.returnType.builtIns.unitType val lambdaReturnType = lambda.returnType.let(::substitute) - c.getBuilder().addSubtypeConstraint(lambdaReturnType, unitType, LambdaArgumentConstraintPosition(lambda)) c.getBuilder().addSubtypeConstraint(unitType, lambdaReturnType, LambdaArgumentConstraintPosition(lambda)) } diff --git a/compiler/testData/diagnostics/tests/functionAsExpression/InferenceParametersTypes.kt b/compiler/testData/diagnostics/tests/functionAsExpression/InferenceParametersTypes.kt index 4f6e830cd0b..b458f865a91 100644 --- a/compiler/testData/diagnostics/tests/functionAsExpression/InferenceParametersTypes.kt +++ b/compiler/testData/diagnostics/tests/functionAsExpression/InferenceParametersTypes.kt @@ -12,7 +12,7 @@ fun test(a: (Int) -> Int) { } fun test2(a: () -> List) { - test2(fun () = listOf()) + test2(fun () = listOf()) } val a: (Int) -> Unit = fun(x) { checkSubtype(x) } diff --git a/compiler/testData/diagnostics/tests/generics/kt34729.fir.kt b/compiler/testData/diagnostics/tests/generics/kt34729.fir.kt new file mode 100644 index 00000000000..017f96bd1b4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/kt34729.fir.kt @@ -0,0 +1,19 @@ +// !WITH_NEW_INFERENCE +// !DIAGNOSTICS: -UNUSED_PARAMETER +// !LANGUAGE: +NewInference + +interface ILength { + val length: Int +} + +class Impl(override val length: Int) : ILength + +fun foo(a: (Int) -> T) = 0 +fun bar(a: (Int) -> T) { + a(42).length +} + +fun test() { + foo { } + bar { } +} diff --git a/compiler/testData/diagnostics/tests/generics/kt34729.kt b/compiler/testData/diagnostics/tests/generics/kt34729.kt new file mode 100644 index 00000000000..665129cf9bd --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/kt34729.kt @@ -0,0 +1,19 @@ +// !WITH_NEW_INFERENCE +// !DIAGNOSTICS: -UNUSED_PARAMETER +// !LANGUAGE: +NewInference + +interface ILength { + val length: Int +} + +class Impl(override val length: Int) : ILength + +fun foo(a: (Int) -> T) = 0 +fun bar(a: (Int) -> T) { + a(42).length +} + +fun test() { + foo { } + bar { } +} diff --git a/compiler/testData/diagnostics/tests/generics/kt34729.txt b/compiler/testData/diagnostics/tests/generics/kt34729.txt new file mode 100644 index 00000000000..9c7df19323a --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/kt34729.txt @@ -0,0 +1,20 @@ +package + +public fun bar(/*0*/ a: (kotlin.Int) -> T): kotlin.Unit +public fun foo(/*0*/ a: (kotlin.Int) -> T): kotlin.Int +public fun test(): kotlin.Unit + +public interface ILength { + public abstract val length: kotlin.Int + 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 Impl : ILength { + public constructor Impl(/*0*/ length: kotlin.Int) + public open override /*1*/ val length: kotlin.Int + 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/anonymousFunction.fir.kt b/compiler/testData/diagnostics/tests/inference/completion/anonymousFunction.fir.kt new file mode 100644 index 00000000000..2bf0ecb995a --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/anonymousFunction.fir.kt @@ -0,0 +1,22 @@ +// !LANGUAGE: +NewInference +// !DIAGNOSTICS: -UNUSED_PARAMETER + +fun take(fn: () -> List) {} +fun inferFromLambda(fn: () -> L): L = TODO() + +fun materialize(): T = TODO() +fun id(arg: I) = arg + +fun testFunctions() { + take { materialize() } + take(fun() = materialize()) + take(fun(): List = materialize()) + take(fun(): List { + return materialize() + }) +} + +fun testNestedCalls() { + id(inferFromLambda { materialize() }) + id(inferFromLambda(fun() = materialize())) +} diff --git a/compiler/testData/diagnostics/tests/inference/completion/anonymousFunction.kt b/compiler/testData/diagnostics/tests/inference/completion/anonymousFunction.kt new file mode 100644 index 00000000000..2bf0ecb995a --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/anonymousFunction.kt @@ -0,0 +1,22 @@ +// !LANGUAGE: +NewInference +// !DIAGNOSTICS: -UNUSED_PARAMETER + +fun take(fn: () -> List) {} +fun inferFromLambda(fn: () -> L): L = TODO() + +fun materialize(): T = TODO() +fun id(arg: I) = arg + +fun testFunctions() { + take { materialize() } + take(fun() = materialize()) + take(fun(): List = materialize()) + take(fun(): List { + return materialize() + }) +} + +fun testNestedCalls() { + id(inferFromLambda { materialize() }) + id(inferFromLambda(fun() = materialize())) +} diff --git a/compiler/testData/diagnostics/tests/inference/completion/anonymousFunction.txt b/compiler/testData/diagnostics/tests/inference/completion/anonymousFunction.txt new file mode 100644 index 00000000000..ba09cc8ce83 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/anonymousFunction.txt @@ -0,0 +1,8 @@ +package + +public fun id(/*0*/ arg: I): I +public fun inferFromLambda(/*0*/ fn: () -> L): L +public fun materialize(): T +public fun take(/*0*/ fn: () -> kotlin.collections.List): kotlin.Unit +public fun testFunctions(): kotlin.Unit +public fun testNestedCalls(): kotlin.Unit diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 9e37a423bd3..f91b807dda1 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -8476,6 +8476,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { runTest("compiler/testData/diagnostics/tests/generics/kt30590.kt"); } + @TestMetadata("kt34729.kt") + public void testKt34729() throws Exception { + runTest("compiler/testData/diagnostics/tests/generics/kt34729.kt"); + } + @TestMetadata("kt5508.kt") public void testKt5508() throws Exception { runTest("compiler/testData/diagnostics/tests/generics/kt5508.kt"); @@ -10544,6 +10549,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/inference/completion"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @TestMetadata("anonymousFunction.kt") + public void testAnonymousFunction() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/anonymousFunction.kt"); + } + @TestMetadata("basic.kt") public void testBasic() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/completion/basic.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 303899fca6f..d189eb5be74 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -8471,6 +8471,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/generics/kt30590.kt"); } + @TestMetadata("kt34729.kt") + public void testKt34729() throws Exception { + runTest("compiler/testData/diagnostics/tests/generics/kt34729.kt"); + } + @TestMetadata("kt5508.kt") public void testKt5508() throws Exception { runTest("compiler/testData/diagnostics/tests/generics/kt5508.kt"); @@ -10539,6 +10544,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/inference/completion"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @TestMetadata("anonymousFunction.kt") + public void testAnonymousFunction() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/anonymousFunction.kt"); + } + @TestMetadata("basic.kt") public void testBasic() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/completion/basic.kt");