Type checking procedure simplified (dramatically!)
This commit is contained in:
@@ -7,6 +7,9 @@ import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.jetbrains.jet.lang.types.Variance.IN_VARIANCE;
|
||||||
|
import static org.jetbrains.jet.lang.types.Variance.OUT_VARIANCE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author abreslav
|
* @author abreslav
|
||||||
*/
|
*/
|
||||||
@@ -76,17 +79,20 @@ public class JetTypeChecker {
|
|||||||
public JetType commonSupertype(@NotNull Collection<JetType> types) {
|
public JetType commonSupertype(@NotNull Collection<JetType> types) {
|
||||||
Collection<JetType> typeSet = new HashSet<JetType>(types);
|
Collection<JetType> typeSet = new HashSet<JetType>(types);
|
||||||
assert !typeSet.isEmpty();
|
assert !typeSet.isEmpty();
|
||||||
|
|
||||||
|
// If any of the types is nullable, the result must be nullable
|
||||||
|
// This also removed Nothing and Nothing? because they are subtypes of everything else
|
||||||
boolean nullable = false;
|
boolean nullable = false;
|
||||||
for (Iterator<JetType> iterator = typeSet.iterator(); iterator.hasNext();) {
|
for (Iterator<JetType> iterator = typeSet.iterator(); iterator.hasNext();) {
|
||||||
JetType type = iterator.next();
|
JetType type = iterator.next();
|
||||||
assert type != null;
|
assert type != null;
|
||||||
// TODO : This admits 'Nothing?'. Review
|
|
||||||
if (JetStandardClasses.isNothingOrNullableNothing(type)) {
|
if (JetStandardClasses.isNothingOrNullableNothing(type)) {
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
}
|
}
|
||||||
nullable |= type.isNullable();
|
nullable |= type.isNullable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Everything deleted => it's Nothing or Nothing?
|
||||||
if (typeSet.isEmpty()) {
|
if (typeSet.isEmpty()) {
|
||||||
// TODO : attributes
|
// TODO : attributes
|
||||||
return nullable ? JetStandardClasses.getNullableNothingType() : JetStandardClasses.getNothingType();
|
return nullable ? JetStandardClasses.getNullableNothingType() : JetStandardClasses.getNothingType();
|
||||||
@@ -96,6 +102,7 @@ public class JetTypeChecker {
|
|||||||
return TypeUtils.makeNullableIfNeeded(typeSet.iterator().next(), nullable);
|
return TypeUtils.makeNullableIfNeeded(typeSet.iterator().next(), nullable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// constructor of the supertype -> all of its instantiations occurring as supertypes
|
||||||
Map<TypeConstructor, Set<JetType>> commonSupertypes = computeCommonRawSupertypes(typeSet);
|
Map<TypeConstructor, Set<JetType>> commonSupertypes = computeCommonRawSupertypes(typeSet);
|
||||||
while (commonSupertypes.size() > 1) {
|
while (commonSupertypes.size() > 1) {
|
||||||
Set<JetType> merge = new HashSet<JetType>();
|
Set<JetType> merge = new HashSet<JetType>();
|
||||||
@@ -105,12 +112,81 @@ public class JetTypeChecker {
|
|||||||
commonSupertypes = computeCommonRawSupertypes(merge);
|
commonSupertypes = computeCommonRawSupertypes(merge);
|
||||||
}
|
}
|
||||||
assert !commonSupertypes.isEmpty() : commonSupertypes + " <- " + types;
|
assert !commonSupertypes.isEmpty() : commonSupertypes + " <- " + types;
|
||||||
Map.Entry<TypeConstructor, Set<JetType>> entry = commonSupertypes.entrySet().iterator().next();
|
|
||||||
JetType result = computeSupertypeProjections(entry.getKey(), entry.getValue());
|
|
||||||
|
|
||||||
|
// constructor of the supertype -> all of its instantiations occurring as supertypes
|
||||||
|
Map.Entry<TypeConstructor, Set<JetType>> entry = commonSupertypes.entrySet().iterator().next();
|
||||||
|
|
||||||
|
// Reconstructing type arguments if possible
|
||||||
|
JetType result = computeSupertypeProjections(entry.getKey(), entry.getValue());
|
||||||
return TypeUtils.makeNullableIfNeeded(result, nullable);
|
return TypeUtils.makeNullableIfNeeded(result, nullable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Raw supertypes are superclasses w/o type arguments
|
||||||
|
// @return TypeConstructor -> all instantiations of this constructor occurring as supertypes
|
||||||
|
@NotNull
|
||||||
|
private Map<TypeConstructor, Set<JetType>> computeCommonRawSupertypes(@NotNull Collection<JetType> types) {
|
||||||
|
assert !types.isEmpty();
|
||||||
|
|
||||||
|
final Map<TypeConstructor, Set<JetType>> constructorToAllInstances = new HashMap<TypeConstructor, Set<JetType>>();
|
||||||
|
Set<TypeConstructor> commonSuperclasses = null;
|
||||||
|
|
||||||
|
List<TypeConstructor> order = null;
|
||||||
|
for (JetType type : types) {
|
||||||
|
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(JetType current) {
|
||||||
|
TypeConstructor constructor = current.getConstructor();
|
||||||
|
|
||||||
|
Set<JetType> instances = constructorToAllInstances.get(constructor);
|
||||||
|
if (instances == null) {
|
||||||
|
instances = new HashSet<JetType>();
|
||||||
|
constructorToAllInstances.put(constructor, instances);
|
||||||
|
}
|
||||||
|
instances.add(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterChildren(JetType 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<JetType>> result = new HashMap<TypeConstructor, Set<JetType>>();
|
||||||
|
for (TypeConstructor superConstructor : order) {
|
||||||
|
if (!commonSuperclasses.contains(superConstructor)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!notSource.contains(superConstructor)) {
|
||||||
|
result.put(superConstructor, constructorToAllInstances.get(superConstructor));
|
||||||
|
markAll(superConstructor, notSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// constructor - type constructor of a supertype to be instantiated
|
||||||
|
// types - instantiations of constructor occurring as supertypes of classes we are trying to intersect
|
||||||
@NotNull
|
@NotNull
|
||||||
private JetType computeSupertypeProjections(@NotNull TypeConstructor constructor, @NotNull Set<JetType> types) {
|
private JetType computeSupertypeProjections(@NotNull TypeConstructor constructor, @NotNull Set<JetType> types) {
|
||||||
// we assume that all the given types are applications of the same type constructor
|
// we assume that all the given types are applications of the same type constructor
|
||||||
@@ -186,83 +262,21 @@ public class JetTypeChecker {
|
|||||||
JetType intersection = TypeUtils.intersect(this, ins);
|
JetType intersection = TypeUtils.intersect(this, ins);
|
||||||
if (intersection == null) {
|
if (intersection == null) {
|
||||||
if (outs != null) {
|
if (outs != null) {
|
||||||
return new TypeProjection(Variance.OUT_VARIANCE, commonSupertype(outs));
|
return new TypeProjection(OUT_VARIANCE, commonSupertype(outs));
|
||||||
}
|
}
|
||||||
return new TypeProjection(Variance.OUT_VARIANCE, commonSupertype(parameterDescriptor.getUpperBounds()));
|
return new TypeProjection(OUT_VARIANCE, commonSupertype(parameterDescriptor.getUpperBounds()));
|
||||||
}
|
}
|
||||||
Variance projectionKind = variance == Variance.IN_VARIANCE ? Variance.INVARIANT : Variance.IN_VARIANCE;
|
Variance projectionKind = variance == IN_VARIANCE ? Variance.INVARIANT : IN_VARIANCE;
|
||||||
return new TypeProjection(projectionKind, intersection);
|
return new TypeProjection(projectionKind, intersection);
|
||||||
} else if (outs != null) {
|
} else if (outs != null) {
|
||||||
Variance projectionKind = variance == Variance.OUT_VARIANCE ? Variance.INVARIANT : Variance.OUT_VARIANCE;
|
Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE;
|
||||||
return new TypeProjection(projectionKind, commonSupertype(outs));
|
return new TypeProjection(projectionKind, commonSupertype(outs));
|
||||||
} else {
|
} else {
|
||||||
Variance projectionKind = variance == Variance.OUT_VARIANCE ? Variance.INVARIANT : Variance.OUT_VARIANCE;
|
Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE;
|
||||||
return new TypeProjection(projectionKind, commonSupertype(parameterDescriptor.getUpperBounds()));
|
return new TypeProjection(projectionKind, commonSupertype(parameterDescriptor.getUpperBounds()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private Map<TypeConstructor, Set<JetType>> computeCommonRawSupertypes(@NotNull Collection<JetType> types) {
|
|
||||||
assert !types.isEmpty();
|
|
||||||
|
|
||||||
final Map<TypeConstructor, Set<JetType>> constructorToAllInstances = new HashMap<TypeConstructor, Set<JetType>>();
|
|
||||||
Set<TypeConstructor> commonSuperclasses = null;
|
|
||||||
|
|
||||||
List<TypeConstructor> order = null;
|
|
||||||
for (JetType type : types) {
|
|
||||||
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(JetType current) {
|
|
||||||
TypeConstructor constructor = current.getConstructor();
|
|
||||||
|
|
||||||
Set<JetType> instances = constructorToAllInstances.get(constructor);
|
|
||||||
if (instances == null) {
|
|
||||||
instances = new HashSet<JetType>();
|
|
||||||
constructorToAllInstances.put(constructor, instances);
|
|
||||||
}
|
|
||||||
instances.add(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterChildren(JetType 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<JetType>> result = new HashMap<TypeConstructor, Set<JetType>>();
|
|
||||||
for (TypeConstructor superConstructor : order) {
|
|
||||||
if (!commonSuperclasses.contains(superConstructor)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!notSource.contains(superConstructor)) {
|
|
||||||
result.put(superConstructor, constructorToAllInstances.get(superConstructor));
|
|
||||||
markAll(superConstructor, notSource);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void markAll(@NotNull TypeConstructor typeConstructor, @NotNull Set<TypeConstructor> markerSet) {
|
private void markAll(@NotNull TypeConstructor typeConstructor, @NotNull Set<TypeConstructor> markerSet) {
|
||||||
markerSet.add(typeConstructor);
|
markerSet.add(typeConstructor);
|
||||||
for (JetType type : typeConstructor.getSupertypes()) {
|
for (JetType type : typeConstructor.getSupertypes()) {
|
||||||
@@ -331,22 +345,28 @@ public class JetTypeChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSubtypeOf(@NotNull JetType subtype, @NotNull JetType supertype) {
|
public boolean isSubtypeOf(@NotNull JetType subtype, @NotNull JetType supertype) {
|
||||||
return new TypeCheckingProcedure().run(subtype, supertype);
|
// return new TypeCheckingProcedure().run(subtype, supertype);
|
||||||
|
return new ExplicitInOutTypeCheckingProcedure().run(subtype, supertype);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean equalTypes(@NotNull JetType a, @NotNull JetType b) {
|
public boolean equalTypes(@NotNull JetType a, @NotNull JetType b) {
|
||||||
return isSubtypeOf(a, b) && isSubtypeOf(b, a);
|
return isSubtypeOf(a, b) && isSubtypeOf(b, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class OldProcedure {
|
private static class ExplicitInOutTypeCheckingProcedure {
|
||||||
public static boolean isSubtypeOf(@NotNull JetType subtype, @NotNull JetType supertype) {
|
|
||||||
|
public boolean run(@NotNull JetType subtype, @NotNull JetType supertype) {
|
||||||
|
return isSubtypeOf(subtype, supertype);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSubtypeOf(@NotNull JetType subtype, @NotNull JetType supertype) {
|
||||||
if (ErrorUtils.isErrorType(subtype) || ErrorUtils.isErrorType(supertype)) {
|
if (ErrorUtils.isErrorType(subtype) || ErrorUtils.isErrorType(supertype)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!supertype.isNullable() && subtype.isNullable()) {
|
if (!supertype.isNullable() && subtype.isNullable()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (JetStandardClasses.isNothing(subtype)) {
|
if (JetStandardClasses.isNothingOrNullableNothing(subtype)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@Nullable JetType closestSupertype = findCorrespondingSupertype(subtype, supertype);
|
@Nullable JetType closestSupertype = findCorrespondingSupertype(subtype, supertype);
|
||||||
@@ -357,70 +377,41 @@ public class JetTypeChecker {
|
|||||||
return checkSubtypeForTheSameConstructor(closestSupertype, supertype);
|
return checkSubtypeForTheSameConstructor(closestSupertype, supertype);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean checkSubtypeForTheSameConstructor(@NotNull JetType subtype, @NotNull JetType supertype) {
|
private boolean checkSubtypeForTheSameConstructor(@NotNull JetType subtype, @NotNull JetType supertype) {
|
||||||
TypeConstructor constructor = subtype.getConstructor();
|
TypeConstructor constructor = subtype.getConstructor();
|
||||||
assert constructor.equals(supertype.getConstructor()) : constructor + " is not " + supertype.getConstructor();
|
assert constructor.equals(supertype.getConstructor()) : constructor + " is not " + supertype.getConstructor();
|
||||||
|
|
||||||
List<TypeProjection> subArguments = subtype.getArguments();
|
List<TypeProjection> subArguments = subtype.getArguments();
|
||||||
List<TypeProjection> superArguments = supertype.getArguments();
|
List<TypeProjection> superArguments = supertype.getArguments();
|
||||||
List<TypeParameterDescriptor> parameters = constructor.getParameters();
|
List<TypeParameterDescriptor> parameters = constructor.getParameters();
|
||||||
boolean status = true;
|
|
||||||
for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) {
|
for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) {
|
||||||
TypeParameterDescriptor parameter = parameters.get(i);
|
TypeParameterDescriptor parameter = parameters.get(i);
|
||||||
TypeProjection subArgument = subArguments.get(i);
|
TypeProjection subArgument = subArguments.get(i);
|
||||||
|
|
||||||
|
JetType subIn = getInType(parameter, subArgument);
|
||||||
|
JetType subOut = getOutType(parameter, subArgument);
|
||||||
|
|
||||||
TypeProjection superArgument = superArguments.get(i);
|
TypeProjection superArgument = superArguments.get(i);
|
||||||
|
|
||||||
JetType subArgumentType = subArgument.getType();
|
JetType superIn = getInType(parameter, superArgument);
|
||||||
JetType superArgumentType = superArgument.getType();
|
JetType superOut = getOutType(parameter, superArgument);
|
||||||
switch (parameter.getVariance()) {
|
|
||||||
case INVARIANT:
|
if (!isSubtypeOf(subOut, superOut) || !isSubtypeOf(superIn, subIn)) {
|
||||||
switch (superArgument.getProjectionKind()) {
|
|
||||||
case INVARIANT:
|
|
||||||
status = subArgumentType.equals(superArgumentType);
|
|
||||||
break;
|
|
||||||
case OUT_VARIANCE:
|
|
||||||
if (!subArgument.getProjectionKind().allowsOutPosition()) {
|
|
||||||
status = false;
|
|
||||||
} else {
|
|
||||||
status = !isSubtypeOf(subArgumentType, superArgumentType);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IN_VARIANCE:
|
|
||||||
if (!subArgument.getProjectionKind().allowsInPosition()) {
|
|
||||||
status = false;
|
|
||||||
} else {
|
|
||||||
status = isSubtypeOf(superArgumentType, subArgumentType);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IN_VARIANCE:
|
|
||||||
switch (superArgument.getProjectionKind()) {
|
|
||||||
case INVARIANT:
|
|
||||||
case IN_VARIANCE:
|
|
||||||
status = isSubtypeOf(superArgumentType, subArgumentType);
|
|
||||||
break;
|
|
||||||
case OUT_VARIANCE:
|
|
||||||
status = isSubtypeOf(subArgumentType, superArgumentType);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case OUT_VARIANCE:
|
|
||||||
switch (superArgument.getProjectionKind()) {
|
|
||||||
case INVARIANT:
|
|
||||||
case OUT_VARIANCE:
|
|
||||||
case IN_VARIANCE:
|
|
||||||
status = isSubtypeOf(subArgumentType, superArgumentType);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!status) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private JetType getOutType(TypeParameterDescriptor parameter, TypeProjection subArgument) {
|
||||||
|
boolean isOutProjected = subArgument.getProjectionKind() == IN_VARIANCE || parameter.getVariance() == IN_VARIANCE;
|
||||||
|
return isOutProjected ? parameter.getBoundsAsType() : subArgument.getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
private JetType getInType(TypeParameterDescriptor parameter, TypeProjection subArgument) {
|
||||||
|
boolean isOutProjected = subArgument.getProjectionKind() == OUT_VARIANCE || parameter.getVariance() == OUT_VARIANCE;
|
||||||
|
return isOutProjected ? JetStandardClasses.getNothingType() : subArgument.getType();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static abstract class AbstractTypeCheckingProcedure<T> {
|
public static abstract class AbstractTypeCheckingProcedure<T> {
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
class Point() {
|
||||||
|
}
|
||||||
|
|
||||||
|
class G<T>() {}
|
||||||
|
|
||||||
|
fun f<T>(expression : T) : G<out T> = G<T>
|
||||||
|
|
||||||
|
|
||||||
|
fun foo() : G<Point> {
|
||||||
|
val p = Point()
|
||||||
|
return <!TYPE_MISMATCH!>f<Point>(p)<!>
|
||||||
|
}
|
||||||
|
|
||||||
|
class Out<out T>() {}
|
||||||
|
|
||||||
|
fun fout<T>(expression : T) : Out<out T> = Out<T>
|
||||||
|
|
||||||
|
fun fooout() : Out<Point> {
|
||||||
|
val p = Point();
|
||||||
|
return fout<Point>(p);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user