Constraint priorities integrated
This commit is contained in:
committed by
Nikolay Krasko
parent
3cf7a44840
commit
0a2108d4d0
@@ -101,7 +101,7 @@ public class CallResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public FunctionDescriptor resolveSimpleCallToFunctionDescriptor(
|
private FunctionDescriptor resolveSimpleCallToFunctionDescriptor(
|
||||||
@NotNull BindingTrace trace,
|
@NotNull BindingTrace trace,
|
||||||
@NotNull JetScope scope,
|
@NotNull JetScope scope,
|
||||||
@NotNull final Call call,
|
@NotNull final Call call,
|
||||||
@@ -439,7 +439,7 @@ public class CallResolver {
|
|||||||
|
|
||||||
ResolutionDebugInfo.Data debugInfo = trace.get(ResolutionDebugInfo.RESOLUTION_DEBUG_INFO, task.getCall().getCallElement());
|
ResolutionDebugInfo.Data debugInfo = trace.get(ResolutionDebugInfo.RESOLUTION_DEBUG_INFO, task.getCall().getCallElement());
|
||||||
|
|
||||||
ConstraintSystem constraintSystem = new ConstraintSystemImpl(new DebugConstraintResolutionListener(debugInfo));
|
ConstraintSystem constraintSystem = new ConstraintSystemWithPriorities(new DebugConstraintResolutionListener(candidateCall, debugInfo));
|
||||||
|
|
||||||
// If the call is recursive, e.g.
|
// If the call is recursive, e.g.
|
||||||
// fun foo<T>(t : T) : T = foo(t)
|
// fun foo<T>(t : T) : T = foo(t)
|
||||||
@@ -472,7 +472,7 @@ public class CallResolver {
|
|||||||
TemporaryBindingTrace traceForUnknown = TemporaryBindingTrace.create(temporaryTrace);
|
TemporaryBindingTrace traceForUnknown = TemporaryBindingTrace.create(temporaryTrace);
|
||||||
ExpressionTypingServices temporaryServices = new ExpressionTypingServices(semanticServices, traceForUnknown);
|
ExpressionTypingServices temporaryServices = new ExpressionTypingServices(semanticServices, traceForUnknown);
|
||||||
JetType type = temporaryServices.getType(scope, expression, substituteDontCare.substitute(valueParameterDescriptor.getOutType(), Variance.INVARIANT));
|
JetType type = temporaryServices.getType(scope, expression, substituteDontCare.substitute(valueParameterDescriptor.getOutType(), Variance.INVARIANT));
|
||||||
if (type != null) {
|
if (type != null && !ErrorUtils.isErrorType(type)) {
|
||||||
constraintSystem.addSubtypingConstraint(VALUE_ARGUMENT.assertSubtyping(type, effectiveExpectedType));
|
constraintSystem.addSubtypingConstraint(VALUE_ARGUMENT.assertSubtyping(type, effectiveExpectedType));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -513,7 +513,16 @@ public class CallResolver {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
tracing.typeInferenceFailed(temporaryTrace, solution.getStatus());
|
tracing.typeInferenceFailed(temporaryTrace, solution.getStatus());
|
||||||
candidateCall.setStatus(checkAllValueArguments(scope, tracing, task, candidateCall));
|
// // Substitute DONT_CARE types to make further type checking as tolerant as possible
|
||||||
|
// D candidateWithDontCares = (D) candidate.substitute(TypeSubstitutor.makeConstantSubstitutor(candidate.getTypeParameters(), DONT_CARE));
|
||||||
|
// if (candidateWithDontCares == null) {
|
||||||
|
// candidateWithDontCares = (D) candidate.substitute(TypeSubstitutor.makeConstantSubstitutor(candidate.getTypeParameters(), Variance.INVARIANT, DONT_CARE));
|
||||||
|
// }
|
||||||
|
// if (!ErrorUtils.isErrorType(candidateWithDontCares.getReturnType())) {
|
||||||
|
// // Returning an error type provokes overload resolution ambiguities that mask errors
|
||||||
|
// candidateCall.setResultingDescriptor(candidateWithDontCares);
|
||||||
|
// }
|
||||||
|
candidateCall.setStatus(OTHER_ERROR.combine(checkAllValueArguments(scope, tracing, task, candidateCall)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -699,7 +708,7 @@ public class CallResolver {
|
|||||||
for (JetExpression argumentExpression : argumentExpressions) {
|
for (JetExpression argumentExpression : argumentExpressions) {
|
||||||
ExpressionTypingServices temporaryServices = new ExpressionTypingServices(semanticServices, candidateCall.getTrace());
|
ExpressionTypingServices temporaryServices = new ExpressionTypingServices(semanticServices, candidateCall.getTrace());
|
||||||
JetType type = temporaryServices.getType(scope, argumentExpression, parameterType, dataFlowInfo);
|
JetType type = temporaryServices.getType(scope, argumentExpression, parameterType, dataFlowInfo);
|
||||||
if (type == null) {
|
if (type == null || ErrorUtils.isErrorType(type)) {
|
||||||
candidateCall.argumentHasNoType();
|
candidateCall.argumentHasNoType();
|
||||||
}
|
}
|
||||||
else if (!semanticServices.getTypeChecker().isSubtypeOf(type, parameterType)) {
|
else if (!semanticServices.getTypeChecker().isSubtypeOf(type, parameterType)) {
|
||||||
|
|||||||
@@ -20,12 +20,13 @@ import java.util.Map;
|
|||||||
public class ResolutionDebugInfo {
|
public class ResolutionDebugInfo {
|
||||||
public static final WritableSlice<One, List<? extends ResolutionTask<? extends CallableDescriptor>>> TASKS = Slices.createSimpleSlice();
|
public static final WritableSlice<One, List<? extends ResolutionTask<? extends CallableDescriptor>>> TASKS = Slices.createSimpleSlice();
|
||||||
public static final WritableSlice<One, ResolvedCall<? extends CallableDescriptor>> RESULT = Slices.createSimpleSlice();
|
public static final WritableSlice<One, ResolvedCall<? extends CallableDescriptor>> RESULT = Slices.createSimpleSlice();
|
||||||
public static final WritableSlice<One, StringBuilder> ERRORS = Slices.createSimpleSlice();
|
|
||||||
public static final WritableSlice<One, StringBuilder> LOG = Slices.createSimpleSlice();
|
public static final WritableSlice<ResolvedCall<? extends CallableDescriptor>, StringBuilder> ERRORS = Slices.createSimpleSlice();
|
||||||
public static final WritableSlice<One, Map<TypeParameterDescriptor, BoundsOwner>> BOUNDS_FOR_UNKNOWNS = Slices.createSimpleSlice();
|
public static final WritableSlice<ResolvedCall<? extends CallableDescriptor>, StringBuilder> LOG = Slices.createSimpleSlice();
|
||||||
public static final WritableSlice<One, Map<JetType, BoundsOwner>> BOUNDS_FOR_KNOWNS = Slices.createSimpleSlice();
|
public static final WritableSlice<ResolvedCall<? extends CallableDescriptor>, Map<TypeParameterDescriptor, BoundsOwner>> BOUNDS_FOR_UNKNOWNS = Slices.createSimpleSlice();
|
||||||
public static final WritableSlice<One, ConstraintSystemSolution> SOLUTION = Slices.createSimpleSlice();
|
public static final WritableSlice<ResolvedCall<? extends CallableDescriptor>, Map<JetType, BoundsOwner>> BOUNDS_FOR_KNOWNS = Slices.createSimpleSlice();
|
||||||
public static final WritableSlice<One, Collection<TypeParameterDescriptor>> UNKNOWNS = Slices.createSimpleSlice();
|
public static final WritableSlice<ResolvedCall<? extends CallableDescriptor>, ConstraintSystemSolution> SOLUTION = Slices.createSimpleSlice();
|
||||||
|
public static final WritableSlice<ResolvedCall<? extends CallableDescriptor>, Collection<TypeParameterDescriptor>> UNKNOWNS = Slices.createSimpleSlice();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
BasicWritableSlice.initSliceDebugNames(ResolutionDebugInfo.class);
|
BasicWritableSlice.initSliceDebugNames(ResolutionDebugInfo.class);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import java.util.Collection;
|
|||||||
*
|
*
|
||||||
* @author abreslav
|
* @author abreslav
|
||||||
*/
|
*/
|
||||||
/*package*/ class ResolutionTask<D extends CallableDescriptor> {
|
public class ResolutionTask<D extends CallableDescriptor> {
|
||||||
private final Call call;
|
private final Call call;
|
||||||
private final Collection<ResolvedCallImpl<D>> candidates;
|
private final Collection<ResolvedCallImpl<D>> candidates;
|
||||||
private final DataFlowInfo dataFlowInfo;
|
private final DataFlowInfo dataFlowInfo;
|
||||||
|
|||||||
+126
-41
@@ -10,7 +10,10 @@ import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
|
|||||||
import org.jetbrains.jet.lang.types.checker.TypeCheckingProcedure;
|
import org.jetbrains.jet.lang.types.checker.TypeCheckingProcedure;
|
||||||
import org.jetbrains.jet.lang.types.checker.TypingConstraints;
|
import org.jetbrains.jet.lang.types.checker.TypingConstraints;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.Comparator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.PriorityQueue;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.jetbrains.jet.lang.resolve.calls.inference.ConstraintType.PARAMETER_BOUND;
|
import static org.jetbrains.jet.lang.resolve.calls.inference.ConstraintType.PARAMETER_BOUND;
|
||||||
|
|
||||||
@@ -200,7 +203,7 @@ public class ConstraintSystemWithPriorities implements ConstraintSystem {
|
|||||||
else {
|
else {
|
||||||
// One unknown and one known
|
// One unknown and one known
|
||||||
if (upper.isKnown()) {
|
if (upper.isKnown()) {
|
||||||
if (TypeUtils.canHaveSubtypes(typeChecker, upper.getType())) {
|
if (!TypeUtils.canHaveSubtypes(typeChecker, upper.getType())) {
|
||||||
// Upper bound is final -> we have to equate the lower bounds to it
|
// Upper bound is final -> we have to equate the lower bounds to it
|
||||||
return expandEqualityConstraint(lower, upper);
|
return expandEqualityConstraint(lower, upper);
|
||||||
}
|
}
|
||||||
@@ -225,17 +228,8 @@ public class ConstraintSystemWithPriorities implements ConstraintSystem {
|
|||||||
Solution solution = new Solution();
|
Solution solution = new Solution();
|
||||||
// At this point we only have type values, no bounds added for them, no values computed for unknown types
|
// 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
|
// After the parameters are inferred we will make sure the initial constraints are satisfied
|
||||||
for (Map.Entry<TypeParameterDescriptor, TypeValue> entry : Sets.newHashSet(unknownTypes.entrySet())) {
|
PriorityQueue<SubtypingConstraint> constraintsToEnsureAfterInference = new PriorityQueue<SubtypingConstraint>(constraintQueue);
|
||||||
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
|
// Expand and solve constraints
|
||||||
while (!constraintQueue.isEmpty()) {
|
while (!constraintQueue.isEmpty()) {
|
||||||
@@ -247,7 +241,7 @@ public class ConstraintSystemWithPriorities implements ConstraintSystem {
|
|||||||
boolean success = expandSubtypingConstraint(lower, upper);
|
boolean success = expandSubtypingConstraint(lower, upper);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
solution.registerError(constraint.getErrorMessage());
|
solution.registerError(constraint.getErrorMessage());
|
||||||
break;
|
// break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// (???) Propagate info
|
// (???) Propagate info
|
||||||
@@ -256,25 +250,6 @@ public class ConstraintSystemWithPriorities implements ConstraintSystem {
|
|||||||
if (unsolvedUnknowns.isEmpty()) break;
|
if (unsolvedUnknowns.isEmpty()) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now, let's check the rest of the constraints
|
|
||||||
|
|
||||||
// Expand custom bounds, e.g. List<T> <: List<Int>
|
|
||||||
// for (Map.Entry<JetType, TypeValue> 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
|
// effective bounds for each node
|
||||||
// Set<TypeValue> visited = Sets.newHashSet();
|
// Set<TypeValue> visited = Sets.newHashSet();
|
||||||
@@ -282,6 +257,16 @@ public class ConstraintSystemWithPriorities implements ConstraintSystem {
|
|||||||
// transitiveClosure(unknownType, visited);
|
// transitiveClosure(unknownType, visited);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
assert constraintQueue.isEmpty() || unsolvedUnknowns.isEmpty() : constraintQueue + " " + unsolvedUnknowns;
|
||||||
|
|
||||||
|
for (TypeValue unknown : unsolvedUnknowns) {
|
||||||
|
if (!computeValueFor(unknown)) {
|
||||||
|
listener.error("Not enough data to compute value for ", unknown);
|
||||||
|
solution.registerError("Not enough data to compute value for " + unknown + ". Please, specify type arguments explicitly");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logging
|
||||||
for (TypeValue unknownType : unknownTypes.values()) {
|
for (TypeValue unknownType : unknownTypes.values()) {
|
||||||
listener.constraintsForUnknown(unknownType.getTypeParameterDescriptor(), unknownType);
|
listener.constraintsForUnknown(unknownType.getTypeParameterDescriptor(), unknownType);
|
||||||
}
|
}
|
||||||
@@ -289,23 +274,117 @@ public class ConstraintSystemWithPriorities implements ConstraintSystem {
|
|||||||
listener.constraintsForKnownType(knownType.getType(), knownType);
|
listener.constraintsForKnownType(knownType.getType(), knownType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now, let's check the rest of the constraints and re-check the initial ones
|
||||||
|
|
||||||
|
// Add constraints for the declared bounds for parameters
|
||||||
|
// Maybe these bounds could reconcile some resolution earlier? Then, move them up
|
||||||
|
for (Map.Entry<TypeParameterDescriptor, TypeValue> entry : Sets.newHashSet(unknownTypes.entrySet())) {
|
||||||
|
TypeParameterDescriptor typeParameterDescriptor = entry.getKey();
|
||||||
|
TypeValue unknown = entry.getValue();
|
||||||
|
for (JetType upperBound : typeParameterDescriptor.getUpperBounds()) {
|
||||||
|
constraintsToEnsureAfterInference.add(PARAMETER_BOUND.assertSubtyping(unknown.getOriginalType(), getTypeValueFor(upperBound).getOriginalType()));
|
||||||
|
// unknown.addUpperBound(new TypeValue(upperBound));
|
||||||
|
}
|
||||||
|
for (JetType lowerBound : typeParameterDescriptor.getLowerBounds()) {
|
||||||
|
constraintsToEnsureAfterInference.add(PARAMETER_BOUND.assertSubtyping(getTypeValueFor(lowerBound).getOriginalType(), unknown.getOriginalType()));
|
||||||
|
// unknown.addLowerBound(new TypeValue(lowerBound));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Find inconsistencies
|
// Find inconsistencies
|
||||||
|
|
||||||
// Compute values and check that all bounds are respected by solutions:
|
// Check that all bounds are respected by solutions:
|
||||||
// we have set some of them from equality constraints with known types
|
// 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
|
// and thus the bounds may be violated if some of the constraints conflict
|
||||||
for (TypeValue unknownType : unknownTypes.values()) {
|
|
||||||
check(unknownType, solution);
|
|
||||||
}
|
for (SubtypingConstraint constraint : constraintsToEnsureAfterInference) {
|
||||||
for (TypeValue knownType : knownTypes.values()) {
|
JetType substitutedSubtype = solution.getSubstitutor().substitute(constraint.getSubtype(), Variance.INVARIANT); // TODO
|
||||||
check(knownType, solution);
|
if (substitutedSubtype == null) continue;
|
||||||
|
JetType substitutedSupertype = solution.getSubstitutor().substitute(constraint.getSupertype(), Variance.INVARIANT); // TODO
|
||||||
|
if (substitutedSupertype == null) continue;
|
||||||
|
|
||||||
|
if (!typeChecker.isSubtypeOf(substitutedSubtype, substitutedSupertype)) {
|
||||||
|
solution.registerError(constraint.getErrorMessage());
|
||||||
|
listener.error("Constraint violation: ", substitutedSubtype, " :< ", substitutedSupertype, " message: ", constraint.getErrorMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// for (TypeValue unknownType : unknownTypes.values()) {
|
||||||
|
// check(unknownType, solution);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for (TypeValue knownType : knownTypes.values()) {
|
||||||
|
// check(knownType, solution);
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
listener.done(solution, unknownTypes.keySet());
|
listener.done(solution, unknownTypes.keySet());
|
||||||
|
|
||||||
return solution;
|
return solution;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final Set<TypeValue> beingComputed = Sets.newHashSet();
|
||||||
|
|
||||||
|
public boolean computeValueFor(TypeValue unknown) {
|
||||||
|
assert !unknown.isKnown();
|
||||||
|
if (beingComputed.contains(unknown)) {
|
||||||
|
throw new LoopInTypeVariableConstraintsException();
|
||||||
|
}
|
||||||
|
if (!unknown.hasValue()) {
|
||||||
|
beingComputed.add(unknown);
|
||||||
|
try {
|
||||||
|
if (unknown.getPositionVariance() == Variance.IN_VARIANCE) {
|
||||||
|
// maximal solution
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// minimal solution
|
||||||
|
|
||||||
|
Set<TypeValue> lowerBounds = unknown.getLowerBounds();
|
||||||
|
Set<TypeValue> upperBounds = unknown.getUpperBounds();
|
||||||
|
if (!lowerBounds.isEmpty()) {
|
||||||
|
Set<JetType> types = getTypes(lowerBounds);
|
||||||
|
|
||||||
|
JetType commonSupertype = CommonSupertypes.commonSupertype(types);
|
||||||
|
for (TypeValue upperBound : upperBounds) {
|
||||||
|
if (!typeChecker.isSubtypeOf(commonSupertype, upperBound.getType())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.log("minimal solution from lower bounds for ", this, " is ", commonSupertype);
|
||||||
|
assignValueTo(unknown, commonSupertype);
|
||||||
|
}
|
||||||
|
else if (!upperBounds.isEmpty()) {
|
||||||
|
Set<JetType> types = getTypes(upperBounds);
|
||||||
|
JetType intersect = TypeUtils.intersect(typeChecker, types);
|
||||||
|
|
||||||
|
if (intersect == null) return false;
|
||||||
|
|
||||||
|
assignValueTo(unknown, intersect);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false; // No bounds to compute the value from
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
beingComputed.remove(unknown);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<JetType> getTypes(Set<TypeValue> lowerBounds) {
|
||||||
|
Set<JetType> types = Sets.newHashSet();
|
||||||
|
for (TypeValue lowerBound : lowerBounds) {
|
||||||
|
types.add(lowerBound.getType());
|
||||||
|
}
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
private void transitiveClosure(TypeValue current, Set<TypeValue> visited) {
|
private void transitiveClosure(TypeValue current, Set<TypeValue> visited) {
|
||||||
if (!visited.add(current)) {
|
if (!visited.add(current)) {
|
||||||
return;
|
return;
|
||||||
@@ -324,6 +403,7 @@ public class ConstraintSystemWithPriorities implements ConstraintSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void check(TypeValue typeValue, Solution solution) {
|
private void check(TypeValue typeValue, Solution solution) {
|
||||||
|
if (!typeValue.hasValue()) return;
|
||||||
try {
|
try {
|
||||||
JetType resultingType = typeValue.getType();
|
JetType resultingType = typeValue.getType();
|
||||||
JetType type = solution.getSubstitutor().substitute(resultingType, Variance.INVARIANT); // TODO
|
JetType type = solution.getSubstitutor().substitute(resultingType, Variance.INVARIANT); // TODO
|
||||||
@@ -377,7 +457,11 @@ public class ConstraintSystemWithPriorities implements ConstraintSystem {
|
|||||||
|
|
||||||
if (!unknownTypes.containsKey(descriptor)) return null;
|
if (!unknownTypes.containsKey(descriptor)) return null;
|
||||||
|
|
||||||
TypeProjection typeProjection = new TypeProjection(getValue(descriptor));
|
JetType value = getValue(descriptor);
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
TypeProjection typeProjection = new TypeProjection(value);
|
||||||
|
|
||||||
listener.log(descriptor, " |-> ", typeProjection);
|
listener.log(descriptor, " |-> ", typeProjection);
|
||||||
|
|
||||||
@@ -411,7 +495,8 @@ public class ConstraintSystemWithPriorities implements ConstraintSystem {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JetType getValue(TypeParameterDescriptor typeParameterDescriptor) {
|
public JetType getValue(TypeParameterDescriptor typeParameterDescriptor) {
|
||||||
return getTypeVariable(typeParameterDescriptor).getType();
|
TypeValue typeVariable = getTypeVariable(typeParameterDescriptor);
|
||||||
|
return typeVariable.hasValue() ? typeVariable.getType() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
+15
-11
@@ -2,8 +2,10 @@ package org.jetbrains.jet.lang.resolve.calls.inference;
|
|||||||
|
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
|
||||||
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
|
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
|
||||||
import org.jetbrains.jet.lang.resolve.calls.ResolutionDebugInfo;
|
import org.jetbrains.jet.lang.resolve.calls.ResolutionDebugInfo;
|
||||||
|
import org.jetbrains.jet.lang.resolve.calls.ResolvedCall;
|
||||||
import org.jetbrains.jet.lang.types.JetType;
|
import org.jetbrains.jet.lang.types.JetType;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -17,18 +19,20 @@ import static org.jetbrains.jet.lang.resolve.calls.ResolutionDebugInfo.*;
|
|||||||
public class DebugConstraintResolutionListener implements ConstraintResolutionListener {
|
public class DebugConstraintResolutionListener implements ConstraintResolutionListener {
|
||||||
|
|
||||||
private final ResolutionDebugInfo.Data debugInfo;
|
private final ResolutionDebugInfo.Data debugInfo;
|
||||||
|
private final ResolvedCall<? extends CallableDescriptor> candidateCall;
|
||||||
|
|
||||||
public DebugConstraintResolutionListener(@NotNull ResolutionDebugInfo.Data debugInfo) {
|
public DebugConstraintResolutionListener(@NotNull ResolvedCall<? extends CallableDescriptor> candidateCall, @NotNull ResolutionDebugInfo.Data debugInfo) {
|
||||||
this.debugInfo = debugInfo;
|
this.debugInfo = debugInfo;
|
||||||
|
this.candidateCall = candidateCall;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void constraintsForUnknown(TypeParameterDescriptor typeParameterDescriptor, BoundsOwner typeValue) {
|
public void constraintsForUnknown(TypeParameterDescriptor typeParameterDescriptor, BoundsOwner typeValue) {
|
||||||
if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return;
|
if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return;
|
||||||
Map<TypeParameterDescriptor, BoundsOwner> map = debugInfo.get(BOUNDS_FOR_UNKNOWNS);
|
Map<TypeParameterDescriptor, BoundsOwner> map = debugInfo.getByKey(BOUNDS_FOR_UNKNOWNS, candidateCall);
|
||||||
if (map == null) {
|
if (map == null) {
|
||||||
map = Maps.newLinkedHashMap();
|
map = Maps.newLinkedHashMap();
|
||||||
debugInfo.set(BOUNDS_FOR_UNKNOWNS, map);
|
debugInfo.putByKey(BOUNDS_FOR_UNKNOWNS, candidateCall, map);
|
||||||
}
|
}
|
||||||
map.put(typeParameterDescriptor, typeValue);
|
map.put(typeParameterDescriptor, typeValue);
|
||||||
}
|
}
|
||||||
@@ -36,10 +40,10 @@ public class DebugConstraintResolutionListener implements ConstraintResolutionLi
|
|||||||
@Override
|
@Override
|
||||||
public void constraintsForKnownType(JetType type, BoundsOwner typeValue) {
|
public void constraintsForKnownType(JetType type, BoundsOwner typeValue) {
|
||||||
if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return;
|
if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return;
|
||||||
Map<JetType,BoundsOwner> map = debugInfo.get(BOUNDS_FOR_KNOWNS);
|
Map<JetType,BoundsOwner> map = debugInfo.getByKey(BOUNDS_FOR_KNOWNS, candidateCall);
|
||||||
if (map == null) {
|
if (map == null) {
|
||||||
map = Maps.newLinkedHashMap();
|
map = Maps.newLinkedHashMap();
|
||||||
debugInfo.set(BOUNDS_FOR_KNOWNS, map);
|
debugInfo.putByKey(BOUNDS_FOR_KNOWNS, candidateCall, map);
|
||||||
}
|
}
|
||||||
map.put(type, typeValue);
|
map.put(type, typeValue);
|
||||||
}
|
}
|
||||||
@@ -47,17 +51,17 @@ public class DebugConstraintResolutionListener implements ConstraintResolutionLi
|
|||||||
@Override
|
@Override
|
||||||
public void done(ConstraintSystemSolution solution, Set<TypeParameterDescriptor> typeParameterDescriptors) {
|
public void done(ConstraintSystemSolution solution, Set<TypeParameterDescriptor> typeParameterDescriptors) {
|
||||||
if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return;
|
if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return;
|
||||||
debugInfo.set(SOLUTION, solution);
|
debugInfo.putByKey(SOLUTION, candidateCall, solution);
|
||||||
debugInfo.set(UNKNOWNS, typeParameterDescriptors);
|
debugInfo.putByKey(UNKNOWNS, candidateCall, typeParameterDescriptors);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void log(Object... messageFragments) {
|
public void log(Object... messageFragments) {
|
||||||
if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return;
|
if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return;
|
||||||
StringBuilder stringBuilder = debugInfo.get(LOG);
|
StringBuilder stringBuilder = debugInfo.getByKey(LOG, candidateCall);
|
||||||
if (stringBuilder == null) {
|
if (stringBuilder == null) {
|
||||||
stringBuilder = new StringBuilder();
|
stringBuilder = new StringBuilder();
|
||||||
debugInfo.set(LOG, stringBuilder);
|
debugInfo.putByKey(LOG, candidateCall, stringBuilder);
|
||||||
}
|
}
|
||||||
for (Object m : messageFragments) {
|
for (Object m : messageFragments) {
|
||||||
stringBuilder.append(m);
|
stringBuilder.append(m);
|
||||||
@@ -68,10 +72,10 @@ public class DebugConstraintResolutionListener implements ConstraintResolutionLi
|
|||||||
@Override
|
@Override
|
||||||
public void error(Object... messageFragments) {
|
public void error(Object... messageFragments) {
|
||||||
if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return;
|
if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return;
|
||||||
StringBuilder stringBuilder = debugInfo.get(ERRORS);
|
StringBuilder stringBuilder = debugInfo.getByKey(ERRORS, candidateCall);
|
||||||
if (stringBuilder == null) {
|
if (stringBuilder == null) {
|
||||||
stringBuilder = new StringBuilder();
|
stringBuilder = new StringBuilder();
|
||||||
debugInfo.set(ERRORS, stringBuilder);
|
debugInfo.putByKey(ERRORS, candidateCall, stringBuilder);
|
||||||
}
|
}
|
||||||
for (Object m : messageFragments) {
|
for (Object m : messageFragments) {
|
||||||
stringBuilder.append(m);
|
stringBuilder.append(m);
|
||||||
|
|||||||
+5
@@ -36,4 +36,9 @@ public class SubtypingConstraint {
|
|||||||
public String getErrorMessage() {
|
public String getErrorMessage() {
|
||||||
return type.makeErrorMessage(this);
|
return type.makeErrorMessage(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getSubtype() + " :< " + getSupertype();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ public class TypeValue implements BoundsOwner {
|
|||||||
this.positionVariance = null;
|
this.positionVariance = null;
|
||||||
this.typeParameterDescriptor = null;
|
this.typeParameterDescriptor = null;
|
||||||
this.originalType = knownType;
|
this.originalType = knownType;
|
||||||
|
this.value = knownType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isKnown() {
|
public boolean isKnown() {
|
||||||
@@ -42,6 +43,11 @@ public class TypeValue implements BoundsOwner {
|
|||||||
return typeParameterDescriptor;
|
return typeParameterDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public Variance getPositionVariance() {
|
||||||
|
return positionVariance;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NotNull
|
@NotNull
|
||||||
public Set<TypeValue> getUpperBounds() {
|
public Set<TypeValue> getUpperBounds() {
|
||||||
@@ -79,4 +85,9 @@ public class TypeValue implements BoundsOwner {
|
|||||||
public boolean hasValue() {
|
public boolean hasValue() {
|
||||||
return value != null;
|
return value != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return isKnown() ? getType().toString() : (getTypeParameterDescriptor() + (hasValue() ? " |-> " + getType() : ""));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class TypeSubstitutor {
|
|||||||
}
|
}
|
||||||
final TypeProjection projection = new TypeProjection(type);
|
final TypeProjection projection = new TypeProjection(type);
|
||||||
|
|
||||||
return create(new TypeSubstitution() {
|
return TypeSubstitutor.create(new TypeSubstitutor.TypeSubstitution() {
|
||||||
@Override
|
@Override
|
||||||
public TypeProjection get(TypeConstructor key) {
|
public TypeProjection get(TypeConstructor key) {
|
||||||
if (constructors.contains(key)) {
|
if (constructors.contains(key)) {
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ fun main(args : Array<String>) {
|
|||||||
|
|
||||||
val b = fooT2<Int>()(1)
|
val b = fooT2<Int>()(1)
|
||||||
b : Int
|
b : Int
|
||||||
fooT2()(1) // : Any?
|
<!TYPE_INFERENCE_FAILED!>fooT2()<!>(1) // : Any?
|
||||||
|
|
||||||
<!CALLEE_NOT_A_FUNCTION!>1<!>()
|
<!CALLEE_NOT_A_FUNCTION!>1<!>()
|
||||||
<!CALLEE_NOT_A_FUNCTION!>1<!>{}
|
<!CALLEE_NOT_A_FUNCTION!>1<!>{}
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ fun test(<!UNUSED_PARAMETER!>a<!> : annotation.RetentionPolicy) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun test() {
|
fun test() {
|
||||||
java.util.Collections.emptyList()
|
java.util.Collections.<!TYPE_INFERENCE_FAILED!>emptyList()<!>
|
||||||
|
val <!UNUSED_VARIABLE!>a<!> : java.util.Collection<String>? = java.util.Collections.emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test(<!UNUSED_PARAMETER!>a<!> : java.lang.Comparable<Int>) {
|
fun test(<!UNUSED_PARAMETER!>a<!> : java.lang.Comparable<Int>) {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ fun test(<!UNUSED_PARAMETER!>l<!> : java.util.List<Int>) {
|
|||||||
Collections.<!UNRESOLVED_REFERENCE!>emptyList<!>
|
Collections.<!UNRESOLVED_REFERENCE!>emptyList<!>
|
||||||
Collections.emptyList<Int>
|
Collections.emptyList<Int>
|
||||||
Collections.emptyList<Int>()
|
Collections.emptyList<Int>()
|
||||||
Collections.emptyList()
|
Collections.<!TYPE_INFERENCE_FAILED!>emptyList()<!>
|
||||||
|
|
||||||
Collections.singleton<Int>(1) : Set<Int>?
|
Collections.singleton<Int>(1) : Set<Int>?
|
||||||
Collections.singleton<Int>(<!ERROR_COMPILE_TIME_VALUE!>1.0<!>)
|
Collections.singleton<Int>(<!ERROR_COMPILE_TIME_VALUE!>1.0<!>)
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
fun fooT2<T>() : fun(t : T) : T {
|
||||||
|
return {it}
|
||||||
|
}
|
||||||
|
|
||||||
|
val n : Nothing = null.sure()
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
<!TYPE_INFERENCE_FAILED!>fooT2()<!>(1)
|
||||||
|
}
|
||||||
@@ -9,15 +9,15 @@ fun foo(a: A) {
|
|||||||
a.gen() //it works: Unit is derived
|
a.gen() //it works: Unit is derived
|
||||||
}
|
}
|
||||||
|
|
||||||
val <!UNUSED_VARIABLE!>u<!>: Unit = a.gen() //type mismatch, but Unit can be derived
|
val <!UNUSED_VARIABLE!>u<!>: Unit = a.gen() // Unit should be inferred
|
||||||
|
|
||||||
if (true) {
|
if (true) {
|
||||||
a.gen() //it works: Unit is derived
|
a.<!TYPE_INFERENCE_FAILED!>gen()<!> // Shouldn't work: no info for inference
|
||||||
}
|
}
|
||||||
|
|
||||||
val <!UNUSED_VARIABLE!>b<!> : fun() : Unit = {
|
val <!UNUSED_VARIABLE!>b<!> : fun() : Unit = {
|
||||||
if (true) {
|
if (true) {
|
||||||
a.gen() //type mismatch, but Unit can be derived
|
a.gen() // unit can be inferred
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
()
|
()
|
||||||
@@ -28,5 +28,5 @@ fun foo(a: A) {
|
|||||||
a.gen() //type mismatch, but Int can be derived
|
a.gen() //type mismatch, but Int can be derived
|
||||||
}
|
}
|
||||||
|
|
||||||
a.gen() //it works: Unit is derived
|
a.<!TYPE_INFERENCE_FAILED!>gen()<!> // Shouldn't work: no info for inference
|
||||||
}
|
}
|
||||||
@@ -24,7 +24,7 @@ public class JetNpeTest extends CodegenTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testNull () throws Exception {
|
public void testNull () throws Exception {
|
||||||
loadText("fun box() = if(null.sure() == 10) \"OK\" else \"fail\"");
|
loadText("fun box() = if((null : Int?).sure() == 10) \"OK\" else \"fail\"");
|
||||||
// System.out.println(generateToText());
|
// System.out.println(generateToText());
|
||||||
Method box = generateFunction("box");
|
Method box = generateFunction("box");
|
||||||
assertThrows(box, NullPointerException.class, null);
|
assertThrows(box, NullPointerException.class, null);
|
||||||
|
|||||||
@@ -28,11 +28,8 @@ import org.jetbrains.jet.lang.psi.JetExpression;
|
|||||||
import org.jetbrains.jet.lang.psi.JetFile;
|
import org.jetbrains.jet.lang.psi.JetFile;
|
||||||
import org.jetbrains.jet.lang.psi.JetReferenceExpression;
|
import org.jetbrains.jet.lang.psi.JetReferenceExpression;
|
||||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||||
import org.jetbrains.jet.lang.resolve.calls.ResolutionDebugInfo;
|
import org.jetbrains.jet.lang.resolve.calls.*;
|
||||||
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.BoundsOwner;
|
||||||
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl;
|
|
||||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
|
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
|
||||||
import org.jetbrains.jet.lang.types.JetType;
|
import org.jetbrains.jet.lang.types.JetType;
|
||||||
import org.jetbrains.jet.plugin.JetFileType;
|
import org.jetbrains.jet.plugin.JetFileType;
|
||||||
@@ -43,6 +40,7 @@ import org.jetbrains.jet.util.slicedmap.WritableSlice;
|
|||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.jetbrains.jet.lang.resolve.BindingContext.*;
|
import static org.jetbrains.jet.lang.resolve.BindingContext.*;
|
||||||
@@ -52,6 +50,9 @@ import static org.jetbrains.jet.lang.resolve.calls.ResolutionDebugInfo.*;
|
|||||||
* @author abreslav
|
* @author abreslav
|
||||||
*/
|
*/
|
||||||
public class ResolveToolwindow extends JPanel {
|
public class ResolveToolwindow extends JPanel {
|
||||||
|
|
||||||
|
public static final String BAR = "\n\n===\n\n";
|
||||||
|
|
||||||
public static class Factory implements ToolWindowFactory {
|
public static class Factory implements ToolWindowFactory {
|
||||||
@Override
|
@Override
|
||||||
public void createToolWindowContent(Project project, ToolWindow toolWindow) {
|
public void createToolWindowContent(Project project, ToolWindow toolWindow) {
|
||||||
@@ -176,36 +177,49 @@ public class ResolveToolwindow extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String renderDebugInfo(PsiElement currentElement, @Nullable ResolutionDebugInfo.Data debugInfo, @Nullable ResolvedCall<? extends CallableDescriptor> call) {
|
private String renderDebugInfo(PsiElement currentElement, @Nullable ResolutionDebugInfo.Data debugInfo, @Nullable ResolvedCall<? extends CallableDescriptor> call) {
|
||||||
final String bar = "\n\n===\n\n";
|
|
||||||
|
|
||||||
StringBuilder result = new StringBuilder();
|
StringBuilder result = new StringBuilder();
|
||||||
|
|
||||||
if (debugInfo != null) {
|
if (debugInfo != null) {
|
||||||
StringBuilder errors = debugInfo.get(ERRORS);
|
List<? extends ResolutionTask<? extends CallableDescriptor>> resolutionTasks = debugInfo.get(TASKS);
|
||||||
if (errors != null) {
|
for (ResolutionTask<? extends CallableDescriptor> resolutionTask : resolutionTasks) {
|
||||||
result.append("Errors: \n").append(errors).append(bar);
|
for (ResolvedCallImpl<? extends CallableDescriptor> resolvedCall : resolutionTask.getCandidates()) {
|
||||||
|
renderResolutionLogForCall(debugInfo, resolvedCall, result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder log = debugInfo.get(LOG);
|
|
||||||
if (log != null) {
|
|
||||||
result.append("Log: \n").append(log).append(bar);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<JetType, BoundsOwner> knowns = debugInfo.get(BOUNDS_FOR_KNOWNS);
|
|
||||||
renderMap(knowns, result);
|
|
||||||
Map<TypeParameterDescriptor, BoundsOwner> unknowns = debugInfo.get(BOUNDS_FOR_UNKNOWNS);
|
|
||||||
renderMap(unknowns, result);
|
|
||||||
|
|
||||||
result.append(bar);
|
|
||||||
|
|
||||||
call = debugInfo.get(RESULT);
|
call = debugInfo.get(RESULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderCall(result, call);
|
if (call != null) {
|
||||||
|
renderCall(result, call);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result.append("Resolved call is null\n");
|
||||||
|
}
|
||||||
result.append(currentElement + ": " + currentElement.getText());
|
result.append(currentElement + ": " + currentElement.getText());
|
||||||
return result.toString();
|
return result.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void renderResolutionLogForCall(Data debugInfo, ResolvedCallImpl<? extends CallableDescriptor> resolvedCall, StringBuilder result) {
|
||||||
|
result.append("Trying to call ").append(resolvedCall.getCandidateDescriptor()).append("\n");
|
||||||
|
StringBuilder errors = debugInfo.getByKey(ERRORS, resolvedCall);
|
||||||
|
if (errors != null) {
|
||||||
|
result.append("Errors: \n").append(errors).append(BAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder log = debugInfo.getByKey(LOG, resolvedCall);
|
||||||
|
if (log != null) {
|
||||||
|
result.append("Log: \n").append(log).append(BAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<JetType, BoundsOwner> knowns = debugInfo.getByKey(BOUNDS_FOR_KNOWNS, resolvedCall);
|
||||||
|
renderMap(knowns, result);
|
||||||
|
Map<TypeParameterDescriptor, BoundsOwner> unknowns = debugInfo.getByKey(BOUNDS_FOR_UNKNOWNS, resolvedCall);
|
||||||
|
renderMap(unknowns, result);
|
||||||
|
|
||||||
|
result.append(BAR);
|
||||||
|
}
|
||||||
|
|
||||||
private <K> void renderMap(Map<K, BoundsOwner> map, StringBuilder builder) {
|
private <K> void renderMap(Map<K, BoundsOwner> map, StringBuilder builder) {
|
||||||
if (map == null) return;
|
if (map == null) return;
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ fun test(<warning>l</warning> : java.util.List<Int>) {
|
|||||||
Collections.<error>emptyList</error>
|
Collections.<error>emptyList</error>
|
||||||
Collections.emptyList<Int>
|
Collections.emptyList<Int>
|
||||||
Collections.emptyList<Int>()
|
Collections.emptyList<Int>()
|
||||||
Collections.emptyList()
|
Collections.<error>emptyList()</error>
|
||||||
|
|
||||||
Collections.singleton<Int>(1) : Set<Int>?
|
Collections.singleton<Int>(1) : Set<Int>?
|
||||||
Collections.singleton<Int>(<error>1.0</error>)
|
Collections.singleton<Int>(<error>1.0</error>)
|
||||||
|
|||||||
Reference in New Issue
Block a user