added declared bound constraints directly to constraint system

added status 'hasViolatedUpperBound'
class TypeConstraintsImpl now stores constraint position for each constraint,
so we can filter out bound constraints and find out if the system is successful without them
This commit is contained in:
Svetlana Isakova
2013-09-26 14:05:49 +04:00
parent ac33ccc0fe
commit 731efd0781
16 changed files with 387 additions and 205 deletions
@@ -354,7 +354,7 @@ public interface Errors {
DiagnosticFactory1<PsiElement, ExtendedInferenceErrorData> TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, ExtendedInferenceErrorData> TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, ExtendedInferenceErrorData> TYPE_INFERENCE_TYPE_CONSTRUCTOR_MISMATCH = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, InferenceErrorData> TYPE_INFERENCE_UPPER_BOUND_VIOLATED = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, ExtendedInferenceErrorData> TYPE_INFERENCE_UPPER_BOUND_VIOLATED = DiagnosticFactory1.create(ERROR);
DiagnosticFactory2<JetExpression, JetType, JetType> TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH = DiagnosticFactory2.create(ERROR);
// Callable references
@@ -28,10 +28,7 @@ import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.JetClass;
import org.jetbrains.jet.lang.psi.JetClassOrObject;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintPosition;
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintsUtil;
import org.jetbrains.jet.lang.resolve.calls.inference.InferenceErrorData;
import org.jetbrains.jet.lang.resolve.calls.inference.TypeConstraints;
import org.jetbrains.jet.lang.resolve.calls.inference.*;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeSubstitutor;
@@ -159,11 +156,11 @@ public class Renderers {
}
};
public static final Renderer<InferenceErrorData> TYPE_INFERENCE_UPPER_BOUND_VIOLATED_RENDERER =
new Renderer<InferenceErrorData>() {
public static final Renderer<ExtendedInferenceErrorData> TYPE_INFERENCE_UPPER_BOUND_VIOLATED_RENDERER =
new Renderer<ExtendedInferenceErrorData>() {
@NotNull
@Override
public String render(@NotNull InferenceErrorData inferenceErrorData) {
public String render(@NotNull ExtendedInferenceErrorData inferenceErrorData) {
return renderUpperBoundViolatedInferenceError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
}
};
@@ -265,32 +262,47 @@ public class Renderers {
.text("Please specify it explicitly."));
}
public static TabledDescriptorRenderer renderUpperBoundViolatedInferenceError(InferenceErrorData inferenceErrorData, TabledDescriptorRenderer result) {
@NotNull
public static TabledDescriptorRenderer renderUpperBoundViolatedInferenceError(ExtendedInferenceErrorData inferenceErrorData, TabledDescriptorRenderer result) {
String errorMessage = "Rendering 'upper bound violated' error for " + inferenceErrorData.descriptor;
TypeParameterDescriptor typeParameterDescriptor = null;
ConstraintSystemImpl constraintSystem = (ConstraintSystemImpl) inferenceErrorData.constraintSystem;
assert constraintSystem.getStatus().hasViolatedUpperBound();
ConstraintSystem systemWithoutWeakConstraints = constraintSystem.getSystemWithoutWeakConstraints();
for (TypeParameterDescriptor typeParameter : inferenceErrorData.descriptor.getTypeParameters()) {
if (!ConstraintsUtil.checkUpperBoundIsSatisfied(inferenceErrorData.constraintSystem, typeParameter, true)) {
if (!ConstraintsUtil.checkUpperBoundIsSatisfied(systemWithoutWeakConstraints, typeParameter, true)) {
typeParameterDescriptor = typeParameter;
break;
}
}
assert typeParameterDescriptor != null;
assert typeParameterDescriptor != null : errorMessage;
JetType inferredValueForTypeParameter = systemWithoutWeakConstraints.getTypeConstraints(typeParameterDescriptor).getValue();
assert inferredValueForTypeParameter != null : errorMessage;
result.text(newText().normal("Type parameter bound for ").strong(typeParameterDescriptor.getName()).normal(" in "))
.table(newTable().
descriptor(inferenceErrorData.descriptor));
JetType inferredValueForTypeParameter = inferenceErrorData.constraintSystem.getTypeConstraints(typeParameterDescriptor).getValue();
assert inferredValueForTypeParameter != null;
JetType upperBound = typeParameterDescriptor.getUpperBoundsAsType();
JetType upperBoundWithSubstitutedInferredTypes = inferenceErrorData.constraintSystem.getResultingSubstitutor().substitute(upperBound, Variance.INVARIANT);
assert upperBoundWithSubstitutedInferredTypes != null;
JetType violatedUpperBound = null;
for (JetType upperBound : typeParameterDescriptor.getUpperBounds()) {
JetType upperBoundWithSubstitutedInferredTypes =
systemWithoutWeakConstraints.getResultingSubstitutor().substitute(upperBound, Variance.INVARIANT);
if (upperBoundWithSubstitutedInferredTypes != null &&
!JetTypeChecker.INSTANCE.isSubtypeOf(inferredValueForTypeParameter, upperBoundWithSubstitutedInferredTypes)) {
violatedUpperBound = upperBoundWithSubstitutedInferredTypes;
break;
}
}
assert violatedUpperBound != null : errorMessage;
Renderer<JetType> typeRenderer = result.getTypeRenderer();
result.text(newText()
.normal(" is not satisfied: inferred type ")
.error(typeRenderer.render(inferredValueForTypeParameter))
.normal(" is not a subtype of ")
.strong(typeRenderer.render(upperBoundWithSubstitutedInferredTypes)));
.strong(typeRenderer.render(violatedUpperBound)));
return result;
}
@@ -232,19 +232,13 @@ public class CandidateResolver {
updateSystemWithConstraintSystemCompleter(context, resolvedCall);
if (resolvedCall.getConstraintSystem().getStatus().hasContradiction()) {
return reportInferenceError(context);
}
updateSystemIfExpectedTypeIsUnit(context, resolvedCall);
boolean boundsAreSatisfied = updateSystemCheckingBounds(resolvedCall);
((ConstraintSystemImpl)resolvedCall.getConstraintSystem()).processDeclaredBoundConstraints();
if (!resolvedCall.getConstraintSystem().getStatus().isSuccessful()) {
return reportInferenceError(context);
}
if (!boundsAreSatisfied) {
context.tracing.upperBoundViolated(context.trace, InferenceErrorData.create(
resolvedCall.getCandidateDescriptor(), resolvedCall.getConstraintSystem()));
}
resolvedCall.setResultingSubstitutor(resolvedCall.getConstraintSystem().getResultingSubstitutor());
completeNestedCallsInference(context);
@@ -307,30 +301,12 @@ public class CandidateResolver {
}
}
private static <D extends CallableDescriptor> boolean updateSystemCheckingBounds(
@NotNull ResolvedCallImpl<D> resolvedCall
) {
ConstraintSystem constraintSystem = resolvedCall.getConstraintSystem();
assert constraintSystem != null;
if (ConstraintsUtil.checkBoundsAreSatisfied(constraintSystem, /*substituteOtherTypeParametersInBounds=*/true)
&& !constraintSystem.getStatus().hasUnknownParameters()) {
return true;
}
ConstraintSystemImpl copy = (ConstraintSystemImpl) constraintSystem.copy();
copy.processDeclaredBoundConstraints();
if (copy.getStatus().isSuccessful() && ConstraintsUtil.checkBoundsAreSatisfied(copy, /*substituteOtherTypeParametersInBounds=*/true)) {
resolvedCall.setConstraintSystem(copy);
return true;
}
return false;
}
private <D extends CallableDescriptor> JetType reportInferenceError(
@NotNull CallCandidateResolutionContext<D> context
) {
ResolvedCallImpl<D> resolvedCall = context.candidateCall;
ConstraintSystem constraintSystem = resolvedCall.getConstraintSystem();
assert constraintSystem != null;
resolvedCall.setResultingSubstitutor(constraintSystem.getResultingSubstitutor());
completeNestedCallsInference(context);
@@ -471,7 +447,8 @@ public class CandidateResolver {
if (expression == null) return;
DataFlowInfo dataFlowInfoForValueArgument = context.candidateCall.getDataFlowInfoForArguments().getInfo(argument);
ResolutionContext<?> newContext = context.replaceExpectedType(context.expectedType).replaceDataFlowInfo(dataFlowInfoForValueArgument);
ResolutionContext<?> newContext = context.replaceExpectedType(context.expectedType).replaceDataFlowInfo(
dataFlowInfoForValueArgument);
DataFlowUtils.checkType(type, expression, newContext);
}
@@ -617,9 +594,8 @@ public class CandidateResolver {
// Solution
boolean hasContradiction = constraintSystem.getStatus().hasContradiction();
boolean boundsAreSatisfied = ConstraintsUtil.checkBoundsAreSatisfied(constraintSystem, /*substituteOtherTypeParametersInBounds=*/false);
candidateCall.setHasUnknownTypeParameters(true);
if (!hasContradiction && boundsAreSatisfied) {
if (!hasContradiction) {
return INCOMPLETE_TYPE_INFERENCE;
}
ValueArgumentsCheckingResult checkingResult = checkAllValueArguments(context, SKIP_FUNCTION_ARGUMENTS);
@@ -23,7 +23,6 @@ import org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.calls.inference.InferenceErrorData;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallWithTrace;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.jet.lang.types.JetType;
@@ -95,9 +94,6 @@ public interface TracingStrategy {
@Override
public void typeInferenceFailed(@NotNull BindingTrace trace, @NotNull ExtendedInferenceErrorData inferenceErrorData) {}
@Override
public void upperBoundViolated(@NotNull BindingTrace trace, @NotNull InferenceErrorData inferenceErrorData) {}
};
<D extends CallableDescriptor> void bindReference(@NotNull BindingTrace trace, @NotNull ResolvedCallWithTrace<D> resolvedCall);
@@ -140,6 +136,4 @@ public interface TracingStrategy {
void invisibleMember(@NotNull BindingTrace trace, @NotNull DeclarationDescriptorWithVisibility descriptor);
void typeInferenceFailed(@NotNull BindingTrace trace, @NotNull ExtendedInferenceErrorData inferenceErrorData);
void upperBoundViolated(@NotNull BindingTrace trace, @NotNull InferenceErrorData inferenceErrorData);
}
@@ -21,7 +21,6 @@ import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem;
@@ -233,6 +232,9 @@ public class TracingStrategyImpl implements TracingStrategy {
assert !noExpectedType(data.expectedType) : "Expected type doesn't exist, but there is an expected type mismatch error";
trace.report(TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH.on(reference, data.expectedType, substitutedReturnType));
}
else if (status.hasViolatedUpperBound()) {
trace.report(TYPE_INFERENCE_UPPER_BOUND_VIOLATED.on(reference, data));
}
else if (status.hasTypeConstructorMismatch()) {
trace.report(TYPE_INFERENCE_TYPE_CONSTRUCTOR_MISMATCH.on(reference, data));
}
@@ -244,9 +246,4 @@ public class TracingStrategyImpl implements TracingStrategy {
trace.report(TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER.on(reference, data));
}
}
@Override
public void upperBoundViolated(@NotNull BindingTrace trace, @NotNull InferenceErrorData inferenceErrorData) {
trace.report(Errors.TYPE_INFERENCE_UPPER_BOUND_VIOLATED.on(reference, inferenceErrorData));
}
}
@@ -477,12 +477,5 @@ public class ControlStructureTypingUtils {
) {
throwError();
}
@Override
public void upperBoundViolated(
@NotNull BindingTrace trace, @NotNull InferenceErrorData inferenceErrorData
) {
throwError();
}
}
}
@@ -16,32 +16,78 @@
package org.jetbrains.jet.lang.resolve.calls.inference;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.intellij.openapi.util.Condition;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Map;
public class ConstraintPosition {
public static final ConstraintPosition RECEIVER_POSITION = new ConstraintPosition("RECEIVER_POSITION");
public static final ConstraintPosition EXPECTED_TYPE_POSITION = new ConstraintPosition("EXPECTED_TYPE_POSITION");
public static final ConstraintPosition BOUND_CONSTRAINT_POSITION = new ConstraintPosition("BOUND_CONSTRAINT_POSITION");
public static final ConstraintPosition FROM_COMPLETER = new ConstraintPosition("FROM_COMPLETER");
public static final ConstraintPosition SPECIAL = new ConstraintPosition("SPECIAL");
public static final ConstraintPosition RECEIVER_POSITION = new ConstraintPosition("RECEIVER_POSITION", true);
public static final ConstraintPosition EXPECTED_TYPE_POSITION = new ConstraintPosition("EXPECTED_TYPE_POSITION", true);
public static final ConstraintPosition FROM_COMPLETER = new ConstraintPosition("FROM_COMPLETER", true);
public static final ConstraintPosition SPECIAL = new ConstraintPosition("SPECIAL", true);
private static final Map<Integer, ConstraintPosition> valueParameterPositions = Maps.newHashMap();
private static final Map<Integer, ConstraintPosition> typeBoundPositions = Maps.newHashMap();
public static ConstraintPosition getValueParameterPosition(int index) {
ConstraintPosition position = valueParameterPositions.get(index);
if (position == null) {
position = new ConstraintPosition("VALUE_PARAMETER_POSITION(" + index + ")");
position = new ConstraintPosition("VALUE_PARAMETER_POSITION(" + index + ")", true);
valueParameterPositions.put(index, position);
}
return position;
}
private final String debugName;
public static ConstraintPosition getTypeBoundPosition(int index) {
ConstraintPosition position = typeBoundPositions.get(index);
if (position == null) {
position = new ConstraintPosition("TYPE_BOUND_POSITION(" + index + ")", false);
typeBoundPositions.put(index, position);
}
return position;
}
private ConstraintPosition(String name) {
public static class CompoundConstraintPosition extends ConstraintPosition {
private final Collection<ConstraintPosition> positions;
public CompoundConstraintPosition(Collection<ConstraintPosition> positions) {
super("COMPOUND_CONSTRAINT_POSITION", hasConstraint(positions, /*strong=*/true));
this.positions = positions;
}
public boolean consistsOfOnlyStrongConstraints() {
return !hasConstraint(positions, /*strong=*/false);
}
private static boolean hasConstraint(@NotNull Collection<ConstraintPosition> positions, final boolean strong) {
return ContainerUtil.exists(positions, new Condition<ConstraintPosition>() {
@Override
public boolean value(ConstraintPosition constraintPosition) {
return constraintPosition.isStrong() == strong;
}
});
}
}
public static ConstraintPosition getCompoundConstraintPosition(ConstraintPosition... positions) {
return new CompoundConstraintPosition(Lists.newArrayList(positions));
}
private final String debugName;
private final boolean isStrong;
private ConstraintPosition(String name, boolean isStrong) {
debugName = name;
this.isStrong = isStrong;
}
public boolean isStrong() {
return isStrong;
}
@Override
@@ -21,6 +21,9 @@ import com.google.common.base.Functions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Conditions;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
@@ -31,6 +34,7 @@ import org.jetbrains.jet.lang.types.checker.TypeCheckingProcedure;
import org.jetbrains.jet.lang.types.checker.TypingConstraints;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -39,7 +43,9 @@ import static org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImp
import static org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.SUB_TYPE;
import static org.jetbrains.jet.lang.resolve.calls.inference.TypeConstraintsImpl.BoundKind;
import static org.jetbrains.jet.lang.resolve.calls.inference.TypeConstraintsImpl.BoundKind.*;
import static org.jetbrains.jet.lang.types.TypeUtils.*;
import static org.jetbrains.jet.lang.resolve.calls.inference.TypeConstraintsImpl.Constraint;
import static org.jetbrains.jet.lang.types.TypeUtils.CANT_INFER_TYPE_PARAMETER;
import static org.jetbrains.jet.lang.types.TypeUtils.DONT_CARE;
public class ConstraintSystemImpl implements ConstraintSystem {
@@ -67,10 +73,16 @@ public class ConstraintSystemImpl implements ConstraintSystem {
return hasTypeConstructorMismatch() || hasConflictingConstraints();
}
@Override
public boolean hasViolatedUpperBound() {
if (isSuccessful()) return false;
ConstraintSystem systemWithoutWeakConstraints = getSystemWithoutWeakConstraints();
return systemWithoutWeakConstraints.getStatus().isSuccessful();
}
@Override
public boolean hasConflictingConstraints() {
for (TypeParameterDescriptor typeParameter : typeParameterConstraints.keySet()) {
TypeConstraints typeConstraints = getTypeConstraints(typeParameter);
for (TypeConstraintsImpl typeConstraints : typeParameterConstraints.values()) {
if (typeConstraints.getValues().size() > 1) return true;
}
return false;
@@ -78,8 +90,8 @@ public class ConstraintSystemImpl implements ConstraintSystem {
@Override
public boolean hasUnknownParameters() {
for (TypeConstraintsImpl constraints : typeParameterConstraints.values()) {
if (constraints.isEmpty()) {
for (TypeConstraintsImpl typeConstraints : typeParameterConstraints.values()) {
if (typeConstraints.isEmpty()) {
return true;
}
}
@@ -167,25 +179,78 @@ public class ConstraintSystemImpl implements ConstraintSystem {
for (Map.Entry<TypeParameterDescriptor, Variance> entry : typeVariables.entrySet()) {
TypeParameterDescriptor typeVariable = entry.getKey();
Variance positionVariance = entry.getValue();
typeParameterConstraints.put(typeVariable, new TypeConstraintsImpl(positionVariance));
typeParameterConstraints.put(typeVariable, new TypeConstraintsImpl(typeVariable, positionVariance));
}
TypeSubstitutor constantSubstitutor = TypeUtils.makeConstantSubstitutor(typeParameterConstraints.keySet(), DONT_CARE);
for (Map.Entry<TypeParameterDescriptor, TypeConstraintsImpl> entry : typeParameterConstraints.entrySet()) {
TypeParameterDescriptor typeVariable = entry.getKey();
TypeConstraintsImpl typeConstraints = entry.getValue();
for (JetType declaredUpperBound : typeVariable.getUpperBounds()) {
if (KotlinBuiltIns.getInstance().getNullableAnyType().equals(declaredUpperBound)) continue; //todo remove this line (?)
JetType substitutedBound = constantSubstitutor.substitute(declaredUpperBound, Variance.INVARIANT);
if (substitutedBound != null) {
typeConstraints.addConstraint(UPPER_BOUND, substitutedBound, ConstraintPosition.getTypeBoundPosition(typeVariable.getIndex()));
}
}
}
}
@Override
@NotNull
public ConstraintSystem copy() {
return replaceTypeVariables(Functions.<TypeParameterDescriptor>identity(), true);
return replaceTypeVariables(Functions.<TypeParameterDescriptor>identity(),
new Function<TypeConstraintsImpl, TypeConstraintsImpl>() {
@Override
public TypeConstraintsImpl apply(TypeConstraintsImpl typeConstraints) {
return typeConstraints.copy();
}
},
Conditions.<ConstraintPosition>alwaysTrue());
}
@NotNull
public ConstraintSystem replaceTypeVariables(@NotNull Function<TypeParameterDescriptor, TypeParameterDescriptor> typeVariablesMap) {
return replaceTypeVariables(typeVariablesMap, false);
return replaceTypeVariables(typeVariablesMap,
Functions.<TypeConstraintsImpl>identity(),
Conditions.<ConstraintPosition>alwaysTrue());
}
@NotNull
public ConstraintSystem filterConstrains(@NotNull final Condition<ConstraintPosition> condition) {
return replaceTypeVariables(Functions.<TypeParameterDescriptor>identity(),
new Function<TypeConstraintsImpl, TypeConstraintsImpl>() {
@Override
public TypeConstraintsImpl apply(TypeConstraintsImpl typeConstraints) {
return typeConstraints.filter(condition);
}
},
condition);
}
@NotNull
public ConstraintSystem getSystemWithoutWeakConstraints() {
return filterConstrains(new Condition<ConstraintPosition>() {
@Override
public boolean value(ConstraintPosition constraintPosition) {
// 'isStrong' for compound means 'has some strong constraints'
// but for testing absence of weak constraints we need 'has only strong constraints' here
if (constraintPosition instanceof ConstraintPosition.CompoundConstraintPosition) {
ConstraintPosition.CompoundConstraintPosition position =
(ConstraintPosition.CompoundConstraintPosition) constraintPosition;
return position.consistsOfOnlyStrongConstraints();
}
return constraintPosition.isStrong();
}
});
}
@NotNull
private ConstraintSystem replaceTypeVariables(
@NotNull Function<TypeParameterDescriptor, TypeParameterDescriptor> typeVariablesMap,
boolean recreateTypeConstraints
@NotNull Function<TypeConstraintsImpl, TypeConstraintsImpl> typeConstraintsMap,
@NotNull Condition<ConstraintPosition> constraintPositionCondition
) {
ConstraintSystemImpl newConstraintSystem = new ConstraintSystemImpl();
for (Map.Entry<TypeParameterDescriptor, TypeConstraintsImpl> entry : typeParameterConstraints.entrySet()) {
@@ -194,9 +259,9 @@ public class ConstraintSystemImpl implements ConstraintSystem {
TypeParameterDescriptor newTypeParameter = typeVariablesMap.apply(typeParameter);
assert newTypeParameter != null;
newConstraintSystem.typeParameterConstraints.put(newTypeParameter, recreateTypeConstraints ? typeConstraints.copy() : typeConstraints);
newConstraintSystem.typeParameterConstraints.put(newTypeParameter, typeConstraintsMap.apply(typeConstraints));
}
newConstraintSystem.errorConstraintPositions.addAll(errorConstraintPositions);
newConstraintSystem.errorConstraintPositions.addAll(ContainerUtil.filter(errorConstraintPositions, constraintPositionCondition));
newConstraintSystem.hasErrorInConstrainingTypes = hasErrorInConstrainingTypes;
return newConstraintSystem;
}
@@ -267,7 +332,7 @@ public class ConstraintSystemImpl implements ConstraintSystem {
}
private boolean isErrorOrSpecialType(@Nullable JetType type) {
if (type == TypeUtils.DONT_CARE || type == TypeUtils.CANT_INFER_TYPE_PARAMETER) {
if (type == DONT_CARE || type == CANT_INFER_TYPE_PARAMETER) {
return true;
}
@@ -308,7 +373,7 @@ public class ConstraintSystemImpl implements ConstraintSystem {
// can be considered as extension function if one is expected
// (special type constructor for function/ extension function should be introduced like PLACEHOLDER_FUNCTION_TYPE)
if (constraintKind == SUB_TYPE && kotlinBuiltIns.isFunctionType(subType) && kotlinBuiltIns.isExtensionFunctionType(superType)) {
subType = createCorrespondingExtensionFunctionType(subType, TypeUtils.DONT_CARE);
subType = createCorrespondingExtensionFunctionType(subType, DONT_CARE);
}
// can be equal for the recursive invocations:
@@ -320,11 +385,11 @@ public class ConstraintSystemImpl implements ConstraintSystem {
if (isMyTypeVariable(subType)) {
generateTypeParameterConstraint(subType, superType, constraintKind == SUB_TYPE ? UPPER_BOUND : EXACT_BOUND);
generateTypeParameterConstraint(subType, superType, constraintKind == SUB_TYPE ? UPPER_BOUND : EXACT_BOUND, constraintPosition);
return;
}
if (isMyTypeVariable(superType)) {
generateTypeParameterConstraint(superType, subType, constraintKind == SUB_TYPE ? LOWER_BOUND : EXACT_BOUND);
generateTypeParameterConstraint(superType, subType, constraintKind == SUB_TYPE ? LOWER_BOUND : EXACT_BOUND, constraintPosition);
return;
}
// if superType is nullable and subType is not nullable, unsafe call error will be generated later,
@@ -335,13 +400,14 @@ public class ConstraintSystemImpl implements ConstraintSystem {
private void generateTypeParameterConstraint(
@NotNull JetType parameterType,
@NotNull JetType constrainingType,
@NotNull BoundKind boundKind
@NotNull BoundKind boundKind,
@NotNull ConstraintPosition constraintPosition
) {
TypeConstraintsImpl typeConstraints = getTypeConstraints(parameterType);
assert typeConstraints != null : "constraint should be generated only for type variables";
if (!parameterType.isNullable() || !constrainingType.isNullable()) {
typeConstraints.addBound(boundKind, constrainingType);
typeConstraints.addConstraint(boundKind, constrainingType, constraintPosition);
return;
}
// For parameter type T:
@@ -349,11 +415,11 @@ public class ConstraintSystemImpl implements ConstraintSystem {
// constraint T? >: Int? should transform to T >: Int
JetType notNullConstrainingType = TypeUtils.makeNotNullable(constrainingType);
if (boundKind == EXACT_BOUND || boundKind == LOWER_BOUND) {
typeConstraints.addBound(LOWER_BOUND, notNullConstrainingType);
typeConstraints.addConstraint(LOWER_BOUND, notNullConstrainingType, constraintPosition);
}
// constraint T? <: Int? should transform to T <: Int?
if (boundKind == EXACT_BOUND || boundKind == UPPER_BOUND) {
typeConstraints.addBound(UPPER_BOUND, constrainingType);
typeConstraints.addConstraint(UPPER_BOUND, constrainingType, constraintPosition);
}
}
@@ -363,8 +429,13 @@ public class ConstraintSystemImpl implements ConstraintSystem {
TypeConstraintsImpl typeConstraints = entry.getValue();
for (JetType declaredUpperBound : typeParameterDescriptor.getUpperBounds()) {
//todo order matters here
for (JetType lowerOrExactBound : Sets.union(typeConstraints.getLowerBounds(), typeConstraints.getExactBounds())) {
addSubtypeConstraint(lowerOrExactBound, declaredUpperBound, ConstraintPosition.BOUND_CONSTRAINT_POSITION);
Collection<Constraint> constraints = Lists.newArrayList(typeConstraints.getConstraints());
for (Constraint constraint : constraints) {
if (constraint.boundKind == LOWER_BOUND || constraint.boundKind == EXACT_BOUND) {
ConstraintPosition position = ConstraintPosition.getCompoundConstraintPosition(
ConstraintPosition.getTypeBoundPosition(typeParameterDescriptor.getIndex()), constraint.constraintPosition);
addSubtypeConstraint(constraint.type, declaredUpperBound, position);
}
}
}
}
@@ -43,6 +43,16 @@ public interface ConstraintSystemStatus {
*/
boolean hasConflictingConstraints();
/**
* Returns <tt>true</tt> if contradiction of type constraints comes from declared bounds for type parameters.
*
* For example, for <pre>fun &lt;R: Any&gt; foo(r: R) {}</pre> in invocation <tt>foo(null)</tt>
* upper bounds <tt>Any</tt> for type parameter <tt>R</tt> is violated. <p/>
*
* It's the special case of 'hasConflictingConstraints' case.
*/
boolean hasViolatedUpperBound();
/**
* Returns <tt>true</tt> if there is no information for some registered type variable.
*
@@ -25,15 +25,6 @@ import java.util.Collection;
import java.util.Set;
public interface TypeConstraints {
@NotNull
Set<JetType> getLowerBounds();
@NotNull
Set<JetType> getUpperBounds();
@NotNull
Set<JetType> getExactBounds();
@NotNull
Variance getVarianceOfPosition();
@@ -18,10 +18,12 @@ package org.jetbrains.jet.lang.resolve.calls.inference;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Pair;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
@@ -30,14 +32,33 @@ import java.util.Collections;
import java.util.Set;
public class TypeConstraintsImpl implements TypeConstraints {
public static enum BoundKind {
LOWER_BOUND, UPPER_BOUND, EXACT_BOUND
}
public static class Constraint {
public final JetType type;
public final BoundKind boundKind;
public final ConstraintPosition constraintPosition;
public Constraint(@NotNull JetType type, @NotNull BoundKind boundKind, @NotNull ConstraintPosition constraintPosition) {
this.type = type;
this.boundKind = boundKind;
this.constraintPosition = constraintPosition;
}
}
private final TypeParameterDescriptor typeVariable;
private final Variance varianceOfPosition;
private final Set<JetType> upperBounds = Sets.newLinkedHashSet();
private final Set<JetType> lowerBounds = Sets.newLinkedHashSet();
private final Set<JetType> exactBounds = Sets.newLinkedHashSet();
private final Set<Constraint> constraints = Sets.newLinkedHashSet();
private Collection<JetType> resultValues;
public TypeConstraintsImpl(Variance varianceOfPosition) {
public TypeConstraintsImpl(
@NotNull TypeParameterDescriptor typeVariable,
@NotNull Variance varianceOfPosition
) {
this.typeVariable = typeVariable;
this.varianceOfPosition = varianceOfPosition;
}
@@ -47,67 +68,65 @@ public class TypeConstraintsImpl implements TypeConstraints {
return varianceOfPosition;
}
public void addBound(@NotNull BoundKind boundKind, @NotNull JetType type) {
public void addConstraint(@NotNull BoundKind boundKind, @NotNull JetType type, @NotNull ConstraintPosition constraintPosition) {
resultValues = null;
switch (boundKind) {
case LOWER_BOUND:
lowerBounds.add(type);
break;
case UPPER_BOUND:
upperBounds.add(type);
break;
case EXACT_BOUND:
exactBounds.add(type);
}
constraints.add(new Constraint(type, boundKind, constraintPosition));
}
@Override
public boolean isEmpty() {
return upperBounds.isEmpty() && lowerBounds.isEmpty() && exactBounds.isEmpty();
return getValues().isEmpty();
}
@NotNull
@Override
public Set<JetType> getLowerBounds() {
return lowerBounds;
public Collection<Constraint> getConstraints() {
return constraints;
}
@NotNull
@Override
public Set<JetType> getUpperBounds() {
return upperBounds;
private static Set<JetType> filterBounds(
@NotNull Collection<Constraint> constraints,
@NotNull BoundKind boundKind
) {
return filterBounds(constraints, boundKind, null);
}
@NotNull
@Override
public Set<JetType> getExactBounds() {
return exactBounds;
private static Set<JetType> filterBounds(
@NotNull Collection<Constraint> constraints,
@NotNull BoundKind boundKind,
@Nullable Collection<JetType> errorValues
) {
Set<JetType> result = Sets.newLinkedHashSet();
for (Constraint constraint : constraints) {
if (constraint.boundKind == boundKind) {
if (!ErrorUtils.containsErrorType(constraint.type)) {
result.add(constraint.type);
}
else if (errorValues != null) {
errorValues.add(constraint.type);
}
}
}
return result;
}
/*package*/ TypeConstraintsImpl copy() {
TypeConstraintsImpl typeConstraints = new TypeConstraintsImpl(varianceOfPosition);
typeConstraints.upperBounds.addAll(upperBounds);
typeConstraints.lowerBounds.addAll(lowerBounds);
typeConstraints.exactBounds.addAll(exactBounds);
TypeConstraintsImpl typeConstraints = new TypeConstraintsImpl(typeVariable, varianceOfPosition);
typeConstraints.constraints.addAll(constraints);
typeConstraints.resultValues = resultValues;
return typeConstraints;
}
public static enum BoundKind {
LOWER_BOUND, UPPER_BOUND, EXACT_BOUND
}
private Collection<Pair<BoundKind, JetType>> getAllBounds() {
Collection<Pair<BoundKind, JetType>> result = Lists.newArrayList();
for (JetType exactBound : exactBounds) {
result.add(Pair.create(BoundKind.EXACT_BOUND, exactBound));
}
for (JetType exactBound : upperBounds) {
result.add(Pair.create(BoundKind.UPPER_BOUND, exactBound));
}
for (JetType exactBound : lowerBounds) {
result.add(Pair.create(BoundKind.LOWER_BOUND, exactBound));
}
@NotNull
public TypeConstraintsImpl filter(@NotNull final Condition<ConstraintPosition> condition) {
TypeConstraintsImpl result = new TypeConstraintsImpl(typeVariable, varianceOfPosition);
result.constraints.addAll(ContainerUtil.filter(constraints, new Condition<Constraint>() {
@Override
public boolean value(Constraint constraint) {
return condition.value(constraint.constraintPosition);
}
}));
return result;
}
@@ -119,47 +138,47 @@ public class TypeConstraintsImpl implements TypeConstraints {
return values.iterator().next();
}
return null;
}
@NotNull
@Override
public Collection<JetType> getValues() {
if (resultValues == null) {
resultValues = computeValues();
resultValues = computeValues(constraints);
}
return resultValues;
}
private Collection<JetType> computeValues() {
@NotNull
private static Collection<JetType> computeValues(@NotNull Collection<Constraint> constraints) {
Set<JetType> values = Sets.newLinkedHashSet();
if (isEmpty()) {
return values;
if (constraints.isEmpty()) {
return Collections.emptyList();
}
TypeConstraints withoutErrorTypes = filterNotContainingErrorType(values);
Collection<JetType> exactBounds = withoutErrorTypes.getExactBounds();
Set<JetType> exactBounds = filterBounds(constraints, BoundKind.EXACT_BOUND, values);
if (exactBounds.size() == 1) {
JetType exactBound = exactBounds.iterator().next();
if (trySuggestion(exactBound)) {
if (trySuggestion(exactBound, constraints)) {
return Collections.singleton(exactBound);
}
}
values.addAll(exactBounds);
Pair<Collection<JetType>, Collection<JetType>> pair =
TypeUtils.filterNumberTypes(withoutErrorTypes.getLowerBounds());
TypeUtils.filterNumberTypes(filterBounds(constraints, BoundKind.LOWER_BOUND, values));
Collection<JetType> generalLowerBounds = pair.getFirst();
Collection<JetType> numberLowerBounds = pair.getSecond();
JetType superTypeOfLowerBounds = CommonSupertypes.commonSupertypeForNonDenotableTypes(generalLowerBounds);
if (trySuggestion(superTypeOfLowerBounds)) {
if (trySuggestion(superTypeOfLowerBounds, constraints)) {
return Collections.singleton(superTypeOfLowerBounds);
}
ContainerUtil.addIfNotNull(superTypeOfLowerBounds, values);
Collection<JetType> upperBounds = withoutErrorTypes.getUpperBounds();
Set<JetType> upperBounds = filterBounds(constraints, BoundKind.UPPER_BOUND, values);
for (JetType upperBound : upperBounds) {
if (trySuggestion(upperBound)) {
if (trySuggestion(upperBound, constraints)) {
return Collections.singleton(upperBound);
}
}
@@ -167,10 +186,10 @@ public class TypeConstraintsImpl implements TypeConstraints {
//fun <T> foo(t: T, consumer: Consumer<T>): T
//foo(1, c: Consumer<Any>) - infer Int, not Any here
values.addAll(withoutErrorTypes.getUpperBounds());
values.addAll(filterBounds(constraints, BoundKind.UPPER_BOUND));
JetType superTypeOfNumberLowerBounds = TypeUtils.commonSupertypeForNumberTypes(numberLowerBounds);
if (trySuggestion(superTypeOfNumberLowerBounds)) {
if (trySuggestion(superTypeOfNumberLowerBounds, constraints)) {
return Collections.singleton(superTypeOfNumberLowerBounds);
}
ContainerUtil.addIfNotNull(superTypeOfNumberLowerBounds, values);
@@ -178,54 +197,42 @@ public class TypeConstraintsImpl implements TypeConstraints {
if (superTypeOfLowerBounds != null && superTypeOfNumberLowerBounds != null) {
JetType superTypeOfAllLowerBounds = CommonSupertypes.commonSupertypeForNonDenotableTypes(
Lists.newArrayList(superTypeOfLowerBounds, superTypeOfNumberLowerBounds));
if (trySuggestion(superTypeOfAllLowerBounds)) {
if (trySuggestion(superTypeOfAllLowerBounds, constraints)) {
return Collections.singleton(superTypeOfAllLowerBounds);
}
}
return values;
}
private boolean trySuggestion(
@Nullable JetType suggestion
private static boolean trySuggestion(
@Nullable JetType suggestion,
@NotNull Collection<Constraint> constraints
) {
if (suggestion == null) return false;
if (!suggestion.getConstructor().isDenotable()) return false;
if (getExactBounds().size() > 1) return false;
if (filterBounds(constraints, BoundKind.EXACT_BOUND).size() > 1) return false;
for (JetType exactBound : getExactBounds()) {
if (!JetTypeChecker.INSTANCE.equalTypes(exactBound, suggestion)) {
return false;
}
}
for (JetType lowerBound : getLowerBounds()) {
if (!JetTypeChecker.INSTANCE.isSubtypeOf(lowerBound, suggestion)) {
return false;
}
}
for (JetType upperBound : getUpperBounds()) {
if (!JetTypeChecker.INSTANCE.isSubtypeOf(suggestion, upperBound)) {
return false;
for (Constraint constraint : constraints) {
switch (constraint.boundKind) {
case LOWER_BOUND:
if (!JetTypeChecker.INSTANCE.isSubtypeOf(constraint.type, suggestion)) {
return false;
}
break;
case UPPER_BOUND:
if (!JetTypeChecker.INSTANCE.isSubtypeOf(suggestion, constraint.type)) {
return false;
}
break;
case EXACT_BOUND:
if (!JetTypeChecker.INSTANCE.equalTypes(constraint.type, suggestion)) {
return false;
}
break;
}
}
return true;
}
@NotNull
private TypeConstraints filterNotContainingErrorType(
@NotNull Collection<JetType> values
) {
TypeConstraintsImpl typeConstraintsWithoutErrorType = new TypeConstraintsImpl(getVarianceOfPosition());
Collection<Pair<TypeConstraintsImpl.BoundKind, JetType>> allBounds = getAllBounds();
for (Pair<TypeConstraintsImpl.BoundKind, JetType> pair : allBounds) {
TypeConstraintsImpl.BoundKind boundKind = pair.getFirst();
JetType type = pair.getSecond();
if (ErrorUtils.containsErrorType(type)) {
values.add(type);
}
else if (type != null) {
typeConstraintsWithoutErrorType.addBound(boundKind, type);
}
}
return typeConstraintsWithoutErrorType;
}
}
@@ -201,11 +201,11 @@ public class IdeRenderers {
}
};
public static final Renderer<InferenceErrorData> HTML_TYPE_INFERENCE_UPPER_BOUND_VIOLATED_RENDERER =
new Renderer<InferenceErrorData>() {
public static final Renderer<ExtendedInferenceErrorData> HTML_TYPE_INFERENCE_UPPER_BOUND_VIOLATED_RENDERER =
new Renderer<ExtendedInferenceErrorData>() {
@NotNull
@Override
public String render(@NotNull InferenceErrorData inferenceErrorData) {
public String render(@NotNull ExtendedInferenceErrorData inferenceErrorData) {
return renderUpperBoundViolatedInferenceError(inferenceErrorData, HtmlTabledDescriptorRenderer.create()).toString();
}
};
@@ -3,5 +3,24 @@ package i
fun foo<R, T: List<R>>(r: R, list: T) {}
fun test1(i: Int, collection: Collection<Int>) {
foo(i, collection)
}
foo(i, collection) //error
}
//--------------
fun bar<V : U, U>(v: V, u: MutableSet<U>) = u
fun test2(a: Any, s: MutableSet<String>) {
bar(a, s) //error
}
//--------------
trait A
class B
fun baz<T: R, R: B>(t: T, r: R) where T: A {
}
fun test3(a: A, b: B) {
baz(a, b) //error
}
@@ -0,0 +1,32 @@
<!-- upperBoundViolated2 -->
<html>
Type parameter bound for <b>
V</b>
in <table>
<tr>
<td width="10%">
</td>
<td align="right" colspan="2" style="white-space:nowrap;font-weight:bold;">
<b>
fun</b>
&lt;V : U, U>
bar</td>
<td style="white-space:nowrap;font-weight:bold;">
(</td>
<td align="right" style="white-space:nowrap;font-weight:bold;">
v: V,</td>
<td align="right" style="white-space:nowrap;font-weight:bold;">
u: jet.MutableSet&lt;U&gt;</td>
<td style="white-space:nowrap;font-weight:bold;">
)</td>
<td style="white-space:nowrap;font-weight:bold;">
: jet.MutableSet&lt;U&gt;</td>
</tr>
</table>
is not satisfied: inferred type <font color=red>
<b>
jet.Any</b>
</font>
is not a subtype of <b>
jet.String</b>
</html>
@@ -0,0 +1,34 @@
<!-- upperBoundViolated3 -->
<html>
Type parameter bound for <b>
T</b>
in <table>
<tr>
<td width="10%">
</td>
<td align="right" colspan="2" style="white-space:nowrap;font-weight:bold;">
<b>
fun</b>
&lt;T : R, R : i.B>
baz</td>
<td style="white-space:nowrap;font-weight:bold;">
(</td>
<td align="right" style="white-space:nowrap;font-weight:bold;">
t: T,</td>
<td align="right" style="white-space:nowrap;font-weight:bold;">
r: R</td>
<td style="white-space:nowrap;font-weight:bold;">
)</td>
<td style="white-space:nowrap;font-weight:bold;">
: jet.Unit <b>
where</b>
T : i.A</td>
</tr>
</table>
is not satisfied: inferred type <font color=red>
<b>
i.A</b>
</font>
is not a subtype of <b>
i.B</b>
</html>
@@ -103,7 +103,7 @@ public class DiagnosticMessageTest extends JetLiteFixture {
}
public void testUpperBoundViolated() throws Exception {
doTest("upperBoundViolated", 1, Errors.TYPE_INFERENCE_UPPER_BOUND_VIOLATED);
doTest("upperBoundViolated", 3, Errors.TYPE_INFERENCE_UPPER_BOUND_VIOLATED);
}
public void testTypeMismatchWithNothing() throws Exception {