use declared upper bounds in type inference (not just check them)

- simple cases supported
#KT-2856 fixed
This commit is contained in:
Svetlana Isakova
2013-02-07 20:29:37 +04:00
parent 677bc55056
commit 8f59172f27
9 changed files with 105 additions and 33 deletions
@@ -266,7 +266,7 @@ public class Renderers {
public static TabledDescriptorRenderer renderUpperBoundViolatedInferenceError(InferenceErrorData inferenceErrorData, TabledDescriptorRenderer result) {
TypeParameterDescriptor typeParameterDescriptor = null;
for (TypeParameterDescriptor typeParameter : inferenceErrorData.descriptor.getTypeParameters()) {
if (!ConstraintsUtil.checkUpperBoundIsSatisfied(inferenceErrorData.constraintSystem, typeParameter)) {
if (!ConstraintsUtil.checkUpperBoundIsSatisfied(inferenceErrorData.constraintSystem, typeParameter, true)) {
typeParameterDescriptor = typeParameter;
break;
}
@@ -41,10 +41,7 @@ import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.jet.lang.types.*;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import static org.jetbrains.jet.lang.diagnostics.Errors.PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT;
import static org.jetbrains.jet.lang.diagnostics.Errors.SUPER_IS_NOT_AN_EXPRESSION;
@@ -238,11 +235,23 @@ public class CandidateResolver {
return;
}
boolean boundsAreSatisfied = ConstraintsUtil.checkBoundsAreSatisfied(constraintSystem, /*substituteOtherTypeParametersInBounds=*/true);
if (!boundsAreSatisfied) {
ConstraintSystemImpl copy = (ConstraintSystemImpl) constraintSystem.copy();
copy.processDeclaredBoundConstraints();
boundsAreSatisfied = copy.isSuccessful() && ConstraintsUtil.checkBoundsAreSatisfied(copy, /*substituteOtherTypeParametersInBounds=*/true);
if (boundsAreSatisfied) {
constraintSystem = copy;
}
}
if (!boundsAreSatisfied) {
context.tracing.upperBoundViolated(resolvedCall.getTrace(), InferenceErrorData.create(resolvedCall.getCandidateDescriptor(), constraintSystem));
}
resolvedCall.setResultingSubstitutor(constraintSystem.getResultingSubstitutor());
// Here we type check the arguments with inferred types expected
checkAllValueArguments(context, RESOLVE_FUNCTION_ARGUMENTS);
checkBounds(resolvedCall, constraintSystem, resolvedCall.getTrace(), context.tracing);
resolvedCall.setHasUnknownTypeParameters(false);
if (resolvedCall.getStatus() == ResolutionStatus.UNKNOWN_STATUS) {
resolvedCall.addStatus(ResolutionStatus.SUCCESS);
@@ -319,7 +328,7 @@ public class CandidateResolver {
// Solution
boolean hasContradiction = constraintSystem.hasContradiction();
boolean boundsAreSatisfied = ConstraintsUtil.checkBoundsAreSatisfied(constraintSystem);
boolean boundsAreSatisfied = ConstraintsUtil.checkBoundsAreSatisfied(constraintSystem, /*substituteOtherTypeParametersInBounds=*/false);
if (!hasContradiction && boundsAreSatisfied) {
candidateCall.setHasUnknownTypeParameters(true);
return SUCCESS;
@@ -526,19 +535,6 @@ public class CandidateResolver {
}
}
private static <D extends CallableDescriptor> void checkBounds(
@NotNull ResolvedCallImpl<D> call,
@NotNull ConstraintSystem constraintSystem,
@NotNull BindingTrace trace,
@NotNull TracingStrategy tracing
) {
for (TypeParameterDescriptor typeParameter : call.getCandidateDescriptor().getTypeParameters()) {
if (!ConstraintsUtil.checkUpperBoundIsSatisfied(constraintSystem, typeParameter)) {
tracing.upperBoundViolated(trace, InferenceErrorData.create(call.getCandidateDescriptor(), constraintSystem));
}
}
}
private static void checkGenericBoundsInAFunctionCall(
@NotNull List<JetTypeProjection> jetTypeArguments,
@NotNull List<JetType> typeArguments,
@@ -23,6 +23,7 @@ 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");
private static final Map<Integer, ConstraintPosition> valueParameterPositions = Maps.newHashMap();
@@ -32,9 +32,7 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import java.util.*;
import static org.jetbrains.jet.lang.resolve.calls.CallResolverUtil.CANT_INFER;
import static org.jetbrains.jet.lang.resolve.calls.CallResolverUtil.DONT_CARE;
import static org.jetbrains.jet.lang.resolve.calls.CallResolverUtil.PLACEHOLDER_FUNCTION_TYPE;
import static org.jetbrains.jet.lang.resolve.calls.CallResolverUtil.*;
import static org.jetbrains.jet.lang.resolve.calls.inference.TypeConstraintsImpl.ConstraintKind.*;
import static org.jetbrains.jet.lang.types.Variance.*;
@@ -348,6 +346,19 @@ public class ConstraintSystemImpl implements ConstraintSystem {
return SUPER_TYPE;
}
public void processDeclaredBoundConstraints() {
for (Map.Entry<TypeParameterDescriptor, TypeConstraintsImpl> entry : typeParameterConstraints.entrySet()) {
TypeParameterDescriptor typeParameterDescriptor = entry.getKey();
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);
}
}
}
}
@NotNull
@Override
public Set<TypeParameterDescriptor> getTypeVariables() {
@@ -153,25 +153,35 @@ public class ConstraintsUtil {
return typeParameter.getUpperBoundsAsType();
}
public static boolean checkUpperBoundIsSatisfied(@NotNull ConstraintSystem constraintSystem,
@NotNull TypeParameterDescriptor typeParameter) {
public static boolean checkUpperBoundIsSatisfied(
@NotNull ConstraintSystem constraintSystem,
@NotNull TypeParameterDescriptor typeParameter,
boolean substituteOtherTypeParametersInBound
) {
TypeConstraints typeConstraints = constraintSystem.getTypeConstraints(typeParameter);
assert typeConstraints != null;
JetType type = getValue(typeConstraints);
JetType upperBound = typeParameter.getUpperBoundsAsType();
JetType substitutedType = constraintSystem.getResultingSubstitutor().substitute(upperBound, Variance.INVARIANT);
if (type == null) return true;
for (JetType upperBound : typeParameter.getUpperBounds()) {
if (!substituteOtherTypeParametersInBound && TypeUtils.dependsOnTypeParameters(upperBound, constraintSystem.getTypeVariables())) {
continue;
}
JetType substitutedUpperBound = constraintSystem.getResultingSubstitutor().substitute(upperBound, Variance.INVARIANT);
if (type != null) {
if (substitutedType == null || !JetTypeChecker.INSTANCE.isSubtypeOf(type, substitutedType)) {
assert substitutedUpperBound != null : "We wanted to substitute projections as a result for " + typeParameter;
if (!JetTypeChecker.INSTANCE.isSubtypeOf(type, substitutedUpperBound)) {
return false;
}
}
return true;
}
public static boolean checkBoundsAreSatisfied(@NotNull ConstraintSystem constraintSystem) {
public static boolean checkBoundsAreSatisfied(
@NotNull ConstraintSystem constraintSystem,
boolean substituteOtherTypeParametersInBounds
) {
for (TypeParameterDescriptor typeVariable : constraintSystem.getTypeVariables()) {
if (!checkUpperBoundIsSatisfied(constraintSystem, typeVariable)) {
if (!checkUpperBoundIsSatisfied(constraintSystem, typeVariable, substituteOtherTypeParametersInBounds)) {
return false;
}
}
@@ -251,7 +251,7 @@ public class ExpressionTypingUtils {
}
constraintSystem.addSubtypeConstraint(receiverType, receiverParameter.getType(), ConstraintPosition.RECEIVER_POSITION);
return constraintSystem.isSuccessful() && ConstraintsUtil.checkBoundsAreSatisfied(constraintSystem);
return constraintSystem.isSuccessful() && ConstraintsUtil.checkBoundsAreSatisfied(constraintSystem, true);
}
private static Set<Name> collectUsedTypeNames(@NotNull JetType jetType) {
@@ -0,0 +1,20 @@
//KT-2856 Fix the getOrElse signature to be able to return any supertype of V
package d
import java.util.HashMap
public inline fun <K,V1, V: V1> Map<K,V>.getOrElse1(key: K, defaultValue: ()-> V1) : V1 {
if (this.containsKey(key)) {
return this.get(key) as V
} else {
return defaultValue()
}
}
fun main(args: Array<String>) {
val map = HashMap<Int, Int>()
println(map.getOrElse1(2, { null })) // Error
}
//from standard library
fun println(<!UNUSED_PARAMETER!>message<!> : Any?) {}
@@ -0,0 +1,15 @@
package a
fun foo<V : U, U>(<!UNUSED_PARAMETER!>v<!>: V, u: U) = u
fun bar<U, V : U>(<!UNUSED_PARAMETER!>v<!>: V, u: U) = u
fun test(a: Any, s: String) {
val <!UNUSED_VARIABLE!>b<!>: Any = <!TYPE_INFERENCE_UPPER_BOUND_VIOLATED!>foo<!>(a, s) //depends on type parameter order, will be fixed in next commit
val <!UNUSED_VARIABLE!>c<!>: Any = bar(a, s)
}
fun baz<V : U, U>(<!UNUSED_PARAMETER!>v<!>: V, u: MutableSet<U>) = u
fun test(a: Any, s: MutableSet<String>) {
<!TYPE_INFERENCE_UPPER_BOUND_VIOLATED!>baz<!>(a, s)
}
@@ -1993,7 +1993,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
}
@TestMetadata("compiler/testData/diagnostics/tests/inference")
@InnerTestClasses({Inference.Regressions.class, Inference.ReportingImprovements.class, Inference.Varargs.class})
@InnerTestClasses({Inference.Regressions.class, Inference.ReportingImprovements.class, Inference.UpperBounds.class, Inference.Varargs.class})
public static class Inference extends AbstractDiagnosticsTestWithEagerResolve {
public void testAllFilesPresentInInference() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/inference"), Pattern.compile("^(.+)\\.kt$"), true);
@@ -2365,6 +2365,24 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
}
@TestMetadata("compiler/testData/diagnostics/tests/inference/upperBounds")
public static class UpperBounds extends AbstractDiagnosticsTestWithEagerResolve {
public void testAllFilesPresentInUpperBounds() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/inference/upperBounds"), "kt", true);
}
@TestMetadata("kt2856.kt")
public void testKt2856() throws Exception {
doTest("compiler/testData/diagnostics/tests/inference/upperBounds/kt2856.kt");
}
@TestMetadata("useBoundsToInferTypeParamsSimple.kt")
public void testUseBoundsToInferTypeParamsSimple() throws Exception {
doTest("compiler/testData/diagnostics/tests/inference/upperBounds/useBoundsToInferTypeParamsSimple.kt");
}
}
@TestMetadata("compiler/testData/diagnostics/tests/inference/varargs")
public static class Varargs extends AbstractDiagnosticsTestWithEagerResolve {
public void testAllFilesPresentInVarargs() throws Exception {
@@ -2383,6 +2401,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
suite.addTestSuite(Inference.class);
suite.addTestSuite(Regressions.class);
suite.addTestSuite(ReportingImprovements.class);
suite.addTestSuite(UpperBounds.class);
suite.addTestSuite(Varargs.class);
return suite;
}