From 08c1863981b3b733e52a347c4fceddb3565a7c63 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Tue, 1 Feb 2011 17:31:56 +0300 Subject: [PATCH] Inferring common supertypes taking projections and bounds into account --- .../jet/lang/psi/JetProjectionKind.java | 8 ++ .../jet/lang/psi/JetTypeProjection.java | 11 +- .../lang/resolve/ClassDescriptorResolver.java | 3 + .../jet/lang/resolve/TypeResolver.java | 21 ++- .../jet/lang/types/ClassDescriptor.java | 6 +- .../jetbrains/jet/lang/types/ErrorType.java | 2 +- .../jet/lang/types/JetStandardClasses.java | 5 +- .../jet/lang/types/JetTypeChecker.java | 133 +++++++++--------- .../jet/lang/types/ProjectionKind.java | 35 ----- .../jet/lang/types/TypeConstructor.java | 8 +- .../jetbrains/jet/lang/types/TypeImpl.java | 6 +- .../lang/types/TypeParameterDescriptor.java | 1 + .../jet/lang/types/TypeProjection.java | 35 +++-- .../jetbrains/jet/lang/types/TypeUtils.java | 87 +++++++++++- .../jetbrains/jet/lang/types/Variance.java | 20 ++- .../jet/types/JetTypeCheckerTest.java | 24 ++-- 16 files changed, 256 insertions(+), 149 deletions(-) create mode 100644 idea/src/org/jetbrains/jet/lang/psi/JetProjectionKind.java delete mode 100644 idea/src/org/jetbrains/jet/lang/types/ProjectionKind.java diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetProjectionKind.java b/idea/src/org/jetbrains/jet/lang/psi/JetProjectionKind.java new file mode 100644 index 00000000000..bb92439df16 --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/psi/JetProjectionKind.java @@ -0,0 +1,8 @@ +package org.jetbrains.jet.lang.psi; + +/** + * @author abreslav + */ +public enum JetProjectionKind { + IN, OUT, STAR, NONE; +} diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetTypeProjection.java b/idea/src/org/jetbrains/jet/lang/psi/JetTypeProjection.java index 92c63b9f4fd..4fe88184c6b 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetTypeProjection.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetTypeProjection.java @@ -4,7 +4,6 @@ import com.intellij.lang.ASTNode; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.JetNodeTypes; -import org.jetbrains.jet.lang.types.ProjectionKind; import org.jetbrains.jet.lexer.JetTokens; /** @@ -16,21 +15,21 @@ public class JetTypeProjection extends JetDeclaration { } @NotNull - public ProjectionKind getProjectionKind() { + public JetProjectionKind getProjectionKind() { JetModifierList modifierList = getModifierList(); if (modifierList != null) { if (modifierList.hasModifier(JetTokens.IN_KEYWORD)) { - return ProjectionKind.IN_ONLY; + return JetProjectionKind.IN; } if (modifierList.hasModifier(JetTokens.OUT_KEYWORD)) { - return ProjectionKind.OUT_ONLY; + return JetProjectionKind.OUT; } } if (findChildByType(JetTokens.MUL) != null) { - return ProjectionKind.NEITHER_OUT_NOR_IN; + return JetProjectionKind.STAR; } - return ProjectionKind.NO_PROJECTION; + return JetProjectionKind.NONE; } @Override diff --git a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java index a328ead6c8c..c05759145a0 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java @@ -10,6 +10,7 @@ import org.jetbrains.jet.lang.types.ClassDescriptor; import org.jetbrains.jet.lang.types.JetStandardClasses; import org.jetbrains.jet.lang.types.Type; import org.jetbrains.jet.lang.types.TypeParameterDescriptor; +import org.jetbrains.jet.lexer.JetTokens; import java.util.*; @@ -33,8 +34,10 @@ public class ClassDescriptorResolver { Collection superclasses = delegationSpecifiers.isEmpty() ? Collections.singleton(JetStandardClasses.getAnyType()) : resolveTypes(extensibleScope, delegationSpecifiers); + boolean open = classElement.getModifierList().hasModifier(JetTokens.OPEN_KEYWORD); return new ClassDescriptor( AttributeResolver.INSTANCE.resolveAttributes(classElement.getModifierList()), + !open, classElement.getName(), typeParameters, superclasses diff --git a/idea/src/org/jetbrains/jet/lang/resolve/TypeResolver.java b/idea/src/org/jetbrains/jet/lang/resolve/TypeResolver.java index 9117b284d8d..805c9e6ea1a 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/TypeResolver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/TypeResolver.java @@ -104,16 +104,29 @@ public class TypeResolver { for (int i = 0, argumentElementsSize = argumentElements.size(); i < argumentElementsSize; i++) { JetTypeProjection argumentElement = argumentElements.get(i); - ProjectionKind projectionKind = argumentElement.getProjectionKind(); + JetProjectionKind projectionKind = argumentElement.getProjectionKind(); Type type; - if (projectionKind == ProjectionKind.NEITHER_OUT_NOR_IN) { + if (projectionKind == JetProjectionKind.STAR) { Set upperBounds = constructor.getParameters().get(i).getUpperBounds(); - arguments.add(new TypeProjection(ProjectionKind.OUT_ONLY, TypeUtils.intersect(upperBounds))); + arguments.add(new TypeProjection(Variance.OUT_VARIANCE, TypeUtils.intersect(upperBounds))); } else { // TODO : handle the Foo case type = resolveType(scope, argumentElement.getTypeReference()); - arguments.add(new TypeProjection(projectionKind, type)); + Variance kind = null; + switch (projectionKind) { + case IN: + kind = Variance.IN_VARIANCE; + break; + case OUT: + kind = Variance.OUT_VARIANCE; + break; + case NONE: + kind = Variance.INVARIANT; + break; + } + assert kind != null; + arguments.add(new TypeProjection(kind, type)); } } return arguments; diff --git a/idea/src/org/jetbrains/jet/lang/types/ClassDescriptor.java b/idea/src/org/jetbrains/jet/lang/types/ClassDescriptor.java index 179030f4096..3adffb0e183 100644 --- a/idea/src/org/jetbrains/jet/lang/types/ClassDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/types/ClassDescriptor.java @@ -13,14 +13,14 @@ public class ClassDescriptor extends MemberDescriptorImpl implements MemberDomai private final TypeConstructor typeConstructor; public ClassDescriptor( - List attributes, + List attributes, boolean sealed, String name, List typeParameters, Collection superclasses) { super(attributes, name); - this.typeConstructor = new TypeConstructor(attributes, name, typeParameters, superclasses); + this.typeConstructor = new TypeConstructor(attributes, sealed, name, typeParameters, superclasses); } public ClassDescriptor(String name) { - this(Collections.emptyList(), + this(Collections.emptyList(), true, name, Collections.emptyList(), Collections.singleton(JetStandardClasses.getAnyType())); } diff --git a/idea/src/org/jetbrains/jet/lang/types/ErrorType.java b/idea/src/org/jetbrains/jet/lang/types/ErrorType.java index 96b660ec231..ce5490505d4 100644 --- a/idea/src/org/jetbrains/jet/lang/types/ErrorType.java +++ b/idea/src/org/jetbrains/jet/lang/types/ErrorType.java @@ -14,7 +14,7 @@ public class ErrorType { throw new UnsupportedOperationException(); // TODO } }; - private static final TypeConstructor ERROR = new TypeConstructor(Collections.emptyList(), "ERROR", Collections.emptyList(), Collections.emptyList()); + private static final TypeConstructor ERROR = new TypeConstructor(Collections.emptyList(), false, "ERROR", Collections.emptyList(), Collections.emptyList()); public static Type createErrorType(String debugMessage) { return new TypeImpl(ERROR, ERROR_DOMAIN); diff --git a/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java b/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java index 77b6e06829e..6bf9eacfe5b 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java @@ -11,6 +11,7 @@ public class JetStandardClasses { private static ClassDescriptor NOTHING_CLASS = new ClassDescriptor( Collections.emptyList(), + true, "Nothing", Collections.emptyList(), new AbstractCollection() { @@ -33,6 +34,7 @@ public class JetStandardClasses { private static final ClassDescriptor ANY = new ClassDescriptor( Collections.emptyList(), + false, "Any", Collections.emptyList(), Collections.emptySet() @@ -64,6 +66,7 @@ public class JetStandardClasses { } TUPLE[i] = new ClassDescriptor( Collections.emptyList(), + true, "Tuple" + i, parameters, Collections.singleton(JetStandardClasses.getAnyType())); @@ -244,7 +247,7 @@ public class JetStandardClasses { private static List toProjections(List arguments) { List result = new ArrayList(); for (Type argument : arguments) { - result.add(new TypeProjection(ProjectionKind.OUT_ONLY, argument)); + result.add(new TypeProjection(Variance.OUT_VARIANCE, argument)); } return result; } diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java index c9d8857543d..08c34617f29 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java @@ -216,7 +216,6 @@ public class JetTypeChecker { } private TypeProjection computeSupertypeProjection(TypeParameterDescriptor parameterDescriptor, Set typeProjections) { - // TODO: equals/hashCode for projections if (typeProjections.size() == 1) { return typeProjections.iterator().next(); } @@ -238,8 +237,8 @@ public class JetTypeChecker { } for (TypeProjection projection : typeProjections) { - ProjectionKind projectionKind = projection.getProjectionKind(); - if (projectionKind.allowsInCalls()) { + Variance projectionKind = projection.getProjectionKind(); + if (projectionKind.allowsInPosition()) { if (ins != null) { ins.add(projection.getType()); } @@ -247,7 +246,7 @@ public class JetTypeChecker { ins = null; } - if (projectionKind.allowsOutCalls()) { + if (projectionKind.allowsOutPosition()) { if (outs != null) { outs.add(projection.getType()); } @@ -257,13 +256,17 @@ public class JetTypeChecker { } if (ins != null) { - ProjectionKind projectionKind = variance == Variance.IN_VARIANCE ? ProjectionKind.NO_PROJECTION : ProjectionKind.IN_ONLY; - return new TypeProjection(projectionKind, TypeUtils.intersect(ins)); + Variance projectionKind = variance == Variance.IN_VARIANCE ? Variance.INVARIANT : Variance.IN_VARIANCE; + Type intersection = TypeUtils.intersect(ins); + if (intersection == null) { + return new TypeProjection(Variance.OUT_VARIANCE, commonSupertype(parameterDescriptor.getUpperBounds())); + } + return new TypeProjection(projectionKind, intersection); } else if (outs != null) { - ProjectionKind projectionKind = variance == Variance.OUT_VARIANCE ? ProjectionKind.NO_PROJECTION : ProjectionKind.OUT_ONLY; + Variance projectionKind = variance == Variance.OUT_VARIANCE ? Variance.INVARIANT : Variance.OUT_VARIANCE; return new TypeProjection(projectionKind, commonSupertype(outs)); } else { - ProjectionKind projectionKind = variance == Variance.OUT_VARIANCE ? ProjectionKind.NO_PROJECTION : ProjectionKind.OUT_ONLY; + Variance projectionKind = variance == Variance.OUT_VARIANCE ? Variance.INVARIANT : Variance.OUT_VARIANCE; return new TypeProjection(projectionKind, commonSupertype(parameterDescriptor.getUpperBounds())); } } @@ -417,17 +420,13 @@ public class JetTypeChecker { @NotNull private TypeProjection substitute(Map parameterValues, TypeProjection subject) { - ProjectionKind projectionKind = subject.getProjectionKind(); - if (projectionKind == ProjectionKind.NEITHER_OUT_NOR_IN) { - return subject; - } @NotNull Type subjectType = subject.getType(); TypeProjection value = parameterValues.get(subjectType.getConstructor()); if (value != null) { return value; } List newArguments = substituteInArguments(parameterValues, subjectType); - return new TypeProjection(projectionKind, specializeType(subjectType, newArguments)); + return new TypeProjection(subject.getProjectionKind(), specializeType(subjectType, newArguments)); } private List substituteInArguments(Map parameterValues, Type subjectType) { @@ -456,62 +455,58 @@ public class JetTypeChecker { Type subArgumentType = subArgument.getType(); Type superArgumentType = superArgument.getType(); - if (superArgument.getProjectionKind() != ProjectionKind.NEITHER_OUT_NOR_IN) { - switch (parameter.getVariance()) { - case INVARIANT: - switch (superArgument.getProjectionKind()) { - case NO_PROJECTION: - if (!equalTypes(subArgumentType, superArgumentType)) { - return false; - } - break; - case OUT_ONLY: - if (!subArgument.getProjectionKind().allowsOutCalls()) { - return false; - } - if (!isSubtypeOf(subArgumentType, superArgumentType)) { - return false; - } - break; - case IN_ONLY: - if (!subArgument.getProjectionKind().allowsInCalls()) { - return false; - } - if (!isSubtypeOf(superArgumentType, subArgumentType)) { - return false; - } - break; - } - break; - case IN_VARIANCE: - switch (superArgument.getProjectionKind()) { - case NO_PROJECTION: - case IN_ONLY: - if (!isSubtypeOf(superArgumentType, subArgumentType)) { - return false; - } - break; - case OUT_ONLY: - if (!isSubtypeOf(subArgumentType, superArgumentType)) { - return false; - } - break; - } - break; - case OUT_VARIANCE: - switch (superArgument.getProjectionKind()) { - case NO_PROJECTION: - case OUT_ONLY: - case IN_ONLY: - if (!isSubtypeOf(subArgumentType, superArgumentType)) { - return false; - } - break; - } - break; - } - } else { - // C< anything > is always a subtype of C<*> + switch (parameter.getVariance()) { + case INVARIANT: + switch (superArgument.getProjectionKind()) { + case INVARIANT: + if (!equalTypes(subArgumentType, superArgumentType)) { + return false; + } + break; + case OUT_VARIANCE: + if (!subArgument.getProjectionKind().allowsOutPosition()) { + return false; + } + if (!isSubtypeOf(subArgumentType, superArgumentType)) { + return false; + } + break; + case IN_VARIANCE: + if (!subArgument.getProjectionKind().allowsInPosition()) { + return false; + } + if (!isSubtypeOf(superArgumentType, subArgumentType)) { + return false; + } + break; + } + break; + case IN_VARIANCE: + switch (superArgument.getProjectionKind()) { + case INVARIANT: + case IN_VARIANCE: + if (!isSubtypeOf(superArgumentType, subArgumentType)) { + return false; + } + break; + case OUT_VARIANCE: + if (!isSubtypeOf(subArgumentType, superArgumentType)) { + return false; + } + break; + } + break; + case OUT_VARIANCE: + switch (superArgument.getProjectionKind()) { + case INVARIANT: + case OUT_VARIANCE: + case IN_VARIANCE: + if (!isSubtypeOf(subArgumentType, superArgumentType)) { + return false; + } + break; + } + break; } } return true; diff --git a/idea/src/org/jetbrains/jet/lang/types/ProjectionKind.java b/idea/src/org/jetbrains/jet/lang/types/ProjectionKind.java deleted file mode 100644 index 14b1c08cea1..00000000000 --- a/idea/src/org/jetbrains/jet/lang/types/ProjectionKind.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.jetbrains.jet.lang.types; - -/** - * @author abreslav - */ -public enum ProjectionKind { - OUT_ONLY("out", false, true), - IN_ONLY("in", true, false), - NEITHER_OUT_NOR_IN("*", false, false), - NO_PROJECTION("", true, true); - - private final String text; - private final boolean allowsInCalls; - private final boolean allowsOutCalls; - - ProjectionKind(String text, boolean allowsInCalls, boolean allowsOutCalls) { - this.text = text; - this.allowsInCalls = allowsInCalls; - this.allowsOutCalls = allowsOutCalls; - - } - - public boolean allowsInCalls() { - return allowsInCalls; - } - - public boolean allowsOutCalls() { - return allowsOutCalls; - } - - @Override - public String toString() { - return text; - } -} diff --git a/idea/src/org/jetbrains/jet/lang/types/TypeConstructor.java b/idea/src/org/jetbrains/jet/lang/types/TypeConstructor.java index f96d3f11d8d..2ca7a263396 100644 --- a/idea/src/org/jetbrains/jet/lang/types/TypeConstructor.java +++ b/idea/src/org/jetbrains/jet/lang/types/TypeConstructor.java @@ -10,9 +10,11 @@ public class TypeConstructor extends AnnotatedImpl { private final List parameters; private final Collection supertypes; private final String debugName; + private final boolean sealed; - public TypeConstructor(List attributes, String debugName, List parameters, Collection supertypes) { + public TypeConstructor(List attributes, boolean sealed, String debugName, List parameters, Collection supertypes) { super(attributes); + this.sealed = sealed; this.debugName = debugName; this.parameters = parameters; this.supertypes = supertypes; @@ -30,4 +32,8 @@ public class TypeConstructor extends AnnotatedImpl { public String toString() { return debugName; } + + public boolean isSealed() { + return sealed; + } } diff --git a/idea/src/org/jetbrains/jet/lang/types/TypeImpl.java b/idea/src/org/jetbrains/jet/lang/types/TypeImpl.java index 16ad5b5520e..b42085ef809 100644 --- a/idea/src/org/jetbrains/jet/lang/types/TypeImpl.java +++ b/idea/src/org/jetbrains/jet/lang/types/TypeImpl.java @@ -109,10 +109,8 @@ public final class TypeImpl extends AnnotatedImpl implements Type { if (typeProjection1.getProjectionKind() != typeProjection2.getProjectionKind()) { return false; } - if (typeProjection1.getProjectionKind() != ProjectionKind.NEITHER_OUT_NOR_IN) { - if (!equalTypes(typeProjection1.getType(), typeProjection2.getType())) { - return false; - } + if (!equalTypes(typeProjection1.getType(), typeProjection2.getType())) { + return false; } } return true; diff --git a/idea/src/org/jetbrains/jet/lang/types/TypeParameterDescriptor.java b/idea/src/org/jetbrains/jet/lang/types/TypeParameterDescriptor.java index 6c304fd48c6..d81bb679341 100644 --- a/idea/src/org/jetbrains/jet/lang/types/TypeParameterDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/types/TypeParameterDescriptor.java @@ -19,6 +19,7 @@ public class TypeParameterDescriptor extends NamedAnnotatedImpl { // TODO: Should we actually pass the attributes on to the type constructor? this.typeConstructor = new TypeConstructor( attributes, + false, "&" + name, Collections.emptyList(), upperBounds); diff --git a/idea/src/org/jetbrains/jet/lang/types/TypeProjection.java b/idea/src/org/jetbrains/jet/lang/types/TypeProjection.java index b8887ce9707..1cebb06abef 100644 --- a/idea/src/org/jetbrains/jet/lang/types/TypeProjection.java +++ b/idea/src/org/jetbrains/jet/lang/types/TypeProjection.java @@ -1,22 +1,24 @@ package org.jetbrains.jet.lang.types; +import org.jetbrains.annotations.NotNull; + /** * @author abreslav */ public class TypeProjection { - private final ProjectionKind projection; + private final Variance projection; private final Type type; - public TypeProjection(ProjectionKind projection, Type type) { + public TypeProjection(@NotNull Variance projection, @NotNull Type type) { this.projection = projection; this.type = type; } public TypeProjection(Type type) { - this(ProjectionKind.NO_PROJECTION, type); + this(Variance.INVARIANT, type); } - public ProjectionKind getProjectionKind() { + public Variance getProjectionKind() { return projection; } @@ -26,12 +28,29 @@ public class TypeProjection { @Override public String toString() { - if (projection == ProjectionKind.NEITHER_OUT_NOR_IN) { - return projection.toString(); - } - if (projection == ProjectionKind.NO_PROJECTION) { + if (projection == Variance.INVARIANT) { return type + ""; } return projection + " " + type; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + TypeProjection that = (TypeProjection) o; + + if (projection != that.projection) return false; + if (type != null ? !type.equals(that.type) : that.type != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = projection != null ? projection.hashCode() : 0; + result = 31 * result + (type != null ? type.hashCode() : 0); + return result; + } } diff --git a/idea/src/org/jetbrains/jet/lang/types/TypeUtils.java b/idea/src/org/jetbrains/jet/lang/types/TypeUtils.java index c0d10921199..41851d31516 100644 --- a/idea/src/org/jetbrains/jet/lang/types/TypeUtils.java +++ b/idea/src/org/jetbrains/jet/lang/types/TypeUtils.java @@ -1,5 +1,7 @@ package org.jetbrains.jet.lang.types; +import org.jetbrains.annotations.Nullable; + import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -16,6 +18,7 @@ public class TypeUtils { return new TypeImpl(type.getAttributes(), type.getConstructor(), true, type.getArguments(), type.getMemberDomain()); } + @Nullable public static Type intersect(Set types) { assert !types.isEmpty(); @@ -28,6 +31,15 @@ public class TypeUtils { for (Iterator iterator = types.iterator(); iterator.hasNext();) { Type type = iterator.next(); + if (!canHaveSubtypes(type)) { + for (Type other : types) { + if (type != other || !JetTypeChecker.INSTANCE.isSubtypeOf(type, other)) { + return null; + } + } + return type; + } + nullable |= type.isNullable(); debugName.append(type.toString()); @@ -37,7 +49,7 @@ public class TypeUtils { } List noAttributes = Collections.emptyList(); - TypeConstructor constructor = new TypeConstructor(noAttributes, debugName.toString(), Collections.emptyList(), types); + TypeConstructor constructor = new TypeConstructor(noAttributes, false, debugName.toString(), Collections.emptyList(), types); return new TypeImpl( noAttributes, constructor, @@ -46,6 +58,79 @@ public class TypeUtils { JetStandardClasses.STUB); } + private static boolean canHaveSubtypes(Type type) { + // TODO : a nullable type can have a subtype -- a non-nullable version + + if (!type.getConstructor().isSealed()) { + return true; + } + + List parameters = type.getConstructor().getParameters(); + List arguments = type.getArguments(); + for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) { + TypeParameterDescriptor parameterDescriptor = parameters.get(i); + TypeProjection typeProjection = arguments.get(i); + Variance projectionKind = typeProjection.getProjectionKind(); + Type argument = typeProjection.getType(); + + switch (parameterDescriptor.getVariance()) { + case INVARIANT: + switch (projectionKind) { + case INVARIANT: + if (lowerThanBound(argument, parameterDescriptor) || canHaveSubtypes(argument)) { + return true; + } + break; + case IN_VARIANCE: + if (lowerThanBound(argument, parameterDescriptor)) { + return true; + } + break; + case OUT_VARIANCE: + if (canHaveSubtypes(argument)) { + return true; + } + break; + } + break; + case IN_VARIANCE: + if (projectionKind != Variance.OUT_VARIANCE) { + if (lowerThanBound(argument, parameterDescriptor)) { + return true; + } + } else { + if (canHaveSubtypes(argument)) { + return true; + } + } + break; + case OUT_VARIANCE: + if (projectionKind != Variance.IN_VARIANCE) { + if (canHaveSubtypes(argument)) { + return true; + } + } else { + if (lowerThanBound(argument, parameterDescriptor)) { + return true; + } + } + break; + } + } + return false; + } + + private static boolean lowerThanBound(Type argument, TypeParameterDescriptor parameterDescriptor) { + for (Type bound : parameterDescriptor.getUpperBounds()) { + if (JetTypeChecker.INSTANCE.isSubtypeOf(argument, bound)) { + if (!argument.getConstructor().equals(bound.getConstructor())) { + return true; + } + } + } + return false; + } + public static Type makeNullableIfNeeded(Type type, boolean nullable) { if (nullable) { return makeNullable(type); diff --git a/idea/src/org/jetbrains/jet/lang/types/Variance.java b/idea/src/org/jetbrains/jet/lang/types/Variance.java index fe96885bfff..0cfdc6b406d 100644 --- a/idea/src/org/jetbrains/jet/lang/types/Variance.java +++ b/idea/src/org/jetbrains/jet/lang/types/Variance.java @@ -4,14 +4,26 @@ package org.jetbrains.jet.lang.types; * @author max */ public enum Variance { - INVARIANT(""), - IN_VARIANCE("in"), - OUT_VARIANCE("out"); + INVARIANT("", true, true), + IN_VARIANCE("in", true, false), + OUT_VARIANCE("out", false, true); private final String label; + private final boolean allowsInPosition; + private final boolean allowsOutPosition; - Variance(String label) { + Variance(String label, boolean allowsInPosition, boolean allowsOutPosition) { this.label = label; + this.allowsInPosition = allowsInPosition; + this.allowsOutPosition = allowsOutPosition; + } + + public boolean allowsInPosition() { + return allowsInPosition; + } + + public boolean allowsOutPosition() { + return allowsOutPosition; } @Override diff --git a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java index 490bcab6ab2..3086329c0fc 100644 --- a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java +++ b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java @@ -93,10 +93,10 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { assertType("if (true) null : DDerived_T? else null : Derived_T?", "Derived_T?"); assertType("if (true) null : DDerived_T? else null : DDerived1_T?", "Derived_T?"); - assertType("if (true) null : Base_T? else null : Base_T?", "Any?"); + assertType("if (true) null : Base_T? else null : Base_T?", "Base_T?"); assertType("if (true) null : Base_T? else null : Base_T?", "Base_T?"); assertType("if (true) null : Derived_T? else null : Base_T?", "Base_T?"); - assertType("if (true) null : Derived_T? else null : Base_T?", "Any?"); + assertType("if (true) null : Derived_T? else null : Base_T?", "Base_T?"); assertType("if (true) null : Base_T? else null : Base_T<*>?", "Base_T<*>?"); } @@ -274,7 +274,7 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { Project project = getProject(); JetExpression jetExpression = JetChangeUtil.createExpression(project, expression); Type type = JetTypeChecker.INSTANCE.getType(ClassDefinitions.BASIC_SCOPE, jetExpression); - assertTrue(type + "!=" + expectedType, JetTypeChecker.INSTANCE.equalTypes(type, expectedType)); + assertTrue(type + " != " + expectedType, JetTypeChecker.INSTANCE.equalTypes(type, expectedType)); } private static void assertType(String expression, String expectedTypeStr) { @@ -282,7 +282,7 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { JetExpression jetExpression = JetChangeUtil.createExpression(project, expression); Type type = JetTypeChecker.INSTANCE.getType(ClassDefinitions.BASIC_SCOPE, jetExpression); Type expectedType = makeType(expectedTypeStr); - assertTrue(type + "!=" + expectedType, JetTypeChecker.INSTANCE.equalTypes(type, expectedType)); + assertTrue(type + " != " + expectedType, JetTypeChecker.INSTANCE.equalTypes(type, expectedType)); } private static Type makeType(String typeStr) { @@ -292,14 +292,14 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { private static class ClassDefinitions { private static Map CLASSES = new HashMap(); private static String[] CLASS_DECLARATIONS = { - "class Base_T", - "class Derived_T : Base_T", - "class DDerived_T : Derived_T", - "class DDerived1_T : Derived_T", - "class Base_inT", - "class Derived_inT : Base_inT", - "class Base_outT", - "class Derived_outT : Base_outT", + "open class Base_T", + "open class Derived_T : Base_T", + "open class DDerived_T : Derived_T", + "open class DDerived1_T : Derived_T", + "open class Base_inT", + "open class Derived_inT : Base_inT", + "open class Base_outT", + "open class Derived_outT : Base_outT", }; public static JetScope BASIC_SCOPE = new JetScopeImpl() {