Refine subtyping check: pay attention to corresponding supertype nullability
F: Any, T : F? => !isSubtype(T, Any) It helps to identify upper bounds violation like in KT-7455 #KT-7455 Fixed #KT-2924 Fixed #KT-3015 Fixed
This commit is contained in:
+13
@@ -0,0 +1,13 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
interface A<T : CharSequence>
|
||||
|
||||
fun <S : CharSequence?> foo1(a: A<<!UPPER_BOUND_VIOLATED!>S<!>>) {}
|
||||
|
||||
class B1<E : String?> : A<<!UPPER_BOUND_VIOLATED!>E<!>>
|
||||
class B2<E : CharSequence?> : A<<!UPPER_BOUND_VIOLATED!>E<!>>
|
||||
class B3<E> : A<<!UPPER_BOUND_VIOLATED!>E<!>>
|
||||
|
||||
class B4<E : CharSequence> : A<E>
|
||||
|
||||
fun <X : CharSequence, Y1 : X, Y2: Y1?> foo(a: A<X>, b: A<Y1>, c: A<<!UPPER_BOUND_VIOLATED!>Y2<!>>) {}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package
|
||||
|
||||
internal fun </*0*/ X : kotlin.CharSequence, /*1*/ Y1 : X, /*2*/ Y2 : Y1?> foo(/*0*/ a: A<X>, /*1*/ b: A<Y1>, /*2*/ c: A<Y2>): kotlin.Unit
|
||||
internal fun </*0*/ S : kotlin.CharSequence?> foo1(/*0*/ a: A<S>): kotlin.Unit
|
||||
|
||||
internal interface A</*0*/ T : kotlin.CharSequence> {
|
||||
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
|
||||
}
|
||||
|
||||
internal final class B1</*0*/ E : kotlin.String?> : A<E> {
|
||||
public constructor B1</*0*/ E : kotlin.String?>()
|
||||
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
|
||||
}
|
||||
|
||||
internal final class B2</*0*/ E : kotlin.CharSequence?> : A<E> {
|
||||
public constructor B2</*0*/ E : kotlin.CharSequence?>()
|
||||
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
|
||||
}
|
||||
|
||||
internal final class B3</*0*/ E> : A<E> {
|
||||
public constructor B3</*0*/ E>()
|
||||
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
|
||||
}
|
||||
|
||||
internal final class B4</*0*/ E : kotlin.CharSequence> : A<E> {
|
||||
public constructor B4</*0*/ E : kotlin.CharSequence>()
|
||||
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
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
class A<T : CharSequence>(x: T)
|
||||
|
||||
fun <E : CharSequence> foo1(x: E) {}
|
||||
fun <E : CharSequence> E.foo2() {}
|
||||
|
||||
fun <F : String?> bar(x: F) {
|
||||
<!TYPE_INFERENCE_UPPER_BOUND_VIOLATED!>A<!>(x)
|
||||
A<<!UPPER_BOUND_VIOLATED!>F<!>>(x)
|
||||
|
||||
<!TYPE_INFERENCE_UPPER_BOUND_VIOLATED!>foo1<!>(x)
|
||||
foo1<<!UPPER_BOUND_VIOLATED!>F<!>>(x)
|
||||
|
||||
x.<!TYPE_INFERENCE_UPPER_BOUND_VIOLATED!>foo2<!>()
|
||||
x.foo2<<!UPPER_BOUND_VIOLATED!>F<!>>()
|
||||
}
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package
|
||||
|
||||
internal fun </*0*/ F : kotlin.String?> bar(/*0*/ x: F): kotlin.Unit
|
||||
internal fun </*0*/ E : kotlin.CharSequence> foo1(/*0*/ x: E): kotlin.Unit
|
||||
internal fun </*0*/ E : kotlin.CharSequence> E.foo2(): kotlin.Unit
|
||||
|
||||
internal final class A</*0*/ T : kotlin.CharSequence> {
|
||||
public constructor A</*0*/ T : kotlin.CharSequence>(/*0*/ x: 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
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER, -BASE_WITH_NULLABLE_UPPER_BOUND
|
||||
|
||||
class A<F> {
|
||||
fun <E : F> foo1(x: E) {}
|
||||
fun <E : F?> foo2(x: E) {}
|
||||
|
||||
fun <Z : F, W : Z?> bar(x: F, y: F?, z: Z, w: W) {
|
||||
foo1<F>(x)
|
||||
foo1(x)
|
||||
foo2<F>(x)
|
||||
foo2(x)
|
||||
|
||||
foo1<<!UPPER_BOUND_VIOLATED!>F?<!>>(y)
|
||||
<!TYPE_INFERENCE_UPPER_BOUND_VIOLATED!>foo1<!>(y)
|
||||
foo2<F?>(y)
|
||||
foo2(y)
|
||||
foo1<F>(<!TYPE_MISMATCH!>y<!>)
|
||||
foo2<F>(<!TYPE_MISMATCH!>y<!>)
|
||||
|
||||
foo1<Z>(z)
|
||||
foo1(z)
|
||||
foo2<Z>(z)
|
||||
foo2(z)
|
||||
|
||||
foo1<<!UPPER_BOUND_VIOLATED!>W<!>>(w)
|
||||
<!TYPE_INFERENCE_UPPER_BOUND_VIOLATED!>foo1<!>(w)
|
||||
foo2<W>(w)
|
||||
foo2(w)
|
||||
foo1<<!UPPER_BOUND_VIOLATED!>W<!>>(w)
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package
|
||||
|
||||
internal final class A</*0*/ F> {
|
||||
public constructor A</*0*/ F>()
|
||||
internal final fun </*0*/ Z : F, /*1*/ W : Z?> bar(/*0*/ x: F, /*1*/ y: F?, /*2*/ z: Z, /*3*/ w: W): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
internal final fun </*0*/ E : F> foo1(/*0*/ x: E): kotlin.Unit
|
||||
internal final fun </*0*/ E : F?> foo2(/*0*/ x: E): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER,-UNUSED_VARIABLE
|
||||
|
||||
fun <T : CharSequence?> bar1(x: T) {}
|
||||
|
||||
fun bar2(x: CharSequence?) {}
|
||||
|
||||
fun <T : CharSequence> bar3(x: T) {}
|
||||
|
||||
fun bar4(x: String) {}
|
||||
|
||||
fun <T : String?> foo(x: T) {
|
||||
bar1(x)
|
||||
bar2(x)
|
||||
|
||||
<!TYPE_INFERENCE_UPPER_BOUND_VIOLATED!>bar3<!>(x)
|
||||
bar4(<!TYPE_MISMATCH!>x<!>)
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package
|
||||
|
||||
internal fun </*0*/ T : kotlin.CharSequence?> bar1(/*0*/ x: T): kotlin.Unit
|
||||
internal fun bar2(/*0*/ x: kotlin.CharSequence?): kotlin.Unit
|
||||
internal fun </*0*/ T : kotlin.CharSequence> bar3(/*0*/ x: T): kotlin.Unit
|
||||
internal fun bar4(/*0*/ x: kotlin.String): kotlin.Unit
|
||||
internal fun </*0*/ T : kotlin.String?> foo(/*0*/ x: T): kotlin.Unit
|
||||
@@ -6137,11 +6137,35 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/generics/nullability"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("declarationsBoundsViolation.kt")
|
||||
public void testDeclarationsBoundsViolation() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/nullability/declarationsBoundsViolation.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("expressionsBoundsViolation.kt")
|
||||
public void testExpressionsBoundsViolation() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/nullability/expressionsBoundsViolation.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nullToGeneric.kt")
|
||||
public void testNullToGeneric() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/nullability/nullToGeneric.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("tpBoundsViolation.kt")
|
||||
public void testTpBoundsViolation() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/nullability/tpBoundsViolation.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("useAsValueArgument.kt")
|
||||
public void testUseAsValueArgument() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/nullability/useAsValueArgument.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/generics/starProjections")
|
||||
|
||||
@@ -195,19 +195,24 @@ public class TypeCheckingProcedure {
|
||||
if (subtype.isError() || supertype.isError()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!supertype.isMarkedNullable() && subtype.isMarkedNullable()) {
|
||||
return false;
|
||||
}
|
||||
subtype = TypeUtils.makeNotNullable(subtype);
|
||||
supertype = TypeUtils.makeNotNullable(supertype);
|
||||
|
||||
if (KotlinBuiltIns.isNothingOrNullableNothing(subtype)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable JetType closestSupertype = findCorrespondingSupertype(subtype, supertype, constraints);
|
||||
if (closestSupertype == null) {
|
||||
return constraints.noCorrespondingSupertype(subtype, supertype); // if this returns true, there still isn't any supertype to continue with
|
||||
}
|
||||
|
||||
if (!supertype.isMarkedNullable() && closestSupertype.isMarkedNullable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return checkSubtypeForTheSameConstructor(closestSupertype, supertype);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.types.checker
|
||||
|
||||
import org.jetbrains.kotlin.types.JetType
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.checker.TypeCheckingProcedureCallbacks
|
||||
import java.util.*
|
||||
@@ -40,14 +41,18 @@ public fun findCorrespondingSupertype(
|
||||
|
||||
if (typeCheckingProcedureCallbacks.assertEqualTypeConstructors(constructor, supertypeConstructor)) {
|
||||
var substituted = currentSubtype
|
||||
var isAnyMarkedNullable = currentSubtype.isMarkedNullable
|
||||
|
||||
var currentPathNode = lastPathNode.previous
|
||||
|
||||
while (currentPathNode != null) {
|
||||
substituted = TypeSubstitutor.create(currentPathNode.type).safeSubstitute(substituted, Variance.INVARIANT)
|
||||
isAnyMarkedNullable = isAnyMarkedNullable || currentPathNode.type.isMarkedNullable
|
||||
|
||||
currentPathNode = currentPathNode.previous
|
||||
}
|
||||
|
||||
return substituted
|
||||
return TypeUtils.makeNullableAsSpecified(substituted, isAnyMarkedNullable)
|
||||
}
|
||||
|
||||
for (immediateSupertype in constructor.getSupertypes()) {
|
||||
|
||||
Reference in New Issue
Block a user