Inferring common supertypes taking projections and bounds into account
This commit is contained in:
@@ -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.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
|
||||
|
||||
@@ -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<? extends Type> 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
|
||||
|
||||
@@ -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<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 {
|
||||
// TODO : handle the Foo<in *> 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;
|
||||
|
||||
@@ -13,14 +13,14 @@ public class ClassDescriptor extends MemberDescriptorImpl implements MemberDomai
|
||||
private final TypeConstructor typeConstructor;
|
||||
|
||||
public ClassDescriptor(
|
||||
List<Attribute> attributes,
|
||||
List<Attribute> attributes, boolean sealed,
|
||||
String name, List<TypeParameterDescriptor> typeParameters, Collection<? extends Type> 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.<Attribute>emptyList(),
|
||||
this(Collections.<Attribute>emptyList(), true,
|
||||
name, Collections.<TypeParameterDescriptor>emptyList(),
|
||||
Collections.<Type>singleton(JetStandardClasses.getAnyType()));
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ public class ErrorType {
|
||||
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) {
|
||||
return new TypeImpl(ERROR, ERROR_DOMAIN);
|
||||
|
||||
@@ -11,6 +11,7 @@ public class JetStandardClasses {
|
||||
|
||||
private static ClassDescriptor NOTHING_CLASS = new ClassDescriptor(
|
||||
Collections.<Attribute>emptyList(),
|
||||
true,
|
||||
"Nothing",
|
||||
Collections.<TypeParameterDescriptor>emptyList(),
|
||||
new AbstractCollection<Type>() {
|
||||
@@ -33,6 +34,7 @@ public class JetStandardClasses {
|
||||
|
||||
private static final ClassDescriptor ANY = new ClassDescriptor(
|
||||
Collections.<Attribute>emptyList(),
|
||||
false,
|
||||
"Any",
|
||||
Collections.<TypeParameterDescriptor>emptyList(),
|
||||
Collections.<Type>emptySet()
|
||||
@@ -64,6 +66,7 @@ public class JetStandardClasses {
|
||||
}
|
||||
TUPLE[i] = new ClassDescriptor(
|
||||
Collections.<Attribute>emptyList(),
|
||||
true,
|
||||
"Tuple" + i,
|
||||
parameters,
|
||||
Collections.singleton(JetStandardClasses.getAnyType()));
|
||||
@@ -244,7 +247,7 @@ public class JetStandardClasses {
|
||||
private static List<TypeProjection> toProjections(List<Type> arguments) {
|
||||
List<TypeProjection> result = new ArrayList<TypeProjection>();
|
||||
for (Type argument : arguments) {
|
||||
result.add(new TypeProjection(ProjectionKind.OUT_ONLY, argument));
|
||||
result.add(new TypeProjection(Variance.OUT_VARIANCE, argument));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -216,7 +216,6 @@ public class JetTypeChecker {
|
||||
}
|
||||
|
||||
private TypeProjection computeSupertypeProjection(TypeParameterDescriptor parameterDescriptor, Set<TypeProjection> 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<TypeConstructor, TypeProjection> 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<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) {
|
||||
@@ -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;
|
||||
|
||||
@@ -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 Collection<? extends Type> supertypes;
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.<TypeParameterDescriptor>emptyList(),
|
||||
upperBounds);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Type> types) {
|
||||
assert !types.isEmpty();
|
||||
|
||||
@@ -28,6 +31,15 @@ public class TypeUtils {
|
||||
for (Iterator<Type> 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<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(
|
||||
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<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) {
|
||||
if (nullable) {
|
||||
return makeNullable(type);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 : 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 : 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<*>?");
|
||||
}
|
||||
|
||||
@@ -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<String, ClassDescriptor> CLASSES = new HashMap<String, ClassDescriptor>();
|
||||
private static String[] CLASS_DECLARATIONS = {
|
||||
"class Base_T<T>",
|
||||
"class Derived_T<T> : Base_T<T>",
|
||||
"class DDerived_T<T> : Derived_T<T>",
|
||||
"class DDerived1_T<T> : Derived_T<T>",
|
||||
"class Base_inT<in T>",
|
||||
"class Derived_inT<in T> : Base_inT<T>",
|
||||
"class Base_outT<out T>",
|
||||
"class Derived_outT<out T> : Base_outT<T>",
|
||||
"open class Base_T<T>",
|
||||
"open class Derived_T<T> : Base_T<T>",
|
||||
"open class DDerived_T<T> : Derived_T<T>",
|
||||
"open class DDerived1_T<T> : Derived_T<T>",
|
||||
"open class Base_inT<in T>",
|
||||
"open class Derived_inT<in T> : Base_inT<T>",
|
||||
"open class Base_outT<out T>",
|
||||
"open class Derived_outT<out T> : Base_outT<T>",
|
||||
};
|
||||
|
||||
public static JetScope BASIC_SCOPE = new JetScopeImpl() {
|
||||
|
||||
Reference in New Issue
Block a user