New type checking algorithm is used for subtyping constraint generation

This commit is contained in:
Andrey Breslav
2011-11-09 18:54:23 +03:00
parent d4ea1d42c8
commit 787d3aa450
4 changed files with 254 additions and 303 deletions
@@ -21,7 +21,7 @@ import org.jetbrains.jet.lang.types.expressions.ExpressionTypingServices;
import org.jetbrains.jet.lang.types.expressions.OperatorConventions;
import org.jetbrains.jet.lang.types.inference.ConstraintSystem;
import org.jetbrains.jet.lang.types.inference.ConstraintSystemSolution;
import org.jetbrains.jet.lang.types.inference.SubtypingOnlyConstraintSystem;
import org.jetbrains.jet.lang.types.inference.ConstraintSystemImpl;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.*;
@@ -414,7 +414,7 @@ public class CallResolver {
if (!candidate.getTypeParameters().isEmpty()) {
// Type argument inference
ConstraintSystem constraintSystem = new SubtypingOnlyConstraintSystem();
ConstraintSystem constraintSystem = new ConstraintSystemImpl();
for (TypeParameterDescriptor typeParameterDescriptor : candidate.getTypeParameters()) {
constraintSystem.registerTypeVariable(typeParameterDescriptor, Variance.INVARIANT); // TODO
}
@@ -4,8 +4,9 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
import java.util.*;
import java.util.List;
import static org.jetbrains.jet.lang.types.Variance.INVARIANT;
import static org.jetbrains.jet.lang.types.Variance.IN_VARIANCE;
import static org.jetbrains.jet.lang.types.Variance.OUT_VARIANCE;
@@ -21,7 +22,7 @@ public class JetTypeChecker {
public boolean isSubtypeOf(@NotNull JetType subtype, @NotNull JetType supertype) {
// return new TypeCheckingProcedure().run(subtype, supertype);
return new ExplicitInOutTypeCheckingProcedure().run(subtype, supertype);
return TYPE_CHECKER.run(subtype, supertype);
}
// This method returns the supertype of the first parameter that has the same constructor
@@ -51,7 +52,39 @@ public class JetTypeChecker {
return isOutProjected ? JetStandardClasses.getNothingType() : argument.getType();
}
private static class ExplicitInOutTypeCheckingProcedure {
/**
* Methods of this class return true to continue type checking and false to fail
*/
public interface TypingConstraintBuilder {
boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b);
boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype);
boolean noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype);
}
private static final TypeCheckingProcedure TYPE_CHECKER = new TypeCheckingProcedure(new TypingConstraintBuilder() {
@Override
public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b) {
return TypeUtils.equalTypes(a, b);
}
@Override
public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype) {
return INSTANCE.isSubtypeOf(subtype, supertype);
}
@Override
public boolean noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) {
return false; // type checking fails
}
});
public static class TypeCheckingProcedure {
private final TypingConstraintBuilder constraintBuilder;
public TypeCheckingProcedure(TypingConstraintBuilder constraintBuilder) {
this.constraintBuilder = constraintBuilder;
}
public boolean run(@NotNull JetType subtype, @NotNull JetType supertype) {
return isSubtypeOf(subtype, supertype);
@@ -69,7 +102,7 @@ public class JetTypeChecker {
}
@Nullable JetType closestSupertype = findCorrespondingSupertype(subtype, supertype);
if (closestSupertype == null) {
return false;
if (!constraintBuilder.noCorrespondingSupertype(subtype, supertype)) return false;
}
return checkSubtypeForTheSameConstructor(closestSupertype, supertype);
@@ -84,6 +117,7 @@ public class JetTypeChecker {
List<TypeParameterDescriptor> parameters = constructor.getParameters();
for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) {
TypeParameterDescriptor parameter = parameters.get(i);
TypeProjection subArgument = subArguments.get(i);
JetType subIn = getInType(parameter, subArgument);
@@ -93,189 +127,193 @@ public class JetTypeChecker {
JetType superIn = getInType(parameter, superArgument);
JetType superOut = getOutType(parameter, superArgument);
if (!isSubtypeOf(subOut, superOut) || !isSubtypeOf(superIn, subIn)) {
return false;
if (parameter.getVariance() == INVARIANT && subArgument.getProjectionKind() == INVARIANT && superArgument.getProjectionKind() == INVARIANT) {
if (!constraintBuilder.assertEqualTypes(subArgument.getType(), superArgument.getType())) return false;
}
else {
if (!constraintBuilder.assertSubtype(subOut, superOut)) return false;
if (!constraintBuilder.assertSubtype(superIn, subIn)) return false;
}
}
return true;
}
}
public static abstract class AbstractTypeCheckingProcedure<T> {
protected enum StatusAction {
PROCEED(false),
DONE_WITH_CURRENT_TYPE(true),
ABORT_ALL(true);
private final boolean abort;
private StatusAction(boolean abort) {
this.abort = abort;
}
public boolean isAbort() {
return abort;
}
}
public final T run(@NotNull JetType subtype, @NotNull JetType supertype) {
proceedOrStop(subtype, supertype);
return result();
}
protected abstract StatusAction startForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype);
protected abstract StatusAction noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype);
protected abstract StatusAction equalTypesRequired(@NotNull JetType subArgumentType, @NotNull JetType superArgumentType);
protected abstract StatusAction varianceConflictFound(@NotNull TypeProjection subArgument, @NotNull TypeProjection superArgument);
protected abstract StatusAction doneForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype);
protected abstract T result();
private StatusAction proceedOrStop(@NotNull JetType subtype, @NotNull JetType supertype) {
StatusAction statusAction = startForPairOfTypes(subtype, supertype);
if (statusAction.isAbort()) {
return statusAction;
}
JetType closestSupertype = findCorrespondingSupertype(subtype, supertype);
if (closestSupertype == null) {
return noCorrespondingSupertype(subtype, supertype);
}
proceed(closestSupertype, supertype);
return doneForPairOfTypes(subtype, supertype);
}
private void proceed(@NotNull JetType subtype, @NotNull JetType supertype) {
TypeConstructor constructor = subtype.getConstructor();
assert constructor.equals(supertype.getConstructor()) : constructor + " is not " + supertype.getConstructor();
List<TypeProjection> subArguments = subtype.getArguments();
List<TypeProjection> superArguments = supertype.getArguments();
List<TypeParameterDescriptor> parameters = constructor.getParameters();
loop:
for (int i = 0; i < parameters.size(); i++) {
TypeParameterDescriptor parameter = parameters.get(i);
TypeProjection subArgument = subArguments.get(i);
TypeProjection superArgument = superArguments.get(i);
JetType subArgumentType = subArgument.getType();
JetType superArgumentType = superArgument.getType();
StatusAction action = null;
switch (parameter.getVariance()) {
case INVARIANT:
switch (superArgument.getProjectionKind()) {
case INVARIANT:
action = equalTypesRequired(subArgumentType, superArgumentType);
break;
case OUT_VARIANCE:
if (!subArgument.getProjectionKind().allowsOutPosition()) {
action = varianceConflictFound(subArgument, superArgument);
}
else {
action = proceedOrStop(subArgumentType, superArgumentType);
}
break;
case IN_VARIANCE:
if (!subArgument.getProjectionKind().allowsInPosition()) {
action = varianceConflictFound(subArgument, superArgument);
}
else {
action = proceedOrStop(superArgumentType, subArgumentType);
}
break;
}
break;
case IN_VARIANCE:
switch (superArgument.getProjectionKind()) {
case INVARIANT:
case IN_VARIANCE:
action = proceedOrStop(superArgumentType, subArgumentType);
break;
case OUT_VARIANCE:
action = proceedOrStop(subArgumentType, superArgumentType);
break;
}
break;
case OUT_VARIANCE:
switch (superArgument.getProjectionKind()) {
case INVARIANT:
case OUT_VARIANCE:
case IN_VARIANCE:
action = proceedOrStop(subArgumentType, superArgumentType);
break;
}
break;
}
switch (action) {
case ABORT_ALL: break loop;
case DONE_WITH_CURRENT_TYPE:
default:
}
}
}
}
// public static abstract class AbstractTypeCheckingProcedure<T> {
//
// protected enum StatusAction {
// PROCEED(false),
// DONE_WITH_CURRENT_TYPE(true),
// ABORT_ALL(true);
//
// private final boolean abort;
//
// private StatusAction(boolean abort) {
// this.abort = abort;
// }
//
// public boolean isAbort() {
// return abort;
// }
// }
//
// public final T run(@NotNull JetType subtype, @NotNull JetType supertype) {
// proceedOrStop(subtype, supertype);
// return result();
// }
//
// protected abstract StatusAction startForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype);
//
// protected abstract StatusAction noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype);
//
// protected abstract StatusAction equalTypesRequired(@NotNull JetType subArgumentType, @NotNull JetType superArgumentType);
//
// protected abstract StatusAction varianceConflictFound(@NotNull TypeProjection subArgument, @NotNull TypeProjection superArgument);
//
// protected abstract StatusAction doneForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype);
//
// protected abstract T result();
//
// private StatusAction proceedOrStop(@NotNull JetType subtype, @NotNull JetType supertype) {
// StatusAction statusAction = startForPairOfTypes(subtype, supertype);
// if (statusAction.isAbort()) {
// return statusAction;
// }
//
// JetType closestSupertype = findCorrespondingSupertype(subtype, supertype);
// if (closestSupertype == null) {
// return noCorrespondingSupertype(subtype, supertype);
// }
//
// proceed(closestSupertype, supertype);
// return doneForPairOfTypes(subtype, supertype);
// }
//
// private void proceed(@NotNull JetType subtype, @NotNull JetType supertype) {
// TypeConstructor constructor = subtype.getConstructor();
// assert constructor.equals(supertype.getConstructor()) : constructor + " is not " + supertype.getConstructor();
//
// List<TypeProjection> subArguments = subtype.getArguments();
// List<TypeProjection> superArguments = supertype.getArguments();
// List<TypeParameterDescriptor> parameters = constructor.getParameters();
//
// loop:
// for (int i = 0; i < parameters.size(); i++) {
// TypeParameterDescriptor parameter = parameters.get(i);
// TypeProjection subArgument = subArguments.get(i);
// TypeProjection superArgument = superArguments.get(i);
//
// JetType subArgumentType = subArgument.getType();
// JetType superArgumentType = superArgument.getType();
//
// StatusAction action = null;
// switch (parameter.getVariance()) {
// case INVARIANT:
// switch (superArgument.getProjectionKind()) {
// case INVARIANT:
// action = equalTypesRequired(subArgumentType, superArgumentType);
// break;
// case OUT_VARIANCE:
// if (!subArgument.getProjectionKind().allowsOutPosition()) {
// action = varianceConflictFound(subArgument, superArgument);
// }
// else {
// action = proceedOrStop(subArgumentType, superArgumentType);
// }
// break;
// case IN_VARIANCE:
// if (!subArgument.getProjectionKind().allowsInPosition()) {
// action = varianceConflictFound(subArgument, superArgument);
// }
// else {
// action = proceedOrStop(superArgumentType, subArgumentType);
// }
// break;
// }
// break;
// case IN_VARIANCE:
// switch (superArgument.getProjectionKind()) {
// case INVARIANT:
// case IN_VARIANCE:
// action = proceedOrStop(superArgumentType, subArgumentType);
// break;
// case OUT_VARIANCE:
// action = proceedOrStop(subArgumentType, superArgumentType);
// break;
// }
// break;
// case OUT_VARIANCE:
// switch (superArgument.getProjectionKind()) {
// case INVARIANT:
// case OUT_VARIANCE:
// case IN_VARIANCE:
// action = proceedOrStop(subArgumentType, superArgumentType);
// break;
// }
// break;
// }
// switch (action) {
// case ABORT_ALL: break loop;
// case DONE_WITH_CURRENT_TYPE:
// default:
// }
// }
// }
//
// }
private static class TypeCheckingProcedure extends AbstractTypeCheckingProcedure<Boolean> {
private boolean result = true;
private StatusAction fail() {
result = false;
return StatusAction.ABORT_ALL;
}
@Override
public StatusAction startForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) {
if (ErrorUtils.isErrorType(subtype) || ErrorUtils.isErrorType(supertype)) {
return StatusAction.DONE_WITH_CURRENT_TYPE;
}
if (!supertype.isNullable() && subtype.isNullable()) {
return fail();
}
if (JetStandardClasses.isNothingOrNullableNothing(subtype)) {
return StatusAction.DONE_WITH_CURRENT_TYPE;
}
return StatusAction.PROCEED;
}
@Override
protected StatusAction noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) {
return fail();
}
@Override
protected StatusAction equalTypesRequired(@NotNull JetType subArgumentType, @NotNull JetType superArgumentType) {
if (!subArgumentType.equals(superArgumentType)) {
return fail();
}
return StatusAction.PROCEED;
}
@Override
protected StatusAction varianceConflictFound(@NotNull TypeProjection subArgument, @NotNull TypeProjection superArgument) {
return fail();
}
@Override
protected StatusAction doneForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) {
return StatusAction.PROCEED;
}
@Override
protected Boolean result() {
return result;
}
}
// private static class TypeCheckingProcedure extends AbstractTypeCheckingProcedure<Boolean> {
//
// private boolean result = true;
//
// private StatusAction fail() {
// result = false;
// return StatusAction.ABORT_ALL;
// }
//
// @Override
// public StatusAction startForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) {
// if (ErrorUtils.isErrorType(subtype) || ErrorUtils.isErrorType(supertype)) {
// return StatusAction.DONE_WITH_CURRENT_TYPE;
// }
// if (!supertype.isNullable() && subtype.isNullable()) {
// return fail();
// }
// if (JetStandardClasses.isNothingOrNullableNothing(subtype)) {
// return StatusAction.DONE_WITH_CURRENT_TYPE;
// }
// return StatusAction.PROCEED;
// }
//
// @Override
// protected StatusAction noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) {
// return fail();
// }
//
// @Override
// protected StatusAction equalTypesRequired(@NotNull JetType subArgumentType, @NotNull JetType superArgumentType) {
// if (!subArgumentType.equals(superArgumentType)) {
// return fail();
// }
// return StatusAction.PROCEED;
// }
//
// @Override
// protected StatusAction varianceConflictFound(@NotNull TypeProjection subArgument, @NotNull TypeProjection superArgument) {
// return fail();
// }
//
// @Override
// protected StatusAction doneForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) {
// return StatusAction.PROCEED;
// }
//
// @Override
// protected Boolean result() {
// return result;
// }
// }
}
@@ -14,7 +14,7 @@ import java.util.Set;
/**
* @author abreslav
*/
public class SubtypingOnlyConstraintSystem implements ConstraintSystem {
public class ConstraintSystemImpl implements ConstraintSystem {
private static class LoopInTypeVariableConstraintsException extends RuntimeException {
private LoopInTypeVariableConstraintsException() {
@@ -158,6 +158,39 @@ public class SubtypingOnlyConstraintSystem implements ConstraintSystem {
private final Map<TypeParameterDescriptor, UnknownType> unknownTypes = Maps.newHashMap();
private final JetTypeChecker typeChecker = JetTypeChecker.INSTANCE;
private final JetTypeChecker.TypeCheckingProcedure constraintExpander = new JetTypeChecker.TypeCheckingProcedure(new JetTypeChecker.TypingConstraintBuilder() {
@Override
public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b) {
return TypeUtils.equalTypes(a, b);
}
@Override
public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype) {
TypeValue subtypeValue = getTypeValueFor(subtype);
TypeValue supertypeValue = getTypeValueFor(supertype);
if (someUnknown(subtypeValue, supertypeValue)) {
addSubtypingConstraintOnTypeValues(subtypeValue, supertypeValue);
}
return true;
}
@Override
public boolean noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) {
// If some of the types is an unknown, the constraint is already generated, and we should carry on
// otherwise there can be no solution, and we should fail
return someUnknown(getTypeValueFor(subtype), getTypeValueFor(supertype));
}
private boolean someUnknown(TypeValue subtypeValue, TypeValue supertypeValue) {
return subtypeValue instanceof UnknownType || supertypeValue instanceof UnknownType;
}
});
public ConstraintSystemImpl() {
}
@NotNull
private TypeValue getTypeValueFor(@NotNull JetType type) {
DeclarationDescriptor declarationDescriptor = type.getConstructor().getDeclarationDescriptor();
@@ -217,7 +250,7 @@ public class SubtypingOnlyConstraintSystem implements ConstraintSystem {
for (TypeValue upperBound : typeValue.getUpperBounds()) {
if (upperBound instanceof KnownType) {
KnownType knownBoundType = (KnownType) upperBound;
boolean ok = new SubtypingConstraintExpander().run(jetType, knownBoundType.getType());
boolean ok = constraintExpander.run(jetType, knownBoundType.getType());
if (!ok) {
return new Solution(true);
}
@@ -344,72 +377,7 @@ public class SubtypingOnlyConstraintSystem implements ConstraintSystem {
}
// private class EqualityConstraintExpander extends JetTypeChecker.AbstractTypeCheckingProcedure<Boolean> {
//
// }
private class SubtypingConstraintExpander extends JetTypeChecker.AbstractTypeCheckingProcedure<Boolean> {
private boolean error = false;
private StatusAction fail() {
error = true;
return StatusAction.ABORT_ALL;
}
@Override
protected StatusAction startForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) {
return tryToAddConstraint(subtype, supertype);
}
private StatusAction tryToAddConstraint(@NotNull JetType subtype, @NotNull JetType supertype) {
TypeValue subtypeValue = getTypeValueFor(subtype);
TypeValue supertypeValue = getTypeValueFor(supertype);
if (someUnknown(subtypeValue, supertypeValue)) {
addSubtypingConstraintOnTypeValues(subtypeValue, supertypeValue);
}
return StatusAction.PROCEED;
}
private boolean someUnknown(TypeValue subtypeValue, TypeValue supertypeValue) {
return subtypeValue instanceof UnknownType || supertypeValue instanceof UnknownType;
}
@Override
protected StatusAction noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) {
if (someUnknown(getTypeValueFor(subtype), getTypeValueFor(supertype))) {
return StatusAction.PROCEED;
}
return fail();
}
@Override
protected StatusAction equalTypesRequired(@NotNull JetType subArgumentType, @NotNull JetType superArgumentType) {
if (!subArgumentType.equals(superArgumentType)) {
return fail();
}
return StatusAction.PROCEED;
}
@Override
protected StatusAction varianceConflictFound(@NotNull TypeProjection subArgument, @NotNull TypeProjection superArgument) {
return fail();
}
@Override
protected StatusAction doneForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) {
return StatusAction.PROCEED;
}
@Override
protected Boolean result() {
return !error;
}
}
private static void println(String message) {
// System.out.println(message);
}
}
@@ -1,55 +0,0 @@
package org.jetbrains.jet.lang.types.inference;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.Variance;
import java.util.List;
import java.util.Map;
/**
* @author abreslav
*/
public class EqualityBasedConstraintSystem implements ConstraintSystem {
private abstract class Constraint {
private final TypeParameterDescriptor typeParameterDescriptor;
protected Constraint(@NotNull TypeParameterDescriptor typeParameterDescriptor) {
this.typeParameterDescriptor = typeParameterDescriptor;
}
@NotNull
public TypeParameterDescriptor getTypeParameterDescriptor() {
return typeParameterDescriptor;
}
}
private class Unknown {
// T -> variance of its position
private final Map<TypeParameterDescriptor, Variance> typeParameters = Maps.newHashMap();
private final List<Constraint> constraints = Lists.newArrayList();
}
private final Map<TypeParameterDescriptor, Unknown> unknowns = Maps.newHashMap();
@Override
public void registerTypeVariable(@NotNull TypeParameterDescriptor typeParameterDescriptor, @NotNull Variance positionVariance) {
throw new UnsupportedOperationException(); // TODO
}
@Override
public void addSubtypingConstraint(@NotNull JetType lower, @NotNull JetType upper) {
throw new UnsupportedOperationException(); // TODO
}
@NotNull
@Override
public ConstraintSystemSolution solve() {
throw new UnsupportedOperationException(); // TODO
}
}