CAST_NEVER_SUCCEED: replace subtyping check with subclassing check #KT-13206 Fixed
(cherry picked from commit 5fc797a)
This commit is contained in:
committed by
Mikhail Glukhikh
parent
17c824f7f2
commit
a902c4901a
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.descriptors.ClassKind
|
|||||||
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.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberType
|
import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberType
|
||||||
|
|
||||||
object CastDiagnosticsUtil {
|
object CastDiagnosticsUtil {
|
||||||
@@ -37,24 +38,11 @@ object CastDiagnosticsUtil {
|
|||||||
rhsType: KotlinType,
|
rhsType: KotlinType,
|
||||||
platformToKotlinClassMap: PlatformToKotlinClassMap): Boolean {
|
platformToKotlinClassMap: PlatformToKotlinClassMap): Boolean {
|
||||||
if (KotlinBuiltIns.isNullableNothing(lhsType) && !TypeUtils.isNullableType(rhsType)) return false
|
if (KotlinBuiltIns.isNullableNothing(lhsType) && !TypeUtils.isNullableType(rhsType)) return false
|
||||||
|
if (lhsType.isError) return true
|
||||||
if (isRelated(lhsType, rhsType, platformToKotlinClassMap)) return true
|
if (isRelated(lhsType, rhsType, platformToKotlinClassMap)) return true
|
||||||
// This is an oversimplification (which does not render the method incomplete):
|
// This is an oversimplification (which does not render the method incomplete):
|
||||||
// we consider any type parameter capable of taking any value, which may be made more precise if we considered bounds
|
// we consider any type parameter capable of taking any value, which may be made more precise if we considered bounds
|
||||||
if (TypeUtils.isTypeParameter(lhsType) || TypeUtils.isTypeParameter(rhsType)) return true
|
if (TypeUtils.isTypeParameter(lhsType) || TypeUtils.isTypeParameter(rhsType)) return true
|
||||||
if (lhsType.constructor == rhsType.constructor) {
|
|
||||||
if (lhsType.arguments.all { TypeUtils.isTypeParameter(it.type) }) return true
|
|
||||||
if (rhsType.arguments.all { TypeUtils.isTypeParameter(it.type) }) return true
|
|
||||||
if (KotlinBuiltIns.isArray(lhsType)) {
|
|
||||||
val lhsArgument = lhsType.arguments.firstOrNull()
|
|
||||||
val rhsArgument = rhsType.arguments.firstOrNull()
|
|
||||||
if (lhsArgument != null && rhsArgument != null) {
|
|
||||||
if (lhsArgument.type.constructor == rhsArgument.type.constructor) return true
|
|
||||||
if (KotlinTypeChecker.DEFAULT.isSubtypeOf(TypeUtils.makeNotNullable(lhsArgument.type), rhsArgument.type)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isFinal(lhsType) || isFinal(rhsType)) return false
|
if (isFinal(lhsType) || isFinal(rhsType)) return false
|
||||||
if (isTrait(lhsType) || isTrait(rhsType)) return true
|
if (isTrait(lhsType) || isTrait(rhsType)) return true
|
||||||
@@ -62,47 +50,28 @@ object CastDiagnosticsUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Two types are related, roughly, when one is a subtype or supertype of the other.
|
* Two types are related, roughly, when one of them is a subtype of the other constructing class
|
||||||
*
|
|
||||||
*
|
*
|
||||||
* Note that some types have platform-specific counterparts, i.e. kotlin.String is mapped to java.lang.String,
|
* Note that some types have platform-specific counterparts, i.e. kotlin.String is mapped to java.lang.String,
|
||||||
* such types (and all their sub- and supertypes) are related too.
|
* such types (and all their sub- and supertypes) are related too.
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* Due to limitations in PlatformToKotlinClassMap, we only consider mapping of platform classes to Kotlin classed
|
* Due to limitations in PlatformToKotlinClassMap, we only consider mapping of platform classes to Kotlin classed
|
||||||
* (i.e. java.lang.String -> kotlin.String) and ignore mappings that go the other way.
|
* (i.e. java.lang.String -> kotlin.String) and ignore mappings that go the other way.
|
||||||
*/
|
*/
|
||||||
private fun isRelated(a: KotlinType, b: KotlinType, platformToKotlinClassMap: PlatformToKotlinClassMap): Boolean {
|
private fun isRelated(a: KotlinType, b: KotlinType, platformToKotlinClassMap: PlatformToKotlinClassMap): Boolean {
|
||||||
val aTypes = mapToPlatformIndependentTypes(TypeUtils.makeNotNullable(a), platformToKotlinClassMap)
|
val aClasses = mapToPlatformClasses(a, platformToKotlinClassMap)
|
||||||
val bTypes = mapToPlatformIndependentTypes(TypeUtils.makeNotNullable(b), platformToKotlinClassMap)
|
val bClasses = mapToPlatformClasses(b, platformToKotlinClassMap)
|
||||||
|
|
||||||
for (aType in aTypes) {
|
return aClasses.any { DescriptorUtils.isSubtypeOfClass(b, it) } || bClasses.any { DescriptorUtils.isSubtypeOfClass(a, it) }
|
||||||
for (bType in bTypes) {
|
|
||||||
if (KotlinTypeChecker.DEFAULT.isSubtypeOf(aType, bType)) return true
|
|
||||||
if (KotlinTypeChecker.DEFAULT.isSubtypeOf(bType, aType)) return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun mapToPlatformIndependentTypes(
|
private fun mapToPlatformClasses(
|
||||||
type: KotlinType,
|
type: KotlinType,
|
||||||
platformToKotlinClassMap: PlatformToKotlinClassMap): List<KotlinType> {
|
platformToKotlinClassMap: PlatformToKotlinClassMap
|
||||||
val descriptor = type.constructor.declarationDescriptor
|
): List<ClassDescriptor> {
|
||||||
if (descriptor !is ClassDescriptor) return listOf(type)
|
val descriptor = type.constructor.declarationDescriptor as? ClassDescriptor ?: return listOf()
|
||||||
|
|
||||||
val kotlinClasses = platformToKotlinClassMap.mapPlatformClass(descriptor)
|
return platformToKotlinClassMap.mapPlatformClass(descriptor) + descriptor
|
||||||
if (kotlinClasses.isEmpty()) return listOf(type)
|
|
||||||
|
|
||||||
val result = Lists.newArrayListWithCapacity<KotlinType>(2)
|
|
||||||
result.add(type)
|
|
||||||
for (classDescriptor in kotlinClasses) {
|
|
||||||
val kotlinType = TypeUtils.substituteProjectionsForParameters(classDescriptor, type.arguments)
|
|
||||||
result.add(kotlinType)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isFinal(type: KotlinType) = !TypeUtils.canHaveSubtypes(KotlinTypeChecker.DEFAULT, type)
|
private fun isFinal(type: KotlinType) = !TypeUtils.canHaveSubtypes(KotlinTypeChecker.DEFAULT, type)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ fun use() {
|
|||||||
|
|
||||||
fun checkArrays(): Array<Any> {
|
fun checkArrays(): Array<Any> {
|
||||||
val someArray = arrayOfNulls<Any>(5)
|
val someArray = arrayOfNulls<Any>(5)
|
||||||
someArray <!CAST_NEVER_SUCCEEDS!>as<!> Array<Int>
|
<!UNCHECKED_CAST!>someArray as Array<Int><!>
|
||||||
return <!UNCHECKED_CAST!>someArray as Array<Any><!>
|
return <!UNCHECKED_CAST!>someArray as Array<Any><!>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ package h
|
|||||||
public class MyClass<S, T>(<!UNUSED_PARAMETER!>param<!>: MyClass<S, T>) {
|
public class MyClass<S, T>(<!UNUSED_PARAMETER!>param<!>: MyClass<S, T>) {
|
||||||
fun test() {
|
fun test() {
|
||||||
val result: MyClass<Any, Any>? = null
|
val result: MyClass<Any, Any>? = null
|
||||||
MyClass<S, Any>(result <!CAST_NEVER_SUCCEEDS!>as<!> MyClass<S, Any>)
|
MyClass<S, Any>(<!UNCHECKED_CAST!>result as MyClass<S, Any><!>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user