From 78c9bbcc0de8269f90ad35871d607fb8907528d5 Mon Sep 17 00:00:00 2001 From: Pavel Kirpichenkov Date: Wed, 22 Jan 2020 15:46:33 +0300 Subject: [PATCH] [NI] Soften restictions on using `Nothing` as proper constraint for full call completion Consider lower `Nothing` constraint non-proper only if there is a dependant not analyzed postponed atom. Early completion to `Nothing` provides data flow info for smart casts. KT-35668 Fixed --- ...irOldFrontendDiagnosticsTestGenerated.java | 10 ++++ .../components/CompletionModeCalculator.kt | 26 ++++++++-- .../KotlinConstraintSystemCompleter.kt | 38 +++++++------- .../controlFlowAnalysis/elvisNotProcessed.kt | 2 +- .../lambdaWithVariableAndNothing.fir.kt | 52 +++++++++++++++++++ .../lambdaWithVariableAndNothing.kt | 52 +++++++++++++++++++ .../lambdaWithVariableAndNothing.txt | 13 +++++ .../tests/regressions/kt35668.fir.kt | 20 +++++++ .../diagnostics/tests/regressions/kt35668.kt | 20 +++++++ .../diagnostics/tests/regressions/kt35668.txt | 3 ++ .../testData/diagnostics/tests/when/kt9929.kt | 2 +- .../checkers/DiagnosticsTestGenerated.java | 10 ++++ .../DiagnosticsUsingJavacTestGenerated.java | 10 ++++ 13 files changed, 234 insertions(+), 24 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/inference/completion/lambdaWithVariableAndNothing.fir.kt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/lambdaWithVariableAndNothing.kt create mode 100644 compiler/testData/diagnostics/tests/inference/completion/lambdaWithVariableAndNothing.txt create mode 100644 compiler/testData/diagnostics/tests/regressions/kt35668.fir.kt create mode 100644 compiler/testData/diagnostics/tests/regressions/kt35668.kt create mode 100644 compiler/testData/diagnostics/tests/regressions/kt35668.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 a9bea3637e6..39d0b18c1ba 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java @@ -10582,6 +10582,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte runTest("compiler/testData/diagnostics/tests/inference/completion/kt33166.kt"); } + @TestMetadata("lambdaWithVariableAndNothing.kt") + public void testLambdaWithVariableAndNothing() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/lambdaWithVariableAndNothing.kt"); + } + @TestMetadata("partialForIlt.kt") public void testPartialForIlt() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt"); @@ -17846,6 +17851,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte runTest("compiler/testData/diagnostics/tests/regressions/kt3535.kt"); } + @TestMetadata("kt35668.kt") + public void testKt35668() throws Exception { + runTest("compiler/testData/diagnostics/tests/regressions/kt35668.kt"); + } + @TestMetadata("kt3647.kt") public void testKt3647() throws Exception { runTest("compiler/testData/diagnostics/tests/regressions/kt3647.kt"); diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CompletionModeCalculator.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CompletionModeCalculator.kt index 9c28b04400c..671d5c2d1dd 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CompletionModeCalculator.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CompletionModeCalculator.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.components.TrivialConstraint import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints import org.jetbrains.kotlin.resolve.calls.model.KotlinResolutionCandidate +import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtom import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.UnwrappedType import org.jetbrains.kotlin.types.model.* @@ -39,11 +40,14 @@ class CompletionModeCalculator { if (csBuilder.isProperType(returnType)) return ConstraintSystemCompletionMode.FULL // For nested call with variables in return type check possibility of full completion - return CalculatorForNestedCall(returnType, csCompleterContext, trivialConstraintTypeInferenceOracle).computeCompletionMode() + return CalculatorForNestedCall( + candidate, returnType, csCompleterContext, trivialConstraintTypeInferenceOracle + ).computeCompletionMode() } } private class CalculatorForNestedCall( + private val candidate: KotlinResolutionCandidate, private val returnType: UnwrappedType?, private val csCompleterContext: CsCompleterContext, private val trivialConstraintTypeInferenceOracle: TrivialConstraintTypeInferenceOracle @@ -57,6 +61,10 @@ class CompletionModeCalculator { private val variablesWithQueuedConstraints = mutableSetOf() private val typesToProcess: Queue = ArrayDeque() + private val postponedAtoms: List by lazy { + KotlinConstraintSystemCompleter.getOrderedNotAnalyzedPostponedArguments(listOf(candidate.resolvedCall)) + } + fun computeCompletionMode(): ConstraintSystemCompletionMode = with(csCompleterContext) { // Add fixation directions for variables based on effective variance in type typesToProcess.add(returnType) @@ -163,13 +171,14 @@ class CompletionModeCalculator { direction: FixationDirection ): Boolean { val constraints = variableWithConstraints.constraints + val variable = variableWithConstraints.typeVariable // todo check correctness for @Exact return constraints.isNotEmpty() && constraints.any { constraint -> constraint.hasRequiredKind(direction) - && !constraint.type.typeConstructor().isIntegerLiteralTypeConstructor() && isProperType(constraint.type) - && trivialConstraintTypeInferenceOracle.isSuitableResultedType(constraint.type) + && !constraint.type.typeConstructor().isIntegerLiteralTypeConstructor() + && !isNothingConstraintForPartiallyAnalyzedVariable(constraint, variable) } } @@ -177,5 +186,16 @@ class CompletionModeCalculator { FixationDirection.TO_SUBTYPE -> kind.isLower() || kind.isEqual() FixationDirection.EQUALITY -> kind.isEqual() } + + private fun CsCompleterContext.isNothingConstraintForPartiallyAnalyzedVariable( + constraint: Constraint, + variable: TypeVariableMarker + ): Boolean { + if (trivialConstraintTypeInferenceOracle.isSuitableResultedType(constraint.type) || !constraint.kind.isLower()) + return false + return postponedAtoms.any { atom -> + atom.expectedType?.contains { type -> variable.defaultType() == type } ?: false + } + } } } \ 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 a3b8368c7f2..a72df3e662d 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 @@ -7,7 +7,6 @@ package org.jetbrains.kotlin.resolve.calls.inference.components import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType import org.jetbrains.kotlin.builtins.isBuiltinFunctionalTypeOrSubtype -import org.jetbrains.kotlin.resolve.calls.components.KotlinResolutionStatelessCallbacks import org.jetbrains.kotlin.resolve.calls.components.transformToResolvedLambda import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder import org.jetbrains.kotlin.resolve.calls.inference.model.* @@ -23,7 +22,6 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs class KotlinConstraintSystemCompleter( private val resultTypeResolver: ResultTypeResolver, val variableFixationFinder: VariableFixationFinder, - private val statelessCallbacks: KotlinResolutionStatelessCallbacks ) { enum class ConstraintSystemCompletionMode { FULL, @@ -214,23 +212,6 @@ class KotlinConstraintSystemCompleter( return false } - private fun getOrderedNotAnalyzedPostponedArguments(topLevelAtoms: List): List { - fun ResolvedAtom.process(to: MutableList) { - to.addIfNotNull(this.safeAs()?.takeUnless { it.analyzed }) - - if (analyzed) { - subResolvedAtoms?.forEach { it.process(to) } - } - } - - val notAnalyzedArguments = arrayListOf() - for (primitive in topLevelAtoms) { - primitive.process(notAnalyzedArguments) - } - - return notAnalyzedArguments - } - private fun getOrderedAllTypeVariables( c: Context, collectVariablesFromContext: Boolean, @@ -352,4 +333,23 @@ class KotlinConstraintSystemCompleter( return null } + + companion object { + fun getOrderedNotAnalyzedPostponedArguments(topLevelAtoms: List): List { + fun ResolvedAtom.process(to: MutableList) { + to.addIfNotNull(this.safeAs()?.takeUnless { it.analyzed }) + + if (analyzed) { + subResolvedAtoms?.forEach { it.process(to) } + } + } + + val notAnalyzedArguments = arrayListOf() + for (primitive in topLevelAtoms) { + primitive.process(notAnalyzedArguments) + } + + return notAnalyzedArguments + } + } } \ 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 c206beef9b1..83a43ec4c73 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/elvisNotProcessed.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/elvisNotProcessed.kt @@ -15,7 +15,7 @@ val ww = if (true) { { true } ?: null!! } else if (true) { - { true } ?: null!! + { true } ?: null!! } else { null!! diff --git a/compiler/testData/diagnostics/tests/inference/completion/lambdaWithVariableAndNothing.fir.kt b/compiler/testData/diagnostics/tests/inference/completion/lambdaWithVariableAndNothing.fir.kt new file mode 100644 index 00000000000..e8c34135f42 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/lambdaWithVariableAndNothing.fir.kt @@ -0,0 +1,52 @@ +// !LANGUAGE: +NewInference +// !DIAGNOSTICS: -UNUSED_PARAMETER + +fun id(it: K) = it + +fun smartCast(arg: E?, fn: () -> Any?): E = TODO() +fun noSmartCast1(arg: E1?, fn: () -> E1): E1 = TODO() +fun noSmartCast2(arg: E2?, fn: E2): E2 = TODO() +fun noSmartCast3(arg: E3?, fn: () -> F): E3 = TODO() +fun noSmartCast4(arg: E4?, fn: F): E4 = TODO() + + +fun testSmartCast(s: String?) { + id( + if (s != null) "" + else smartCast(null) { "" } + ) + s.length +} + +fun testNoSmartCast1(s: String?) { + id( + if (s != null) "" + else noSmartCast1(null) { "" } + ) + s.length +} + +fun testNoSmartCast2(s: String?) { + id( + if (s != null) ( {""} ) + else noSmartCast2(null) { "" } + ) + s.length +} + +fun testNoSmartCast3(s: String?) { + id( + if (s != null) "" + else noSmartCast3(null) { "" } + ) + s.length +} + +// KT-36069 +fun testNoSmartCast4(s: String?) { + id( + if (s != null) ( {""} ) + else noSmartCast4(null) { "" } + ) + s.length +} diff --git a/compiler/testData/diagnostics/tests/inference/completion/lambdaWithVariableAndNothing.kt b/compiler/testData/diagnostics/tests/inference/completion/lambdaWithVariableAndNothing.kt new file mode 100644 index 00000000000..cc50ca60bde --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/lambdaWithVariableAndNothing.kt @@ -0,0 +1,52 @@ +// !LANGUAGE: +NewInference +// !DIAGNOSTICS: -UNUSED_PARAMETER + +fun id(it: K) = it + +fun smartCast(arg: E?, fn: () -> Any?): E = TODO() +fun noSmartCast1(arg: E1?, fn: () -> E1): E1 = TODO() +fun noSmartCast2(arg: E2?, fn: E2): E2 = TODO() +fun noSmartCast3(arg: E3?, fn: () -> F): E3 = TODO() +fun noSmartCast4(arg: E4?, fn: F): E4 = TODO() + + +fun testSmartCast(s: String?) { + id( + if (s != null) "" + else smartCast(null) { "" } + ) + s.length +} + +fun testNoSmartCast1(s: String?) { + id( + if (s != null) "" + else noSmartCast1(null) { "" } + ) + s.length +} + +fun testNoSmartCast2(s: String?) { + id( + if (s != null) ( {""} ) + else noSmartCast2(null) { "" } + ) + s.length +} + +fun testNoSmartCast3(s: String?) { + id( + if (s != null) "" + else noSmartCast3(null) { "" } + ) + s.length +} + +// KT-36069 +fun testNoSmartCast4(s: String?) { + id( + if (s != null) ( {""} ) + else noSmartCast4(null) { "" } + ) + s.length +} diff --git a/compiler/testData/diagnostics/tests/inference/completion/lambdaWithVariableAndNothing.txt b/compiler/testData/diagnostics/tests/inference/completion/lambdaWithVariableAndNothing.txt new file mode 100644 index 00000000000..3375a3ee01f --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/completion/lambdaWithVariableAndNothing.txt @@ -0,0 +1,13 @@ +package + +public fun id(/*0*/ it: K): K +public fun noSmartCast1(/*0*/ arg: E1?, /*1*/ fn: () -> E1): E1 +public fun noSmartCast2(/*0*/ arg: E2?, /*1*/ fn: E2): E2 +public fun noSmartCast3(/*0*/ arg: E3?, /*1*/ fn: () -> F): E3 +public fun noSmartCast4(/*0*/ arg: E4?, /*1*/ fn: F): E4 +public fun smartCast(/*0*/ arg: E?, /*1*/ fn: () -> kotlin.Any?): E +public fun testNoSmartCast1(/*0*/ s: kotlin.String?): kotlin.Unit +public fun testNoSmartCast2(/*0*/ s: kotlin.String?): kotlin.Unit +public fun testNoSmartCast3(/*0*/ s: kotlin.String?): kotlin.Unit +public fun testNoSmartCast4(/*0*/ s: kotlin.String?): kotlin.Unit +public fun testSmartCast(/*0*/ s: kotlin.String?): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/regressions/kt35668.fir.kt b/compiler/testData/diagnostics/tests/regressions/kt35668.fir.kt new file mode 100644 index 00000000000..f32337094bb --- /dev/null +++ b/compiler/testData/diagnostics/tests/regressions/kt35668.fir.kt @@ -0,0 +1,20 @@ +// !LANGUAGE: +NewInference +// !DIAGNOSTICS: -UNUSED_PARAMETER + +fun main() { + val baseDir: String? = "" + val networkParameters: String? = "" + if (baseDir != null) { + if (networkParameters != null) { + Unit + } else if (true){ + return + } else { + return + } + } else { + return + } + + networkParameters.length // unsafe call +} diff --git a/compiler/testData/diagnostics/tests/regressions/kt35668.kt b/compiler/testData/diagnostics/tests/regressions/kt35668.kt new file mode 100644 index 00000000000..fc378921ef4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/regressions/kt35668.kt @@ -0,0 +1,20 @@ +// !LANGUAGE: +NewInference +// !DIAGNOSTICS: -UNUSED_PARAMETER + +fun main() { + val baseDir: String? = "" + val networkParameters: String? = "" + if (baseDir != null) { + if (networkParameters != null) { + Unit + } else if (true){ + return + } else { + return + } + } else { + return + } + + networkParameters.length // unsafe call +} diff --git a/compiler/testData/diagnostics/tests/regressions/kt35668.txt b/compiler/testData/diagnostics/tests/regressions/kt35668.txt new file mode 100644 index 00000000000..a9fef69810f --- /dev/null +++ b/compiler/testData/diagnostics/tests/regressions/kt35668.txt @@ -0,0 +1,3 @@ +package + +public fun main(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/when/kt9929.kt b/compiler/testData/diagnostics/tests/when/kt9929.kt index c94ac61f194..f1dc2682288 100644 --- a/compiler/testData/diagnostics/tests/when/kt9929.kt +++ b/compiler/testData/diagnostics/tests/when/kt9929.kt @@ -1,5 +1,5 @@ // !WITH_NEW_INFERENCE -val test: Int = if (true) { +val test: Int = if (true) { when (2) { 1 -> 1 else -> null diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 092f1046385..07ae2a1702f 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -10589,6 +10589,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { runTest("compiler/testData/diagnostics/tests/inference/completion/kt33166.kt"); } + @TestMetadata("lambdaWithVariableAndNothing.kt") + public void testLambdaWithVariableAndNothing() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/lambdaWithVariableAndNothing.kt"); + } + @TestMetadata("partialForIlt.kt") public void testPartialForIlt() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt"); @@ -17858,6 +17863,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { runTest("compiler/testData/diagnostics/tests/regressions/kt3535.kt"); } + @TestMetadata("kt35668.kt") + public void testKt35668() throws Exception { + runTest("compiler/testData/diagnostics/tests/regressions/kt35668.kt"); + } + @TestMetadata("kt3647.kt") public void testKt3647() throws Exception { runTest("compiler/testData/diagnostics/tests/regressions/kt3647.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index a954d9b505f..1db03737994 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -10584,6 +10584,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/inference/completion/kt33166.kt"); } + @TestMetadata("lambdaWithVariableAndNothing.kt") + public void testLambdaWithVariableAndNothing() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/completion/lambdaWithVariableAndNothing.kt"); + } + @TestMetadata("partialForIlt.kt") public void testPartialForIlt() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt"); @@ -17848,6 +17853,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/regressions/kt3535.kt"); } + @TestMetadata("kt35668.kt") + public void testKt35668() throws Exception { + runTest("compiler/testData/diagnostics/tests/regressions/kt35668.kt"); + } + @TestMetadata("kt3647.kt") public void testKt3647() throws Exception { runTest("compiler/testData/diagnostics/tests/regressions/kt3647.kt");