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:
+6
@@ -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 {
|
||||
|
||||
+10
@@ -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()
|
||||
|
||||
Vendored
+2
-2
@@ -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
|
||||
|
||||
Vendored
+15
@@ -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))
|
||||
Vendored
+27
@@ -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>
|
||||
}
|
||||
Generated
+6
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user