From 91d2f32a572571b0c5030c53e549e3c67d778178 Mon Sep 17 00:00:00 2001 From: Victor Petukhov Date: Thu, 11 Mar 2021 12:50:42 +0300 Subject: [PATCH] Don't lose upper non-expected type constraints to include them to intersection type during finding the result type of the fixing type variable --- ...irOldFrontendDiagnosticsTestGenerated.java | 6 +++++ .../model/MutableConstraintStorage.kt | 10 +++++++ ...IntersectUpperBoundWithExpectedType.fir.kt | 4 +-- ...dontLoseUpperNonExpectedTypeConstraints.kt | 15 +++++++++++ ...ontLoseUpperNonExpectedTypeConstraints.txt | 27 +++++++++++++++++++ .../tests/typeParameters/kt42042.fir.kt | 2 +- .../tests/typeParameters/kt42396.fir.kt | 6 ++--- .../tests/typeParameters/kt42472.fir.kt | 4 +-- .../test/runners/DiagnosticTestGenerated.java | 6 +++++ 9 files changed, 72 insertions(+), 8 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/typeParameters/dontLoseUpperNonExpectedTypeConstraints.kt create mode 100644 compiler/testData/diagnostics/tests/typeParameters/dontLoseUpperNonExpectedTypeConstraints.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 e4b96f629b0..b650082203f 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 @@ -28737,6 +28737,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/typeParameters/dontIntersectUpperBoundWithExpectedType.kt"); } + @Test + @TestMetadata("dontLoseUpperNonExpectedTypeConstraints.kt") + public void testDontLoseUpperNonExpectedTypeConstraints() throws Exception { + runTest("compiler/testData/diagnostics/tests/typeParameters/dontLoseUpperNonExpectedTypeConstraints.kt"); + } + @Test @TestMetadata("extFunctionTypeAsUpperBound.kt") public void testExtFunctionTypeAsUpperBound() throws Exception { diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt index f235223fc93..b2c0ccbdf0a 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt @@ -125,6 +125,16 @@ class MutableVariableWithConstraints private constructor( if (old.position.from is DeclaredUpperBoundConstraintPosition<*> && new.position.from !is DeclaredUpperBoundConstraintPosition<*>) return false + /* + * We discriminate upper expected type constraints during finding a result type to fix variable (see ResultTypeResolver.kt): + * namely, we don't intersect the expected type with other upper constraints' types to prevent cases like this: + * fun materialize(): T = null as T + * val bar: Int = materialize() // T is inferred into String & Int without discriminating upper expected type constraints + * So here we shouldn't lose upper non-expected type constraints. + */ + if (old.position.from is ExpectedTypeConstraintPosition<*> && new.position.from !is ExpectedTypeConstraintPosition<*> && old.kind.isUpper() && new.kind.isUpper()) + return false + return when (old.kind) { ConstraintKind.EQUALITY -> true ConstraintKind.LOWER -> new.kind.isLower() diff --git a/compiler/testData/diagnostics/tests/typeParameters/dontIntersectUpperBoundWithExpectedType.fir.kt b/compiler/testData/diagnostics/tests/typeParameters/dontIntersectUpperBoundWithExpectedType.fir.kt index 1a3513b89b0..a8b6e5a069b 100644 --- a/compiler/testData/diagnostics/tests/typeParameters/dontIntersectUpperBoundWithExpectedType.fir.kt +++ b/compiler/testData/diagnostics/tests/typeParameters/dontIntersectUpperBoundWithExpectedType.fir.kt @@ -9,9 +9,9 @@ fun foo(): T? { } fun main() { - val a: Bar? = foo() + val a: Bar? = foo() } fun wtf(): T = TODO() -val bar: Int = wtf() // happily compiles +val bar: Int = wtf() // happily compiles diff --git a/compiler/testData/diagnostics/tests/typeParameters/dontLoseUpperNonExpectedTypeConstraints.kt b/compiler/testData/diagnostics/tests/typeParameters/dontLoseUpperNonExpectedTypeConstraints.kt new file mode 100644 index 00000000000..0d0bba6ef3e --- /dev/null +++ b/compiler/testData/diagnostics/tests/typeParameters/dontLoseUpperNonExpectedTypeConstraints.kt @@ -0,0 +1,15 @@ +// FIR_IDENTICAL +// !DIAGNOSTICS: -UNUSED_PARAMETER -CAST_NEVER_SUCCEEDS + +open class Expression + +class ModOp( + val expr1: Expression, + val expr2: Expression +) + +class QueryParameter : Expression() + +fun Expression.wrap(value: T): QueryParameter = null as QueryParameter + +fun Expression.rem(t: S): ModOp = ModOp(this, wrap(t)) diff --git a/compiler/testData/diagnostics/tests/typeParameters/dontLoseUpperNonExpectedTypeConstraints.txt b/compiler/testData/diagnostics/tests/typeParameters/dontLoseUpperNonExpectedTypeConstraints.txt new file mode 100644 index 00000000000..e63900f1dba --- /dev/null +++ b/compiler/testData/diagnostics/tests/typeParameters/dontLoseUpperNonExpectedTypeConstraints.txt @@ -0,0 +1,27 @@ +package + +public fun Expression.rem(/*0*/ t: S): ModOp +public fun Expression.wrap(/*0*/ value: T): QueryParameter + +public open class Expression { + public constructor Expression() + 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 ModOp { + public constructor ModOp(/*0*/ expr1: Expression, /*1*/ expr2: Expression) + public final val expr1: Expression + public final val expr2: Expression + 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 QueryParameter : Expression { + public constructor QueryParameter() + 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/kt42042.fir.kt b/compiler/testData/diagnostics/tests/typeParameters/kt42042.fir.kt index cc6fee6ab05..564a991062b 100644 --- a/compiler/testData/diagnostics/tests/typeParameters/kt42042.fir.kt +++ b/compiler/testData/diagnostics/tests/typeParameters/kt42042.fir.kt @@ -6,6 +6,6 @@ sealed class Subtype { } fun unsafeCast(value: A): B { - val proof: Subtype = Subtype.Trivial() + val proof: Subtype = Subtype.Trivial() return proof.cast(value) } diff --git a/compiler/testData/diagnostics/tests/typeParameters/kt42396.fir.kt b/compiler/testData/diagnostics/tests/typeParameters/kt42396.fir.kt index b5a9710868c..2f2dee34a5d 100644 --- a/compiler/testData/diagnostics/tests/typeParameters/kt42396.fir.kt +++ b/compiler/testData/diagnostics/tests/typeParameters/kt42396.fir.kt @@ -11,7 +11,7 @@ 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 + 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/kt42472.fir.kt b/compiler/testData/diagnostics/tests/typeParameters/kt42472.fir.kt index 9526b7e7201..d0caa6e96e5 100644 --- a/compiler/testData/diagnostics/tests/typeParameters/kt42472.fir.kt +++ b/compiler/testData/diagnostics/tests/typeParameters/kt42472.fir.kt @@ -7,6 +7,6 @@ fun interface ReadOnlyProperty { } class Problem { - val variable: Int by delegate() // delegate returns `ReadOnlyProperty` - fun delegate() = null as ReadOnlyProperty + val variable: Int by delegate() // delegate returns `ReadOnlyProperty` + fun delegate() = null as ReadOnlyProperty } \ No newline at end of file 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 5077b3dd679..833867abbfa 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 @@ -28833,6 +28833,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/typeParameters/dontIntersectUpperBoundWithExpectedType.kt"); } + @Test + @TestMetadata("dontLoseUpperNonExpectedTypeConstraints.kt") + public void testDontLoseUpperNonExpectedTypeConstraints() throws Exception { + runTest("compiler/testData/diagnostics/tests/typeParameters/dontLoseUpperNonExpectedTypeConstraints.kt"); + } + @Test @TestMetadata("extFunctionTypeAsUpperBound.kt") public void testExtFunctionTypeAsUpperBound() throws Exception {