diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java index ac9709bc744..3707180d809 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -354,7 +354,7 @@ public interface Errors { DiagnosticFactory1 TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 TYPE_INFERENCE_TYPE_CONSTRUCTOR_MISMATCH = DiagnosticFactory1.create(ERROR); - DiagnosticFactory1 TYPE_INFERENCE_UPPER_BOUND_VIOLATED = DiagnosticFactory1.create(ERROR); + DiagnosticFactory1 TYPE_INFERENCE_UPPER_BOUND_VIOLATED = DiagnosticFactory1.create(ERROR); DiagnosticFactory2 TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH = DiagnosticFactory2.create(ERROR); // Callable references diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/Renderers.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/Renderers.java index 918e743e8a6..0ec2114a188 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/Renderers.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/Renderers.java @@ -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 TYPE_INFERENCE_UPPER_BOUND_VIOLATED_RENDERER = - new Renderer() { + public static final Renderer TYPE_INFERENCE_UPPER_BOUND_VIOLATED_RENDERER = + new Renderer() { @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 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; } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java index ef4196b3802..8a9885f7d19 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java @@ -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 boolean updateSystemCheckingBounds( - @NotNull ResolvedCallImpl 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 JetType reportInferenceError( @NotNull CallCandidateResolutionContext context ) { ResolvedCallImpl 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); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TracingStrategy.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TracingStrategy.java index 4e152fcc247..49a0dfc6717 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TracingStrategy.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TracingStrategy.java @@ -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) {} }; void bindReference(@NotNull BindingTrace trace, @NotNull ResolvedCallWithTrace 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); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TracingStrategyImpl.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TracingStrategyImpl.java index 0ff64e4108d..9c254243b8a 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TracingStrategyImpl.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TracingStrategyImpl.java @@ -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)); - } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingUtils.java index a2f7488699f..088253f51a1 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingUtils.java @@ -477,12 +477,5 @@ public class ControlStructureTypingUtils { ) { throwError(); } - - @Override - public void upperBoundViolated( - @NotNull BindingTrace trace, @NotNull InferenceErrorData inferenceErrorData - ) { - throwError(); - } } } diff --git a/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintPosition.java b/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintPosition.java index e36aa2d30b5..86aec6c47b9 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintPosition.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintPosition.java @@ -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 valueParameterPositions = Maps.newHashMap(); + private static final Map 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 positions; + + public CompoundConstraintPosition(Collection 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 positions, final boolean strong) { + return ContainerUtil.exists(positions, new Condition() { + @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 diff --git a/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemImpl.java b/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemImpl.java index f9b3d689905..a30c386e7c7 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemImpl.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemImpl.java @@ -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 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 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.identity(), true); + return replaceTypeVariables(Functions.identity(), + new Function() { + @Override + public TypeConstraintsImpl apply(TypeConstraintsImpl typeConstraints) { + return typeConstraints.copy(); + } + }, + Conditions.alwaysTrue()); } @NotNull public ConstraintSystem replaceTypeVariables(@NotNull Function typeVariablesMap) { - return replaceTypeVariables(typeVariablesMap, false); + return replaceTypeVariables(typeVariablesMap, + Functions.identity(), + Conditions.alwaysTrue()); + } + + @NotNull + public ConstraintSystem filterConstrains(@NotNull final Condition condition) { + return replaceTypeVariables(Functions.identity(), + new Function() { + @Override + public TypeConstraintsImpl apply(TypeConstraintsImpl typeConstraints) { + return typeConstraints.filter(condition); + } + }, + condition); + } + + @NotNull + public ConstraintSystem getSystemWithoutWeakConstraints() { + return filterConstrains(new Condition() { + @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 typeVariablesMap, - boolean recreateTypeConstraints + @NotNull Function typeConstraintsMap, + @NotNull Condition constraintPositionCondition + ) { ConstraintSystemImpl newConstraintSystem = new ConstraintSystemImpl(); for (Map.Entry 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 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); + } } } } diff --git a/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemStatus.java b/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemStatus.java index 76a0cc6df62..1c87cf69b02 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemStatus.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemStatus.java @@ -43,6 +43,16 @@ public interface ConstraintSystemStatus { */ boolean hasConflictingConstraints(); + /** + * Returns true if contradiction of type constraints comes from declared bounds for type parameters. + * + * For example, for
fun <R: Any> foo(r: R) {}
in invocation foo(null) + * upper bounds Any for type parameter R is violated.

+ * + * It's the special case of 'hasConflictingConstraints' case. + */ + boolean hasViolatedUpperBound(); + /** * Returns true if there is no information for some registered type variable. * diff --git a/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/TypeConstraints.java b/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/TypeConstraints.java index 5a9bbff7000..45d93aa348f 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/TypeConstraints.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/TypeConstraints.java @@ -25,15 +25,6 @@ import java.util.Collection; import java.util.Set; public interface TypeConstraints { - @NotNull - Set getLowerBounds(); - - @NotNull - Set getUpperBounds(); - - @NotNull - Set getExactBounds(); - @NotNull Variance getVarianceOfPosition(); diff --git a/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/TypeConstraintsImpl.java b/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/TypeConstraintsImpl.java index fd8a1c27cc1..1c7c4421095 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/TypeConstraintsImpl.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/resolve/calls/inference/TypeConstraintsImpl.java @@ -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 upperBounds = Sets.newLinkedHashSet(); - private final Set lowerBounds = Sets.newLinkedHashSet(); - private final Set exactBounds = Sets.newLinkedHashSet(); + private final Set constraints = Sets.newLinkedHashSet(); private Collection 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 getLowerBounds() { - return lowerBounds; + public Collection getConstraints() { + return constraints; } @NotNull - @Override - public Set getUpperBounds() { - return upperBounds; + private static Set filterBounds( + @NotNull Collection constraints, + @NotNull BoundKind boundKind + ) { + return filterBounds(constraints, boundKind, null); } @NotNull - @Override - public Set getExactBounds() { - return exactBounds; + private static Set filterBounds( + @NotNull Collection constraints, + @NotNull BoundKind boundKind, + @Nullable Collection errorValues + ) { + Set 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> getAllBounds() { - Collection> 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 condition) { + TypeConstraintsImpl result = new TypeConstraintsImpl(typeVariable, varianceOfPosition); + result.constraints.addAll(ContainerUtil.filter(constraints, new Condition() { + @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 getValues() { if (resultValues == null) { - resultValues = computeValues(); + resultValues = computeValues(constraints); } return resultValues; } - private Collection computeValues() { + @NotNull + private static Collection computeValues(@NotNull Collection constraints) { Set values = Sets.newLinkedHashSet(); - if (isEmpty()) { - return values; + if (constraints.isEmpty()) { + return Collections.emptyList(); } - TypeConstraints withoutErrorTypes = filterNotContainingErrorType(values); - Collection exactBounds = withoutErrorTypes.getExactBounds(); + + Set 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> pair = - TypeUtils.filterNumberTypes(withoutErrorTypes.getLowerBounds()); + TypeUtils.filterNumberTypes(filterBounds(constraints, BoundKind.LOWER_BOUND, values)); Collection generalLowerBounds = pair.getFirst(); Collection numberLowerBounds = pair.getSecond(); JetType superTypeOfLowerBounds = CommonSupertypes.commonSupertypeForNonDenotableTypes(generalLowerBounds); - if (trySuggestion(superTypeOfLowerBounds)) { + if (trySuggestion(superTypeOfLowerBounds, constraints)) { return Collections.singleton(superTypeOfLowerBounds); } ContainerUtil.addIfNotNull(superTypeOfLowerBounds, values); - Collection upperBounds = withoutErrorTypes.getUpperBounds(); + Set 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 foo(t: T, consumer: Consumer): T //foo(1, c: Consumer) - 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 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 values - ) { - TypeConstraintsImpl typeConstraintsWithoutErrorType = new TypeConstraintsImpl(getVarianceOfPosition()); - Collection> allBounds = getAllBounds(); - for (Pair 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; - } } diff --git a/idea/src/org/jetbrains/jet/plugin/highlighter/IdeRenderers.java b/idea/src/org/jetbrains/jet/plugin/highlighter/IdeRenderers.java index 5fd65b7b40f..6aecf4fdf64 100644 --- a/idea/src/org/jetbrains/jet/plugin/highlighter/IdeRenderers.java +++ b/idea/src/org/jetbrains/jet/plugin/highlighter/IdeRenderers.java @@ -201,11 +201,11 @@ public class IdeRenderers { } }; - public static final Renderer HTML_TYPE_INFERENCE_UPPER_BOUND_VIOLATED_RENDERER = - new Renderer() { + public static final Renderer HTML_TYPE_INFERENCE_UPPER_BOUND_VIOLATED_RENDERER = + new Renderer() { @NotNull @Override - public String render(@NotNull InferenceErrorData inferenceErrorData) { + public String render(@NotNull ExtendedInferenceErrorData inferenceErrorData) { return renderUpperBoundViolatedInferenceError(inferenceErrorData, HtmlTabledDescriptorRenderer.create()).toString(); } }; diff --git a/idea/testData/diagnosticMessage/upperBoundViolated.kt b/idea/testData/diagnosticMessage/upperBoundViolated.kt index 82991ee56c4..19393027042 100644 --- a/idea/testData/diagnosticMessage/upperBoundViolated.kt +++ b/idea/testData/diagnosticMessage/upperBoundViolated.kt @@ -3,5 +3,24 @@ package i fun foo>(r: R, list: T) {} fun test1(i: Int, collection: Collection) { - foo(i, collection) -} \ No newline at end of file + foo(i, collection) //error +} + +//-------------- +fun bar(v: V, u: MutableSet) = u + +fun test2(a: Any, s: MutableSet) { + bar(a, s) //error +} + +//-------------- +trait A +class B + +fun baz(t: T, r: R) where T: A { + +} + +fun test3(a: A, b: B) { + baz(a, b) //error +} diff --git a/idea/testData/diagnosticMessage/upperBoundViolated2.html b/idea/testData/diagnosticMessage/upperBoundViolated2.html new file mode 100644 index 00000000000..a410c813008 --- /dev/null +++ b/idea/testData/diagnosticMessage/upperBoundViolated2.html @@ -0,0 +1,32 @@ + + +Type parameter bound for +V + in + + + + + + + + + +
+ + +fun + <V : U, U> + bar +( +v: V, +u: jet.MutableSet<U> +) +: jet.MutableSet<U>
+ is not satisfied: inferred type + +jet.Any + + is not a subtype of +jet.String + diff --git a/idea/testData/diagnosticMessage/upperBoundViolated3.html b/idea/testData/diagnosticMessage/upperBoundViolated3.html new file mode 100644 index 00000000000..bff5b9b62b2 --- /dev/null +++ b/idea/testData/diagnosticMessage/upperBoundViolated3.html @@ -0,0 +1,34 @@ + + +Type parameter bound for +T + in + + + + + + + + + +
+ + +fun + <T : R, R : i.B> + baz +( +t: T, +r: R +) +: jet.Unit +where + T : i.A
+ is not satisfied: inferred type + +i.A + + is not a subtype of +i.B + diff --git a/idea/tests/org/jetbrains/jet/plugin/highlighter/DiagnosticMessageTest.java b/idea/tests/org/jetbrains/jet/plugin/highlighter/DiagnosticMessageTest.java index 3a330edeb1b..3ff6e5a79a8 100644 --- a/idea/tests/org/jetbrains/jet/plugin/highlighter/DiagnosticMessageTest.java +++ b/idea/tests/org/jetbrains/jet/plugin/highlighter/DiagnosticMessageTest.java @@ -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 {