From 796cdea50eb03a163e3b5127015b9f7561d04bac Mon Sep 17 00:00:00 2001 From: Mikhail Zarechenskiy Date: Tue, 18 Jun 2019 17:50:27 +0300 Subject: [PATCH] [NI] Require all proper constraints for `Exact` return type Consider call `foo(bar())` where bar() returns some type variable `T`; We had a contract that call `bar` can be completed without completion of foo (type variables can be inferred from the current context) if `T` has at least one proper lower constraint (ProperType <: T). Indeed, new constraints can be added only as upper ones, so there is no need to grow constraint system. Unfortunately, we have Exact annotation that is used on return type of elvis. Now, consider the following situation: ``` fun foo(a: Any) {} fun bar(e: T): @Exact T foo(bar("str")) ``` Here, because of Exact annotation, constraint with `Any`-type will be added as an equal one => our prerequisite that there will be no new lower constraints is false. `bar("str")` is inferred to Any in OI, this seems conceptually wrong, but it's another topic of discussion. In NI we can't just grow constraint system to use outer call because of another important use-case: ``` fun generic(i: Inv) {} fun test(a: Inv<*>?, b: Inv<*>) { generic(a ?: b) } ``` Common constraint system for these two calls can't be solved (fundamentally) for this example, only if (a ?: b) and generic(result) are computed separately. So, to mitigate initial issue, we'll grow constraint system only if there is at least one non-proper constraint. #KT-31969 Fixed --- .../fir/FirDiagnosticsSmokeTestGenerated.java | 10 ++++++++ .../calls/components/KotlinCallCompleter.kt | 7 ++++-- .../tests/inference/commonSystem/kt31969.kt | 2 +- ...cTypeForArgumentCallWithExactAnnotation.kt | 17 ++++++++++++++ ...TypeForArgumentCallWithExactAnnotation.txt | 23 +++++++++++++++++++ ...peForArgumentCallWithExactAnnotation_ni.kt | 17 ++++++++++++++ ...eForArgumentCallWithExactAnnotation_ni.txt | 23 +++++++++++++++++++ .../postponedCompletionWithExactAnnotation.kt | 1 + ...stponedCompletionWithExactAnnotation_ni.kt | 2 +- .../checkers/DiagnosticsTestGenerated.java | 10 ++++++++ .../DiagnosticsUsingJavacTestGenerated.java | 10 ++++++++ 11 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/inference/commonSystem/lessSpecificTypeForArgumentCallWithExactAnnotation.kt create mode 100644 compiler/testData/diagnostics/tests/inference/commonSystem/lessSpecificTypeForArgumentCallWithExactAnnotation.txt create mode 100644 compiler/testData/diagnostics/tests/inference/commonSystem/lessSpecificTypeForArgumentCallWithExactAnnotation_ni.kt create mode 100644 compiler/testData/diagnostics/tests/inference/commonSystem/lessSpecificTypeForArgumentCallWithExactAnnotation_ni.txt diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java index 361d76d658b..3bb595cc7e2 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java @@ -10011,6 +10011,16 @@ public class FirDiagnosticsSmokeTestGenerated extends AbstractFirDiagnosticsSmok runTest("compiler/testData/diagnostics/tests/inference/commonSystem/kt3372toCollection.kt"); } + @TestMetadata("lessSpecificTypeForArgumentCallWithExactAnnotation.kt") + public void testLessSpecificTypeForArgumentCallWithExactAnnotation() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/commonSystem/lessSpecificTypeForArgumentCallWithExactAnnotation.kt"); + } + + @TestMetadata("lessSpecificTypeForArgumentCallWithExactAnnotation_ni.kt") + public void testLessSpecificTypeForArgumentCallWithExactAnnotation_ni() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/commonSystem/lessSpecificTypeForArgumentCallWithExactAnnotation_ni.kt"); + } + @TestMetadata("nestedLambdas.kt") public void testNestedLambdas() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/commonSystem/nestedLambdas.kt"); 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 12e0e5c3982..64ae07b98dc 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 @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage.Empt import org.jetbrains.kotlin.resolve.calls.inference.model.ExpectedTypeConstraintPosition import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.tower.forceResolution +import org.jetbrains.kotlin.resolve.descriptorUtil.hasExactAnnotation import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.UnwrappedType @@ -242,14 +243,16 @@ class KotlinCallCompleter( val constructor = typeVariable.constructor val variableWithConstraints = csBuilder.currentStorage().notFixedTypeVariables[constructor] ?: return false val constraints = variableWithConstraints.constraints - return constraints.isNotEmpty() && constraints.any { + return constraints.isNotEmpty() && constraints.anyOrAll(requireAll = typeVariable.hasExactAnnotation()) { !it.type.typeConstructor(context).isIntegerLiteralTypeConstructor(context) && (it.kind.isLower() || it.kind.isEqual()) && csBuilder.isProperType(it.type) } - } + private inline fun Iterable.anyOrAll(requireAll: Boolean, p: (T) -> Boolean): Boolean = + if (requireAll) all(p) else any(p) + private fun KotlinResolutionCandidate.computeReturnTypeWithSmartCastInfo( returnType: UnwrappedType, resolutionCallbacks: KotlinResolutionCallbacks diff --git a/compiler/testData/diagnostics/tests/inference/commonSystem/kt31969.kt b/compiler/testData/diagnostics/tests/inference/commonSystem/kt31969.kt index 24beb4355a6..6477411e023 100644 --- a/compiler/testData/diagnostics/tests/inference/commonSystem/kt31969.kt +++ b/compiler/testData/diagnostics/tests/inference/commonSystem/kt31969.kt @@ -4,7 +4,7 @@ open class View fun test() { - val target = foo() ?: foo() ?: run {} + val target = foo() ?: foo() ?: run {} } fun foo(): T? { diff --git a/compiler/testData/diagnostics/tests/inference/commonSystem/lessSpecificTypeForArgumentCallWithExactAnnotation.kt b/compiler/testData/diagnostics/tests/inference/commonSystem/lessSpecificTypeForArgumentCallWithExactAnnotation.kt new file mode 100644 index 00000000000..2ef39a49a17 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/commonSystem/lessSpecificTypeForArgumentCallWithExactAnnotation.kt @@ -0,0 +1,17 @@ +// !LANGUAGE: -NewInference +// !DIAGNOSTICS: -UNUSED_PARAMETER + +interface A +interface B : A +interface C : A + +@Suppress("INVISIBLE_REFERENCE") +fun select(x: K, y: K): @kotlin.internal.Exact K = x + +fun foo(a: Any) {} + +fun test(b: B, c: C) { + foo( + select(b, c) + ) +} diff --git a/compiler/testData/diagnostics/tests/inference/commonSystem/lessSpecificTypeForArgumentCallWithExactAnnotation.txt b/compiler/testData/diagnostics/tests/inference/commonSystem/lessSpecificTypeForArgumentCallWithExactAnnotation.txt new file mode 100644 index 00000000000..02d30ed8689 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/commonSystem/lessSpecificTypeForArgumentCallWithExactAnnotation.txt @@ -0,0 +1,23 @@ +package + +public fun foo(/*0*/ a: kotlin.Any): kotlin.Unit +@kotlin.Suppress(names = {"INVISIBLE_REFERENCE"}) public fun select(/*0*/ x: K, /*1*/ y: K): K +public fun test(/*0*/ b: B, /*1*/ c: C): kotlin.Unit + +public interface 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 +} + +public interface B : 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 +} + +public interface C : 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/testData/diagnostics/tests/inference/commonSystem/lessSpecificTypeForArgumentCallWithExactAnnotation_ni.kt b/compiler/testData/diagnostics/tests/inference/commonSystem/lessSpecificTypeForArgumentCallWithExactAnnotation_ni.kt new file mode 100644 index 00000000000..01900f71d39 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/commonSystem/lessSpecificTypeForArgumentCallWithExactAnnotation_ni.kt @@ -0,0 +1,17 @@ +// !LANGUAGE: +NewInference +// !DIAGNOSTICS: -UNUSED_PARAMETER + +interface A +interface B : A +interface C : A + +@Suppress("INVISIBLE_REFERENCE") +fun select(x: K, y: K): @kotlin.internal.Exact K = x + +fun foo(a: Any) {} + +fun test(b: B, c: C) { + foo( + select(b, c) + ) +} diff --git a/compiler/testData/diagnostics/tests/inference/commonSystem/lessSpecificTypeForArgumentCallWithExactAnnotation_ni.txt b/compiler/testData/diagnostics/tests/inference/commonSystem/lessSpecificTypeForArgumentCallWithExactAnnotation_ni.txt new file mode 100644 index 00000000000..02d30ed8689 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/commonSystem/lessSpecificTypeForArgumentCallWithExactAnnotation_ni.txt @@ -0,0 +1,23 @@ +package + +public fun foo(/*0*/ a: kotlin.Any): kotlin.Unit +@kotlin.Suppress(names = {"INVISIBLE_REFERENCE"}) public fun select(/*0*/ x: K, /*1*/ y: K): K +public fun test(/*0*/ b: B, /*1*/ c: C): kotlin.Unit + +public interface 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 +} + +public interface B : 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 +} + +public interface C : 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/testData/diagnostics/tests/inference/commonSystem/postponedCompletionWithExactAnnotation.kt b/compiler/testData/diagnostics/tests/inference/commonSystem/postponedCompletionWithExactAnnotation.kt index 66f4cee761e..2d1baf24d3e 100644 --- a/compiler/testData/diagnostics/tests/inference/commonSystem/postponedCompletionWithExactAnnotation.kt +++ b/compiler/testData/diagnostics/tests/inference/commonSystem/postponedCompletionWithExactAnnotation.kt @@ -1,4 +1,5 @@ // !DIAGNOSTICS: -UNUSED_PARAMETER +// !LANGUAGE: -NewInference interface ISample diff --git a/compiler/testData/diagnostics/tests/inference/commonSystem/postponedCompletionWithExactAnnotation_ni.kt b/compiler/testData/diagnostics/tests/inference/commonSystem/postponedCompletionWithExactAnnotation_ni.kt index 19fc23b88aa..756fa69c457 100644 --- a/compiler/testData/diagnostics/tests/inference/commonSystem/postponedCompletionWithExactAnnotation_ni.kt +++ b/compiler/testData/diagnostics/tests/inference/commonSystem/postponedCompletionWithExactAnnotation_ni.kt @@ -22,7 +22,7 @@ fun test(nullableSample: ISample, any: Any) { ) elvisSimple( - elvisExact(nullableSample, materialize()), + elvisExact(nullableSample, materialize()), any ) } diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 4ff23e87928..5053c106238 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -10018,6 +10018,16 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { runTest("compiler/testData/diagnostics/tests/inference/commonSystem/kt3372toCollection.kt"); } + @TestMetadata("lessSpecificTypeForArgumentCallWithExactAnnotation.kt") + public void testLessSpecificTypeForArgumentCallWithExactAnnotation() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/commonSystem/lessSpecificTypeForArgumentCallWithExactAnnotation.kt"); + } + + @TestMetadata("lessSpecificTypeForArgumentCallWithExactAnnotation_ni.kt") + public void testLessSpecificTypeForArgumentCallWithExactAnnotation_ni() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/commonSystem/lessSpecificTypeForArgumentCallWithExactAnnotation_ni.kt"); + } + @TestMetadata("nestedLambdas.kt") public void testNestedLambdas() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/commonSystem/nestedLambdas.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 1e9c707b053..31766c56fa8 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -10013,6 +10013,16 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/inference/commonSystem/kt3372toCollection.kt"); } + @TestMetadata("lessSpecificTypeForArgumentCallWithExactAnnotation.kt") + public void testLessSpecificTypeForArgumentCallWithExactAnnotation() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/commonSystem/lessSpecificTypeForArgumentCallWithExactAnnotation.kt"); + } + + @TestMetadata("lessSpecificTypeForArgumentCallWithExactAnnotation_ni.kt") + public void testLessSpecificTypeForArgumentCallWithExactAnnotation_ni() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/commonSystem/lessSpecificTypeForArgumentCallWithExactAnnotation_ni.kt"); + } + @TestMetadata("nestedLambdas.kt") public void testNestedLambdas() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/commonSystem/nestedLambdas.kt");