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.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() {
|
||||||
|
|||||||
Reference in New Issue
Block a user