Working on the typechecker: inferring common supertypes taking projections and bounds into account
This commit is contained in:
@@ -9,6 +9,7 @@ import org.jetbrains.jet.lang.types.*;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author abreslav
|
* @author abreslav
|
||||||
@@ -35,11 +36,12 @@ public class TypeResolver {
|
|||||||
public void visitUserType(JetUserType type) {
|
public void visitUserType(JetUserType type) {
|
||||||
ClassDescriptor classDescriptor = resolveClass(scope, type);
|
ClassDescriptor classDescriptor = resolveClass(scope, type);
|
||||||
if (classDescriptor != null) {
|
if (classDescriptor != null) {
|
||||||
|
TypeConstructor typeConstructor = classDescriptor.getTypeConstructor();
|
||||||
result[0] = new TypeImpl(
|
result[0] = new TypeImpl(
|
||||||
attributes,
|
attributes,
|
||||||
classDescriptor.getTypeConstructor(),
|
typeConstructor,
|
||||||
nullable,
|
nullable,
|
||||||
resolveTypeProjections(scope, type.getTypeArguments()),
|
resolveTypeProjections(scope, typeConstructor, type.getTypeArguments()),
|
||||||
JetStandardClasses.STUB
|
JetStandardClasses.STUB
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -97,18 +99,22 @@ public class TypeResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private List<TypeProjection> resolveTypeProjections(JetScope scope, List<JetTypeProjection> argumentElements) {
|
private List<TypeProjection> resolveTypeProjections(JetScope scope, TypeConstructor constructor, List<JetTypeProjection> argumentElements) {
|
||||||
final List<TypeProjection> arguments = new ArrayList<TypeProjection>();
|
final List<TypeProjection> arguments = new ArrayList<TypeProjection>();
|
||||||
for (JetTypeProjection argumentElement : argumentElements) {
|
for (int i = 0, argumentElementsSize = argumentElements.size(); i < argumentElementsSize; i++) {
|
||||||
|
JetTypeProjection argumentElement = argumentElements.get(i);
|
||||||
|
|
||||||
ProjectionKind projectionKind = argumentElement.getProjectionKind();
|
ProjectionKind projectionKind = argumentElement.getProjectionKind();
|
||||||
Type type;
|
Type type;
|
||||||
if (projectionKind == ProjectionKind.NEITHER_OUT_NOR_IN) {
|
if (projectionKind == ProjectionKind.NEITHER_OUT_NOR_IN) {
|
||||||
type = null;
|
Set<Type> upperBounds = constructor.getParameters().get(i).getUpperBounds();
|
||||||
} else {
|
arguments.add(new TypeProjection(ProjectionKind.OUT_ONLY, TypeUtils.intersect(upperBounds)));
|
||||||
type = resolveType(scope, argumentElement.getTypeReference());
|
}
|
||||||
|
else {
|
||||||
|
// TODO : handle the Foo<in *> case
|
||||||
|
type = resolveType(scope, argumentElement.getTypeReference());
|
||||||
|
arguments.add(new TypeProjection(projectionKind, type));
|
||||||
}
|
}
|
||||||
TypeProjection typeProjection = new TypeProjection(projectionKind, type);
|
|
||||||
arguments.add(typeProjection);
|
|
||||||
}
|
}
|
||||||
return arguments;
|
return arguments;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package org.jetbrains.jet.lang.types;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author abreslav
|
||||||
|
*/
|
||||||
|
public class DfsNodeHandler<R> {
|
||||||
|
|
||||||
|
public void beforeChildren(Type current) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void afterChildren(Type current) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public R result() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,7 @@ public class JetStandardClasses {
|
|||||||
);
|
);
|
||||||
|
|
||||||
private static final Type ANY_TYPE = new TypeImpl(ANY.getTypeConstructor(), TypeMemberDomain.EMPTY);
|
private static final Type ANY_TYPE = new TypeImpl(ANY.getTypeConstructor(), TypeMemberDomain.EMPTY);
|
||||||
|
private static final Type NULLABLE_ANY_TYPE = TypeUtils.makeNullable(ANY_TYPE);
|
||||||
|
|
||||||
private static final ClassDescriptor BYTE = new ClassDescriptor("Byte");
|
private static final ClassDescriptor BYTE = new ClassDescriptor("Byte");
|
||||||
private static final ClassDescriptor CHAR = new ClassDescriptor("Char");
|
private static final ClassDescriptor CHAR = new ClassDescriptor("Char");
|
||||||
@@ -104,6 +105,10 @@ public class JetStandardClasses {
|
|||||||
return ANY_TYPE;
|
return ANY_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Type getNullableAnyType() {
|
||||||
|
return NULLABLE_ANY_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static ClassDescriptor getByte() {
|
public static ClassDescriptor getByte() {
|
||||||
return BYTE;
|
return BYTE;
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ public class JetTypeChecker {
|
|||||||
} else {
|
} else {
|
||||||
Type thenType = getType(scope, expression.getThen());
|
Type thenType = getType(scope, expression.getThen());
|
||||||
Type elseType = getType(scope, elseBranch);
|
Type elseType = getType(scope, elseBranch);
|
||||||
result[0] = commonSupertype(thenType, elseType);
|
result[0] = commonSupertype(Arrays.asList(thenType, elseType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,53 +150,213 @@ public class JetTypeChecker {
|
|||||||
return result[0];
|
return result[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
private Type commonSupertype(Type thenType, Type elseType) {
|
private Type commonSupertype(Collection<Type> types) {
|
||||||
if (JetStandardClasses.isNothing(thenType)) {
|
Collection<Type> typeSet = new HashSet<Type>(types);
|
||||||
return elseType;
|
assert !typeSet.isEmpty();
|
||||||
}
|
boolean nullable = false;
|
||||||
if (JetStandardClasses.isNothing(elseType)) {
|
for (Iterator<Type> iterator = typeSet.iterator(); iterator.hasNext();) {
|
||||||
return thenType;
|
Type type = iterator.next();
|
||||||
|
if (JetStandardClasses.isNothing(type)) {
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
nullable |= type.isNullable();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Type> thenOrder = new LinkedList<Type>(); // adding to the beginning
|
if (typeSet.isEmpty()) {
|
||||||
Map<TypeConstructor, Type> visited = new HashMap<TypeConstructor, Type>();
|
// TODO : attributes
|
||||||
topologicallySort(thenType, visited, thenOrder, null);
|
return nullable ? JetStandardClasses.getNullableNothingType() : JetStandardClasses.getNothingType();
|
||||||
|
|
||||||
List<Type> elseOrder = new LinkedList<Type>(); // adding to the beginning
|
|
||||||
topologicallySort(elseType, new HashMap<TypeConstructor, Type>(), elseOrder, visited);
|
|
||||||
|
|
||||||
assert !elseOrder.isEmpty() : "No common supertype";
|
|
||||||
// TODO: support multiple common supertypes
|
|
||||||
|
|
||||||
Type result = elseOrder.get(0);
|
|
||||||
|
|
||||||
if (thenType.isNullable() || elseType.isNullable()) {
|
|
||||||
return TypeUtils.makeNullable(result);
|
|
||||||
}
|
}
|
||||||
assert !result.isNullable();
|
|
||||||
|
if (typeSet.size() == 1) {
|
||||||
|
return TypeUtils.makeNullableIfNeeded(typeSet.iterator().next(), nullable);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<TypeConstructor, Set<Type>> commonSupertypes = computeCommonRawSupertypes(typeSet);
|
||||||
|
while (commonSupertypes.size() > 1) {
|
||||||
|
HashSet<Type> merge = new HashSet<Type>();
|
||||||
|
for (Set<Type> supertypes : commonSupertypes.values()) {
|
||||||
|
merge.addAll(supertypes);
|
||||||
|
}
|
||||||
|
commonSupertypes = computeCommonRawSupertypes(merge);
|
||||||
|
}
|
||||||
|
assert !commonSupertypes.isEmpty();
|
||||||
|
Map.Entry<TypeConstructor, Set<Type>> entry = commonSupertypes.entrySet().iterator().next();
|
||||||
|
Type result = computeSupertypeProjections(entry.getKey(), entry.getValue());
|
||||||
|
|
||||||
|
return TypeUtils.makeNullableIfNeeded(result, nullable);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Type computeSupertypeProjections(TypeConstructor constructor, Set<Type> types) {
|
||||||
|
// we assume that all the given types are applications of the same type constructor
|
||||||
|
|
||||||
|
assert !types.isEmpty();
|
||||||
|
|
||||||
|
if (types.size() == 1) {
|
||||||
|
return types.iterator().next();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<TypeParameterDescriptor> parameters = constructor.getParameters();
|
||||||
|
List<TypeProjection> newProjections = new ArrayList<TypeProjection>();
|
||||||
|
for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) {
|
||||||
|
TypeParameterDescriptor parameterDescriptor = parameters.get(i);
|
||||||
|
Set<TypeProjection> typeProjections = new HashSet<TypeProjection>();
|
||||||
|
for (Type type : types) {
|
||||||
|
typeProjections.add(type.getArguments().get(i));
|
||||||
|
}
|
||||||
|
newProjections.add(computeSupertypeProjection(parameterDescriptor, typeProjections));
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean nullable = false;
|
||||||
|
for (Type type : types) {
|
||||||
|
nullable |= type.isNullable();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO : attributes?
|
||||||
|
return new TypeImpl(Collections.<Attribute>emptyList(), constructor, nullable, newProjections, JetStandardClasses.STUB);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeProjection computeSupertypeProjection(TypeParameterDescriptor parameterDescriptor, Set<TypeProjection> typeProjections) {
|
||||||
|
// TODO: equals/hashCode for projections
|
||||||
|
if (typeProjections.size() == 1) {
|
||||||
|
return typeProjections.iterator().next();
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Type> ins = new HashSet<Type>();
|
||||||
|
Set<Type> outs = new HashSet<Type>();
|
||||||
|
|
||||||
|
Variance variance = parameterDescriptor.getVariance();
|
||||||
|
switch (variance) {
|
||||||
|
case INVARIANT:
|
||||||
|
// Nothing
|
||||||
|
break;
|
||||||
|
case IN_VARIANCE:
|
||||||
|
outs = null;
|
||||||
|
break;
|
||||||
|
case OUT_VARIANCE:
|
||||||
|
ins = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (TypeProjection projection : typeProjections) {
|
||||||
|
ProjectionKind projectionKind = projection.getProjectionKind();
|
||||||
|
if (projectionKind.allowsInCalls()) {
|
||||||
|
if (ins != null) {
|
||||||
|
ins.add(projection.getType());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ins = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (projectionKind.allowsOutCalls()) {
|
||||||
|
if (outs != null) {
|
||||||
|
outs.add(projection.getType());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
outs = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ins != null) {
|
||||||
|
ProjectionKind projectionKind = variance == Variance.IN_VARIANCE ? ProjectionKind.NO_PROJECTION : ProjectionKind.IN_ONLY;
|
||||||
|
return new TypeProjection(projectionKind, TypeUtils.intersect(ins));
|
||||||
|
} else if (outs != null) {
|
||||||
|
ProjectionKind projectionKind = variance == Variance.OUT_VARIANCE ? ProjectionKind.NO_PROJECTION : ProjectionKind.OUT_ONLY;
|
||||||
|
return new TypeProjection(projectionKind, commonSupertype(outs));
|
||||||
|
} else {
|
||||||
|
ProjectionKind projectionKind = variance == Variance.OUT_VARIANCE ? ProjectionKind.NO_PROJECTION : ProjectionKind.OUT_ONLY;
|
||||||
|
return new TypeProjection(projectionKind, commonSupertype(parameterDescriptor.getUpperBounds()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<TypeConstructor, Set<Type>> computeCommonRawSupertypes(Collection<Type> types) {
|
||||||
|
assert !types.isEmpty();
|
||||||
|
|
||||||
|
final Map<TypeConstructor, Set<Type>> constructorToAllInstances = new HashMap<TypeConstructor, Set<Type>>();
|
||||||
|
Set<TypeConstructor> commonSuperclasses = null;
|
||||||
|
|
||||||
|
List<TypeConstructor> order = null;
|
||||||
|
for (Iterator<Type> iterator = types.iterator(); iterator.hasNext();) {
|
||||||
|
Type type = iterator.next();
|
||||||
|
|
||||||
|
Set<TypeConstructor> visited = new HashSet<TypeConstructor>();
|
||||||
|
|
||||||
|
order = dfs(type, visited, new DfsNodeHandler<List<TypeConstructor>>() {
|
||||||
|
public LinkedList<TypeConstructor> list = new LinkedList<TypeConstructor>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeChildren(Type current) {
|
||||||
|
TypeConstructor constructor = current.getConstructor();
|
||||||
|
|
||||||
|
Set<Type> instances = constructorToAllInstances.get(constructor);
|
||||||
|
if (instances == null) {
|
||||||
|
instances = new HashSet<Type>();
|
||||||
|
constructorToAllInstances.put(constructor, instances);
|
||||||
|
}
|
||||||
|
instances.add(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterChildren(Type current) {
|
||||||
|
list.addFirst(current.getConstructor());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<TypeConstructor> result() {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (commonSuperclasses == null) {
|
||||||
|
commonSuperclasses = visited;
|
||||||
|
} else {
|
||||||
|
commonSuperclasses.retainAll(visited);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert order != null;
|
||||||
|
|
||||||
|
Set<TypeConstructor> notSource = new HashSet<TypeConstructor>();
|
||||||
|
Map<TypeConstructor, Set<Type>> result = new HashMap<TypeConstructor, Set<Type>>();
|
||||||
|
for (TypeConstructor superConstructor : order) {
|
||||||
|
if (!commonSuperclasses.contains(superConstructor)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!notSource.contains(superConstructor)) {
|
||||||
|
result.put(superConstructor, constructorToAllInstances.get(superConstructor));
|
||||||
|
markAll(superConstructor, notSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void topologicallySort(Type current, Map<TypeConstructor, Type> visited, List<Type> topologicalOrder, @Nullable Map<TypeConstructor, Type> filter) {
|
private void markAll(TypeConstructor typeConstructor, Set<TypeConstructor> markerSet) {
|
||||||
Type visitedOccurrence = visited.put(current.getConstructor(), current);
|
markerSet.add(typeConstructor);
|
||||||
if (visitedOccurrence != null) {
|
for (Type type : typeConstructor.getSupertypes()) {
|
||||||
assert equalTypes(visitedOccurrence, current);
|
markAll(type.getConstructor(), markerSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <R> R dfs(Type current, Set<TypeConstructor> visited, DfsNodeHandler<R> handler) {
|
||||||
|
doDfs(current, visited, handler);
|
||||||
|
return handler.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doDfs(Type current, Set<TypeConstructor> visited, DfsNodeHandler<?> handler) {
|
||||||
|
if (!visited.add(current.getConstructor())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
handler.beforeChildren(current);
|
||||||
Map<TypeConstructor, TypeProjection> substitutionContext = getSubstitutionContext(current);
|
Map<TypeConstructor, TypeProjection> substitutionContext = getSubstitutionContext(current);
|
||||||
for (Type supertype : current.getConstructor().getSupertypes()) {
|
for (Type supertype : current.getConstructor().getSupertypes()) {
|
||||||
TypeConstructor supertypeConstructor = supertype.getConstructor();
|
TypeConstructor supertypeConstructor = supertype.getConstructor();
|
||||||
if (visited.containsKey(supertypeConstructor)) {
|
if (visited.contains(supertypeConstructor)) {
|
||||||
assert equalTypes(visited.get(supertypeConstructor), supertype);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Type substitutedSupertype = substituteInType(substitutionContext, supertype);
|
Type substitutedSupertype = substituteInType(substitutionContext, supertype);
|
||||||
topologicallySort(substitutedSupertype, visited, topologicalOrder, filter);
|
dfs(substitutedSupertype, visited, handler);
|
||||||
}
|
|
||||||
if (filter != null && filter.containsKey(current.getConstructor())) {
|
|
||||||
assert equalTypes(filter.get(current.getConstructor()), current) : filter.get(current.getConstructor()) + " != " + current;
|
|
||||||
topologicalOrder.add(0, current);
|
|
||||||
}
|
}
|
||||||
|
handler.afterChildren(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isConvertibleTo(JetExpression expression, Type type) {
|
public boolean isConvertibleTo(JetExpression expression, Type type) {
|
||||||
@@ -358,27 +518,7 @@ public class JetTypeChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean equalTypes(@NotNull Type type1, @NotNull Type type2) {
|
public boolean equalTypes(@NotNull Type type1, @NotNull Type type2) {
|
||||||
if (!type1.getConstructor().equals(type2.getConstructor())) {
|
return TypeImpl.equalTypes(type1, type2);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
List<TypeProjection> type1Arguments = type1.getArguments();
|
|
||||||
List<TypeProjection> type2Arguments = type2.getArguments();
|
|
||||||
if (type1Arguments.size() != type2Arguments.size()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < type1Arguments.size(); i++) {
|
|
||||||
TypeProjection typeProjection1 = type1Arguments.get(i);
|
|
||||||
TypeProjection typeProjection2 = type2Arguments.get(i);
|
|
||||||
if (typeProjection1.getProjectionKind() != typeProjection2.getProjectionKind()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (typeProjection1.getProjectionKind() != ProjectionKind.NEITHER_OUT_NOR_IN) {
|
|
||||||
if (!equalTypes(typeProjection1.getType(), typeProjection2.getType())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -13,6 +13,4 @@ public interface Type extends Annotated {
|
|||||||
boolean isNullable();
|
boolean isNullable();
|
||||||
|
|
||||||
@NotNull TypeMemberDomain getMemberDomain();
|
@NotNull TypeMemberDomain getMemberDomain();
|
||||||
|
|
||||||
@NotNull Type getNullableVersion();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package org.jetbrains.jet.lang.types;
|
package org.jetbrains.jet.lang.types;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -26,15 +28,6 @@ public final class TypeImpl extends AnnotatedImpl implements Type {
|
|||||||
this(Collections.<Attribute>emptyList(), constructor, false, Collections.<TypeProjection>emptyList(), memberDomain);
|
this(Collections.<Attribute>emptyList(), constructor, false, Collections.<TypeProjection>emptyList(), memberDomain);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Type getNullableVersion() {
|
|
||||||
if (isNullable()) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
// TODO: cache these?
|
|
||||||
return new TypeImpl(getAttributes(), constructor, true, arguments, memberDomain);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TypeConstructor getConstructor() {
|
public TypeConstructor getConstructor() {
|
||||||
return constructor;
|
return constructor;
|
||||||
@@ -72,4 +65,58 @@ public final class TypeImpl extends AnnotatedImpl implements Type {
|
|||||||
return stringBuilder;
|
return stringBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
TypeImpl type = (TypeImpl) o;
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
return equalTypes(this, type);
|
||||||
|
// if (nullable != type.nullable) return false;
|
||||||
|
// if (arguments != null ? !arguments.equals(type.arguments) : type.arguments != null) return false;
|
||||||
|
// if (constructor != null ? !constructor.equals(type.constructor) : type.constructor != null) return false;
|
||||||
|
// if (memberDomain != null ? !memberDomain.equals(type.memberDomain) : type.memberDomain != null) return false;
|
||||||
|
|
||||||
|
// return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = constructor != null ? constructor.hashCode() : 0;
|
||||||
|
result = 31 * result + (arguments != null ? arguments.hashCode() : 0);
|
||||||
|
result = 31 * result + (nullable ? 1 : 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean equalTypes(@NotNull Type type1, @NotNull Type type2) {
|
||||||
|
if (type1.isNullable() != type2.isNullable()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!type1.getConstructor().equals(type2.getConstructor())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
List<TypeProjection> type1Arguments = type1.getArguments();
|
||||||
|
List<TypeProjection> type2Arguments = type2.getArguments();
|
||||||
|
if (type1Arguments.size() != type2Arguments.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < type1Arguments.size(); i++) {
|
||||||
|
TypeProjection typeProjection1 = type1Arguments.get(i);
|
||||||
|
TypeProjection typeProjection2 = type2Arguments.get(i);
|
||||||
|
if (typeProjection1.getProjectionKind() != typeProjection2.getProjectionKind()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (typeProjection1.getProjectionKind() != ProjectionKind.NEITHER_OUT_NOR_IN) {
|
||||||
|
if (!equalTypes(typeProjection1.getType(), typeProjection2.getType())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
package org.jetbrains.jet.lang.types;
|
package org.jetbrains.jet.lang.types;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author abreslav
|
* @author abreslav
|
||||||
*/
|
*/
|
||||||
public class TypeParameterDescriptor extends NamedAnnotatedImpl {
|
public class TypeParameterDescriptor extends NamedAnnotatedImpl {
|
||||||
private final Variance variance;
|
private final Variance variance;
|
||||||
private final Collection<Type> upperBounds;
|
private final Set<Type> upperBounds;
|
||||||
private final TypeConstructor typeConstructor;
|
private final TypeConstructor typeConstructor;
|
||||||
|
|
||||||
public TypeParameterDescriptor(List<Attribute> attributes, Variance variance, String name, Collection<Type> upperBounds) {
|
public TypeParameterDescriptor(List<Attribute> attributes, Variance variance, String name, Set<Type> upperBounds) {
|
||||||
super(attributes, name);
|
super(attributes, name);
|
||||||
this.variance = variance;
|
this.variance = variance;
|
||||||
this.upperBounds = upperBounds;
|
this.upperBounds = upperBounds;
|
||||||
@@ -24,11 +24,15 @@ public class TypeParameterDescriptor extends NamedAnnotatedImpl {
|
|||||||
upperBounds);
|
upperBounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TypeParameterDescriptor(List<Attribute> attributes, Variance variance, String name) {
|
||||||
|
this(attributes, variance, name, Collections.singleton(JetStandardClasses.getNullableAnyType()));
|
||||||
|
}
|
||||||
|
|
||||||
public Variance getVariance() {
|
public Variance getVariance() {
|
||||||
return variance;
|
return variance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<Type> getUpperBounds() {
|
public Set<Type> getUpperBounds() {
|
||||||
return upperBounds;
|
return upperBounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
package org.jetbrains.jet.lang.types;
|
package org.jetbrains.jet.lang.types;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author abreslav
|
* @author abreslav
|
||||||
*/
|
*/
|
||||||
@@ -10,4 +15,41 @@ 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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Type intersect(Set<Type> types) {
|
||||||
|
assert !types.isEmpty();
|
||||||
|
|
||||||
|
if (types.size() == 1) {
|
||||||
|
return types.iterator().next();
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder debugName = new StringBuilder();
|
||||||
|
boolean nullable = false;
|
||||||
|
for (Iterator<Type> iterator = types.iterator(); iterator.hasNext();) {
|
||||||
|
Type type = iterator.next();
|
||||||
|
|
||||||
|
nullable |= type.isNullable();
|
||||||
|
|
||||||
|
debugName.append(type.toString());
|
||||||
|
if (iterator.hasNext()) {
|
||||||
|
debugName.append(" & ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Attribute> noAttributes = Collections.<Attribute>emptyList();
|
||||||
|
TypeConstructor constructor = new TypeConstructor(noAttributes, debugName.toString(), Collections.<TypeParameterDescriptor>emptyList(), types);
|
||||||
|
return new TypeImpl(
|
||||||
|
noAttributes,
|
||||||
|
constructor,
|
||||||
|
nullable,
|
||||||
|
Collections.<TypeProjection>emptyList(),
|
||||||
|
JetStandardClasses.STUB);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Type makeNullableIfNeeded(Type type, boolean nullable) {
|
||||||
|
if (nullable) {
|
||||||
|
return makeNullable(type);
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user