From df79fa2f3dd23d5913990a6a7cbb44fe72838f8e Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Wed, 9 Nov 2011 15:22:25 +0300 Subject: [PATCH] Type checking procedure simplified (dramatically!) --- .../jet/lang/types/JetTypeChecker.java | 235 +++++++++--------- .../quick/regressions/OutProjections.jet | 21 ++ 2 files changed, 134 insertions(+), 122 deletions(-) create mode 100644 compiler/testData/checkerWithErrorTypes/quick/regressions/OutProjections.jet diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeChecker.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeChecker.java index d3a36c2652c..3b8ea6843e6 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeChecker.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeChecker.java @@ -7,6 +7,9 @@ import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; import java.util.*; +import static org.jetbrains.jet.lang.types.Variance.IN_VARIANCE; +import static org.jetbrains.jet.lang.types.Variance.OUT_VARIANCE; + /** * @author abreslav */ @@ -76,17 +79,20 @@ public class JetTypeChecker { public JetType commonSupertype(@NotNull Collection types) { Collection typeSet = new HashSet(types); assert !typeSet.isEmpty(); + + // If any of the types is nullable, the result must be nullable + // This also removed Nothing and Nothing? because they are subtypes of everything else boolean nullable = false; for (Iterator iterator = typeSet.iterator(); iterator.hasNext();) { JetType type = iterator.next(); assert type != null; - // TODO : This admits 'Nothing?'. Review if (JetStandardClasses.isNothingOrNullableNothing(type)) { iterator.remove(); } nullable |= type.isNullable(); } + // Everything deleted => it's Nothing or Nothing? if (typeSet.isEmpty()) { // TODO : attributes return nullable ? JetStandardClasses.getNullableNothingType() : JetStandardClasses.getNothingType(); @@ -96,6 +102,7 @@ public class JetTypeChecker { return TypeUtils.makeNullableIfNeeded(typeSet.iterator().next(), nullable); } + // constructor of the supertype -> all of its instantiations occurring as supertypes Map> commonSupertypes = computeCommonRawSupertypes(typeSet); while (commonSupertypes.size() > 1) { Set merge = new HashSet(); @@ -105,12 +112,81 @@ public class JetTypeChecker { commonSupertypes = computeCommonRawSupertypes(merge); } assert !commonSupertypes.isEmpty() : commonSupertypes + " <- " + types; - Map.Entry> entry = commonSupertypes.entrySet().iterator().next(); - JetType result = computeSupertypeProjections(entry.getKey(), entry.getValue()); + // constructor of the supertype -> all of its instantiations occurring as supertypes + Map.Entry> entry = commonSupertypes.entrySet().iterator().next(); + + // Reconstructing type arguments if possible + JetType result = computeSupertypeProjections(entry.getKey(), entry.getValue()); return TypeUtils.makeNullableIfNeeded(result, nullable); } + // Raw supertypes are superclasses w/o type arguments + // @return TypeConstructor -> all instantiations of this constructor occurring as supertypes + @NotNull + private Map> computeCommonRawSupertypes(@NotNull Collection types) { + assert !types.isEmpty(); + + final Map> constructorToAllInstances = new HashMap>(); + Set commonSuperclasses = null; + + List order = null; + for (JetType type : types) { + Set visited = new HashSet(); + + order = dfs(type, visited, new DfsNodeHandler>() { + public LinkedList list = new LinkedList(); + + @Override + public void beforeChildren(JetType current) { + TypeConstructor constructor = current.getConstructor(); + + Set instances = constructorToAllInstances.get(constructor); + if (instances == null) { + instances = new HashSet(); + constructorToAllInstances.put(constructor, instances); + } + instances.add(current); + } + + @Override + public void afterChildren(JetType current) { + list.addFirst(current.getConstructor()); + } + + @Override + public List result() { + return list; + } + }); + + if (commonSuperclasses == null) { + commonSuperclasses = visited; + } + else { + commonSuperclasses.retainAll(visited); + } + } + assert order != null; + + Set notSource = new HashSet(); + Map> result = new HashMap>(); + for (TypeConstructor superConstructor : order) { + if (!commonSuperclasses.contains(superConstructor)) { + continue; + } + + if (!notSource.contains(superConstructor)) { + result.put(superConstructor, constructorToAllInstances.get(superConstructor)); + markAll(superConstructor, notSource); + } + } + + return result; + } + + // constructor - type constructor of a supertype to be instantiated + // types - instantiations of constructor occurring as supertypes of classes we are trying to intersect @NotNull private JetType computeSupertypeProjections(@NotNull TypeConstructor constructor, @NotNull Set types) { // we assume that all the given types are applications of the same type constructor @@ -186,83 +262,21 @@ public class JetTypeChecker { JetType intersection = TypeUtils.intersect(this, ins); if (intersection == null) { if (outs != null) { - return new TypeProjection(Variance.OUT_VARIANCE, commonSupertype(outs)); + return new TypeProjection(OUT_VARIANCE, commonSupertype(outs)); } - return new TypeProjection(Variance.OUT_VARIANCE, commonSupertype(parameterDescriptor.getUpperBounds())); + return new TypeProjection(OUT_VARIANCE, commonSupertype(parameterDescriptor.getUpperBounds())); } - Variance projectionKind = variance == Variance.IN_VARIANCE ? Variance.INVARIANT : Variance.IN_VARIANCE; + Variance projectionKind = variance == IN_VARIANCE ? Variance.INVARIANT : IN_VARIANCE; return new TypeProjection(projectionKind, intersection); } else if (outs != null) { - Variance projectionKind = variance == Variance.OUT_VARIANCE ? Variance.INVARIANT : Variance.OUT_VARIANCE; + Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE; return new TypeProjection(projectionKind, commonSupertype(outs)); } else { - Variance projectionKind = variance == Variance.OUT_VARIANCE ? Variance.INVARIANT : Variance.OUT_VARIANCE; + Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE; return new TypeProjection(projectionKind, commonSupertype(parameterDescriptor.getUpperBounds())); } } - @NotNull - private Map> computeCommonRawSupertypes(@NotNull Collection types) { - assert !types.isEmpty(); - - final Map> constructorToAllInstances = new HashMap>(); - Set commonSuperclasses = null; - - List order = null; - for (JetType type : types) { - Set visited = new HashSet(); - - order = dfs(type, visited, new DfsNodeHandler>() { - public LinkedList list = new LinkedList(); - - @Override - public void beforeChildren(JetType current) { - TypeConstructor constructor = current.getConstructor(); - - Set instances = constructorToAllInstances.get(constructor); - if (instances == null) { - instances = new HashSet(); - constructorToAllInstances.put(constructor, instances); - } - instances.add(current); - } - - @Override - public void afterChildren(JetType current) { - list.addFirst(current.getConstructor()); - } - - @Override - public List result() { - return list; - } - }); - - if (commonSuperclasses == null) { - commonSuperclasses = visited; - } - else { - commonSuperclasses.retainAll(visited); - } - } - assert order != null; - - Set notSource = new HashSet(); - Map> result = new HashMap>(); - for (TypeConstructor superConstructor : order) { - if (!commonSuperclasses.contains(superConstructor)) { - continue; - } - - if (!notSource.contains(superConstructor)) { - result.put(superConstructor, constructorToAllInstances.get(superConstructor)); - markAll(superConstructor, notSource); - } - } - - return result; - } - private void markAll(@NotNull TypeConstructor typeConstructor, @NotNull Set markerSet) { markerSet.add(typeConstructor); for (JetType type : typeConstructor.getSupertypes()) { @@ -331,22 +345,28 @@ public class JetTypeChecker { } public boolean isSubtypeOf(@NotNull JetType subtype, @NotNull JetType supertype) { - return new TypeCheckingProcedure().run(subtype, supertype); +// return new TypeCheckingProcedure().run(subtype, supertype); + return new ExplicitInOutTypeCheckingProcedure().run(subtype, supertype); } public boolean equalTypes(@NotNull JetType a, @NotNull JetType b) { return isSubtypeOf(a, b) && isSubtypeOf(b, a); } - private static class OldProcedure { - public static boolean isSubtypeOf(@NotNull JetType subtype, @NotNull JetType supertype) { + private static class ExplicitInOutTypeCheckingProcedure { + + public boolean run(@NotNull JetType subtype, @NotNull JetType supertype) { + return isSubtypeOf(subtype, supertype); + } + + public boolean isSubtypeOf(@NotNull JetType subtype, @NotNull JetType supertype) { if (ErrorUtils.isErrorType(subtype) || ErrorUtils.isErrorType(supertype)) { return true; } if (!supertype.isNullable() && subtype.isNullable()) { return false; } - if (JetStandardClasses.isNothing(subtype)) { + if (JetStandardClasses.isNothingOrNullableNothing(subtype)) { return true; } @Nullable JetType closestSupertype = findCorrespondingSupertype(subtype, supertype); @@ -357,70 +377,41 @@ public class JetTypeChecker { return checkSubtypeForTheSameConstructor(closestSupertype, supertype); } - private static boolean checkSubtypeForTheSameConstructor(@NotNull JetType subtype, @NotNull JetType supertype) { + private boolean checkSubtypeForTheSameConstructor(@NotNull JetType subtype, @NotNull JetType supertype) { TypeConstructor constructor = subtype.getConstructor(); assert constructor.equals(supertype.getConstructor()) : constructor + " is not " + supertype.getConstructor(); List subArguments = subtype.getArguments(); List superArguments = supertype.getArguments(); List parameters = constructor.getParameters(); - boolean status = true; for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) { TypeParameterDescriptor parameter = parameters.get(i); TypeProjection subArgument = subArguments.get(i); + + JetType subIn = getInType(parameter, subArgument); + JetType subOut = getOutType(parameter, subArgument); + TypeProjection superArgument = superArguments.get(i); - JetType subArgumentType = subArgument.getType(); - JetType superArgumentType = superArgument.getType(); - switch (parameter.getVariance()) { - case INVARIANT: - switch (superArgument.getProjectionKind()) { - case INVARIANT: - status = subArgumentType.equals(superArgumentType); - break; - case OUT_VARIANCE: - if (!subArgument.getProjectionKind().allowsOutPosition()) { - status = false; - } else { - status = !isSubtypeOf(subArgumentType, superArgumentType); - } - break; - case IN_VARIANCE: - if (!subArgument.getProjectionKind().allowsInPosition()) { - status = false; - } else { - status = isSubtypeOf(superArgumentType, subArgumentType); - } - break; - } - break; - case IN_VARIANCE: - switch (superArgument.getProjectionKind()) { - case INVARIANT: - case IN_VARIANCE: - status = isSubtypeOf(superArgumentType, subArgumentType); - break; - case OUT_VARIANCE: - status = isSubtypeOf(subArgumentType, superArgumentType); - break; - } - break; - case OUT_VARIANCE: - switch (superArgument.getProjectionKind()) { - case INVARIANT: - case OUT_VARIANCE: - case IN_VARIANCE: - status = isSubtypeOf(subArgumentType, superArgumentType); - break; - } - break; - } - if (!status) { + JetType superIn = getInType(parameter, superArgument); + JetType superOut = getOutType(parameter, superArgument); + + if (!isSubtypeOf(subOut, superOut) || !isSubtypeOf(superIn, subIn)) { return false; } } return true; } + + private JetType getOutType(TypeParameterDescriptor parameter, TypeProjection subArgument) { + boolean isOutProjected = subArgument.getProjectionKind() == IN_VARIANCE || parameter.getVariance() == IN_VARIANCE; + return isOutProjected ? parameter.getBoundsAsType() : subArgument.getType(); + } + + private JetType getInType(TypeParameterDescriptor parameter, TypeProjection subArgument) { + boolean isOutProjected = subArgument.getProjectionKind() == OUT_VARIANCE || parameter.getVariance() == OUT_VARIANCE; + return isOutProjected ? JetStandardClasses.getNothingType() : subArgument.getType(); + } } public static abstract class AbstractTypeCheckingProcedure { diff --git a/compiler/testData/checkerWithErrorTypes/quick/regressions/OutProjections.jet b/compiler/testData/checkerWithErrorTypes/quick/regressions/OutProjections.jet new file mode 100644 index 00000000000..f6c32d8f776 --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/quick/regressions/OutProjections.jet @@ -0,0 +1,21 @@ +class Point() { +} + +class G() {} + +fun f(expression : T) : G = G + + +fun foo() : G { + val p = Point() + return f(p) +} + +class Out() {} + +fun fout(expression : T) : Out = Out + +fun fooout() : Out { + val p = Point(); + return fout(p); +} \ No newline at end of file