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
This commit is contained in:
Victor Petukhov
2021-03-03 16:02:10 +03:00
parent e06bacafad
commit 7f7bb70596
18 changed files with 220 additions and 6 deletions

View File

@@ -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 {

View File

@@ -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 <T : String> 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,

View File

@@ -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<*>

View File

@@ -2,6 +2,6 @@ fun <T> f(): T? = "OK" as? T
fun g(): Nothing = throw RuntimeException("fail")
fun <T : Any> h(): T = run { f() } ?: run { g() }
fun <T : Any> h(): T = run<T?> { f() } ?: run { g() }
fun box(): String = h<String>()

View File

@@ -12,7 +12,7 @@ fun test(x: X<Number>) {
fun <S, D: S> g() {
fun <T : S> foo(): T = TODO()
val <!UNUSED_VARIABLE!>y<!> = <!TYPE_INFERENCE_UPPER_BOUND_VIOLATED{OI}!>foo<!>() as Int
val <!UNUSED_VARIABLE!>y<!> = <!TYPE_INFERENCE_UPPER_BOUND_VIOLATED{OI}, TYPE_MISMATCH!>foo<!>() as Int
val <!UNUSED_VARIABLE!>y2<!> = foo() as D
}

View File

@@ -14,7 +14,7 @@ fun <T: A> emptyNullableListOfA(): List<T>? = null
fun testExclExcl() {
doList(<!TYPE_INFERENCE_UPPER_BOUND_VIOLATED{OI}!>emptyNullableListOfA<!>()!!) //should be an error here
val <!UNUSED_VARIABLE!>l<!>: List<Int> = id(<!TYPE_INFERENCE_UPPER_BOUND_VIOLATED{OI}!>emptyNullableListOfA<!>()!!)
val <!UNUSED_VARIABLE!>l<!>: List<Int> = <!TYPE_MISMATCH!><!TYPE_MISMATCH!>id<!>(<!TYPE_INFERENCE_UPPER_BOUND_VIOLATED{OI}, TYPE_MISMATCH!>emptyNullableListOfA<!>()<!TYPE_MISMATCH!>!!<!>)<!>
doList(strangeNullableList { doInt(it) }!!) //lambda should be analyzed (at completion phase)
}

View File

@@ -0,0 +1,17 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE
open class Foo
class Bar
fun <T : Foo> foo(): T? {
return null
}
fun main() {
val a: Bar? = <!DEBUG_INFO_EXPRESSION_TYPE("Bar?")!><!TYPE_MISMATCH!>foo<!>()<!>
}
fun <T : Appendable> wtf(): T = TODO()
val bar: Int = <!TYPE_MISMATCH!>wtf<!>() // happily compiles

View File

@@ -0,0 +1,17 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE
open class Foo
class Bar
fun <T : Foo> foo(): T? {
return null
}
fun main() {
val a: Bar? = <!DEBUG_INFO_EXPRESSION_TYPE("Foo?"), TYPE_MISMATCH!><!TYPE_MISMATCH!>foo<!>()<!>
}
fun <T : Appendable> wtf(): T = TODO()
val bar: Int = <!TYPE_MISMATCH!><!TYPE_MISMATCH!>wtf<!>()<!> // happily compiles

View File

@@ -0,0 +1,20 @@
package
public val bar: kotlin.Int
public fun </*0*/ T : Foo> foo(): T?
public fun main(): kotlin.Unit
public fun </*0*/ T : kotlin.text.Appendable /* = java.lang.Appendable */> 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
}

View File

@@ -18,6 +18,6 @@ private object Scope {
fun <T> materialize(): T = Any() as T
fun test(i: Inv<out Number>) {
val <!UNUSED_VARIABLE!>p<!>: Int by <!IMPLICIT_NOTHING_TYPE_ARGUMENT_IN_RETURN_POSITION!>Scope.Delegate(i)<!>
val <!UNUSED_VARIABLE!>p<!>: Int by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE{OI}, IMPLICIT_NOTHING_TYPE_ARGUMENT_IN_RETURN_POSITION!>Scope.<!TYPE_MISMATCH{OI}!>Delegate<!>(<!TYPE_MISMATCH{OI}!>i<!>)<!>
}
}

View File

@@ -0,0 +1,17 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE
interface A
interface B
class Out<out F>
class Inv<F>
fun <F> materializeOutOfAAndB(): Out<F> where F : A, F : B = Out()
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
}

View File

@@ -0,0 +1,17 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE
interface A
interface B
class Out<out F>
class Inv<F>
fun <F> materializeOutOfAAndB(): Out<F> where F : A, F : B = Out()
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> = 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
}

View File

@@ -0,0 +1,32 @@
package
public fun main(/*0*/ a: A): kotlin.Unit
public fun </*0*/ F : A> materializeInvOfAAndB(): Inv<F> where F : B
public fun </*0*/ F : A> materializeOutOfAAndB(): Out<F> where F : B
public fun </*0*/ F : A> wrapAAndBToOut(/*0*/ x: F): Out<F> 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</*0*/ F> {
public constructor Inv</*0*/ F>()
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</*0*/ out F> {
public constructor Out</*0*/ out F>()
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
}

View File

@@ -0,0 +1,12 @@
// WITH_REFLECT
import kotlin.reflect.KProperty
fun interface ReadOnlyProperty<in T, out V> {
operator fun getValue(thisRef: T, property: KProperty<*>): 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>
}

View File

@@ -0,0 +1,12 @@
// WITH_REFLECT
import kotlin.reflect.KProperty
fun interface ReadOnlyProperty<in T, out V> {
operator fun getValue(thisRef: T, property: KProperty<*>): 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>
}

View File

@@ -0,0 +1,18 @@
package
public final class Problem {
public constructor Problem()
public final val variable: kotlin.Int
public final fun </*0*/ T : kotlin.CharSequence> delegate(): ReadOnlyProperty<Problem, T>
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</*0*/ in T, /*1*/ out V> {
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
}

View File

@@ -8,5 +8,5 @@ fun test1(l: List<Number>) {
val i: Int = l.firstTyped()
val s: String = l.<!TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH{OI}!>firstTyped()<!>
val s: String = <!TYPE_MISMATCH!>l.<!TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH{OI}!><!TYPE_MISMATCH!>firstTyped<!>()<!><!>
}

View File

@@ -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 {