From 7f7bb705969ec0d5b4f8ceed005cc6bf8a499708 Mon Sep 17 00:00:00 2001 From: Victor Petukhov Date: Wed, 3 Mar 2021 16:02:10 +0300 Subject: [PATCH] Don't fix a type variable into the intersection type if there is an explicit expected type ^KT-43303 Fixed ^KT-42396 Fixed ^KT-42472 Fixed --- ...irOldFrontendDiagnosticsTestGenerated.java | 18 +++++++++++ .../components/ResultTypeResolver.kt | 15 ++++++++- .../model/ConstraintPositionAndErrors.kt | 3 ++ .../box/elvis/ofNonNullableResultType.kt | 2 +- .../inference/expectedTypeWithGenerics.kt | 2 +- .../specialConstructions/exclExclAsCall.kt | 2 +- ...IntersectUpperBoundWithExpectedType.fir.kt | 17 ++++++++++ ...dontIntersectUpperBoundWithExpectedType.kt | 17 ++++++++++ ...ontIntersectUpperBoundWithExpectedType.txt | 20 ++++++++++++ .../implicitNothingOnDelegates.kt | 2 +- .../tests/typeParameters/kt42396.fir.kt | 17 ++++++++++ .../tests/typeParameters/kt42396.kt | 17 ++++++++++ .../tests/typeParameters/kt42396.txt | 32 +++++++++++++++++++ .../tests/typeParameters/kt42472.fir.kt | 12 +++++++ .../tests/typeParameters/kt42472.kt | 12 +++++++ .../tests/typeParameters/kt42472.txt | 18 +++++++++++ .../annotationsForResolve/exactAnnotation.kt | 2 +- .../test/runners/DiagnosticTestGenerated.java | 18 +++++++++++ 18 files changed, 220 insertions(+), 6 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/typeParameters/dontIntersectUpperBoundWithExpectedType.fir.kt create mode 100644 compiler/testData/diagnostics/tests/typeParameters/dontIntersectUpperBoundWithExpectedType.kt create mode 100644 compiler/testData/diagnostics/tests/typeParameters/dontIntersectUpperBoundWithExpectedType.txt create mode 100644 compiler/testData/diagnostics/tests/typeParameters/kt42396.fir.kt create mode 100644 compiler/testData/diagnostics/tests/typeParameters/kt42396.kt create mode 100644 compiler/testData/diagnostics/tests/typeParameters/kt42396.txt create mode 100644 compiler/testData/diagnostics/tests/typeParameters/kt42472.fir.kt create mode 100644 compiler/testData/diagnostics/tests/typeParameters/kt42472.kt create mode 100644 compiler/testData/diagnostics/tests/typeParameters/kt42472.txt diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index 485f7919e2c..3b262b3bfb4 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -28731,6 +28731,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/typeParameters/deprecatedSyntax.kt"); } + @Test + @TestMetadata("dontIntersectUpperBoundWithExpectedType.kt") + public void testDontIntersectUpperBoundWithExpectedType() throws Exception { + runTest("compiler/testData/diagnostics/tests/typeParameters/dontIntersectUpperBoundWithExpectedType.kt"); + } + @Test @TestMetadata("extFunctionTypeAsUpperBound.kt") public void testExtFunctionTypeAsUpperBound() throws Exception { @@ -28767,6 +28773,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/typeParameters/implicitNothingOnDelegates.kt"); } + @Test + @TestMetadata("kt42396.kt") + public void testKt42396() throws Exception { + runTest("compiler/testData/diagnostics/tests/typeParameters/kt42396.kt"); + } + + @Test + @TestMetadata("kt42472.kt") + public void testKt42472() throws Exception { + runTest("compiler/testData/diagnostics/tests/typeParameters/kt42472.kt"); + } + @Test @TestMetadata("misplacedConstraints.kt") public void testMisplacedConstraints() throws Exception { diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt index 84e5affdb0c..691aa9db840 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt @@ -215,7 +215,20 @@ class ResultTypeResolver( val upperConstraints = variableWithConstraints.constraints.filter { it.kind == ConstraintKind.UPPER && this@findSuperType.isProperTypeForFixation(it.type) } if (upperConstraints.isNotEmpty()) { - val upperType = intersectTypes(upperConstraints.map { it.type }) + val intersectionUpperType = intersectTypes(upperConstraints.map { it.type }) + val isThereExpectedTypeConstraint = upperConstraints.any { it.isExpectedTypePosition() } + val nonExpectedTypeConstraints = upperConstraints.filterNot { it.isExpectedTypePosition() } + val resultIsActuallyIntersection = intersectionUpperType.typeConstructor().isIntersection() + val upperType = if (isThereExpectedTypeConstraint && nonExpectedTypeConstraints.isNotEmpty() && resultIsActuallyIntersection) { + /* + * We shouldn't infer a type variable into the intersection type if there is an explicit expected type, + * otherwise it can lead to something like this: + * + * fun materialize(): T = null as T + * val bar: Int = materialize() // no errors, T is inferred into String & Int + */ + intersectTypes(nonExpectedTypeConstraints.map { it.type }) + } else intersectionUpperType return typeApproximator.approximateToSubType( upperType, diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintPositionAndErrors.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintPositionAndErrors.kt index 7289d3cbaca..0bf1dbb0d47 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintPositionAndErrors.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintPositionAndErrors.kt @@ -106,3 +106,6 @@ class ConstrainingTypeIsError( class OnlyInputTypesDiagnostic(val typeVariable: TypeVariableMarker) : ConstraintSystemError(INAPPLICABLE) object LowerPriorityToPreserveCompatibility : ConstraintSystemError(RESOLVED_NEED_PRESERVE_COMPATIBILITY) + +fun Constraint.isExpectedTypePosition() = + position.from is ExpectedTypeConstraintPosition<*> || position.from is DelegatedPropertyConstraintPosition<*> diff --git a/compiler/testData/codegen/box/elvis/ofNonNullableResultType.kt b/compiler/testData/codegen/box/elvis/ofNonNullableResultType.kt index 0564ce69ccd..8f707fbf302 100644 --- a/compiler/testData/codegen/box/elvis/ofNonNullableResultType.kt +++ b/compiler/testData/codegen/box/elvis/ofNonNullableResultType.kt @@ -2,6 +2,6 @@ fun f(): T? = "OK" as? T fun g(): Nothing = throw RuntimeException("fail") -fun h(): T = run { f() } ?: run { g() } +fun h(): T = run { f() } ?: run { g() } fun box(): String = h() diff --git a/compiler/testData/diagnostics/tests/inference/expectedTypeWithGenerics.kt b/compiler/testData/diagnostics/tests/inference/expectedTypeWithGenerics.kt index bf83286aed6..1334956aaa4 100644 --- a/compiler/testData/diagnostics/tests/inference/expectedTypeWithGenerics.kt +++ b/compiler/testData/diagnostics/tests/inference/expectedTypeWithGenerics.kt @@ -12,7 +12,7 @@ fun test(x: X) { fun g() { fun foo(): T = TODO() - val y = foo() as Int + val y = foo() as Int val y2 = foo() as D } diff --git a/compiler/testData/diagnostics/tests/resolve/specialConstructions/exclExclAsCall.kt b/compiler/testData/diagnostics/tests/resolve/specialConstructions/exclExclAsCall.kt index 63f6dc53415..aed12c733ab 100644 --- a/compiler/testData/diagnostics/tests/resolve/specialConstructions/exclExclAsCall.kt +++ b/compiler/testData/diagnostics/tests/resolve/specialConstructions/exclExclAsCall.kt @@ -14,7 +14,7 @@ fun emptyNullableListOfA(): List? = null fun testExclExcl() { doList(emptyNullableListOfA()!!) //should be an error here - val l: List = id(emptyNullableListOfA()!!) + val l: List = id(emptyNullableListOfA()!!) doList(strangeNullableList { doInt(it) }!!) //lambda should be analyzed (at completion phase) } diff --git a/compiler/testData/diagnostics/tests/typeParameters/dontIntersectUpperBoundWithExpectedType.fir.kt b/compiler/testData/diagnostics/tests/typeParameters/dontIntersectUpperBoundWithExpectedType.fir.kt new file mode 100644 index 00000000000..1a3513b89b0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/typeParameters/dontIntersectUpperBoundWithExpectedType.fir.kt @@ -0,0 +1,17 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE + +open class Foo + +class Bar + +fun foo(): T? { + return null +} + +fun main() { + val a: Bar? = foo() +} + + +fun wtf(): T = TODO() +val bar: Int = wtf() // happily compiles diff --git a/compiler/testData/diagnostics/tests/typeParameters/dontIntersectUpperBoundWithExpectedType.kt b/compiler/testData/diagnostics/tests/typeParameters/dontIntersectUpperBoundWithExpectedType.kt new file mode 100644 index 00000000000..b5bd428c04f --- /dev/null +++ b/compiler/testData/diagnostics/tests/typeParameters/dontIntersectUpperBoundWithExpectedType.kt @@ -0,0 +1,17 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE + +open class Foo + +class Bar + +fun foo(): T? { + return null +} + +fun main() { + val a: Bar? = foo() +} + + +fun wtf(): T = TODO() +val bar: Int = wtf() // happily compiles diff --git a/compiler/testData/diagnostics/tests/typeParameters/dontIntersectUpperBoundWithExpectedType.txt b/compiler/testData/diagnostics/tests/typeParameters/dontIntersectUpperBoundWithExpectedType.txt new file mode 100644 index 00000000000..0409dfb5718 --- /dev/null +++ b/compiler/testData/diagnostics/tests/typeParameters/dontIntersectUpperBoundWithExpectedType.txt @@ -0,0 +1,20 @@ +package + +public val bar: kotlin.Int +public fun foo(): T? +public fun main(): kotlin.Unit +public fun wtf(): 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 open class Foo { + public constructor Foo() + 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/typeParameters/implicitNothingOnDelegates.kt b/compiler/testData/diagnostics/tests/typeParameters/implicitNothingOnDelegates.kt index dc200ea99da..0553d167d1e 100644 --- a/compiler/testData/diagnostics/tests/typeParameters/implicitNothingOnDelegates.kt +++ b/compiler/testData/diagnostics/tests/typeParameters/implicitNothingOnDelegates.kt @@ -18,6 +18,6 @@ private object Scope { fun materialize(): T = Any() as T fun test(i: Inv) { - val p: Int by Scope.Delegate(i) + val p: Int by Scope.Delegate(i) } } diff --git a/compiler/testData/diagnostics/tests/typeParameters/kt42396.fir.kt b/compiler/testData/diagnostics/tests/typeParameters/kt42396.fir.kt new file mode 100644 index 00000000000..b5a9710868c --- /dev/null +++ b/compiler/testData/diagnostics/tests/typeParameters/kt42396.fir.kt @@ -0,0 +1,17 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE + +interface A +interface B + +class Out +class Inv + +fun materializeOutOfAAndB(): Out where F : A, F : B = Out() +fun materializeInvOfAAndB(): Inv where F : A, F : B = Inv() +fun wrapAAndBToOut(x: F): Out where F : A, F : B = Out() + +fun main(a: A) { + val x: Out = materializeOutOfAAndB() // OI: inferred type A is not a subtype of B; `F` is instantiated as `A`, so upper bounds was violated + val y: Inv = materializeInvOfAAndB() // OI and NI: required B, found A + val z: Out = wrapAAndBToOut(a) // OI and NI: required B, found A +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/typeParameters/kt42396.kt b/compiler/testData/diagnostics/tests/typeParameters/kt42396.kt new file mode 100644 index 00000000000..51c1a436c0b --- /dev/null +++ b/compiler/testData/diagnostics/tests/typeParameters/kt42396.kt @@ -0,0 +1,17 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE + +interface A +interface B + +class Out +class Inv + +fun materializeOutOfAAndB(): Out where F : A, F : B = Out() +fun materializeInvOfAAndB(): Inv where F : A, F : B = Inv() +fun wrapAAndBToOut(x: F): Out where F : A, F : B = Out() + +fun main(a: A) { + val x: Out = materializeOutOfAAndB() // OI: inferred type A is not a subtype of B; `F` is instantiated as `A`, so upper bounds was violated + val y: Inv = materializeInvOfAAndB() // OI and NI: required B, found A + val z: Out = wrapAAndBToOut(a) // OI and NI: required B, found A +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/typeParameters/kt42396.txt b/compiler/testData/diagnostics/tests/typeParameters/kt42396.txt new file mode 100644 index 00000000000..4c83036d33c --- /dev/null +++ b/compiler/testData/diagnostics/tests/typeParameters/kt42396.txt @@ -0,0 +1,32 @@ +package + +public fun main(/*0*/ a: A): kotlin.Unit +public fun materializeInvOfAAndB(): Inv where F : B +public fun materializeOutOfAAndB(): Out where F : B +public fun wrapAAndBToOut(/*0*/ x: F): Out where F : B + +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 { + 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 Inv { + public constructor Inv() + 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 Out { + public constructor Out() + 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/typeParameters/kt42472.fir.kt b/compiler/testData/diagnostics/tests/typeParameters/kt42472.fir.kt new file mode 100644 index 00000000000..9526b7e7201 --- /dev/null +++ b/compiler/testData/diagnostics/tests/typeParameters/kt42472.fir.kt @@ -0,0 +1,12 @@ +// WITH_REFLECT + +import kotlin.reflect.KProperty + +fun interface ReadOnlyProperty { + operator fun getValue(thisRef: T, property: KProperty<*>): V +} + +class Problem { + val variable: Int by delegate() // delegate returns `ReadOnlyProperty` + fun delegate() = null as ReadOnlyProperty +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/typeParameters/kt42472.kt b/compiler/testData/diagnostics/tests/typeParameters/kt42472.kt new file mode 100644 index 00000000000..9526b7e7201 --- /dev/null +++ b/compiler/testData/diagnostics/tests/typeParameters/kt42472.kt @@ -0,0 +1,12 @@ +// WITH_REFLECT + +import kotlin.reflect.KProperty + +fun interface ReadOnlyProperty { + operator fun getValue(thisRef: T, property: KProperty<*>): V +} + +class Problem { + val variable: Int by delegate() // delegate returns `ReadOnlyProperty` + fun delegate() = null as ReadOnlyProperty +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/typeParameters/kt42472.txt b/compiler/testData/diagnostics/tests/typeParameters/kt42472.txt new file mode 100644 index 00000000000..25b09a04227 --- /dev/null +++ b/compiler/testData/diagnostics/tests/typeParameters/kt42472.txt @@ -0,0 +1,18 @@ +package + +public final class Problem { + public constructor Problem() + public final val variable: kotlin.Int + public final fun delegate(): ReadOnlyProperty + 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 fun interface ReadOnlyProperty { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract operator fun getValue(/*0*/ thisRef: T, /*1*/ property: kotlin.reflect.KProperty<*>): V + 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/inference/annotationsForResolve/exactAnnotation.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/exactAnnotation.kt index 613f83cb9cd..a5834d19d3c 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/exactAnnotation.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/exactAnnotation.kt @@ -8,5 +8,5 @@ fun test1(l: List) { val i: Int = l.firstTyped() - val s: String = l.firstTyped() + val s: String = l.firstTyped() } diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 215f7d5612b..519a7a1a6cf 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -28827,6 +28827,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/typeParameters/deprecatedSyntax.kt"); } + @Test + @TestMetadata("dontIntersectUpperBoundWithExpectedType.kt") + public void testDontIntersectUpperBoundWithExpectedType() throws Exception { + runTest("compiler/testData/diagnostics/tests/typeParameters/dontIntersectUpperBoundWithExpectedType.kt"); + } + @Test @TestMetadata("extFunctionTypeAsUpperBound.kt") public void testExtFunctionTypeAsUpperBound() throws Exception { @@ -28863,6 +28869,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/typeParameters/implicitNothingOnDelegates.kt"); } + @Test + @TestMetadata("kt42396.kt") + public void testKt42396() throws Exception { + runTest("compiler/testData/diagnostics/tests/typeParameters/kt42396.kt"); + } + + @Test + @TestMetadata("kt42472.kt") + public void testKt42472() throws Exception { + runTest("compiler/testData/diagnostics/tests/typeParameters/kt42472.kt"); + } + @Test @TestMetadata("misplacedConstraints.kt") public void testMisplacedConstraints() throws Exception {