Prohibit check for instance of a non-reified, non-subtype type parameter
#KT-12683 Fixed #KT-9986 Fixed
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2015 JetBrains s.r.o.
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.platform.PlatformToKotlinClassMap
|
|||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||||
import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure
|
import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||||
|
|
||||||
object CastDiagnosticsUtil {
|
object CastDiagnosticsUtil {
|
||||||
|
|
||||||
@@ -88,16 +89,28 @@ object CastDiagnosticsUtil {
|
|||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun isCastErased(supertype: KotlinType, subtype: KotlinType, typeChecker: KotlinTypeChecker): Boolean {
|
fun isCastErased(supertype: KotlinType, subtype: KotlinType, typeChecker: KotlinTypeChecker): Boolean {
|
||||||
|
val isNonReifiedTypeParameter = TypeUtils.isNonReifiedTypeParameter(subtype)
|
||||||
|
val isUpcast = typeChecker.isSubtypeOf(supertype, subtype)
|
||||||
|
|
||||||
|
// here we want to restrict cases such as `x is T` for x = T?, when T might have nullable upper bound
|
||||||
|
if (isNonReifiedTypeParameter && !isUpcast) {
|
||||||
|
// hack to save previous behavior in case when `x is T`, where T is not nullable, see IsErasedNullableTasT.kt
|
||||||
|
val nullableToDefinitelyNotNull = !TypeUtils.isNullableType(subtype) && supertype.makeNotNullable() == subtype
|
||||||
|
if (!nullableToDefinitelyNotNull) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// cast between T and T? is always OK
|
// cast between T and T? is always OK
|
||||||
if (supertype.isMarkedNullable || subtype.isMarkedNullable) {
|
if (supertype.isMarkedNullable || subtype.isMarkedNullable) {
|
||||||
return isCastErased(TypeUtils.makeNotNullable(supertype), TypeUtils.makeNotNullable(subtype), typeChecker)
|
return isCastErased(TypeUtils.makeNotNullable(supertype), TypeUtils.makeNotNullable(subtype), typeChecker)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if it is a upcast, it's never erased
|
// if it is a upcast, it's never erased
|
||||||
if (typeChecker.isSubtypeOf(supertype, subtype)) return false
|
if (isUpcast) return false
|
||||||
|
|
||||||
// downcasting to a non-reified type parameter is always erased
|
// downcasting to a non-reified type parameter is always erased
|
||||||
if (TypeUtils.isNonReifiedTypeParameter(subtype)) return true
|
if (isNonReifiedTypeParameter) return true
|
||||||
|
|
||||||
// Check that we are actually casting to a generic type
|
// Check that we are actually casting to a generic type
|
||||||
// NOTE: this does not account for 'as Array<List<T>>'
|
// NOTE: this does not account for 'as Array<List<T>>'
|
||||||
|
|||||||
+1
@@ -26,6 +26,7 @@ import kotlin.TuplesKt;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.KtNodeTypes;
|
import org.jetbrains.kotlin.KtNodeTypes;
|
||||||
|
import org.jetbrains.kotlin.builtins.FunctionTypesKt;
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||||
import org.jetbrains.kotlin.config.LanguageFeature;
|
import org.jetbrains.kotlin.config.LanguageFeature;
|
||||||
import org.jetbrains.kotlin.config.LanguageVersionSettings;
|
import org.jetbrains.kotlin.config.LanguageVersionSettings;
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
fun <T, S : T> test(x: T?, y: S, z: T) {
|
||||||
|
x is <!CANNOT_CHECK_FOR_ERASED!>T<!>
|
||||||
|
x is T?
|
||||||
|
|
||||||
|
y is T
|
||||||
|
y is S
|
||||||
|
y is T<!USELESS_NULLABLE_CHECK!>?<!>
|
||||||
|
y is S<!USELESS_NULLABLE_CHECK!>?<!>
|
||||||
|
|
||||||
|
z is T
|
||||||
|
z is T<!USELESS_NULLABLE_CHECK!>?<!>
|
||||||
|
|
||||||
|
<!UNCHECKED_CAST!>null as T<!>
|
||||||
|
null <!USELESS_CAST!>as T?<!>
|
||||||
|
<!UNCHECKED_CAST!>null as S<!>
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T> test(x: T?) {
|
||||||
|
x is T
|
||||||
|
null as T
|
||||||
|
null <!USELESS_CAST!>as T?<!>
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> foo(x: List<T>, y: List<T>?) {
|
||||||
|
x is List<T>
|
||||||
|
y is List<T>
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun </*0*/ T> foo(/*0*/ x: kotlin.collections.List<T>, /*1*/ y: kotlin.collections.List<T>?): kotlin.Unit
|
||||||
|
public inline fun </*0*/ reified T> test(/*0*/ x: T?): kotlin.Unit
|
||||||
|
public fun </*0*/ T, /*1*/ S : T> test(/*0*/ x: T?, /*1*/ y: S, /*2*/ z: T): kotlin.Unit
|
||||||
Vendored
+1
-1
@@ -25,7 +25,7 @@ class MyProperty<R, T> {
|
|||||||
|
|
||||||
operator fun getValue(thisRef: R, desc: KProperty<*>): T {
|
operator fun getValue(thisRef: R, desc: KProperty<*>): T {
|
||||||
println("get $thisRef ${desc.name}")
|
println("get $thisRef ${desc.name}")
|
||||||
return null as T
|
return <!UNCHECKED_CAST!>null as T<!>
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun setValue(thisRef: R, desc: KProperty<*>, value: T) {
|
operator fun setValue(thisRef: R, desc: KProperty<*>, value: T) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import java.util.HashMap
|
|||||||
|
|
||||||
public inline fun <K,V1, V: V1> Map<K,V>.getOrElse1(key: K, defaultValue: ()-> V1) : V1 {
|
public inline fun <K,V1, V: V1> Map<K,V>.getOrElse1(key: K, defaultValue: ()-> V1) : V1 {
|
||||||
if (this.containsKey(key)) {
|
if (this.containsKey(key)) {
|
||||||
return this.get(key) as V
|
return <!UNCHECKED_CAST!>this.get(key) as V<!>
|
||||||
} else {
|
} else {
|
||||||
return defaultValue()
|
return defaultValue()
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
// SKIP_TXT
|
// SKIP_TXT
|
||||||
class StateMachine<Q> internal constructor() {
|
class StateMachine<Q> internal constructor() {
|
||||||
fun getInputStub(): Q = null as Q
|
fun getInputStub(): Q = <!UNCHECKED_CAST!>null as Q<!>
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> stateMachine(<!UNUSED_PARAMETER!>block<!>: suspend StateMachine<T>.() -> Unit): StateMachine<T> {
|
fun <T> stateMachine(<!UNUSED_PARAMETER!>block<!>: suspend StateMachine<T>.() -> Unit): StateMachine<T> {
|
||||||
@@ -8,7 +8,7 @@ fun <T> stateMachine(<!UNUSED_PARAMETER!>block<!>: suspend StateMachine<T>.() ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Problem<F>(){
|
class Problem<F>(){
|
||||||
fun getInputStub(): F = null as F
|
fun getInputStub(): F = <!UNCHECKED_CAST!>null as F<!>
|
||||||
|
|
||||||
fun createStateMachine(): StateMachine<F> = stateMachine {
|
fun createStateMachine(): StateMachine<F> = stateMachine {
|
||||||
val letter = getInputStub()
|
val letter = getInputStub()
|
||||||
|
|||||||
@@ -2914,6 +2914,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("IsErasedUpcastToNonReified.kt")
|
||||||
|
public void testIsErasedUpcastToNonReified() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cast/IsErasedUpcastToNonReified.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("IsForTypeWithComplexUpperBound.kt")
|
@TestMetadata("IsForTypeWithComplexUpperBound.kt")
|
||||||
public void testIsForTypeWithComplexUpperBound() throws Exception {
|
public void testIsForTypeWithComplexUpperBound() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cast/IsForTypeWithComplexUpperBound.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cast/IsForTypeWithComplexUpperBound.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user