diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/CastDiagnosticsUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/CastDiagnosticsUtil.kt index 178ba33a131..2fcd219728e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/CastDiagnosticsUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/CastDiagnosticsUtil.kt @@ -14,208 +14,182 @@ * limitations under the License. */ -package org.jetbrains.kotlin.types; +package org.jetbrains.kotlin.types -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import kotlin.collections.CollectionsKt; -import kotlin.jvm.functions.Function1; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.kotlin.platform.PlatformToKotlinClassMap; -import org.jetbrains.kotlin.descriptors.ClassDescriptor; -import org.jetbrains.kotlin.descriptors.ClassKind; -import org.jetbrains.kotlin.descriptors.ClassifierDescriptor; -import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor; -import org.jetbrains.kotlin.types.checker.KotlinTypeChecker; -import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure; -import org.jetbrains.kotlin.builtins.KotlinBuiltIns; +import com.google.common.base.Predicates +import com.google.common.collect.Lists +import com.google.common.collect.Maps +import org.jetbrains.kotlin.platform.PlatformToKotlinClassMap +import org.jetbrains.kotlin.descriptors.ClassDescriptor +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 java.util.*; - -public class CastDiagnosticsUtil { +object CastDiagnosticsUtil { // As this method produces a warning, it must be _complete_ (not sound), i.e. every time it says "cast impossible", // it must be really impossible - public static boolean isCastPossible( - @NotNull KotlinType lhsType, - @NotNull KotlinType rhsType, - @NotNull PlatformToKotlinClassMap platformToKotlinClassMap - ) { - if (KotlinBuiltIns.isNullableNothing(lhsType) && !TypeUtils.isNullableType(rhsType)) return false; - if (isRelated(lhsType, rhsType, platformToKotlinClassMap)) return true; + @JvmStatic + fun isCastPossible( + lhsType: KotlinType, + rhsType: KotlinType, + platformToKotlinClassMap: PlatformToKotlinClassMap): Boolean { + if (KotlinBuiltIns.isNullableNothing(lhsType) && !TypeUtils.isNullableType(rhsType)) return false + 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 (isFinal(lhsType) || isFinal(rhsType)) return false; - if (isTrait(lhsType) || isTrait(rhsType)) return true; - return false; + if (TypeUtils.isTypeParameter(lhsType) || TypeUtils.isTypeParameter(rhsType)) return true + if (isFinal(lhsType) || isFinal(rhsType)) return false + if (isTrait(lhsType) || isTrait(rhsType)) return true + return false } /** * Two types are related, roughly, when one is a subtype or supertype of the other. - *

+ * + * * 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 static boolean isRelated(@NotNull KotlinType a, @NotNull KotlinType b, @NotNull PlatformToKotlinClassMap platformToKotlinClassMap) { - List aTypes = mapToPlatformIndependentTypes(TypeUtils.makeNotNullable(a), platformToKotlinClassMap); - List bTypes = mapToPlatformIndependentTypes(TypeUtils.makeNotNullable(b), platformToKotlinClassMap); + private fun isRelated(a: KotlinType, b: KotlinType, platformToKotlinClassMap: PlatformToKotlinClassMap): Boolean { + val aTypes = mapToPlatformIndependentTypes(TypeUtils.makeNotNullable(a), platformToKotlinClassMap) + val bTypes = mapToPlatformIndependentTypes(TypeUtils.makeNotNullable(b), platformToKotlinClassMap) - for (KotlinType aType : aTypes) { - for (KotlinType bType : bTypes) { - if (KotlinTypeChecker.DEFAULT.isSubtypeOf(aType, bType)) return true; - if (KotlinTypeChecker.DEFAULT.isSubtypeOf(bType, aType)) return true; + 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 false } - private static List mapToPlatformIndependentTypes( - @NotNull KotlinType type, - @NotNull PlatformToKotlinClassMap platformToKotlinClassMap - ) { - ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor(); - if (!(descriptor instanceof ClassDescriptor)) return Collections.singletonList(type); + private fun mapToPlatformIndependentTypes( + type: KotlinType, + platformToKotlinClassMap: PlatformToKotlinClassMap): List { + val descriptor = type.constructor.declarationDescriptor + if (descriptor !is ClassDescriptor) return listOf(type) - ClassDescriptor originalClass = (ClassDescriptor) descriptor; - Collection kotlinClasses = platformToKotlinClassMap.mapPlatformClass(originalClass); - if (kotlinClasses.isEmpty()) return Collections.singletonList(type); + val kotlinClasses = platformToKotlinClassMap.mapPlatformClass(descriptor) + if (kotlinClasses.isEmpty()) return listOf(type) - List result = Lists.newArrayListWithCapacity(2); - result.add(type); - for (ClassDescriptor classDescriptor : kotlinClasses) { - KotlinType kotlinType = TypeUtils.substituteProjectionsForParameters(classDescriptor, type.getArguments()); - result.add(kotlinType); + val result = Lists.newArrayListWithCapacity(2) + result.add(type) + for (classDescriptor in kotlinClasses) { + val kotlinType = TypeUtils.substituteProjectionsForParameters(classDescriptor, type.arguments) + result.add(kotlinType) } - return result; + return result } - private static boolean isFinal(@NotNull KotlinType type) { - return !TypeUtils.canHaveSubtypes(KotlinTypeChecker.DEFAULT, type); - } + private fun isFinal(type: KotlinType) = !TypeUtils.canHaveSubtypes(KotlinTypeChecker.DEFAULT, type) - private static boolean isTrait(@NotNull KotlinType type) { - ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor(); - return descriptor instanceof ClassDescriptor && ((ClassDescriptor) descriptor).getKind() == ClassKind.INTERFACE; - } + private fun isTrait(type: KotlinType) = + type.constructor.declarationDescriptor.let { it is ClassDescriptor && it.kind == ClassKind.INTERFACE } /** * Check if cast from supertype to subtype is erased. * It is an error in "is" statement and warning in "as". */ - public static boolean isCastErased(@NotNull KotlinType supertype, @NotNull KotlinType subtype, @NotNull KotlinTypeChecker typeChecker) { + @JvmStatic + fun isCastErased(supertype: KotlinType, subtype: KotlinType, typeChecker: KotlinTypeChecker): Boolean { // cast between T and T? is always OK - if (supertype.isMarkedNullable() || subtype.isMarkedNullable()) { - return isCastErased(TypeUtils.makeNotNullable(supertype), TypeUtils.makeNotNullable(subtype), typeChecker); + if (supertype.isMarkedNullable || subtype.isMarkedNullable) { + return isCastErased(TypeUtils.makeNotNullable(supertype), TypeUtils.makeNotNullable(subtype), typeChecker) } // if it is a upcast, it's never erased - if (typeChecker.isSubtypeOf(supertype, subtype)) return false; + if (typeChecker.isSubtypeOf(supertype, subtype)) return false // downcasting to a non-reified type parameter is always erased - if (TypeUtils.isNonReifiedTypeParameter(subtype)) return true; + if (TypeUtils.isNonReifiedTypeParameter(subtype)) return true // Check that we are actually casting to a generic type // NOTE: this does not account for 'as Array>' - if (allParametersReified(subtype)) return false; + if (allParametersReified(subtype)) return false - KotlinType staticallyKnownSubtype = findStaticallyKnownSubtype(supertype, subtype.getConstructor()).getResultingType(); + val staticallyKnownSubtype = findStaticallyKnownSubtype(supertype, subtype.constructor).resultingType ?: return true // If the substitution failed, it means that the result is an impossible type, e.g. something like Out // In this case, we can't guarantee anything, so the cast is considered to be erased - if (staticallyKnownSubtype == null) return true; // If the type we calculated is a subtype of the cast target, it's OK to use the cast target instead. // If not, it's wrong to use it - return !typeChecker.isSubtypeOf(staticallyKnownSubtype, subtype); + return !typeChecker.isSubtypeOf(staticallyKnownSubtype, subtype) } /** - * Remember that we are trying to cast something of type {@code supertype} to {@code subtype}. - * + * Remember that we are trying to cast something of type `supertype` to `subtype`. + * Since at runtime we can only check the class (type constructor), the rest of the subtype should be known statically, from supertype. * This method reconstructs all static information that can be obtained from supertype. - * + * Example 1: - * supertype = Collection - * subtype = List<...> - * result = List, all arguments are inferred - * + * supertype = Collection + * subtype = List<...> + * result = List, all arguments are inferred + * Example 2: - * supertype = Any - * subtype = List<...> - * result = List<*>, some arguments were not inferred, replaced with '*' + * supertype = Any + * subtype = List<...> + * result = List<*>, some arguments were not inferred, replaced with '*' */ - public static TypeReconstructionResult findStaticallyKnownSubtype(@NotNull KotlinType supertype, @NotNull TypeConstructor subtypeConstructor) { - assert !supertype.isMarkedNullable() : "This method only makes sense for non-nullable types"; + @JvmStatic + fun findStaticallyKnownSubtype(supertype: KotlinType, subtypeConstructor: TypeConstructor): TypeReconstructionResult { + assert(!supertype.isMarkedNullable) { "This method only makes sense for non-nullable types" } // Assume we are casting an expression of type Collection to List // First, let's make List, where T is a type variable - ClassifierDescriptor descriptor = subtypeConstructor.getDeclarationDescriptor(); - assert descriptor != null : "Can't create default type for " + subtypeConstructor; - KotlinType subtypeWithVariables = descriptor.getDefaultType(); + val descriptor = subtypeConstructor.declarationDescriptor ?: error("Can't create default type for " + subtypeConstructor) + val subtypeWithVariables = descriptor.defaultType // Now, let's find a supertype of List that is a Collection of something, // in this case it will be Collection - KotlinType supertypeWithVariables = TypeCheckingProcedure.findCorrespondingSupertype(subtypeWithVariables, supertype); + val supertypeWithVariables = TypeCheckingProcedure.findCorrespondingSupertype(subtypeWithVariables, supertype) - List variables = subtypeWithVariables.getConstructor().getParameters(); - Set variableConstructors = CollectionsKt.toSet( - CollectionsKt.map(variables, new Function1() { - @Override - public TypeConstructor invoke(TypeParameterDescriptor descriptor) { - return descriptor.getTypeConstructor(); - } - })); + val variables = subtypeWithVariables.constructor.parameters + val variableConstructors = variables.map { descriptor -> descriptor.typeConstructor }.toSet() - Map substitution; + val substitution: MutableMap if (supertypeWithVariables != null) { // Now, let's try to unify Collection and Collection solution is a map from T to Foo - TypeUnifier.UnificationResult solution = TypeUnifier.unify( - new TypeProjectionImpl(supertype), new TypeProjectionImpl(supertypeWithVariables), - Predicates.in(variableConstructors)); - substitution = Maps.newHashMap(solution.getSubstitution()); + val solution = TypeUnifier.unify( + TypeProjectionImpl(supertype), TypeProjectionImpl(supertypeWithVariables), + Predicates.`in`(variableConstructors)) + substitution = Maps.newHashMap(solution.substitution) } else { // If there's no corresponding supertype, no variables are determined // This may be OK, e.g. in case 'Any as List<*>' - substitution = Maps.newHashMapWithExpectedSize(variables.size()); + substitution = Maps.newHashMapWithExpectedSize(variables.size) } // If some of the parameters are not determined by unification, it means that these parameters are lost, // let's put stars instead, so that we can only cast to something like List<*>, e.g. (a: Any) as List<*> - boolean allArgumentsInferred = true; - for (TypeParameterDescriptor variable : variables) { - TypeProjection value = substitution.get(variable.getTypeConstructor()); + var allArgumentsInferred = true + for (variable in variables) { + val value = substitution[variable.typeConstructor] if (value == null) { substitution.put( - variable.getTypeConstructor(), - TypeUtils.makeStarProjection(variable) - ); - allArgumentsInferred = false; + variable.typeConstructor, + TypeUtils.makeStarProjection(variable)) + allArgumentsInferred = false } } // At this point we have values for all type parameters of List // Let's make a type by substituting them: List -> List - KotlinType substituted = TypeSubstitutor.create(substitution).substitute(subtypeWithVariables, Variance.INVARIANT); + val substituted = TypeSubstitutor.create(substitution).substitute(subtypeWithVariables, Variance.INVARIANT) - return new TypeReconstructionResult(substituted, allArgumentsInferred); + return TypeReconstructionResult(substituted, allArgumentsInferred) } - private static boolean allParametersReified(KotlinType subtype) { - for (TypeParameterDescriptor parameterDescriptor : subtype.getConstructor().getParameters()) { - if (!parameterDescriptor.isReified()) return false; - } - return true; - } - - private CastDiagnosticsUtil() {} + private fun allParametersReified(subtype: KotlinType) = subtype.constructor.parameters.all { it.isReified } }