From 54336a285dd1efe769a450b1afe95bddbdb350fd Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Tue, 13 Dec 2011 12:38:37 +0400 Subject: [PATCH] Minor refactoring while working on prioritized constraints --- .../resolve/calls/ResolutionDebugInfo.java | 6 +- .../resolve/calls/inference/BoundsOwner.java | 16 + .../ConstraintResolutionListener.java | 16 +- .../calls/inference/ConstraintSystemImpl.java | 32 +- .../ConstraintSystemWithPriorities.java | 470 ++++++++++++++++++ .../DebugConstraintResolutionListener.java | 22 +- .../PrintingConstraintResolutionListener.java | 22 +- .../resolve/calls/inference/TypeValue.java | 82 +++ .../jet/lang/types/TypeSubstitutor.java | 28 +- .../lang/types/checker/JetTypeChecker.java | 4 +- .../lang/types/checker/TypingConstraints.java | 4 +- .../resolvewindow/ResolveToolwindow.java | 15 +- 12 files changed, 659 insertions(+), 58 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/BoundsOwner.java create mode 100644 compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemWithPriorities.java create mode 100644 compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/TypeValue.java diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionDebugInfo.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionDebugInfo.java index 59968bae9a4..d254ac771e4 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionDebugInfo.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionDebugInfo.java @@ -5,7 +5,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.psi.PsiElement; import org.jetbrains.jet.lang.descriptors.CallableDescriptor; import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; -import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl; +import org.jetbrains.jet.lang.resolve.calls.inference.BoundsOwner; import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemSolution; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.util.slicedmap.*; @@ -22,8 +22,8 @@ public class ResolutionDebugInfo { public static final WritableSlice> RESULT = Slices.createSimpleSlice(); public static final WritableSlice ERRORS = Slices.createSimpleSlice(); public static final WritableSlice LOG = Slices.createSimpleSlice(); - public static final WritableSlice> BOUNDS_FOR_UNKNOWNS = Slices.createSimpleSlice(); - public static final WritableSlice> BOUNDS_FOR_KNOWNS = Slices.createSimpleSlice(); + public static final WritableSlice> BOUNDS_FOR_UNKNOWNS = Slices.createSimpleSlice(); + public static final WritableSlice> BOUNDS_FOR_KNOWNS = Slices.createSimpleSlice(); public static final WritableSlice SOLUTION = Slices.createSimpleSlice(); public static final WritableSlice> UNKNOWNS = Slices.createSimpleSlice(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/BoundsOwner.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/BoundsOwner.java new file mode 100644 index 00000000000..512b1437d77 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/BoundsOwner.java @@ -0,0 +1,16 @@ +package org.jetbrains.jet.lang.resolve.calls.inference; + +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +/** + * @author abreslav + */ +public interface BoundsOwner { + @NotNull + Set getUpperBounds(); + + @NotNull + Set getLowerBounds(); +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintResolutionListener.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintResolutionListener.java index 6cbf129fb73..eda1db1082d 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintResolutionListener.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintResolutionListener.java @@ -12,11 +12,11 @@ public interface ConstraintResolutionListener { public static final ConstraintResolutionListener DO_NOTHING = new ConstraintResolutionListener() { @Override - public void constraintsForUnknown(TypeParameterDescriptor typeParameterDescriptor, ConstraintSystemImpl.TypeValue typeValue) { + public void constraintsForUnknown(TypeParameterDescriptor typeParameterDescriptor, BoundsOwner typeValue) { } @Override - public void constraintsForKnownType(JetType type, ConstraintSystemImpl.TypeValue typeValue) { + public void constraintsForKnownType(JetType type, BoundsOwner typeValue) { } @Override @@ -24,18 +24,18 @@ public interface ConstraintResolutionListener { } @Override - public void log(Object message) { + public void log(Object... messageFragments) { } @Override - public void error(Object message) { + public void error(Object... messageFragments) { } }; - void constraintsForUnknown(TypeParameterDescriptor typeParameterDescriptor, ConstraintSystemImpl.TypeValue typeValue); - void constraintsForKnownType(JetType type, ConstraintSystemImpl.TypeValue typeValue); + void constraintsForUnknown(TypeParameterDescriptor typeParameterDescriptor, BoundsOwner typeValue); + void constraintsForKnownType(JetType type, BoundsOwner typeValue); void done(ConstraintSystemSolution solution, Set typeParameterDescriptors); - void log(Object message); - void error(Object message); + void log(Object... messageFragments); + void error(Object... messageFragments); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemImpl.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemImpl.java index 931371e5f3e..cb68fc94d2c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemImpl.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemImpl.java @@ -143,7 +143,7 @@ public class ConstraintSystemImpl implements ConstraintSystem { } } - listener.log("minimal solution from lowerbounds for " + this + " is " + commonSupertype); + listener.log("minimal solution from lowerbounds for ", this, " is ", commonSupertype); value = new KnownType(commonSupertype); } else { @@ -247,10 +247,10 @@ public class ConstraintSystemImpl implements ConstraintSystem { } @Override - public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b, TypeCheckingProcedure typeCheckingProcedure) { + public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b, @NotNull TypeCheckingProcedure typeCheckingProcedure) { boolean result = delegate.assertEqualTypes(a, b, typeCheckingProcedure); if (!result) { - listener.error("-- Failed to equate " + a + " and " + b); + listener.error("-- Failed to equate ", a, " and ", b); } return result; } @@ -259,16 +259,16 @@ public class ConstraintSystemImpl implements ConstraintSystem { public boolean assertEqualTypeConstructors(@NotNull TypeConstructor a, @NotNull TypeConstructor b) { boolean result = delegate.assertEqualTypeConstructors(a, b); if (!result) { - listener.error("-- Type constructors are not equal: " + a + " and " + b); + listener.error("-- Type constructors are not equal: ", a, " and ", b); } return result; } @Override - public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype, TypeCheckingProcedure typeCheckingProcedure) { + public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype, @NotNull TypeCheckingProcedure typeCheckingProcedure) { boolean result = delegate.assertSubtype(subtype, supertype, typeCheckingProcedure); if (!result) { - listener.error("-- " + subtype + " can't be a subtype of " + supertype); + listener.error("-- ", subtype, " can't be a subtype of ", supertype); } return result; } @@ -277,7 +277,7 @@ public class ConstraintSystemImpl implements ConstraintSystem { public boolean noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) { boolean result = delegate.noCorrespondingSupertype(subtype, supertype); if (!result) { - listener.error("-- " + subtype + " has no supertype corresponding to " + supertype); + listener.error("-- ", subtype, " has no supertype corresponding to ", supertype); } return result; } @@ -294,7 +294,7 @@ public class ConstraintSystemImpl implements ConstraintSystem { private TypeCheckingProcedure createConstraintExpander() { return new TypeCheckingProcedure(new TypeConstraintBuilderAdapter(new TypingConstraints() { @Override - public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b, TypeCheckingProcedure typeCheckingProcedure) { + public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b, @NotNull TypeCheckingProcedure typeCheckingProcedure) { TypeValue aValue = getTypeValueFor(a); TypeValue bValue = getTypeValueFor(b); @@ -309,7 +309,7 @@ public class ConstraintSystemImpl implements ConstraintSystem { } @Override - public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype, TypeCheckingProcedure typeCheckingProcedure) { + public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype, @NotNull TypeCheckingProcedure typeCheckingProcedure) { TypeValue subtypeValue = getTypeValueFor(subtype); TypeValue supertypeValue = getTypeValueFor(supertype); @@ -389,7 +389,7 @@ public class ConstraintSystemImpl implements ConstraintSystem { } private void addSubtypingConstraintOnTypeValues(TypeValue typeValueForLower, TypeValue typeValueForUpper) { - listener.log("Constraint added: " + typeValueForLower + " :< " + typeValueForUpper); + listener.log("Constraint added: ", typeValueForLower, " :< ", typeValueForUpper); if (typeValueForLower != typeValueForUpper) { typeValueForLower.addUpperBound(typeValueForUpper); typeValueForUpper.addLowerBound(typeValueForLower); @@ -409,7 +409,7 @@ public class ConstraintSystemImpl implements ConstraintSystem { KnownType knownBoundType = (KnownType) upperBound; boolean ok = constraintExpander.isSubtypeOf(jetType, knownBoundType.getType()); if (!ok) { - listener.error("Error while expanding '" + jetType + " :< " + knownBoundType.getType() + "'"); + listener.error("Error while expanding '", jetType, " :< ", knownBoundType.getType(), "'"); return new Solution().registerError("Mismatch while expanding constraints"); } } @@ -443,11 +443,11 @@ public class ConstraintSystemImpl implements ConstraintSystem { } for (UnknownType unknownType : unknownTypes.values()) { - listener.constraintsForUnknown(unknownType.getTypeParameterDescriptor(), unknownType); +// listener.constraintsForUnknown(unknownType.getTypeParameterDescriptor(), unknownType); } for (KnownType knownType : knownTypes.values()) { - listener.constraintsForKnownType(knownType.getType(), knownType); +// listener.constraintsForKnownType(knownType.getType(), knownType); } // Find inconsistencies @@ -477,14 +477,14 @@ public class ConstraintSystemImpl implements ConstraintSystem { JetType boundingType = solution.getSubstitutor().substitute(upperBound.getValue().getType(), Variance.INVARIANT); if (!typeChecker.isSubtypeOf(type, boundingType)) { // TODO solution.registerError("Constraint violation: " + type + " is not a subtype of " + boundingType); - listener.error("Constraint violation: " + type + " :< " + boundingType); + listener.error("Constraint violation: ", type, " :< ", boundingType); } } for (TypeValue lowerBound : typeValue.getLowerBounds()) { JetType boundingType = solution.getSubstitutor().substitute(lowerBound.getValue().getType(), Variance.INVARIANT); if (!typeChecker.isSubtypeOf(boundingType, type)) { solution.registerError("Constraint violation: " + boundingType + " is not a subtype of " + type); - listener.error("Constraint violation: " + boundingType + " :< " + type); + listener.error("Constraint violation: ", boundingType, " :< ", type); } } } @@ -542,7 +542,7 @@ public class ConstraintSystemImpl implements ConstraintSystem { TypeProjection typeProjection = new TypeProjection(getValue(descriptor)); - listener.log(descriptor + " |-> " + typeProjection); + listener.log(descriptor, " |-> ", typeProjection); return typeProjection; } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemWithPriorities.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemWithPriorities.java new file mode 100644 index 00000000000..fd408957971 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemWithPriorities.java @@ -0,0 +1,470 @@ +package org.jetbrains.jet.lang.resolve.calls.inference; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; +import org.jetbrains.jet.lang.types.*; +import org.jetbrains.jet.lang.types.checker.JetTypeChecker; +import org.jetbrains.jet.lang.types.checker.TypeCheckingProcedure; +import org.jetbrains.jet.lang.types.checker.TypingConstraints; + +import java.util.*; + +import static org.jetbrains.jet.lang.resolve.calls.inference.ConstraintType.PARAMETER_BOUND; + +/** + * @author abreslav + */ +public class ConstraintSystemWithPriorities implements ConstraintSystem { + + public static final Comparator SUBTYPING_CONSTRAINT_ORDER = new Comparator() { + @Override + public int compare(SubtypingConstraint o1, SubtypingConstraint o2) { + return o1.getType().compareTo(o2.getType()); + } + }; + + private static class LoopInTypeVariableConstraintsException extends RuntimeException { + public LoopInTypeVariableConstraintsException() {} + } + + //========================================================================================================================================================== + + private final Map knownTypes = Maps.newLinkedHashMap(); // linked - for easier debugging + private final Map unknownTypes = Maps.newLinkedHashMap(); // linked - for easier debugging + private final Set unsolvedUnknowns = Sets.newLinkedHashSet(); // linked - for easier debugging + private final PriorityQueue constraintQueue = new PriorityQueue(10, SUBTYPING_CONSTRAINT_ORDER); + + private final JetTypeChecker typeChecker = JetTypeChecker.INSTANCE; + private final TypeCheckingProcedure constraintExpander; + private final ConstraintResolutionListener listener; + + public ConstraintSystemWithPriorities(ConstraintResolutionListener listener) { + this.listener = listener; + this.constraintExpander = createConstraintExpander(); + } + + @NotNull + private TypeValue getTypeValueFor(@NotNull JetType type) { + DeclarationDescriptor declarationDescriptor = type.getConstructor().getDeclarationDescriptor(); + if (declarationDescriptor instanceof TypeParameterDescriptor) { + TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) declarationDescriptor; + // Checking that this is not a T?, but exactly T + if (typeParameterDescriptor.getDefaultType().isNullable() == type.isNullable()) { + TypeValue unknownType = unknownTypes.get(typeParameterDescriptor); + if (unknownType != null) { + return unknownType; + } + } + } + + TypeValue typeValue = knownTypes.get(type); + if (typeValue == null) { + typeValue = new TypeValue(type); + knownTypes.put(type, typeValue); + } + return typeValue; + } + + @Override + public void registerTypeVariable(@NotNull TypeParameterDescriptor typeParameterDescriptor, @NotNull Variance positionVariance) { + assert !unknownTypes.containsKey(typeParameterDescriptor); + TypeValue typeValue = new TypeValue(typeParameterDescriptor, positionVariance); + unknownTypes.put(typeParameterDescriptor, typeValue); + unsolvedUnknowns.add(typeValue); + } + + @NotNull + private TypeValue getTypeVariable(TypeParameterDescriptor typeParameterDescriptor) { + TypeValue unknownType = unknownTypes.get(typeParameterDescriptor); + if (unknownType == null) { + throw new IllegalArgumentException("This type parameter is not an unknown in this constraint system: " + typeParameterDescriptor); + } + return unknownType; + } + + @Override + public void addSubtypingConstraint(@NotNull SubtypingConstraint constraint) { + constraintQueue.add(constraint); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Constraint expansion + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + private TypeCheckingProcedure createConstraintExpander() { + return new TypeCheckingProcedure(new TypeConstraintBuilderAdapter(new TypingConstraints() { + @Override + public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b, @NotNull TypeCheckingProcedure typeCheckingProcedure) { + TypeValue aValue = getTypeValueFor(a); + TypeValue bValue = getTypeValueFor(b); + + return expandEqualityConstraint(aValue, bValue); + } + + @Override + public boolean assertEqualTypeConstructors(@NotNull TypeConstructor a, @NotNull TypeConstructor b) { + return a.equals(b) + || unknownTypes.containsKey(a.getDeclarationDescriptor()) + || unknownTypes.containsKey(b.getDeclarationDescriptor()); + } + + @Override + public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype, @NotNull TypeCheckingProcedure typeCheckingProcedure) { + TypeValue subtypeValue = getTypeValueFor(subtype); + TypeValue supertypeValue = getTypeValueFor(supertype); + + if (someUnknown(subtypeValue, supertypeValue)) { + expandSubtypingConstraint(subtypeValue, supertypeValue); + } + return true; // For known types further expansion happens automatically + } + + @Override + public boolean noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) { + // If some of the types is an unknown, the constraint must be generated, and we should carry on + // otherwise there can be no solution, and we should fail + TypeValue subTypeValue = getTypeValueFor(subtype); + TypeValue superTypeValue = getTypeValueFor(supertype); + boolean someUnknown = someUnknown(subTypeValue, superTypeValue); + if (someUnknown) { + expandSubtypingConstraint(subTypeValue, superTypeValue); + } + return someUnknown; + } + + private boolean someUnknown(TypeValue subtypeValue, TypeValue supertypeValue) { + return !subtypeValue.isKnown() || !supertypeValue.isKnown(); + } + + }, listener)); + } + + private boolean assignValueTo(TypeValue unknown, JetType value) { + if (unknown.hasValue()) { + // If we have already assigned a value to this unknown, + // it is a conflict to assign another one, unless this new one is equal to the previous + return TypeUtils.equalTypes(unknown.getType(), value); + } + unsolvedUnknowns.remove(unknown); + unknown.setValue(value); + return true; + } + + private boolean mergeUnknowns(@NotNull TypeValue a, @NotNull TypeValue b) { + assert !a.isKnown() && !b.isKnown(); + listener.error("!!!mergeUnknowns() is not implemented!!!"); + return false; + } + + public boolean expandEqualityConstraint(TypeValue a, TypeValue b) { + if (a.isKnown() && b.isKnown()) { + return constraintExpander.equalTypes(a.getType(), b.getType()); + } + + // At least one of them is unknown + if (a.isKnown()) { + TypeValue tmp = a; + a = b; + b = tmp; + } + + // Now a is definitely unknown + if (b.isKnown()) { + return assignValueTo(a, b.getType()); + } + + // They are both unknown + return mergeUnknowns(a, b); + } + + private boolean expandSubtypingConstraint(TypeValue lower, TypeValue upper) { + listener.log("Constraint added: ", lower, " :< ", upper); + + if (lower == upper) return true; + + // Remember for a later check + lower.addUpperBound(upper); + upper.addLowerBound(lower); + + if (lower.isKnown() && upper.isKnown()) { + // Two known types: expand constraints + return constraintExpander.isSubtypeOf(lower.getType(), upper.getType()); + } + else if (!lower.isKnown() && !upper.isKnown()) { + // Two unknown types: merge them into one variable + return mergeUnknowns(lower, upper); + } + else { + // One unknown and one known + if (upper.isKnown()) { + if (TypeUtils.canHaveSubtypes(typeChecker, upper.getType())) { + // Upper bound is final -> we have to equate the lower bounds to it + return expandEqualityConstraint(lower, upper); + } + if (lower.getLowerBounds().contains(upper)) { + // upper :< lower :< upper + return expandEqualityConstraint(lower, upper); + } + } + else { + if (upper.getUpperBounds().contains(lower)) { + // lower :< upper :< lower + return expandEqualityConstraint(lower, upper); + } + } + } + return true; + } + + @Override + @NotNull + public ConstraintSystemSolution solve() { + Solution solution = new Solution(); + // At this point we only have type values, no bounds added for them, no values computed for unknown types + + // Generate low-priority constraints from bounds + for (Map.Entry entry : Sets.newHashSet(unknownTypes.entrySet())) { + TypeParameterDescriptor typeParameterDescriptor = entry.getKey(); + TypeValue typeValue = entry.getValue(); + for (JetType upperBound : typeParameterDescriptor.getUpperBounds()) { + addSubtypingConstraint(PARAMETER_BOUND.assertSubtyping(typeValue.getOriginalType(), getTypeValueFor(upperBound).getOriginalType())); + } + for (JetType lowerBound : typeParameterDescriptor.getLowerBounds()) { + addSubtypingConstraint(PARAMETER_BOUND.assertSubtyping(getTypeValueFor(lowerBound).getOriginalType(), typeValue.getOriginalType())); + } + } + + // Expand and solve constraints + while (!constraintQueue.isEmpty()) { + SubtypingConstraint constraint = constraintQueue.poll(); + + // Apply constraint + TypeValue lower = getTypeValueFor(constraint.getSubtype()); + TypeValue upper = getTypeValueFor(constraint.getSupertype()); + boolean success = expandSubtypingConstraint(lower, upper); + if (!success) { + solution.registerError(constraint.getErrorMessage()); + break; + } + + // (???) Propagate info + + // Any unknowns left? + if (unsolvedUnknowns.isEmpty()) break; + } + + // Now, let's check the rest of the constraints + + // Expand custom bounds, e.g. List <: List +// for (Map.Entry entry : Sets.newHashSet(knownTypes.entrySet())) { +// JetType jetType = entry.getKey(); +// TypeValue typeValue = entry.getValue(); +// +// for (TypeValue upperBound : typeValue.getUpperBounds()) { +// if (upperBound.isKnown()) { +// boolean ok = constraintExpander.isSubtypeOf(jetType, upperBound.getType()); +// if (!ok) { +// listener.error("Error while expanding '" + jetType + " :< " + upperBound.getType() + "'"); +// return new Solution().registerError("Mismatch while expanding constraints"); +// } +// } +// } +// +// // Lower bounds. Probably not needed for known types, as everything is symmetrical anyway +// } + + // effective bounds for each node +// Set visited = Sets.newHashSet(); +// for (TypeValue unknownType : unknownTypes.values()) { +// transitiveClosure(unknownType, visited); +// } + + for (TypeValue unknownType : unknownTypes.values()) { + listener.constraintsForUnknown(unknownType.getTypeParameterDescriptor(), unknownType); + } + for (TypeValue knownType : knownTypes.values()) { + listener.constraintsForKnownType(knownType.getType(), knownType); + } + + // Find inconsistencies + + // Compute values and check that all bounds are respected by solutions: + // we have set some of them from equality constraints with known types + // and thus the bounds may be violated if some of the constraints conflict + for (TypeValue unknownType : unknownTypes.values()) { + check(unknownType, solution); + } + for (TypeValue knownType : knownTypes.values()) { + check(knownType, solution); + } + + listener.done(solution, unknownTypes.keySet()); + + return solution; + } + + private void transitiveClosure(TypeValue current, Set visited) { + if (!visited.add(current)) { + return; + } + + for (TypeValue upperBound : Sets.newHashSet(current.getUpperBounds())) { + if (upperBound.isKnown()) { + continue; + } + transitiveClosure(upperBound, visited); + Set upperBounds = upperBound.getUpperBounds(); + for (TypeValue transitiveBound : upperBounds) { + expandSubtypingConstraint(current, transitiveBound); + } + } + } + + private void check(TypeValue typeValue, Solution solution) { + try { + JetType resultingType = typeValue.getType(); + JetType type = solution.getSubstitutor().substitute(resultingType, Variance.INVARIANT); // TODO + for (TypeValue upperBound : typeValue.getUpperBounds()) { + JetType boundingType = solution.getSubstitutor().substitute(upperBound.getType(), Variance.INVARIANT); + if (!typeChecker.isSubtypeOf(type, boundingType)) { // TODO + solution.registerError("Constraint violation: " + type + " is not a subtype of " + boundingType); + listener.error("Constraint violation: ", type, " :< ", boundingType); + } + } + for (TypeValue lowerBound : typeValue.getLowerBounds()) { + JetType boundingType = solution.getSubstitutor().substitute(lowerBound.getType(), Variance.INVARIANT); + if (!typeChecker.isSubtypeOf(boundingType, type)) { + solution.registerError("Constraint violation: " + boundingType + " is not a subtype of " + type); + listener.error("Constraint violation: ", boundingType, " :< ", type); + } + } + } + catch (LoopInTypeVariableConstraintsException e) { + listener.error("Loop detected"); + solution.registerError("[TODO] Loop in constraints"); + } + } + + private static class Error implements SolutionStatus { + + private final String message; + + private Error(String message) { + this.message = message; + } + + @Override + public boolean isSuccessful() { + return false; + } + + @Override + public String toString() { + return message; + } + } + + public class Solution implements ConstraintSystemSolution { + private final TypeSubstitutor typeSubstitutor = TypeSubstitutor.create(new TypeSubstitutor.TypeSubstitution() { + @Override + public TypeProjection get(TypeConstructor key) { + DeclarationDescriptor declarationDescriptor = key.getDeclarationDescriptor(); + if (declarationDescriptor instanceof TypeParameterDescriptor) { + TypeParameterDescriptor descriptor = (TypeParameterDescriptor) declarationDescriptor; + + if (!unknownTypes.containsKey(descriptor)) return null; + + TypeProjection typeProjection = new TypeProjection(getValue(descriptor)); + + listener.log(descriptor, " |-> ", typeProjection); + + return typeProjection; + } + return null; + } + + @Override + public boolean isEmpty() { + return false; + } + }); + + private SolutionStatus status; + + public Solution() { + this.status = SolutionStatus.SUCCESS; + } + + private Solution registerError(String message) { + status = new Error(message); + return this; + } + + @NotNull + @Override + public SolutionStatus getStatus() { + return status; + } + + @Override + public JetType getValue(TypeParameterDescriptor typeParameterDescriptor) { + return getTypeVariable(typeParameterDescriptor).getType(); + } + + @NotNull + @Override + public TypeSubstitutor getSubstitutor() { + return typeSubstitutor; + } + + } + private static final class TypeConstraintBuilderAdapter implements TypingConstraints { + private final TypingConstraints delegate; + private final ConstraintResolutionListener listener; + + private TypeConstraintBuilderAdapter(TypingConstraints delegate, ConstraintResolutionListener listener) { + this.delegate = delegate; + this.listener = listener; + } + + @Override + public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b, @NotNull TypeCheckingProcedure typeCheckingProcedure) { + boolean result = delegate.assertEqualTypes(a, b, typeCheckingProcedure); + if (!result) { + listener.error("-- Failed to equate ", a, " and ", b); + } + return result; + } + + @Override + public boolean assertEqualTypeConstructors(@NotNull TypeConstructor a, @NotNull TypeConstructor b) { + boolean result = delegate.assertEqualTypeConstructors(a, b); + if (!result) { + listener.error("-- Type constructors are not equal: ", a, " and ", b); + } + return result; + } + + @Override + public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype, @NotNull TypeCheckingProcedure typeCheckingProcedure) { + boolean result = delegate.assertSubtype(subtype, supertype, typeCheckingProcedure); + if (!result) { + listener.error("-- " + subtype + " can't be a subtype of " + supertype); + } + return result; + } + + @Override + public boolean noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) { + boolean result = delegate.noCorrespondingSupertype(subtype, supertype); + if (!result) { + listener.error("-- " + subtype + " has no supertype corresponding to " + supertype); + } + return result; + } + } + +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/DebugConstraintResolutionListener.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/DebugConstraintResolutionListener.java index b6e5731f7cf..52edf22c3f0 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/DebugConstraintResolutionListener.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/DebugConstraintResolutionListener.java @@ -23,9 +23,9 @@ public class DebugConstraintResolutionListener implements ConstraintResolutionLi } @Override - public void constraintsForUnknown(TypeParameterDescriptor typeParameterDescriptor, ConstraintSystemImpl.TypeValue typeValue) { + public void constraintsForUnknown(TypeParameterDescriptor typeParameterDescriptor, BoundsOwner typeValue) { if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return; - Map map = debugInfo.get(BOUNDS_FOR_UNKNOWNS); + Map map = debugInfo.get(BOUNDS_FOR_UNKNOWNS); if (map == null) { map = Maps.newLinkedHashMap(); debugInfo.set(BOUNDS_FOR_UNKNOWNS, map); @@ -34,9 +34,9 @@ public class DebugConstraintResolutionListener implements ConstraintResolutionLi } @Override - public void constraintsForKnownType(JetType type, ConstraintSystemImpl.TypeValue typeValue) { + public void constraintsForKnownType(JetType type, BoundsOwner typeValue) { if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return; - Map map = debugInfo.get(BOUNDS_FOR_KNOWNS); + Map map = debugInfo.get(BOUNDS_FOR_KNOWNS); if (map == null) { map = Maps.newLinkedHashMap(); debugInfo.set(BOUNDS_FOR_KNOWNS, map); @@ -52,24 +52,30 @@ public class DebugConstraintResolutionListener implements ConstraintResolutionLi } @Override - public void log(Object message) { + public void log(Object... messageFragments) { if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return; StringBuilder stringBuilder = debugInfo.get(LOG); if (stringBuilder == null) { stringBuilder = new StringBuilder(); debugInfo.set(LOG, stringBuilder); } - stringBuilder.append(message).append("\n"); + for (Object m : messageFragments) { + stringBuilder.append(m); + } + stringBuilder.append("\n"); } @Override - public void error(Object message) { + public void error(Object... messageFragments) { if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return; StringBuilder stringBuilder = debugInfo.get(ERRORS); if (stringBuilder == null) { stringBuilder = new StringBuilder(); debugInfo.set(ERRORS, stringBuilder); } - stringBuilder.append(message).append("\n"); + for (Object m : messageFragments) { + stringBuilder.append(m); + } + stringBuilder.append("\n"); } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/PrintingConstraintResolutionListener.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/PrintingConstraintResolutionListener.java index 3fe1a169eeb..ac3e3528b8d 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/PrintingConstraintResolutionListener.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/PrintingConstraintResolutionListener.java @@ -12,13 +12,13 @@ import java.util.Set; public class PrintingConstraintResolutionListener implements ConstraintResolutionListener { @Override - public void constraintsForUnknown(TypeParameterDescriptor typeParameterDescriptor, ConstraintSystemImpl.TypeValue typeValue) { + public void constraintsForUnknown(TypeParameterDescriptor typeParameterDescriptor, BoundsOwner typeValue) { println("Constraints for " + typeParameterDescriptor); printTypeValue(typeValue); } @Override - public void constraintsForKnownType(JetType type, ConstraintSystemImpl.TypeValue typeValue) { + public void constraintsForKnownType(JetType type, BoundsOwner typeValue) { println("Constraints for " + type); printTypeValue(typeValue); } @@ -31,20 +31,24 @@ public class PrintingConstraintResolutionListener implements ConstraintResolutio } @Override - public void log(Object message) { - println(message); + public void log(Object... messageFragments) { + for (Object fragment : messageFragments) { + println(fragment); + } } @Override - public void error(Object message) { - println(message); + public void error(Object... messageFragments) { + for (Object fragment : messageFragments) { + println(fragment); + } } - private void printTypeValue(ConstraintSystemImpl.TypeValue typeValue) { - for (ConstraintSystemImpl.TypeValue bound : typeValue.getUpperBounds()) { + private void printTypeValue(BoundsOwner typeValue) { + for (BoundsOwner bound : typeValue.getUpperBounds()) { println(" :< " + bound); } - for (ConstraintSystemImpl.TypeValue bound : typeValue.getLowerBounds()) { + for (BoundsOwner bound : typeValue.getLowerBounds()) { println(" :> " + bound); } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/TypeValue.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/TypeValue.java new file mode 100644 index 00000000000..837036e003f --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/TypeValue.java @@ -0,0 +1,82 @@ +package org.jetbrains.jet.lang.resolve.calls.inference; + +import com.google.common.collect.Sets; +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.Set; + +/** +* @author abreslav +*/ +public class TypeValue implements BoundsOwner { + private final Set upperBounds = Sets.newLinkedHashSet(); + private final Set lowerBounds = Sets.newLinkedHashSet(); + + private final Variance positionVariance; + private final TypeParameterDescriptor typeParameterDescriptor; // Null for known types + private final JetType originalType; + private JetType value; // For an unknown — the value found by constraint resolution, for a known — just it's value + + // Unknown type + public TypeValue(@NotNull TypeParameterDescriptor typeParameterDescriptor, @NotNull Variance positionVariance) { + this.positionVariance = positionVariance; + this.typeParameterDescriptor = typeParameterDescriptor; + this.originalType = typeParameterDescriptor.getDefaultType(); + } + + // Known type + public TypeValue(@NotNull JetType knownType) { + this.positionVariance = null; + this.typeParameterDescriptor = null; + this.originalType = knownType; + } + + public boolean isKnown() { + return typeParameterDescriptor == null; + } + + public TypeParameterDescriptor getTypeParameterDescriptor() { + return typeParameterDescriptor; + } + + @Override + @NotNull + public Set getUpperBounds() { + return upperBounds; + } + + @Override + @NotNull + public Set getLowerBounds() { + return lowerBounds; + } + + @NotNull + public JetType getType() { + return value; + } + + @NotNull + public JetType getOriginalType() { + return originalType; + } + + public void addUpperBound(@NotNull TypeValue bound) { + upperBounds.add(bound); + } + + public void addLowerBound(@NotNull TypeValue bound) { + lowerBounds.add(bound); + } + + public void setValue(@NotNull JetType value) { + this.value = value; + } + + public boolean hasValue() { + return value != null; + } +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java index e0e2a35b24d..55c861b18c8 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java @@ -1,19 +1,41 @@ package org.jetbrains.jet.lang.types; +import com.google.common.collect.Sets; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; import org.jetbrains.jet.lang.resolve.scopes.SubstitutingScope; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.util.*; /** * @author abreslav */ public class TypeSubstitutor { + public static TypeSubstitutor makeConstantSubstitutor(Collection typeParameterDescriptors, JetType type) { + final Set constructors = Sets.newHashSet(); + for (TypeParameterDescriptor typeParameterDescriptor : typeParameterDescriptors) { + constructors.add(typeParameterDescriptor.getTypeConstructor()); + } + final TypeProjection projection = new TypeProjection(type); + + return create(new TypeSubstitution() { + @Override + public TypeProjection get(TypeConstructor key) { + if (constructors.contains(key)) { + return projection; + } + return null; + } + + @Override + public boolean isEmpty() { + return false; + } + }); + } + public interface TypeSubstitution { TypeSubstitution EMPTY = new TypeSubstitution() { @Override diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/checker/JetTypeChecker.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/checker/JetTypeChecker.java index 7147158c66c..691966d302d 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/checker/JetTypeChecker.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/checker/JetTypeChecker.java @@ -46,7 +46,7 @@ public class JetTypeChecker { private static class TypeCheckerTypingConstraints implements TypingConstraints { @Override - public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b, TypeCheckingProcedure typeCheckingProcedure) { + public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b, @NotNull TypeCheckingProcedure typeCheckingProcedure) { return typeCheckingProcedure.equalTypes(a, b); // return TypeUtils.equalTypes(a, b); } @@ -57,7 +57,7 @@ public class JetTypeChecker { } @Override - public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype, TypeCheckingProcedure typeCheckingProcedure) { + public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype, @NotNull TypeCheckingProcedure typeCheckingProcedure) { return typeCheckingProcedure.isSubtypeOf(subtype, supertype); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/checker/TypingConstraints.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/checker/TypingConstraints.java index 0276f816184..754bff7f5f8 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/checker/TypingConstraints.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/checker/TypingConstraints.java @@ -8,11 +8,11 @@ import org.jetbrains.jet.lang.types.TypeConstructor; * Methods of this class return true to continue type checking and false to fail */ public interface TypingConstraints { - boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b, TypeCheckingProcedure typeCheckingProcedure); + boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b, @NotNull TypeCheckingProcedure typeCheckingProcedure); boolean assertEqualTypeConstructors(@NotNull TypeConstructor a, @NotNull TypeConstructor b); - boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype, TypeCheckingProcedure typeCheckingProcedure); + boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype, @NotNull TypeCheckingProcedure typeCheckingProcedure); boolean noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype); } diff --git a/idea/src/org/jetbrains/jet/plugin/internal/resolvewindow/ResolveToolwindow.java b/idea/src/org/jetbrains/jet/plugin/internal/resolvewindow/ResolveToolwindow.java index 94a47c390c7..4b9a6fbc102 100644 --- a/idea/src/org/jetbrains/jet/plugin/internal/resolvewindow/ResolveToolwindow.java +++ b/idea/src/org/jetbrains/jet/plugin/internal/resolvewindow/ResolveToolwindow.java @@ -31,6 +31,7 @@ import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.calls.ResolutionDebugInfo; import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; import org.jetbrains.jet.lang.resolve.calls.ResolvedValueArgument; +import org.jetbrains.jet.lang.resolve.calls.inference.BoundsOwner; import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; import org.jetbrains.jet.lang.types.JetType; @@ -190,9 +191,9 @@ public class ResolveToolwindow extends JPanel { result.append("Log: \n").append(log).append(bar); } - Map knowns = debugInfo.get(BOUNDS_FOR_KNOWNS); + Map knowns = debugInfo.get(BOUNDS_FOR_KNOWNS); renderMap(knowns, result); - Map unknowns = debugInfo.get(BOUNDS_FOR_UNKNOWNS); + Map unknowns = debugInfo.get(BOUNDS_FOR_UNKNOWNS); renderMap(unknowns, result); result.append(bar); @@ -205,17 +206,17 @@ public class ResolveToolwindow extends JPanel { return result.toString(); } - private void renderMap(Map map, StringBuilder builder) { + private void renderMap(Map map, StringBuilder builder) { if (map == null) return; - for (Map.Entry entry : map.entrySet()) { + for (Map.Entry entry : map.entrySet()) { K key = entry.getKey(); - ConstraintSystemImpl.TypeValue typeValue = entry.getValue(); + BoundsOwner typeValue = entry.getValue(); builder.append("Bounds for ").append(key).append("\n"); - for (ConstraintSystemImpl.TypeValue bound : typeValue.getLowerBounds()) { + for (BoundsOwner bound : typeValue.getLowerBounds()) { builder.append(" >: ").append(bound).append("\n"); } - for (ConstraintSystemImpl.TypeValue bound : typeValue.getUpperBounds()) { + for (BoundsOwner bound : typeValue.getUpperBounds()) { builder.append(" <: ").append(bound).append("\n"); } }