Inferring common supertypes taking projections and bounds into account

This commit is contained in:
Andrey Breslav
2011-02-01 17:31:56 +03:00
parent c6a60d8bf6
commit 08c1863981
16 changed files with 256 additions and 149 deletions
@@ -0,0 +1,8 @@
package org.jetbrains.jet.lang.psi;
/**
* @author abreslav
*/
public enum JetProjectionKind {
IN, OUT, STAR, NONE;
}
@@ -4,7 +4,6 @@ import com.intellij.lang.ASTNode;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.JetNodeTypes; import org.jetbrains.jet.JetNodeTypes;
import org.jetbrains.jet.lang.types.ProjectionKind;
import org.jetbrains.jet.lexer.JetTokens; import org.jetbrains.jet.lexer.JetTokens;
/** /**
@@ -16,21 +15,21 @@ public class JetTypeProjection extends JetDeclaration {
} }
@NotNull @NotNull
public ProjectionKind getProjectionKind() { public JetProjectionKind getProjectionKind() {
JetModifierList modifierList = getModifierList(); JetModifierList modifierList = getModifierList();
if (modifierList != null) { if (modifierList != null) {
if (modifierList.hasModifier(JetTokens.IN_KEYWORD)) { if (modifierList.hasModifier(JetTokens.IN_KEYWORD)) {
return ProjectionKind.IN_ONLY; return JetProjectionKind.IN;
} }
if (modifierList.hasModifier(JetTokens.OUT_KEYWORD)) { if (modifierList.hasModifier(JetTokens.OUT_KEYWORD)) {
return ProjectionKind.OUT_ONLY; return JetProjectionKind.OUT;
} }
} }
if (findChildByType(JetTokens.MUL) != null) { if (findChildByType(JetTokens.MUL) != null) {
return ProjectionKind.NEITHER_OUT_NOR_IN; return JetProjectionKind.STAR;
} }
return ProjectionKind.NO_PROJECTION; return JetProjectionKind.NONE;
} }
@Override @Override
@@ -10,6 +10,7 @@ import org.jetbrains.jet.lang.types.ClassDescriptor;
import org.jetbrains.jet.lang.types.JetStandardClasses; import org.jetbrains.jet.lang.types.JetStandardClasses;
import org.jetbrains.jet.lang.types.Type; import org.jetbrains.jet.lang.types.Type;
import org.jetbrains.jet.lang.types.TypeParameterDescriptor; import org.jetbrains.jet.lang.types.TypeParameterDescriptor;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.*; import java.util.*;
@@ -33,8 +34,10 @@ public class ClassDescriptorResolver {
Collection<? extends Type> superclasses = delegationSpecifiers.isEmpty() Collection<? extends Type> superclasses = delegationSpecifiers.isEmpty()
? Collections.singleton(JetStandardClasses.getAnyType()) ? Collections.singleton(JetStandardClasses.getAnyType())
: resolveTypes(extensibleScope, delegationSpecifiers); : resolveTypes(extensibleScope, delegationSpecifiers);
boolean open = classElement.getModifierList().hasModifier(JetTokens.OPEN_KEYWORD);
return new ClassDescriptor( return new ClassDescriptor(
AttributeResolver.INSTANCE.resolveAttributes(classElement.getModifierList()), AttributeResolver.INSTANCE.resolveAttributes(classElement.getModifierList()),
!open,
classElement.getName(), classElement.getName(),
typeParameters, typeParameters,
superclasses superclasses
@@ -104,16 +104,29 @@ public class TypeResolver {
for (int i = 0, argumentElementsSize = argumentElements.size(); i < argumentElementsSize; i++) { for (int i = 0, argumentElementsSize = argumentElements.size(); i < argumentElementsSize; i++) {
JetTypeProjection argumentElement = argumentElements.get(i); JetTypeProjection argumentElement = argumentElements.get(i);
ProjectionKind projectionKind = argumentElement.getProjectionKind(); JetProjectionKind projectionKind = argumentElement.getProjectionKind();
Type type; Type type;
if (projectionKind == ProjectionKind.NEITHER_OUT_NOR_IN) { if (projectionKind == JetProjectionKind.STAR) {
Set<Type> upperBounds = constructor.getParameters().get(i).getUpperBounds(); Set<Type> 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 { else {
// TODO : handle the Foo<in *> case // TODO : handle the Foo<in *> case
type = resolveType(scope, argumentElement.getTypeReference()); 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; return arguments;
@@ -13,14 +13,14 @@ public class ClassDescriptor extends MemberDescriptorImpl implements MemberDomai
private final TypeConstructor typeConstructor; private final TypeConstructor typeConstructor;
public ClassDescriptor( public ClassDescriptor(
List<Attribute> attributes, List<Attribute> attributes, boolean sealed,
String name, List<TypeParameterDescriptor> typeParameters, Collection<? extends Type> superclasses) { String name, List<TypeParameterDescriptor> typeParameters, Collection<? extends Type> superclasses) {
super(attributes, name); super(attributes, name);
this.typeConstructor = new TypeConstructor(attributes, name, typeParameters, superclasses); this.typeConstructor = new TypeConstructor(attributes, sealed, name, typeParameters, superclasses);
} }
public ClassDescriptor(String name) { public ClassDescriptor(String name) {
this(Collections.<Attribute>emptyList(), this(Collections.<Attribute>emptyList(), true,
name, Collections.<TypeParameterDescriptor>emptyList(), name, Collections.<TypeParameterDescriptor>emptyList(),
Collections.<Type>singleton(JetStandardClasses.getAnyType())); Collections.<Type>singleton(JetStandardClasses.getAnyType()));
} }
@@ -14,7 +14,7 @@ public class ErrorType {
throw new UnsupportedOperationException(); // TODO throw new UnsupportedOperationException(); // TODO
} }
}; };
private static final TypeConstructor ERROR = new TypeConstructor(Collections.<Attribute>emptyList(), "ERROR", Collections.<TypeParameterDescriptor>emptyList(), Collections.<Type>emptyList()); private static final TypeConstructor ERROR = new TypeConstructor(Collections.<Attribute>emptyList(), false, "ERROR", Collections.<TypeParameterDescriptor>emptyList(), Collections.<Type>emptyList());
public static Type createErrorType(String debugMessage) { public static Type createErrorType(String debugMessage) {
return new TypeImpl(ERROR, ERROR_DOMAIN); return new TypeImpl(ERROR, ERROR_DOMAIN);
@@ -11,6 +11,7 @@ public class JetStandardClasses {
private static ClassDescriptor NOTHING_CLASS = new ClassDescriptor( private static ClassDescriptor NOTHING_CLASS = new ClassDescriptor(
Collections.<Attribute>emptyList(), Collections.<Attribute>emptyList(),
true,
"Nothing", "Nothing",
Collections.<TypeParameterDescriptor>emptyList(), Collections.<TypeParameterDescriptor>emptyList(),
new AbstractCollection<Type>() { new AbstractCollection<Type>() {
@@ -33,6 +34,7 @@ public class JetStandardClasses {
private static final ClassDescriptor ANY = new ClassDescriptor( private static final ClassDescriptor ANY = new ClassDescriptor(
Collections.<Attribute>emptyList(), Collections.<Attribute>emptyList(),
false,
"Any", "Any",
Collections.<TypeParameterDescriptor>emptyList(), Collections.<TypeParameterDescriptor>emptyList(),
Collections.<Type>emptySet() Collections.<Type>emptySet()
@@ -64,6 +66,7 @@ public class JetStandardClasses {
} }
TUPLE[i] = new ClassDescriptor( TUPLE[i] = new ClassDescriptor(
Collections.<Attribute>emptyList(), Collections.<Attribute>emptyList(),
true,
"Tuple" + i, "Tuple" + i,
parameters, parameters,
Collections.singleton(JetStandardClasses.getAnyType())); Collections.singleton(JetStandardClasses.getAnyType()));
@@ -244,7 +247,7 @@ public class JetStandardClasses {
private static List<TypeProjection> toProjections(List<Type> arguments) { private static List<TypeProjection> toProjections(List<Type> arguments) {
List<TypeProjection> result = new ArrayList<TypeProjection>(); List<TypeProjection> result = new ArrayList<TypeProjection>();
for (Type argument : arguments) { for (Type argument : arguments) {
result.add(new TypeProjection(ProjectionKind.OUT_ONLY, argument)); result.add(new TypeProjection(Variance.OUT_VARIANCE, argument));
} }
return result; return result;
} }
@@ -216,7 +216,6 @@ public class JetTypeChecker {
} }
private TypeProjection computeSupertypeProjection(TypeParameterDescriptor parameterDescriptor, Set<TypeProjection> typeProjections) { private TypeProjection computeSupertypeProjection(TypeParameterDescriptor parameterDescriptor, Set<TypeProjection> typeProjections) {
// TODO: equals/hashCode for projections
if (typeProjections.size() == 1) { if (typeProjections.size() == 1) {
return typeProjections.iterator().next(); return typeProjections.iterator().next();
} }
@@ -238,8 +237,8 @@ public class JetTypeChecker {
} }
for (TypeProjection projection : typeProjections) { for (TypeProjection projection : typeProjections) {
ProjectionKind projectionKind = projection.getProjectionKind(); Variance projectionKind = projection.getProjectionKind();
if (projectionKind.allowsInCalls()) { if (projectionKind.allowsInPosition()) {
if (ins != null) { if (ins != null) {
ins.add(projection.getType()); ins.add(projection.getType());
} }
@@ -247,7 +246,7 @@ public class JetTypeChecker {
ins = null; ins = null;
} }
if (projectionKind.allowsOutCalls()) { if (projectionKind.allowsOutPosition()) {
if (outs != null) { if (outs != null) {
outs.add(projection.getType()); outs.add(projection.getType());
} }
@@ -257,13 +256,17 @@ public class JetTypeChecker {
} }
if (ins != null) { if (ins != null) {
ProjectionKind projectionKind = variance == Variance.IN_VARIANCE ? ProjectionKind.NO_PROJECTION : ProjectionKind.IN_ONLY; Variance projectionKind = variance == Variance.IN_VARIANCE ? Variance.INVARIANT : Variance.IN_VARIANCE;
return new TypeProjection(projectionKind, TypeUtils.intersect(ins)); 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) { } 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)); return new TypeProjection(projectionKind, commonSupertype(outs));
} else { } 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())); return new TypeProjection(projectionKind, commonSupertype(parameterDescriptor.getUpperBounds()));
} }
} }
@@ -417,17 +420,13 @@ public class JetTypeChecker {
@NotNull @NotNull
private TypeProjection substitute(Map<TypeConstructor, TypeProjection> parameterValues, TypeProjection subject) { private TypeProjection substitute(Map<TypeConstructor, TypeProjection> parameterValues, TypeProjection subject) {
ProjectionKind projectionKind = subject.getProjectionKind();
if (projectionKind == ProjectionKind.NEITHER_OUT_NOR_IN) {
return subject;
}
@NotNull Type subjectType = subject.getType(); @NotNull Type subjectType = subject.getType();
TypeProjection value = parameterValues.get(subjectType.getConstructor()); TypeProjection value = parameterValues.get(subjectType.getConstructor());
if (value != null) { if (value != null) {
return value; return value;
} }
List<TypeProjection> newArguments = substituteInArguments(parameterValues, subjectType); List<TypeProjection> newArguments = substituteInArguments(parameterValues, subjectType);
return new TypeProjection(projectionKind, specializeType(subjectType, newArguments)); return new TypeProjection(subject.getProjectionKind(), specializeType(subjectType, newArguments));
} }
private List<TypeProjection> substituteInArguments(Map<TypeConstructor, TypeProjection> parameterValues, Type subjectType) { private List<TypeProjection> substituteInArguments(Map<TypeConstructor, TypeProjection> parameterValues, Type subjectType) {
@@ -456,62 +455,58 @@ public class JetTypeChecker {
Type subArgumentType = subArgument.getType(); Type subArgumentType = subArgument.getType();
Type superArgumentType = superArgument.getType(); Type superArgumentType = superArgument.getType();
if (superArgument.getProjectionKind() != ProjectionKind.NEITHER_OUT_NOR_IN) { switch (parameter.getVariance()) {
switch (parameter.getVariance()) { case INVARIANT:
case INVARIANT: switch (superArgument.getProjectionKind()) {
switch (superArgument.getProjectionKind()) { case INVARIANT:
case NO_PROJECTION: if (!equalTypes(subArgumentType, superArgumentType)) {
if (!equalTypes(subArgumentType, superArgumentType)) { return false;
return false; }
} break;
break; case OUT_VARIANCE:
case OUT_ONLY: if (!subArgument.getProjectionKind().allowsOutPosition()) {
if (!subArgument.getProjectionKind().allowsOutCalls()) { return false;
return false; }
} if (!isSubtypeOf(subArgumentType, superArgumentType)) {
if (!isSubtypeOf(subArgumentType, superArgumentType)) { return false;
return false; }
} break;
break; case IN_VARIANCE:
case IN_ONLY: if (!subArgument.getProjectionKind().allowsInPosition()) {
if (!subArgument.getProjectionKind().allowsInCalls()) { return false;
return false; }
} if (!isSubtypeOf(superArgumentType, subArgumentType)) {
if (!isSubtypeOf(superArgumentType, subArgumentType)) { return false;
return false; }
} break;
break; }
} break;
break; case IN_VARIANCE:
case IN_VARIANCE: switch (superArgument.getProjectionKind()) {
switch (superArgument.getProjectionKind()) { case INVARIANT:
case NO_PROJECTION: case IN_VARIANCE:
case IN_ONLY: if (!isSubtypeOf(superArgumentType, subArgumentType)) {
if (!isSubtypeOf(superArgumentType, subArgumentType)) { return false;
return false; }
} break;
break; case OUT_VARIANCE:
case OUT_ONLY: if (!isSubtypeOf(subArgumentType, superArgumentType)) {
if (!isSubtypeOf(subArgumentType, superArgumentType)) { return false;
return false; }
} break;
break; }
} break;
break; case OUT_VARIANCE:
case OUT_VARIANCE: switch (superArgument.getProjectionKind()) {
switch (superArgument.getProjectionKind()) { case INVARIANT:
case NO_PROJECTION: case OUT_VARIANCE:
case OUT_ONLY: case IN_VARIANCE:
case IN_ONLY: if (!isSubtypeOf(subArgumentType, superArgumentType)) {
if (!isSubtypeOf(subArgumentType, superArgumentType)) { return false;
return false; }
} break;
break; }
} break;
break;
}
} else {
// C< anything > is always a subtype of C<*>
} }
} }
return true; return true;
@@ -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;
}
}
@@ -10,9 +10,11 @@ public class TypeConstructor extends AnnotatedImpl {
private final List<TypeParameterDescriptor> parameters; private final List<TypeParameterDescriptor> parameters;
private final Collection<? extends Type> supertypes; private final Collection<? extends Type> supertypes;
private final String debugName; private final String debugName;
private final boolean sealed;
public TypeConstructor(List<Attribute> attributes, String debugName, List<TypeParameterDescriptor> parameters, Collection<? extends Type> supertypes) { public TypeConstructor(List<Attribute> attributes, boolean sealed, String debugName, List<TypeParameterDescriptor> parameters, Collection<? extends Type> supertypes) {
super(attributes); super(attributes);
this.sealed = sealed;
this.debugName = debugName; this.debugName = debugName;
this.parameters = parameters; this.parameters = parameters;
this.supertypes = supertypes; this.supertypes = supertypes;
@@ -30,4 +32,8 @@ public class TypeConstructor extends AnnotatedImpl {
public String toString() { public String toString() {
return debugName; return debugName;
} }
public boolean isSealed() {
return sealed;
}
} }
@@ -109,10 +109,8 @@ public final class TypeImpl extends AnnotatedImpl implements Type {
if (typeProjection1.getProjectionKind() != typeProjection2.getProjectionKind()) { if (typeProjection1.getProjectionKind() != typeProjection2.getProjectionKind()) {
return false; return false;
} }
if (typeProjection1.getProjectionKind() != ProjectionKind.NEITHER_OUT_NOR_IN) { if (!equalTypes(typeProjection1.getType(), typeProjection2.getType())) {
if (!equalTypes(typeProjection1.getType(), typeProjection2.getType())) { return false;
return false;
}
} }
} }
return true; return true;
@@ -19,6 +19,7 @@ public class TypeParameterDescriptor extends NamedAnnotatedImpl {
// TODO: Should we actually pass the attributes on to the type constructor? // TODO: Should we actually pass the attributes on to the type constructor?
this.typeConstructor = new TypeConstructor( this.typeConstructor = new TypeConstructor(
attributes, attributes,
false,
"&" + name, "&" + name,
Collections.<TypeParameterDescriptor>emptyList(), Collections.<TypeParameterDescriptor>emptyList(),
upperBounds); upperBounds);
@@ -1,22 +1,24 @@
package org.jetbrains.jet.lang.types; package org.jetbrains.jet.lang.types;
import org.jetbrains.annotations.NotNull;
/** /**
* @author abreslav * @author abreslav
*/ */
public class TypeProjection { public class TypeProjection {
private final ProjectionKind projection; private final Variance projection;
private final Type type; private final Type type;
public TypeProjection(ProjectionKind projection, Type type) { public TypeProjection(@NotNull Variance projection, @NotNull Type type) {
this.projection = projection; this.projection = projection;
this.type = type; this.type = type;
} }
public TypeProjection(Type type) { public TypeProjection(Type type) {
this(ProjectionKind.NO_PROJECTION, type); this(Variance.INVARIANT, type);
} }
public ProjectionKind getProjectionKind() { public Variance getProjectionKind() {
return projection; return projection;
} }
@@ -26,12 +28,29 @@ public class TypeProjection {
@Override @Override
public String toString() { public String toString() {
if (projection == ProjectionKind.NEITHER_OUT_NOR_IN) { if (projection == Variance.INVARIANT) {
return projection.toString();
}
if (projection == ProjectionKind.NO_PROJECTION) {
return type + ""; return type + "";
} }
return projection + " " + 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;
}
} }
@@ -1,5 +1,7 @@
package org.jetbrains.jet.lang.types; package org.jetbrains.jet.lang.types;
import org.jetbrains.annotations.Nullable;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@@ -16,6 +18,7 @@ public class TypeUtils {
return new TypeImpl(type.getAttributes(), type.getConstructor(), true, type.getArguments(), type.getMemberDomain()); return new TypeImpl(type.getAttributes(), type.getConstructor(), true, type.getArguments(), type.getMemberDomain());
} }
@Nullable
public static Type intersect(Set<Type> types) { public static Type intersect(Set<Type> types) {
assert !types.isEmpty(); assert !types.isEmpty();
@@ -28,6 +31,15 @@ public class TypeUtils {
for (Iterator<Type> iterator = types.iterator(); iterator.hasNext();) { for (Iterator<Type> iterator = types.iterator(); iterator.hasNext();) {
Type type = iterator.next(); 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(); nullable |= type.isNullable();
debugName.append(type.toString()); debugName.append(type.toString());
@@ -37,7 +49,7 @@ public class TypeUtils {
} }
List<Attribute> noAttributes = Collections.<Attribute>emptyList(); List<Attribute> noAttributes = Collections.<Attribute>emptyList();
TypeConstructor constructor = new TypeConstructor(noAttributes, debugName.toString(), Collections.<TypeParameterDescriptor>emptyList(), types); TypeConstructor constructor = new TypeConstructor(noAttributes, false, debugName.toString(), Collections.<TypeParameterDescriptor>emptyList(), types);
return new TypeImpl( return new TypeImpl(
noAttributes, noAttributes,
constructor, constructor,
@@ -46,6 +58,79 @@ public class TypeUtils {
JetStandardClasses.STUB); 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<TypeParameterDescriptor> parameters = type.getConstructor().getParameters();
List<TypeProjection> 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) { public static Type makeNullableIfNeeded(Type type, boolean nullable) {
if (nullable) { if (nullable) {
return makeNullable(type); return makeNullable(type);
@@ -4,14 +4,26 @@ package org.jetbrains.jet.lang.types;
* @author max * @author max
*/ */
public enum Variance { public enum Variance {
INVARIANT(""), INVARIANT("", true, true),
IN_VARIANCE("in"), IN_VARIANCE("in", true, false),
OUT_VARIANCE("out"); OUT_VARIANCE("out", false, true);
private final String label; 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.label = label;
this.allowsInPosition = allowsInPosition;
this.allowsOutPosition = allowsOutPosition;
}
public boolean allowsInPosition() {
return allowsInPosition;
}
public boolean allowsOutPosition() {
return allowsOutPosition;
} }
@Override @Override
@@ -93,10 +93,10 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
assertType("if (true) null : DDerived_T<Int>? else null : Derived_T<Int>?", "Derived_T<Int>?"); assertType("if (true) null : DDerived_T<Int>? else null : Derived_T<Int>?", "Derived_T<Int>?");
assertType("if (true) null : DDerived_T<Int>? else null : DDerived1_T<Int>?", "Derived_T<Int>?"); assertType("if (true) null : DDerived_T<Int>? else null : DDerived1_T<Int>?", "Derived_T<Int>?");
assertType("if (true) null : Base_T<Int>? else null : Base_T<Boolean>?", "Any?"); assertType("if (true) null : Base_T<Int>? else null : Base_T<Boolean>?", "Base_T<out Any>?");
assertType("if (true) null : Base_T<Int>? else null : Base_T<in Int>?", "Base_T<in Int>?"); assertType("if (true) null : Base_T<Int>? else null : Base_T<in Int>?", "Base_T<in Int>?");
assertType("if (true) null : Derived_T<Int>? else null : Base_T<in Int>?", "Base_T<in Int>?"); assertType("if (true) null : Derived_T<Int>? else null : Base_T<in Int>?", "Base_T<in Int>?");
assertType("if (true) null : Derived_T<in Int>? else null : Base_T<Int>?", "Any?"); assertType("if (true) null : Derived_T<in Int>? else null : Base_T<Int>?", "Base_T<in Int>?");
assertType("if (true) null : Base_T<Int>? else null : Base_T<*>?", "Base_T<*>?"); assertType("if (true) null : Base_T<Int>? else null : Base_T<*>?", "Base_T<*>?");
} }
@@ -274,7 +274,7 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
Project project = getProject(); Project project = getProject();
JetExpression jetExpression = JetChangeUtil.createExpression(project, expression); JetExpression jetExpression = JetChangeUtil.createExpression(project, expression);
Type type = JetTypeChecker.INSTANCE.getType(ClassDefinitions.BASIC_SCOPE, jetExpression); 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) { private static void assertType(String expression, String expectedTypeStr) {
@@ -282,7 +282,7 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
JetExpression jetExpression = JetChangeUtil.createExpression(project, expression); JetExpression jetExpression = JetChangeUtil.createExpression(project, expression);
Type type = JetTypeChecker.INSTANCE.getType(ClassDefinitions.BASIC_SCOPE, jetExpression); Type type = JetTypeChecker.INSTANCE.getType(ClassDefinitions.BASIC_SCOPE, jetExpression);
Type expectedType = makeType(expectedTypeStr); 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) { private static Type makeType(String typeStr) {
@@ -292,14 +292,14 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
private static class ClassDefinitions { private static class ClassDefinitions {
private static Map<String, ClassDescriptor> CLASSES = new HashMap<String, ClassDescriptor>(); private static Map<String, ClassDescriptor> CLASSES = new HashMap<String, ClassDescriptor>();
private static String[] CLASS_DECLARATIONS = { private static String[] CLASS_DECLARATIONS = {
"class Base_T<T>", "open class Base_T<T>",
"class Derived_T<T> : Base_T<T>", "open class Derived_T<T> : Base_T<T>",
"class DDerived_T<T> : Derived_T<T>", "open class DDerived_T<T> : Derived_T<T>",
"class DDerived1_T<T> : Derived_T<T>", "open class DDerived1_T<T> : Derived_T<T>",
"class Base_inT<in T>", "open class Base_inT<in T>",
"class Derived_inT<in T> : Base_inT<T>", "open class Derived_inT<in T> : Base_inT<T>",
"class Base_outT<out T>", "open class Base_outT<out T>",
"class Derived_outT<out T> : Base_outT<T>", "open class Derived_outT<out T> : Base_outT<T>",
}; };
public static JetScope BASIC_SCOPE = new JetScopeImpl() { public static JetScope BASIC_SCOPE = new JetScopeImpl() {