From 982cbf1148196cc737be28f7a7f3a6e46564a52d Mon Sep 17 00:00:00 2001 From: Victor Petukhov Date: Thu, 4 Jun 2020 19:28:07 +0300 Subject: [PATCH] NI: clear calls info in coroutine inference before the second analysis of `+=` right side ^KT-39376 Fixed --- .../inference/CoroutineInferenceSession.kt | 16 +++++++ .../ExpressionTypingVisitorForStatements.java | 5 +++ .../inference/plusAssignInCoroutineContext.kt | 37 ++++++++++++++++ .../plusAssignInCoroutineContext.txt | 29 +++++++++++++ .../inference/plusAssignWithLambda.kt | 42 +++++++++++++++++++ .../inference/plusAssignWithLambda.txt | 28 +++++++++++++ .../inference/plusAssignWithLambda2.kt | 20 +++++++++ .../inference/plusAssignWithLambda2.txt | 16 +++++++ .../DiagnosticsTestWithStdLibGenerated.java | 15 +++++++ ...ticsTestWithStdLibUsingJavacGenerated.java | 15 +++++++ 10 files changed, 223 insertions(+) create mode 100644 compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignInCoroutineContext.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignInCoroutineContext.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda2.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda2.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/CoroutineInferenceSession.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/CoroutineInferenceSession.kt index 59b10a6067f..3650a4346bf 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/CoroutineInferenceSession.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/CoroutineInferenceSession.kt @@ -5,13 +5,16 @@ package org.jetbrains.kotlin.resolve.calls.inference +import com.intellij.psi.PsiElement import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.KtDoubleColonExpression +import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtReferenceExpression +import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.psi.psiUtil.isAncestor import org.jetbrains.kotlin.resolve.BindingTrace @@ -337,6 +340,19 @@ class CoroutineInferenceSession( deprecationResolver, moduleDescriptor, context.dataFlowValueFactory, typeApproximator, missingSupertypesResolver ) } + + /* + * It's used only for `+=` resolve to clear calls info before the second analysis of right side. + * TODO: remove it after moving `+=` resolve into OR mechanism + */ + fun clearCallsInfoByContainingElement(containingElement: KtElement) { + commonCalls.removeIf remove@{ callInfo -> + val atom = callInfo.callResolutionResult.resultCallAtom.atom + if (atom !is PSIKotlinCallImpl) return@remove false + + containingElement.anyDescendantOfType { it == atom.psiCall.callElement } + } + } } class ComposedSubstitutor(val left: NewTypeSubstitutor, val right: NewTypeSubstitutor) : NewTypeSubstitutor { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java index e6981b20d5e..0ceb58308dc 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java @@ -34,6 +34,7 @@ import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver; import org.jetbrains.kotlin.resolve.calls.context.CallPosition; import org.jetbrains.kotlin.resolve.calls.context.ContextDependency; import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache; +import org.jetbrains.kotlin.resolve.calls.inference.CoroutineInferenceSession; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults; import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsImpl; @@ -245,6 +246,10 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito boolean lhsAssignable = basic.checkLValue(ignoreReportsTrace, context, left, right, expression, false); if (assignmentOperationType == null || lhsAssignable) { // Check for '+' + // We should clear calls info for coroutine inference within right side as here we analyze it a second time in another context + if (context.inferenceSession instanceof CoroutineInferenceSession) { + ((CoroutineInferenceSession) context.inferenceSession).clearCallsInfoByContainingElement(expression); + } Name counterpartName = OperatorConventions.BINARY_OPERATION_NAMES.get(OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS.get(operationType)); binaryOperationDescriptors = components.callResolver.resolveBinaryCall( context.replaceTraceAndCache(temporaryForBinaryOperation).replaceScope(scope), diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignInCoroutineContext.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignInCoroutineContext.kt new file mode 100644 index 00000000000..24a4e194a6d --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignInCoroutineContext.kt @@ -0,0 +1,37 @@ +// !DIAGNOSTICS: -UNCHECKED_CAST -EXPERIMENTAL_API_USAGE_ERROR -UNUSED_PARAMETER + +class Bar + +fun materialize() = null as T + +interface FlowCollector { + suspend fun emit(value: T) +} + +interface Flow + +public fun flow(@BuilderInference block: suspend FlowCollector.() -> Unit) = materialize>() + +fun foo(total: Int, next: Int) = 10 +fun foo(total: Int, next: Float) = 10 +fun foo(total: Float, next: Int) = 10 + +fun call(x: String) {} + +suspend fun foo(x: Int) = flow { + var y = 1 + y += if (x > 2) 1 else 2 + + var newValue = 1 + newValue += listOf().asSequence().fold(0) { total, next -> + call(11) + total + next + } + newValue += listOf().asSequence().fold(0, fun(total, next): Int { return total + next }) + newValue += listOf().asSequence().fold(0, fun(total, next) = total + next) + newValue += listOf().asSequence().fold(0, ::foo) + + emit(materialize()) + + newValue += listOf().asSequence().fold(0) { total, next -> total + next } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignInCoroutineContext.txt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignInCoroutineContext.txt new file mode 100644 index 00000000000..2b5c4d5f111 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignInCoroutineContext.txt @@ -0,0 +1,29 @@ +package + +public fun call(/*0*/ x: kotlin.String): kotlin.Unit +public fun flow(/*0*/ @kotlin.BuilderInference block: suspend FlowCollector.() -> kotlin.Unit): Flow +public fun foo(/*0*/ total: kotlin.Float, /*1*/ next: kotlin.Int): kotlin.Int +public suspend fun foo(/*0*/ x: kotlin.Int): Flow +public fun foo(/*0*/ total: kotlin.Int, /*1*/ next: kotlin.Float): kotlin.Int +public fun foo(/*0*/ total: kotlin.Int, /*1*/ next: kotlin.Int): kotlin.Int +public fun materialize(): T + +public final class Bar { + public constructor Bar() + 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 Flow { + 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 FlowCollector { + public abstract suspend fun emit(/*0*/ value: T): 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/coroutines/inference/plusAssignWithLambda.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda.kt new file mode 100644 index 00000000000..d7323032074 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda.kt @@ -0,0 +1,42 @@ +// !DIAGNOSTICS: -UNUSED_ANONYMOUS_PARAMETER -UNUSED_PARAMETER + +fun foo(total: Int, next: Int) = 10 +fun foo(total: Int, next: Float) = 10 +fun foo(total: Float, next: Int) = 10 + +fun call(x: String) {} + +fun foo(x: Float, y: Float) = { + var newValue = 1 + newValue += listOf().asSequence().fold(0) { total, next -> + call(11) + total + next + } + newValue += listOf().asSequence().fold(0, fun(total, next): Int { return total + next }) + newValue += listOf().asSequence().fold(0, fun(total, next) = total + next) + newValue += listOf().asSequence().fold(0, ::foo) +} + +class A { + operator fun plus(x: Int) = A() + operator fun plusAssign(x: Float) {} +} + +fun id(x: T) = x + +fun foo2() = { + var x = A() + x += { "" } + var y = A() + y += 1 +} + +class B { + operator fun plus(x: () -> String) = A() + operator fun plusAssign(x: () -> Int) {} +} + +fun foo3(x: B) = { + x += { "" } + x += id { "" } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda.txt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda.txt new file mode 100644 index 00000000000..4e411d704aa --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda.txt @@ -0,0 +1,28 @@ +package + +public fun call(/*0*/ x: kotlin.String): kotlin.Unit +public fun foo(/*0*/ x: kotlin.Float, /*1*/ y: kotlin.Float): () -> kotlin.Unit +public fun foo(/*0*/ total: kotlin.Float, /*1*/ next: kotlin.Int): kotlin.Int +public fun foo(/*0*/ total: kotlin.Int, /*1*/ next: kotlin.Float): kotlin.Int +public fun foo(/*0*/ total: kotlin.Int, /*1*/ next: kotlin.Int): kotlin.Int +public fun foo2(): () -> kotlin.Unit +public fun foo3(/*0*/ x: B): () -> kotlin.Unit +public fun id(/*0*/ x: T): T + +public final class A { + public constructor 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 final operator fun plus(/*0*/ x: kotlin.Int): A + public final operator fun plusAssign(/*0*/ x: kotlin.Float): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class B { + 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 final operator fun plus(/*0*/ x: () -> kotlin.String): A + public final operator fun plusAssign(/*0*/ x: () -> kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda2.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda2.kt new file mode 100644 index 00000000000..b89f785f567 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda2.kt @@ -0,0 +1,20 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_ANONYMOUS_PARAMETER + +open class A + +operator fun T.plus(x: (T) -> Int) = A() +operator fun T.plusAssign(x: (Int) -> T) {} + +fun foo(total: Int) = 10 +fun foo(total: Float) = 10 +fun foo(total: String) = 10 + +fun id(x: T) = x + +fun main() { + var newValue = A() + newValue += id { total -> A() } + newValue += id(fun(total) = A()) + newValue += id(fun(total): A { return A() }) + newValue += id(::foo) +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda2.txt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda2.txt new file mode 100644 index 00000000000..e819c96a41e --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda2.txt @@ -0,0 +1,16 @@ +package + +public fun foo(/*0*/ total: kotlin.Float): kotlin.Int +public fun foo(/*0*/ total: kotlin.Int): kotlin.Int +public fun foo(/*0*/ total: kotlin.String): kotlin.Int +public fun id(/*0*/ x: T): T +public fun main(): kotlin.Unit +public operator fun T.plus(/*0*/ x: (T) -> kotlin.Int): A +public operator fun T.plusAssign(/*0*/ x: (kotlin.Int) -> T): kotlin.Unit + +public open class A { + public constructor 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 +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index 844ca799f50..76943946bbb 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -2062,6 +2062,21 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/nestedSuspendCallInsideLambda.kt"); } + @TestMetadata("plusAssignInCoroutineContext.kt") + public void testPlusAssignInCoroutineContext() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignInCoroutineContext.kt"); + } + + @TestMetadata("plusAssignWithLambda.kt") + public void testPlusAssignWithLambda() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda.kt"); + } + + @TestMetadata("plusAssignWithLambda2.kt") + public void testPlusAssignWithLambda2() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda2.kt"); + } + @TestMetadata("qualifiedResolvedExpressionInsideBuilderInference.kt") public void testQualifiedResolvedExpressionInsideBuilderInference() throws Exception { runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/qualifiedResolvedExpressionInsideBuilderInference.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java index 23d3c12e28c..8a57f0d6443 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java @@ -2062,6 +2062,21 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/nestedSuspendCallInsideLambda.kt"); } + @TestMetadata("plusAssignInCoroutineContext.kt") + public void testPlusAssignInCoroutineContext() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignInCoroutineContext.kt"); + } + + @TestMetadata("plusAssignWithLambda.kt") + public void testPlusAssignWithLambda() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda.kt"); + } + + @TestMetadata("plusAssignWithLambda2.kt") + public void testPlusAssignWithLambda2() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda2.kt"); + } + @TestMetadata("qualifiedResolvedExpressionInsideBuilderInference.kt") public void testQualifiedResolvedExpressionInsideBuilderInference() throws Exception { runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/qualifiedResolvedExpressionInsideBuilderInference.kt");