Don't lose upper non-expected type constraints to include them to intersection type during finding the result type of the fixing type variable

This commit is contained in:
Victor Petukhov
2021-03-11 12:50:42 +03:00
parent b87c2a15b5
commit 91d2f32a57
9 changed files with 72 additions and 8 deletions
@@ -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 {
@@ -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 <T : String> 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()
@@ -9,9 +9,9 @@ fun <T : Foo> foo(): T? {
}
fun main() {
val a: Bar? = <!DEBUG_INFO_EXPRESSION_TYPE("Bar?")!><!TYPE_MISMATCH!>foo<!>()<!>
val a: Bar? = <!DEBUG_INFO_EXPRESSION_TYPE("Foo? & Bar?")!>foo()<!>
}
fun <T : Appendable> wtf(): T = TODO()
val bar: Int = <!TYPE_MISMATCH!>wtf<!>() // happily compiles
val bar: Int = wtf() // happily compiles
@@ -0,0 +1,15 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER -CAST_NEVER_SUCCEEDS
open class Expression<K>
class ModOp<T : Number?, S : Number?>(
val expr1: Expression<T>,
val expr2: Expression<S>
)
class QueryParameter<A> : Expression<A>()
fun <T, S : T?> Expression<in S>.wrap(value: T): QueryParameter<T> = null as QueryParameter<T>
fun <T : Number?, S : T> Expression<T>.rem(t: S): ModOp<T, S> = ModOp(this, wrap(t))
@@ -0,0 +1,27 @@
package
public fun </*0*/ T : kotlin.Number?, /*1*/ S : T> Expression<T>.rem(/*0*/ t: S): ModOp<T, S>
public fun </*0*/ T, /*1*/ S : T?> Expression<in S>.wrap(/*0*/ value: T): QueryParameter<T>
public open class Expression</*0*/ K> {
public constructor Expression</*0*/ K>()
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</*0*/ T : kotlin.Number?, /*1*/ S : kotlin.Number?> {
public constructor ModOp</*0*/ T : kotlin.Number?, /*1*/ S : kotlin.Number?>(/*0*/ expr1: Expression<T>, /*1*/ expr2: Expression<S>)
public final val expr1: Expression<T>
public final val expr2: Expression<S>
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</*0*/ A> : Expression<A> {
public constructor QueryParameter</*0*/ 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
}
@@ -6,6 +6,6 @@ sealed class Subtype<A1, B1> {
}
fun <A, B> unsafeCast(value: A): B {
val proof: Subtype<A, B> = Subtype.Trivial()
val proof: Subtype<A, B> = <!UPPER_BOUND_VIOLATED!>Subtype.Trivial()<!>
return proof.cast(value)
}
@@ -11,7 +11,7 @@ fun <F> materializeInvOfAAndB(): Inv<F> where F : A, F : B = Inv()
fun <F> wrapAAndBToOut(x: F): Out<F> where F : A, F : B = Out()
fun main(a: A) {
val x: Out<A> = <!TYPE_MISMATCH!>materializeOutOfAAndB<!>() // OI: inferred type A is not a subtype of B; `F` is instantiated as `A`, so upper bounds was violated
val y: Inv<A> = <!TYPE_MISMATCH!>materializeInvOfAAndB()<!> // OI and NI: required B, found A
val z: Out<A> = wrapAAndBToOut(<!TYPE_MISMATCH!>a<!>) // OI and NI: required B, found A
val x: Out<A> = materializeOutOfAAndB() // OI: inferred type A is not a subtype of B; `F` is instantiated as `A`, so upper bounds was violated
val y: Inv<A> = materializeInvOfAAndB() // OI and NI: required B, found A
val z: Out<A> = <!INAPPLICABLE_CANDIDATE!>wrapAAndBToOut<!>(a) // OI and NI: required B, found A
}
@@ -7,6 +7,6 @@ fun interface ReadOnlyProperty<in T, out V> {
}
class Problem {
val variable: Int by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE!><!TYPE_MISMATCH!>delegate<!>()<!> // delegate returns `ReadOnlyProperty<Problem, {CharSequence & Int}>`
fun <T : CharSequence> delegate() = null <!CAST_NEVER_SUCCEEDS!>as<!> ReadOnlyProperty<Problem, T>
val variable: Int by delegate() // delegate returns `ReadOnlyProperty<Problem, {CharSequence & Int}>`
fun <T : CharSequence> delegate() = null as ReadOnlyProperty<Problem, T>
}
@@ -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 {