diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java index f73557fcdd4..3db5f295442 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java @@ -13,16 +13,13 @@ import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.*; import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastServiceImpl; import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; +import org.jetbrains.jet.lang.resolve.calls.inference.*; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; import org.jetbrains.jet.lang.types.*; import org.jetbrains.jet.lang.types.expressions.ExpressionTypingServices; import org.jetbrains.jet.lang.types.expressions.OperatorConventions; -import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem; -import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemSolution; -import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl; -import org.jetbrains.jet.lang.resolve.calls.inference.SolutionStatus; import org.jetbrains.jet.lexer.JetTokens; import java.util.*; @@ -241,6 +238,12 @@ public class CallResolver { @NotNull JetType expectedType, @NotNull final List> prioritizedTasks, // high to low priority @NotNull final JetReferenceExpression reference) { + + ResolutionDebugInfo.Data debugInfo = ResolutionDebugInfo.create(); + trace.record(ResolutionDebugInfo.RESOLUTION_DEBUG_INFO, call.getCallElement(), debugInfo); + + debugInfo.set(ResolutionDebugInfo.TASKS, prioritizedTasks); + TracingStrategy tracing = new TracingStrategy() { @Override public void bindReference(@NotNull BindingTrace trace, @NotNull ResolvedCallImpl resolvedCall) { @@ -371,6 +374,9 @@ public class CallResolver { OverloadResolutionResults results = performResolution(temporaryTrace, scope, expectedType, task, tracing); if (results.isSuccess()) { temporaryTrace.commit(); + + debugInfo.set(ResolutionDebugInfo.RESULT, results.getResult()); + return results.getResult(); } if (traceForFirstNonemptyCandidateSet == null && !task.getCandidates().isEmpty()) { @@ -381,6 +387,9 @@ public class CallResolver { if (traceForFirstNonemptyCandidateSet != null) { traceForFirstNonemptyCandidateSet.commit(); if (resultsForFirstNonemptyCandidateSet.singleDescriptor()) { + + debugInfo.set(ResolutionDebugInfo.RESULT, resultsForFirstNonemptyCandidateSet.getResult()); + return resultsForFirstNonemptyCandidateSet.getResult(); } } @@ -420,7 +429,10 @@ public class CallResolver { if (!candidate.getTypeParameters().isEmpty()) { // Type argument inference - ConstraintSystem constraintSystem = new ConstraintSystemImpl(); + ResolutionDebugInfo.Data debugInfo = trace.get(ResolutionDebugInfo.RESOLUTION_DEBUG_INFO, task.getCall().getCallElement()); + + ConstraintSystem constraintSystem = new ConstraintSystemImpl(new DebugConstraintResolutionListener(debugInfo)); + for (TypeParameterDescriptor typeParameterDescriptor : candidate.getTypeParameters()) { constraintSystem.registerTypeVariable(typeParameterDescriptor, Variance.INVARIANT); // TODO } 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 new file mode 100644 index 00000000000..59968bae9a4 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionDebugInfo.java @@ -0,0 +1,115 @@ +package org.jetbrains.jet.lang.resolve.calls; + +import com.intellij.openapi.application.Application; +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.ConstraintSystemSolution; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.util.slicedmap.*; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * @author abreslav + */ +public class ResolutionDebugInfo { + public static final WritableSlice>> TASKS = Slices.createSimpleSlice(); + 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 SOLUTION = Slices.createSimpleSlice(); + public static final WritableSlice> UNKNOWNS = Slices.createSimpleSlice(); + + static { + BasicWritableSlice.initSliceDebugNames(ResolutionDebugInfo.class); + } + + public static boolean RESOLUTION_DEBUG_INFO_ENABLED = false; + + public static boolean isResolutionDebugEnabled() { + Application application = ApplicationManager.getApplication(); + return (RESOLUTION_DEBUG_INFO_ENABLED || application.isInternal()) && !application.isUnitTestMode(); + } + + public static final Data NO_DEBUG_INFO = new AbstractData() { + + @Override + public String toString() { + return "NO_DEBUG_INFO"; + } + + @Override + public V getByKey(ReadOnlySlice slice, K key) { + return SlicedMap.DO_NOTHING.get(slice, key); + } + + @Override + public void putByKey(WritableSlice slice, K key, V value) { + } + }; + public static final WritableSlice RESOLUTION_DEBUG_INFO = new BasicWritableSlice(Slices.ONLY_REWRITE_TO_EQUAL) { + @Override + public boolean check(PsiElement key, Data value) { + return isResolutionDebugEnabled(); + } + + @Override + public Data computeValue(SlicedMap map, PsiElement key, Data value, boolean valueNotFound) { + if (valueNotFound) return NO_DEBUG_INFO; + return super.computeValue(map, key, value, valueNotFound); + } + }; + + public static Data create() { + return isResolutionDebugEnabled() ? new DataImpl() : NO_DEBUG_INFO; + } + + public enum One { KEY } + + public interface Data { + V getByKey(ReadOnlySlice slice, K key); + void putByKey(WritableSlice slice, K key, V value); + + void set(WritableSlice slice, V value); + V get(ReadOnlySlice slice); + } + + private static abstract class AbstractData implements Data { + @Override + public void set(WritableSlice slice, V value) { + putByKey(slice, One.KEY, value); + } + + @Override + public V get(ReadOnlySlice slice) { + return getByKey(slice, One.KEY); + } + } + + private static class DataImpl extends AbstractData { + private final MutableSlicedMap map = SlicedMapImpl.create(); + + @Override + public V getByKey(ReadOnlySlice slice, K key) { + return map.get(slice, key); + } + + @Override + public void putByKey(WritableSlice slice, K key, V value) { + map.put(slice, key, value); + } + } + + public static void println(Object message) { + if (isResolutionDebugEnabled()) { + System.out.println(message); + } + } +} 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 new file mode 100644 index 00000000000..783f6591e32 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintResolutionListener.java @@ -0,0 +1,19 @@ +package org.jetbrains.jet.lang.resolve.calls.inference; + +import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; +import org.jetbrains.jet.lang.types.JetType; + +import java.util.Set; + +/** + * @author abreslav + */ +public interface ConstraintResolutionListener { + + void constraintsForUnknown(TypeParameterDescriptor typeParameterDescriptor, ConstraintSystemImpl.TypeValue typeValue); + void constraintsForKnownType(JetType type, ConstraintSystemImpl.TypeValue typeValue); + void done(ConstraintSystemSolution solution, Set typeParameterDescriptors); + + void log(Object message); + void error(Object message); +} 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 fd274762a09..e6f8263432f 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 @@ -44,7 +44,6 @@ public class ConstraintSystemImpl implements ConstraintSystem { }); } - private static class LoopInTypeVariableConstraintsException extends RuntimeException { public LoopInTypeVariableConstraintsException() {} } @@ -80,6 +79,8 @@ public class ConstraintSystemImpl implements ConstraintSystem { public abstract boolean equate(TypeValue other); } +//========================================================================================================================================================== + private class UnknownType extends TypeValue { private final TypeParameterDescriptor typeParameterDescriptor; @@ -142,7 +143,7 @@ public class ConstraintSystemImpl implements ConstraintSystem { } } - println("minimal solution from lowerbounds for " + this + " is " + commonSupertype); + listener.log("minimal solution from lowerbounds for " + this + " is " + commonSupertype); value = new KnownType(commonSupertype); } else { @@ -238,16 +239,18 @@ public class ConstraintSystemImpl implements ConstraintSystem { private static final class TypeConstraintBuilderAdapter implements TypingConstraints { private final TypingConstraints delegate; + private final ConstraintResolutionListener listener; - private TypeConstraintBuilderAdapter(TypingConstraints delegate) { + private TypeConstraintBuilderAdapter(TypingConstraints delegate, ConstraintResolutionListener listener) { this.delegate = delegate; + this.listener = listener; } @Override public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b, TypeCheckingProcedure typeCheckingProcedure) { boolean result = delegate.assertEqualTypes(a, b, typeCheckingProcedure); if (!result) { - println("-- Failed to equate " + a + " and " + b); + listener.error("-- Failed to equate " + a + " and " + b); } return result; } @@ -256,7 +259,7 @@ public class ConstraintSystemImpl implements ConstraintSystem { public boolean assertEqualTypeConstructors(@NotNull TypeConstructor a, @NotNull TypeConstructor b) { boolean result = delegate.assertEqualTypeConstructors(a, b); if (!result) { - println("-- Type constructors are not equal: " + a + " and " + b); + listener.error("-- Type constructors are not equal: " + a + " and " + b); } return result; } @@ -265,7 +268,7 @@ public class ConstraintSystemImpl implements ConstraintSystem { public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype, TypeCheckingProcedure typeCheckingProcedure) { boolean result = delegate.assertSubtype(subtype, supertype, typeCheckingProcedure); if (!result) { - println("-- " + subtype + " can't be a subtype of " + supertype); + listener.error("-- " + subtype + " can't be a subtype of " + supertype); } return result; } @@ -274,60 +277,67 @@ public class ConstraintSystemImpl implements ConstraintSystem { public boolean noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) { boolean result = delegate.noCorrespondingSupertype(subtype, supertype); if (!result) { - println("-- " + subtype + " has no supertype corresponding to " + supertype); + listener.error("-- " + subtype + " has no supertype corresponding to " + supertype); } return result; } } - - private final TypeCheckingProcedure constraintExpander = new TypeCheckingProcedure(new TypeConstraintBuilderAdapter(new TypingConstraints() { - @Override - public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b, TypeCheckingProcedure typeCheckingProcedure) { - TypeValue aValue = getTypeValueFor(a); - TypeValue bValue = getTypeValueFor(b); - return aValue.equate(bValue); - } + private final TypeCheckingProcedure constraintExpander; + private final ConstraintResolutionListener listener; - @Override - public boolean assertEqualTypeConstructors(@NotNull TypeConstructor a, @NotNull TypeConstructor b) { - return a.equals(b) - || unknownTypes.containsKey(a.getDeclarationDescriptor()) - || unknownTypes.containsKey(b.getDeclarationDescriptor()); - } + public ConstraintSystemImpl(ConstraintResolutionListener listener) { + this.listener = listener; + this.constraintExpander = createConstraintExpander(); + } - @Override - public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype, TypeCheckingProcedure typeCheckingProcedure) { - TypeValue subtypeValue = getTypeValueFor(subtype); - TypeValue supertypeValue = getTypeValueFor(supertype); + private TypeCheckingProcedure createConstraintExpander() { + return new TypeCheckingProcedure(new TypeConstraintBuilderAdapter(new TypingConstraints() { + @Override + public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b, TypeCheckingProcedure typeCheckingProcedure) { + TypeValue aValue = getTypeValueFor(a); + TypeValue bValue = getTypeValueFor(b); - if (someUnknown(subtypeValue, supertypeValue)) { - addSubtypingConstraintOnTypeValues(subtypeValue, supertypeValue); + return aValue.equate(bValue); } - return true; - } - @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) { - addSubtypingConstraintOnTypeValues(subTypeValue, superTypeValue); + @Override + public boolean assertEqualTypeConstructors(@NotNull TypeConstructor a, @NotNull TypeConstructor b) { + return a.equals(b) + || unknownTypes.containsKey(a.getDeclarationDescriptor()) + || unknownTypes.containsKey(b.getDeclarationDescriptor()); } - return someUnknown; - } - private boolean someUnknown(TypeValue subtypeValue, TypeValue supertypeValue) { - return subtypeValue instanceof UnknownType || supertypeValue instanceof UnknownType; - } + @Override + public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype, TypeCheckingProcedure typeCheckingProcedure) { + TypeValue subtypeValue = getTypeValueFor(subtype); + TypeValue supertypeValue = getTypeValueFor(supertype); - })) - ; + if (someUnknown(subtypeValue, supertypeValue)) { + addSubtypingConstraintOnTypeValues(subtypeValue, supertypeValue); + } + return true; + } - public ConstraintSystemImpl() {} + @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) { + addSubtypingConstraintOnTypeValues(subTypeValue, superTypeValue); + } + return someUnknown; + } + + private boolean someUnknown(TypeValue subtypeValue, TypeValue supertypeValue) { + return subtypeValue instanceof UnknownType || supertypeValue instanceof UnknownType; + } + + }, listener)); + } @NotNull private TypeValue getTypeValueFor(@NotNull JetType type) { @@ -368,7 +378,7 @@ public class ConstraintSystemImpl implements ConstraintSystem { } private void mergeUnknowns(@NotNull UnknownType a, @NotNull UnknownType b) { - System.err.println("!!!mergeUnknowns() is not implemented!!!"); + listener.error("!!!mergeUnknowns() is not implemented!!!"); } @Override @@ -379,7 +389,7 @@ public class ConstraintSystemImpl implements ConstraintSystem { } private void addSubtypingConstraintOnTypeValues(TypeValue typeValueForLower, TypeValue typeValueForUpper) { - println(typeValueForLower + " :< " + typeValueForUpper); + listener.log("Constraint added: " + typeValueForLower + " :< " + typeValueForUpper); if (typeValueForLower != typeValueForUpper) { typeValueForLower.addUpperBound(typeValueForUpper); typeValueForUpper.addLowerBound(typeValueForLower); @@ -424,13 +434,11 @@ public class ConstraintSystemImpl implements ConstraintSystem { } for (UnknownType unknownType : unknownTypes.values()) { - println("Constraints for " + unknownType.getTypeParameterDescriptor()); - printTypeValue(unknownType); + listener.constraintsForUnknown(unknownType.getTypeParameterDescriptor(), unknownType); } for (KnownType knownType : knownTypes.values()) { - println("Constraints for " + knownType.getType()); - printTypeValue(knownType); + listener.constraintsForKnownType(knownType.getType(), knownType); } // Find inconsistencies @@ -447,22 +455,11 @@ public class ConstraintSystemImpl implements ConstraintSystem { // 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 - println("===================================="); - println(""); - println(""); + listener.done(solution, unknownTypes.keySet()); return solution; } - private void printTypeValue(TypeValue typeValue) { - for (TypeValue bound : typeValue.getUpperBounds()) { - println(" :< " + bound); - } - for (TypeValue bound : typeValue.getLowerBounds()) { - println(" :> " + bound); - } - } - private void check(TypeValue typeValue, Solution solution) { try { KnownType resultingValue = typeValue.getValue(); @@ -471,33 +468,20 @@ 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); - println("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); - println("Constraint violation: " + boundingType + " :< " + type); + listener.error("Constraint violation: " + boundingType + " :< " + type); } } } catch (LoopInTypeVariableConstraintsException e) { - println("-------------------------------------------------------------------"); - for (Map.Entry entry : unknownTypes.entrySet()) { - println("Unknown: " + entry.getKey()); - UnknownType unknownType = entry.getValue(); - println("Lower bounds: "); - for (TypeValue lowerBound : unknownType.getLowerBounds()) { - println(" " + lowerBound); - } - println("Upper bounds: "); - for (TypeValue lowerBound : unknownType.getUpperBounds()) { - println(" " + lowerBound); - } - } + listener.error("Loop detected"); solution.registerError("[TODO] Loop in constraints"); -// e.printStackTrace(); } } @@ -544,9 +528,14 @@ public class ConstraintSystemImpl implements ConstraintSystem { DeclarationDescriptor declarationDescriptor = key.getDeclarationDescriptor(); if (declarationDescriptor instanceof TypeParameterDescriptor) { TypeParameterDescriptor descriptor = (TypeParameterDescriptor) declarationDescriptor; + if (!unknownTypes.containsKey(descriptor)) return null; - println(descriptor + " |-> " + getValue(descriptor)); - return new TypeProjection(getValue(descriptor)); + + TypeProjection typeProjection = new TypeProjection(getValue(descriptor)); + + listener.log(descriptor + " |-> " + typeProjection); + + return typeProjection; } return null; } @@ -588,7 +577,4 @@ public class ConstraintSystemImpl implements ConstraintSystem { } - private static void println(String message) { -// System.out.println(message); - } } \ No newline at end of file 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 new file mode 100644 index 00000000000..b6e5731f7cf --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/DebugConstraintResolutionListener.java @@ -0,0 +1,75 @@ +package org.jetbrains.jet.lang.resolve.calls.inference; + +import com.google.common.collect.Maps; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; +import org.jetbrains.jet.lang.resolve.calls.ResolutionDebugInfo; +import org.jetbrains.jet.lang.types.JetType; + +import java.util.Map; +import java.util.Set; + +import static org.jetbrains.jet.lang.resolve.calls.ResolutionDebugInfo.*; + +/** + * @author abreslav + */ +public class DebugConstraintResolutionListener implements ConstraintResolutionListener { + + private final ResolutionDebugInfo.Data debugInfo; + + public DebugConstraintResolutionListener(@NotNull ResolutionDebugInfo.Data debugInfo) { + this.debugInfo = debugInfo; + } + + @Override + public void constraintsForUnknown(TypeParameterDescriptor typeParameterDescriptor, ConstraintSystemImpl.TypeValue typeValue) { + if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return; + Map map = debugInfo.get(BOUNDS_FOR_UNKNOWNS); + if (map == null) { + map = Maps.newLinkedHashMap(); + debugInfo.set(BOUNDS_FOR_UNKNOWNS, map); + } + map.put(typeParameterDescriptor, typeValue); + } + + @Override + public void constraintsForKnownType(JetType type, ConstraintSystemImpl.TypeValue typeValue) { + if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return; + Map map = debugInfo.get(BOUNDS_FOR_KNOWNS); + if (map == null) { + map = Maps.newLinkedHashMap(); + debugInfo.set(BOUNDS_FOR_KNOWNS, map); + } + map.put(type, typeValue); + } + + @Override + public void done(ConstraintSystemSolution solution, Set typeParameterDescriptors) { + if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return; + debugInfo.set(SOLUTION, solution); + debugInfo.set(UNKNOWNS, typeParameterDescriptors); + } + + @Override + public void log(Object message) { + if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return; + StringBuilder stringBuilder = debugInfo.get(LOG); + if (stringBuilder == null) { + stringBuilder = new StringBuilder(); + debugInfo.set(LOG, stringBuilder); + } + stringBuilder.append(message).append("\n"); + } + + @Override + public void error(Object message) { + if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return; + StringBuilder stringBuilder = debugInfo.get(ERRORS); + if (stringBuilder == null) { + stringBuilder = new StringBuilder(); + debugInfo.set(ERRORS, stringBuilder); + } + stringBuilder.append(message).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 new file mode 100644 index 00000000000..3fe1a169eeb --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/PrintingConstraintResolutionListener.java @@ -0,0 +1,55 @@ +package org.jetbrains.jet.lang.resolve.calls.inference; + +import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; +import org.jetbrains.jet.lang.resolve.calls.ResolutionDebugInfo; +import org.jetbrains.jet.lang.types.JetType; + +import java.util.Set; + +/** + * @author abreslav + */ +public class PrintingConstraintResolutionListener implements ConstraintResolutionListener { + + @Override + public void constraintsForUnknown(TypeParameterDescriptor typeParameterDescriptor, ConstraintSystemImpl.TypeValue typeValue) { + println("Constraints for " + typeParameterDescriptor); + printTypeValue(typeValue); + } + + @Override + public void constraintsForKnownType(JetType type, ConstraintSystemImpl.TypeValue typeValue) { + println("Constraints for " + type); + printTypeValue(typeValue); + } + + @Override + public void done(ConstraintSystemSolution solution, Set typeParameterDescriptors) { + println("=================================================="); + println(""); + println(""); + } + + @Override + public void log(Object message) { + println(message); + } + + @Override + public void error(Object message) { + println(message); + } + + private void printTypeValue(ConstraintSystemImpl.TypeValue typeValue) { + for (ConstraintSystemImpl.TypeValue bound : typeValue.getUpperBounds()) { + println(" :< " + bound); + } + for (ConstraintSystemImpl.TypeValue bound : typeValue.getLowerBounds()) { + println(" :> " + bound); + } + } + + private static void println(Object message) { + ResolutionDebugInfo.println(message); + } +} diff --git a/compiler/frontend/src/org/jetbrains/jet/util/slicedmap/SlicedMap.java b/compiler/frontend/src/org/jetbrains/jet/util/slicedmap/SlicedMap.java index 79e26176135..f06598d2cb3 100644 --- a/compiler/frontend/src/org/jetbrains/jet/util/slicedmap/SlicedMap.java +++ b/compiler/frontend/src/org/jetbrains/jet/util/slicedmap/SlicedMap.java @@ -1,12 +1,37 @@ package org.jetbrains.jet.util.slicedmap; import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; import java.util.Map; /** * @author abreslav */ public interface SlicedMap extends Iterable, ?>> { + + SlicedMap DO_NOTHING = new SlicedMap() { + @Override + public V get(ReadOnlySlice slice, K key) { + return slice.computeValue(this, key, null, true); + } + + @Override + public boolean containsKey(ReadOnlySlice slice, K key) { + return false; + } + + @Override + public Collection getKeys(WritableSlice slice) { + return Collections.emptySet(); + } + + @Override + public Iterator, ?>> iterator() { + return Collections., ?>>emptySet().iterator(); + } + }; + V get(ReadOnlySlice slice, K key); boolean containsKey(ReadOnlySlice slice, K key); 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 4e35628ae2b..94a47c390c7 100644 --- a/idea/src/org/jetbrains/jet/plugin/internal/resolvewindow/ResolveToolwindow.java +++ b/idea/src/org/jetbrains/jet/plugin/internal/resolvewindow/ResolveToolwindow.java @@ -21,27 +21,31 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtilCore; import com.intellij.ui.content.ContentFactory; import com.intellij.util.Alarm; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.JetElement; import org.jetbrains.jet.lang.psi.JetExpression; import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.psi.JetReferenceExpression; 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.ConstraintSystemImpl; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.plugin.JetFileType; import org.jetbrains.jet.plugin.compiler.WholeProjectAnalyzerFacade; import org.jetbrains.jet.plugin.internal.codewindow.BytecodeToolwindow; +import org.jetbrains.jet.util.slicedmap.ReadOnlySlice; +import org.jetbrains.jet.util.slicedmap.WritableSlice; import javax.swing.*; import java.awt.*; import java.util.Map; -import static org.jetbrains.jet.lang.resolve.BindingContext.EXPRESSION_TYPE; -import static org.jetbrains.jet.lang.resolve.BindingContext.REFERENCE_TARGET; -import static org.jetbrains.jet.lang.resolve.BindingContext.RESOLVED_CALL; +import static org.jetbrains.jet.lang.resolve.BindingContext.*; +import static org.jetbrains.jet.lang.resolve.calls.ResolutionDebugInfo.*; /* * @author abreslav @@ -121,19 +125,20 @@ public class ResolveToolwindow extends JPanel { PsiElement currentElement = elementAtOffset; boolean callFound = false; - while (currentElement != null && !(currentElement instanceof PsiFile)) { - if (currentElement instanceof JetElement) { - JetElement atOffset = (JetElement) currentElement; - ResolvedCall resolvedCall = bindingContext.get(RESOLVED_CALL, (JetElement) atOffset); - if (resolvedCall != null) { - setText(renderCall(resolvedCall) + "\n===\n" + currentElement + ": " + currentElement.getText()); - callFound = true; - break; - } - } - currentElement = currentElement.getParent(); + PsiElement elementWithDebugInfo = findData(bindingContext, currentElement, RESOLUTION_DEBUG_INFO); + if (elementWithDebugInfo != null) { + callFound = true; + setText(renderDebugInfo(elementWithDebugInfo, bindingContext.get(RESOLUTION_DEBUG_INFO, elementWithDebugInfo), null)); } + else { + PsiElement elementWithResolvedCall = findData(bindingContext, currentElement, (WritableSlice) RESOLVED_CALL); + if (elementWithResolvedCall instanceof JetElement) { + callFound = true; + setText(renderDebugInfo(elementWithResolvedCall, null, bindingContext.get(RESOLVED_CALL, (JetElement) elementWithResolvedCall))); + } + } + if (!callFound) { JetExpression parentExpression = (elementAtOffset instanceof JetExpression) ? (JetExpression) elementAtOffset @@ -153,8 +158,70 @@ public class ResolveToolwindow extends JPanel { } } - private String renderCall(ResolvedCall resolvedCall) { - StringBuilder builder = new StringBuilder(); + @Nullable + private PsiElement findData(BindingContext bindingContext, PsiElement currentElement, ReadOnlySlice slice) { + while (currentElement != null && !(currentElement instanceof PsiFile)) { + if (currentElement instanceof JetElement) { + JetElement atOffset = (JetElement) currentElement; + D data = bindingContext.get(slice, atOffset); + if (data != null && data != NO_DEBUG_INFO) { + return currentElement; + } + + } + currentElement = currentElement.getParent(); + } + return null; + } + + private String renderDebugInfo(PsiElement currentElement, @Nullable ResolutionDebugInfo.Data debugInfo, @Nullable ResolvedCall call) { + final String bar = "\n\n===\n\n"; + + StringBuilder result = new StringBuilder(); + + if (debugInfo != null) { + StringBuilder errors = debugInfo.get(ERRORS); + if (errors != null) { + result.append("Errors: \n").append(errors).append(bar); + } + + StringBuilder log = debugInfo.get(LOG); + if (log != null) { + result.append("Log: \n").append(log).append(bar); + } + + Map knowns = debugInfo.get(BOUNDS_FOR_KNOWNS); + renderMap(knowns, result); + Map unknowns = debugInfo.get(BOUNDS_FOR_UNKNOWNS); + renderMap(unknowns, result); + + result.append(bar); + + call = debugInfo.get(RESULT); + } + + renderCall(result, call); + result.append(currentElement + ": " + currentElement.getText()); + return result.toString(); + } + + private void renderMap(Map map, StringBuilder builder) { + if (map == null) return; + + for (Map.Entry entry : map.entrySet()) { + K key = entry.getKey(); + ConstraintSystemImpl.TypeValue typeValue = entry.getValue(); + builder.append("Bounds for ").append(key).append("\n"); + for (ConstraintSystemImpl.TypeValue bound : typeValue.getLowerBounds()) { + builder.append(" >: ").append(bound).append("\n"); + } + for (ConstraintSystemImpl.TypeValue bound : typeValue.getUpperBounds()) { + builder.append(" <: ").append(bound).append("\n"); + } + } + } + + private String renderCall(StringBuilder builder, ResolvedCall resolvedCall) { CallableDescriptor resultingDescriptor = resolvedCall.getResultingDescriptor(); ReceiverDescriptor receiverArgument = resolvedCall.getReceiverArgument();