From b88336d6631ce86566f68553120d8ae6c8da3616 Mon Sep 17 00:00:00 2001 From: Evgeny Gerashchenko Date: Thu, 8 Nov 2012 21:52:22 +0400 Subject: [PATCH] Added detailed comments for submethods of isTypeErased() and buildDeepSubstitutionMultimap() --- .../jet/lang/types/SubstitutionUtils.java | 14 ++++ .../BasicExpressionTypingVisitor.java | 78 +++++++++++++++---- 2 files changed, 76 insertions(+), 16 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/SubstitutionUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/SubstitutionUtils.java index a54de67adae..3c37989d3ae 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/SubstitutionUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/SubstitutionUtils.java @@ -50,6 +50,20 @@ public class SubstitutionUtils { return typeSubstitutor; } + /* + For each supertype of a given type, we map type parameters to type arguments. + + For instance, we have the following class hierarchy: + trait Hashable + trait Iterable + trait Collection: Iterable, Hashable + trait MyFooCollection: Collection> + + For MyFunCollection, the following multimap will be returned: + T declared in Iterable -> Foo + E declared in Collection -> Foo + F declared in MyFooCollection -> out CharSequence + */ @NotNull public static Multimap buildDeepSubstitutionMultimap(@NotNull JetType type) { Multimap fullSubstitution = CommonSuppliers.newLinkedHashSetHashSetMultimap(); 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 6fe3fbebbfe..7c77b798067 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 @@ -297,14 +297,23 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { return hasDowncastsInTypeArguments(supertype, subtype, typeChecker) || hasErasedTypeArguments(supertype, subtype); } + /* + Check if type arguments are downcasted, which cannot be checked at run-time. + + Examples: + 1. a: MutableList is MutableList - true, because 'String' is more specific + 2. a: Collection is List - false, because 'Any' is less specific, and it is guaranteed by static checker + 3. a: MutableCollection is MutableList - false, because these types have empty intersection (type parameter is invariant) + */ private static boolean hasDowncastsInTypeArguments( @NotNull JetType supertype, @NotNull JetType subtype, @NotNull JetTypeChecker typeChecker ) { List superParameters = supertype.getConstructor().getParameters(); - Multimap subtypeSubstitutionMap = - SubstitutionUtils.buildDeepSubstitutionMultimap(subtype); + + // This map holds arguments for type parameters of all superclasses of a type (see method comment for sample) + Multimap subtypeSubstitutionMap = SubstitutionUtils.buildDeepSubstitutionMultimap(subtype); for (int i = 0; i < superParameters.size(); i++) { TypeProjection superArgument = supertype.getArguments().get(i); @@ -316,15 +325,28 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { Collection substituted = subtypeSubstitutionMap.get(parameter.getTypeConstructor()); for (TypeProjection substitutedArgument : substituted) { - JetType superIn = TypeCheckingProcedure.getInType(parameter, superArgument); + // For sample #2 (a: Collection is List): + // parameter = E declared in Collection + // superArgument = String + // substitutedArgument = Any, because Collection is the supertype of List + + // 1. Any..Nothing + // 2. String..Nothing + // 3. String..String JetType superOut = TypeCheckingProcedure.getOutType(parameter, superArgument); + JetType superIn = TypeCheckingProcedure.getInType(parameter, superArgument); - JetType subIn = TypeCheckingProcedure.getInType(parameter, substitutedArgument); + // 1. String..String + // 2. Any..Nothing + // 3. Any..Any JetType subOut = TypeCheckingProcedure.getOutType(parameter, substitutedArgument); + JetType subIn = TypeCheckingProcedure.getInType(parameter, substitutedArgument); + // super type range must be a subset of sub type range if (typeChecker.isSubtypeOf(superOut, subOut) && typeChecker.isSubtypeOf(subIn, superIn)) { - // super type range must be subset of sub type range - } else { + // continue + } + else { return true; } } @@ -333,42 +355,66 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { return false; } + /* + Check if type arguments are erased, that is they are not mapped to type parameters of supertype's class + + Examples (MyMap is defined like this: trait MyMap: Map): + 1. a: Any is List - true + 2. a: Collection is List - false + 3. a: Map is MyMap - false + */ private static boolean hasErasedTypeArguments( @NotNull JetType supertype, @NotNull JetType subtype ) { + // Erase all type arguments, replacing them with unsubstituted versions: + // 1. List + // 2. List + // 3. MyMap JetType subtypeCleared = TypeUtils.makeUnsubstitutedType( (ClassDescriptor) subtype.getConstructor().getDeclarationDescriptor(), null); + // This map holds arguments for type parameters of all superclasses of a type (see method comment for sample) + // For all "E" declared in Collection, Iterable, etc., value will be type "E", where the latter E is declared in List Multimap clearTypeSubstitutionMap = SubstitutionUtils.buildDeepSubstitutionMultimap(subtypeCleared); + + // This set will contain all arguments for type parameters of superclass which are mapped from type parameters of subtype's class + // 1. empty + // 2. [E declared in List] + // 3. [T declared in MyMap] Set clearSubstituted = new HashSet(); List superParameters = supertype.getConstructor().getParameters(); - for (TypeParameterDescriptor superTypeParameter : superParameters) { - Collection substituted = clearTypeSubstitutionMap.get(superTypeParameter.getTypeConstructor()); + for (TypeParameterDescriptor superParameter : superParameters) { + Collection substituted = clearTypeSubstitutionMap.get(superParameter.getTypeConstructor()); for (TypeProjection substitutedProjection : substituted) { clearSubstituted.add(substitutedProjection.getType()); } } - List subTypeParameters = subtype.getConstructor().getParameters(); - for (int i = 0; i < subTypeParameters.size(); i++) { - TypeParameterDescriptor typeParameter = subTypeParameters.get(i); + // For each type parameter of subtype's class, we check that it is mapped to type parameters of supertype, + // that is its type is present in clearSubstituted set + List subParameters = subtype.getConstructor().getParameters(); + for (int i = 0; i < subParameters.size(); i++) { + TypeParameterDescriptor parameter = subParameters.get(i); + TypeProjection argument = subtype.getArguments().get(i); - if (typeParameter.isReified()) { + if (parameter.isReified()) { continue; } - // "is List<*>" - TypeProjection typeArgument = subtype.getArguments().get(i); - if (typeArgument.equals(SubstitutionUtils.makeStarProjection(typeParameter))) { + // "is List<*>", no check for type argument, actually + if (argument.equals(SubstitutionUtils.makeStarProjection(parameter))) { continue; } // if parameter is mapped to nothing then it is erased - if (!clearSubstituted.contains(typeParameter.getDefaultType())) { + // 1. return from here + // 2. contains = true, don't return + // 3. contains = true, don't return + if (!clearSubstituted.contains(parameter.getDefaultType())) { return true; } }