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.TypeCheckingProcedure
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberType
|
||||
|
||||
object CastDiagnosticsUtil {
|
||||
@@ -37,24 +38,11 @@ object CastDiagnosticsUtil {
|
||||
rhsType: KotlinType,
|
||||
platformToKotlinClassMap: PlatformToKotlinClassMap): Boolean {
|
||||
if (KotlinBuiltIns.isNullableNothing(lhsType) && !TypeUtils.isNullableType(rhsType)) return false
|
||||
if (lhsType.isError) return true
|
||||
if (isRelated(lhsType, rhsType, platformToKotlinClassMap)) return true
|
||||
// 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
|
||||
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 (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,
|
||||
* 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
|
||||
* (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 {
|
||||
val aTypes = mapToPlatformIndependentTypes(TypeUtils.makeNotNullable(a), platformToKotlinClassMap)
|
||||
val bTypes = mapToPlatformIndependentTypes(TypeUtils.makeNotNullable(b), platformToKotlinClassMap)
|
||||
val aClasses = mapToPlatformClasses(a, platformToKotlinClassMap)
|
||||
val bClasses = mapToPlatformClasses(b, platformToKotlinClassMap)
|
||||
|
||||
for (aType in aTypes) {
|
||||
for (bType in bTypes) {
|
||||
if (KotlinTypeChecker.DEFAULT.isSubtypeOf(aType, bType)) return true
|
||||
if (KotlinTypeChecker.DEFAULT.isSubtypeOf(bType, aType)) return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return aClasses.any { DescriptorUtils.isSubtypeOfClass(b, it) } || bClasses.any { DescriptorUtils.isSubtypeOfClass(a, it) }
|
||||
}
|
||||
|
||||
private fun mapToPlatformIndependentTypes(
|
||||
private fun mapToPlatformClasses(
|
||||
type: KotlinType,
|
||||
platformToKotlinClassMap: PlatformToKotlinClassMap): List<KotlinType> {
|
||||
val descriptor = type.constructor.declarationDescriptor
|
||||
if (descriptor !is ClassDescriptor) return listOf(type)
|
||||
platformToKotlinClassMap: PlatformToKotlinClassMap
|
||||
): List<ClassDescriptor> {
|
||||
val descriptor = type.constructor.declarationDescriptor as? ClassDescriptor ?: return listOf()
|
||||
|
||||
val kotlinClasses = platformToKotlinClassMap.mapPlatformClass(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
|
||||
return platformToKotlinClassMap.mapPlatformClass(descriptor) + descriptor
|
||||
}
|
||||
|
||||
private fun isFinal(type: KotlinType) = !TypeUtils.canHaveSubtypes(KotlinTypeChecker.DEFAULT, type)
|
||||
|
||||
@@ -12,7 +12,7 @@ fun use() {
|
||||
|
||||
fun checkArrays(): Array<Any> {
|
||||
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><!>
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,6 @@ package h
|
||||
public class MyClass<S, T>(<!UNUSED_PARAMETER!>param<!>: MyClass<S, T>) {
|
||||
fun test() {
|
||||
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