From b550687cbcddca557e07de6fc7766c31aa48bdee Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Wed, 4 Sep 2013 14:22:46 +0400 Subject: [PATCH] Cast impossibility checking logic moved to a separate class --- .../jet/lang/types/CastDiagnosticsUtil.java | 95 +++++++++++++++++++ .../BasicExpressionTypingVisitor.java | 71 +------------- 2 files changed, 96 insertions(+), 70 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/jet/lang/types/CastDiagnosticsUtil.java diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/CastDiagnosticsUtil.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/CastDiagnosticsUtil.java new file mode 100644 index 00000000000..bb3b0ae2376 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/CastDiagnosticsUtil.java @@ -0,0 +1,95 @@ +package org.jetbrains.jet.lang.types; + +import com.google.common.collect.Lists; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.PlatformToKotlinClassMap; +import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.descriptors.ClassKind; +import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor; +import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; +import org.jetbrains.jet.lang.types.checker.JetTypeChecker; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public class 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 JetType lhsType, + @NotNull JetType rhsType, + @NotNull PlatformToKotlinClassMap platformToKotlinClassMap + ) { + 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 (isTypeParameter(lhsType) || 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. jet.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 -> jet.String) and ignore mappings that go the other way. + */ + private static boolean isRelated(@NotNull JetType a, @NotNull JetType b, @NotNull PlatformToKotlinClassMap platformToKotlinClassMap) { + List aTypes = mapToPlatformIndependentTypes(a, platformToKotlinClassMap); + List bTypes = mapToPlatformIndependentTypes(b, platformToKotlinClassMap); + + for (int i = 0; i < aTypes.size(); i++) { + JetType aType = aTypes.get(i); + for (int j = 0; j < bTypes.size(); j++) { + JetType bType = bTypes.get(j); + + if (JetTypeChecker.INSTANCE.isSubtypeOf(aType, bType)) return true; + if (JetTypeChecker.INSTANCE.isSubtypeOf(bType, aType)) return true; + } + } + + return false; + } + + private static List mapToPlatformIndependentTypes( + @NotNull JetType type, + @NotNull PlatformToKotlinClassMap platformToKotlinClassMap + ) { + ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor(); + if (!(descriptor instanceof ClassDescriptor)) return Collections.singletonList(type); + + ClassDescriptor originalClass = (ClassDescriptor) descriptor; + Collection kotlinClasses = platformToKotlinClassMap.mapPlatformClass(originalClass); + if (kotlinClasses.isEmpty()) return Collections.singletonList(type); + + List result = Lists.newArrayListWithCapacity(2); + result.add(type); + for (ClassDescriptor classDescriptor : kotlinClasses) { + JetType kotlinType = TypeUtils.substituteProjectionsForParameters(classDescriptor, type.getArguments()); + result.add(kotlinType); + } + + return result; + } + + private static boolean isTypeParameter(@NotNull JetType type) { + return type.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor; + } + + private static boolean isFinal(@NotNull JetType type) { + return !TypeUtils.canHaveSubtypes(JetTypeChecker.INSTANCE, type); + } + + private static boolean isTrait(@NotNull JetType type) { + ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor(); + return descriptor instanceof ClassDescriptor && ((ClassDescriptor) descriptor).getKind() == ClassKind.TRAIT; + } + + private CastDiagnosticsUtil() {} +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java index bb1dc5d38fb..5220e82410c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java @@ -220,7 +220,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { ) { if (actualType == null || noExpectedType(targetType)) return; - if (!isCastPossible(actualType, targetType)) { + if (!CastDiagnosticsUtil.isCastPossible(actualType, targetType, platformToKotlinClassMap)) { context.trace.report(CAST_NEVER_SUCCEEDS.on(expression.getOperationReference())); } @@ -242,75 +242,6 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } } - // 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 boolean isCastPossible(@NotNull JetType lhsType, @NotNull JetType rhsType) { - if (isRelated(lhsType, rhsType)) 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 (isTypeParameter(lhsType) || 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. jet.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 -> jet.String) and ignore mappings that go the other way. - */ - private boolean isRelated(@NotNull JetType a, @NotNull JetType b) { - List aTypes = mapToPlatformIndependentTypes(a); - List bTypes = mapToPlatformIndependentTypes(b); - - for (int i = 0; i < aTypes.size(); i++) { - JetType aType = aTypes.get(i); - for (int j = 0; j < bTypes.size(); j++) { - JetType bType = bTypes.get(j); - - if (JetTypeChecker.INSTANCE.isSubtypeOf(aType, bType)) return true; - if (JetTypeChecker.INSTANCE.isSubtypeOf(bType, aType)) return true; - } - } - - return false; - } - - private List mapToPlatformIndependentTypes(@NotNull JetType type) { - ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor(); - if (!(descriptor instanceof ClassDescriptor)) return Collections.singletonList(type); - - ClassDescriptor originalClass = (ClassDescriptor) descriptor; - Collection kotlinClasses = platformToKotlinClassMap.mapPlatformClass(originalClass); - if (kotlinClasses.isEmpty()) return Collections.singletonList(type); - - List result = Lists.newArrayListWithCapacity(2); - result.add(type); - for (ClassDescriptor classDescriptor : kotlinClasses) { - JetType kotlinType = TypeUtils.substituteProjectionsForParameters(classDescriptor, type.getArguments()); - result.add(kotlinType); - } - - return result; - } - - private static boolean isTypeParameter(@NotNull JetType type) { - return type.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor; - } - - private static boolean isFinal(@NotNull JetType type) { - return !TypeUtils.canHaveSubtypes(JetTypeChecker.INSTANCE, type); - } - - private static boolean isTrait(@NotNull JetType type) { - ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor(); - return descriptor instanceof ClassDescriptor && ((ClassDescriptor) descriptor).getKind() == ClassKind.TRAIT; - } - /** * Check if assignment from supertype to subtype is erased. * It is an error in "is" statement and warning in "as".