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.expressions.OperatorConventions;
import org.jetbrains.jet.lang.types.inference.ConstraintSystem; import org.jetbrains.jet.lang.types.inference.ConstraintSystem;
import org.jetbrains.jet.lang.types.inference.ConstraintSystemSolution; 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 org.jetbrains.jet.lexer.JetTokens;
import java.util.*; import java.util.*;
@@ -414,7 +414,7 @@ public class CallResolver {
if (!candidate.getTypeParameters().isEmpty()) { if (!candidate.getTypeParameters().isEmpty()) {
// Type argument inference // Type argument inference
ConstraintSystem constraintSystem = new SubtypingOnlyConstraintSystem(); ConstraintSystem constraintSystem = new ConstraintSystemImpl();
for (TypeParameterDescriptor typeParameterDescriptor : candidate.getTypeParameters()) { for (TypeParameterDescriptor typeParameterDescriptor : candidate.getTypeParameters()) {
constraintSystem.registerTypeVariable(typeParameterDescriptor, Variance.INVARIANT); // TODO constraintSystem.registerTypeVariable(typeParameterDescriptor, Variance.INVARIANT); // TODO
} }
@@ -4,8 +4,9 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; 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.IN_VARIANCE;
import static org.jetbrains.jet.lang.types.Variance.OUT_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) { 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); return TYPE_CHECKER.run(subtype, supertype);
} }
// This method returns the supertype of the first parameter that has the same constructor // 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(); 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) { public boolean run(@NotNull JetType subtype, @NotNull JetType supertype) {
return isSubtypeOf(subtype, supertype); return isSubtypeOf(subtype, supertype);
@@ -69,7 +102,7 @@ public class JetTypeChecker {
} }
@Nullable JetType closestSupertype = findCorrespondingSupertype(subtype, supertype); @Nullable JetType closestSupertype = findCorrespondingSupertype(subtype, supertype);
if (closestSupertype == null) { if (closestSupertype == null) {
return false; if (!constraintBuilder.noCorrespondingSupertype(subtype, supertype)) return false;
} }
return checkSubtypeForTheSameConstructor(closestSupertype, supertype); return checkSubtypeForTheSameConstructor(closestSupertype, supertype);
@@ -84,6 +117,7 @@ public class JetTypeChecker {
List<TypeParameterDescriptor> parameters = constructor.getParameters(); List<TypeParameterDescriptor> parameters = constructor.getParameters();
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 subIn = getInType(parameter, subArgument);
@@ -93,189 +127,193 @@ public class JetTypeChecker {
JetType superIn = getInType(parameter, superArgument); JetType superIn = getInType(parameter, superArgument);
JetType superOut = getOutType(parameter, superArgument); JetType superOut = getOutType(parameter, superArgument);
if (!isSubtypeOf(subOut, superOut) || !isSubtypeOf(superIn, subIn)) { if (parameter.getVariance() == INVARIANT && subArgument.getProjectionKind() == INVARIANT && superArgument.getProjectionKind() == INVARIANT) {
return false; 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; return true;
} }
} }
public static abstract class AbstractTypeCheckingProcedure<T> { // public static abstract class AbstractTypeCheckingProcedure<T> {
//
protected enum StatusAction { // protected enum StatusAction {
PROCEED(false), // PROCEED(false),
DONE_WITH_CURRENT_TYPE(true), // DONE_WITH_CURRENT_TYPE(true),
ABORT_ALL(true); // ABORT_ALL(true);
//
private final boolean abort; // private final boolean abort;
//
private StatusAction(boolean abort) { // private StatusAction(boolean abort) {
this.abort = abort; // this.abort = abort;
} // }
//
public boolean isAbort() { // public boolean isAbort() {
return abort; // return abort;
} // }
} // }
//
public final T run(@NotNull JetType subtype, @NotNull JetType supertype) { // public final T run(@NotNull JetType subtype, @NotNull JetType supertype) {
proceedOrStop(subtype, supertype); // proceedOrStop(subtype, supertype);
return result(); // return result();
} // }
//
protected abstract StatusAction startForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype); // protected abstract StatusAction startForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype);
//
protected abstract StatusAction noCorrespondingSupertype(@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 equalTypesRequired(@NotNull JetType subArgumentType, @NotNull JetType superArgumentType);
//
protected abstract StatusAction varianceConflictFound(@NotNull TypeProjection subArgument, @NotNull TypeProjection superArgument); // protected abstract StatusAction varianceConflictFound(@NotNull TypeProjection subArgument, @NotNull TypeProjection superArgument);
//
protected abstract StatusAction doneForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype); // protected abstract StatusAction doneForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype);
//
protected abstract T result(); // protected abstract T result();
//
private StatusAction proceedOrStop(@NotNull JetType subtype, @NotNull JetType supertype) { // private StatusAction proceedOrStop(@NotNull JetType subtype, @NotNull JetType supertype) {
StatusAction statusAction = startForPairOfTypes(subtype, supertype); // StatusAction statusAction = startForPairOfTypes(subtype, supertype);
if (statusAction.isAbort()) { // if (statusAction.isAbort()) {
return statusAction; // return statusAction;
} // }
//
JetType closestSupertype = findCorrespondingSupertype(subtype, supertype); // JetType closestSupertype = findCorrespondingSupertype(subtype, supertype);
if (closestSupertype == null) { // if (closestSupertype == null) {
return noCorrespondingSupertype(subtype, supertype); // return noCorrespondingSupertype(subtype, supertype);
} // }
//
proceed(closestSupertype, supertype); // proceed(closestSupertype, supertype);
return doneForPairOfTypes(subtype, supertype); // return doneForPairOfTypes(subtype, supertype);
} // }
//
private void proceed(@NotNull JetType subtype, @NotNull JetType supertype) { // private void proceed(@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();
//
loop: // loop:
for (int i = 0; i < parameters.size(); i++) { // for (int i = 0; i < parameters.size(); i++) {
TypeParameterDescriptor parameter = parameters.get(i); // TypeParameterDescriptor parameter = parameters.get(i);
TypeProjection subArgument = subArguments.get(i); // TypeProjection subArgument = subArguments.get(i);
TypeProjection superArgument = superArguments.get(i); // TypeProjection superArgument = superArguments.get(i);
//
JetType subArgumentType = subArgument.getType(); // JetType subArgumentType = subArgument.getType();
JetType superArgumentType = superArgument.getType(); // JetType superArgumentType = superArgument.getType();
//
StatusAction action = null; // StatusAction action = null;
switch (parameter.getVariance()) { // switch (parameter.getVariance()) {
case INVARIANT: // case INVARIANT:
switch (superArgument.getProjectionKind()) { // switch (superArgument.getProjectionKind()) {
case INVARIANT: // case INVARIANT:
action = equalTypesRequired(subArgumentType, superArgumentType); // action = equalTypesRequired(subArgumentType, superArgumentType);
break; // break;
case OUT_VARIANCE: // case OUT_VARIANCE:
if (!subArgument.getProjectionKind().allowsOutPosition()) { // if (!subArgument.getProjectionKind().allowsOutPosition()) {
action = varianceConflictFound(subArgument, superArgument); // action = varianceConflictFound(subArgument, superArgument);
} // }
else { // else {
action = proceedOrStop(subArgumentType, superArgumentType); // action = proceedOrStop(subArgumentType, superArgumentType);
} // }
break; // break;
case IN_VARIANCE: // case IN_VARIANCE:
if (!subArgument.getProjectionKind().allowsInPosition()) { // if (!subArgument.getProjectionKind().allowsInPosition()) {
action = varianceConflictFound(subArgument, superArgument); // action = varianceConflictFound(subArgument, superArgument);
} // }
else { // else {
action = proceedOrStop(superArgumentType, subArgumentType); // action = proceedOrStop(superArgumentType, subArgumentType);
} // }
break; // break;
} // }
break; // break;
case IN_VARIANCE: // case IN_VARIANCE:
switch (superArgument.getProjectionKind()) { // switch (superArgument.getProjectionKind()) {
case INVARIANT: // case INVARIANT:
case IN_VARIANCE: // case IN_VARIANCE:
action = proceedOrStop(superArgumentType, subArgumentType); // action = proceedOrStop(superArgumentType, subArgumentType);
break; // break;
case OUT_VARIANCE: // case OUT_VARIANCE:
action = proceedOrStop(subArgumentType, superArgumentType); // action = proceedOrStop(subArgumentType, superArgumentType);
break; // break;
} // }
break; // break;
case OUT_VARIANCE: // case OUT_VARIANCE:
switch (superArgument.getProjectionKind()) { // switch (superArgument.getProjectionKind()) {
case INVARIANT: // case INVARIANT:
case OUT_VARIANCE: // case OUT_VARIANCE:
case IN_VARIANCE: // case IN_VARIANCE:
action = proceedOrStop(subArgumentType, superArgumentType); // action = proceedOrStop(subArgumentType, superArgumentType);
break; // break;
} // }
break; // break;
} // }
switch (action) { // switch (action) {
case ABORT_ALL: break loop; // case ABORT_ALL: break loop;
case DONE_WITH_CURRENT_TYPE: // case DONE_WITH_CURRENT_TYPE:
default: // default:
} // }
} // }
} // }
//
} // }
private static class TypeCheckingProcedure extends AbstractTypeCheckingProcedure<Boolean> { // private static class TypeCheckingProcedure extends AbstractTypeCheckingProcedure<Boolean> {
//
private boolean result = true; // private boolean result = true;
//
private StatusAction fail() { // private StatusAction fail() {
result = false; // result = false;
return StatusAction.ABORT_ALL; // return StatusAction.ABORT_ALL;
} // }
//
@Override // @Override
public StatusAction startForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) { // public StatusAction startForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) {
if (ErrorUtils.isErrorType(subtype) || ErrorUtils.isErrorType(supertype)) { // if (ErrorUtils.isErrorType(subtype) || ErrorUtils.isErrorType(supertype)) {
return StatusAction.DONE_WITH_CURRENT_TYPE; // return StatusAction.DONE_WITH_CURRENT_TYPE;
} // }
if (!supertype.isNullable() && subtype.isNullable()) { // if (!supertype.isNullable() && subtype.isNullable()) {
return fail(); // return fail();
} // }
if (JetStandardClasses.isNothingOrNullableNothing(subtype)) { // if (JetStandardClasses.isNothingOrNullableNothing(subtype)) {
return StatusAction.DONE_WITH_CURRENT_TYPE; // return StatusAction.DONE_WITH_CURRENT_TYPE;
} // }
return StatusAction.PROCEED; // return StatusAction.PROCEED;
} // }
//
@Override // @Override
protected StatusAction noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) { // protected StatusAction noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) {
return fail(); // return fail();
} // }
//
@Override // @Override
protected StatusAction equalTypesRequired(@NotNull JetType subArgumentType, @NotNull JetType superArgumentType) { // protected StatusAction equalTypesRequired(@NotNull JetType subArgumentType, @NotNull JetType superArgumentType) {
if (!subArgumentType.equals(superArgumentType)) { // if (!subArgumentType.equals(superArgumentType)) {
return fail(); // return fail();
} // }
return StatusAction.PROCEED; // return StatusAction.PROCEED;
} // }
//
@Override // @Override
protected StatusAction varianceConflictFound(@NotNull TypeProjection subArgument, @NotNull TypeProjection superArgument) { // protected StatusAction varianceConflictFound(@NotNull TypeProjection subArgument, @NotNull TypeProjection superArgument) {
return fail(); // return fail();
} // }
//
@Override // @Override
protected StatusAction doneForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) { // protected StatusAction doneForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) {
return StatusAction.PROCEED; // return StatusAction.PROCEED;
} // }
//
@Override // @Override
protected Boolean result() { // protected Boolean result() {
return result; // return result;
} // }
} // }
} }
@@ -14,7 +14,7 @@ import java.util.Set;
/** /**
* @author abreslav * @author abreslav
*/ */
public class SubtypingOnlyConstraintSystem implements ConstraintSystem { public class ConstraintSystemImpl implements ConstraintSystem {
private static class LoopInTypeVariableConstraintsException extends RuntimeException { private static class LoopInTypeVariableConstraintsException extends RuntimeException {
private LoopInTypeVariableConstraintsException() { private LoopInTypeVariableConstraintsException() {
@@ -158,6 +158,39 @@ public class SubtypingOnlyConstraintSystem implements ConstraintSystem {
private final Map<TypeParameterDescriptor, UnknownType> unknownTypes = Maps.newHashMap(); private final Map<TypeParameterDescriptor, UnknownType> unknownTypes = Maps.newHashMap();
private final JetTypeChecker typeChecker = JetTypeChecker.INSTANCE; 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 @NotNull
private TypeValue getTypeValueFor(@NotNull JetType type) { private TypeValue getTypeValueFor(@NotNull JetType type) {
DeclarationDescriptor declarationDescriptor = type.getConstructor().getDeclarationDescriptor(); DeclarationDescriptor declarationDescriptor = type.getConstructor().getDeclarationDescriptor();
@@ -217,7 +250,7 @@ public class SubtypingOnlyConstraintSystem implements ConstraintSystem {
for (TypeValue upperBound : typeValue.getUpperBounds()) { for (TypeValue upperBound : typeValue.getUpperBounds()) {
if (upperBound instanceof KnownType) { if (upperBound instanceof KnownType) {
KnownType knownBoundType = (KnownType) upperBound; KnownType knownBoundType = (KnownType) upperBound;
boolean ok = new SubtypingConstraintExpander().run(jetType, knownBoundType.getType()); boolean ok = constraintExpander.run(jetType, knownBoundType.getType());
if (!ok) { if (!ok) {
return new Solution(true); 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) { private static void println(String message) {
// System.out.println(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
}
}