From 8f59172f27969c1da96a3739c3dd69981f2aad34 Mon Sep 17 00:00:00 2001 From: Svetlana Isakova Date: Thu, 7 Feb 2013 20:29:37 +0400 Subject: [PATCH] use declared upper bounds in type inference (not just check them) - simple cases supported #KT-2856 fixed --- .../lang/diagnostics/rendering/Renderers.java | 2 +- .../lang/resolve/calls/CandidateResolver.java | 34 ++++++++----------- .../calls/inference/ConstraintPosition.java | 1 + .../calls/inference/ConstraintSystemImpl.java | 17 ++++++++-- .../calls/inference/ConstraintsUtil.java | 26 +++++++++----- .../expressions/ExpressionTypingUtils.java | 2 +- .../tests/inference/upperBounds/kt2856.kt | 20 +++++++++++ .../useBoundsToInferTypeParamsSimple.kt | 15 ++++++++ .../checkers/JetDiagnosticsTestGenerated.java | 21 +++++++++++- 9 files changed, 105 insertions(+), 33 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/inference/upperBounds/kt2856.kt create mode 100644 compiler/testData/diagnostics/tests/inference/upperBounds/useBoundsToInferTypeParamsSimple.kt 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 cb6d963549e..284bb4a9326 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 @@ -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; } 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 23eaad5c1de..3789d72e756 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 @@ -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 void checkBounds( - @NotNull ResolvedCallImpl 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 jetTypeArguments, @NotNull List typeArguments, diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintPosition.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintPosition.java index 59f9b6eb71f..c8c77099e8d 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintPosition.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintPosition.java @@ -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 valueParameterPositions = Maps.newHashMap(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemImpl.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemImpl.java index 8b01ae71806..8979dfc5ee1 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemImpl.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemImpl.java @@ -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 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 getTypeVariables() { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintsUtil.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintsUtil.java index c209a221f57..615de44e75c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintsUtil.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintsUtil.java @@ -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; } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java index 69fd2943e9e..3d1f8a19b5a 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java @@ -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 collectUsedTypeNames(@NotNull JetType jetType) { diff --git a/compiler/testData/diagnostics/tests/inference/upperBounds/kt2856.kt b/compiler/testData/diagnostics/tests/inference/upperBounds/kt2856.kt new file mode 100644 index 00000000000..041e9ae0a40 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/upperBounds/kt2856.kt @@ -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 Map.getOrElse1(key: K, defaultValue: ()-> V1) : V1 { + if (this.containsKey(key)) { + return this.get(key) as V + } else { + return defaultValue() + } +} + +fun main(args: Array) { + val map = HashMap() + println(map.getOrElse1(2, { null })) // Error +} + +//from standard library +fun println(message : Any?) {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/upperBounds/useBoundsToInferTypeParamsSimple.kt b/compiler/testData/diagnostics/tests/inference/upperBounds/useBoundsToInferTypeParamsSimple.kt new file mode 100644 index 00000000000..2f57370b7b1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/upperBounds/useBoundsToInferTypeParamsSimple.kt @@ -0,0 +1,15 @@ +package a + +fun foo(v: V, u: U) = u +fun bar(v: V, u: U) = u + +fun test(a: Any, s: String) { + val b: Any = foo(a, s) //depends on type parameter order, will be fixed in next commit + val c: Any = bar(a, s) +} + +fun baz(v: V, u: MutableSet) = u + +fun test(a: Any, s: MutableSet) { + baz(a, s) +} diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index ccbca8c9593..cd3c80e2af5 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -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; }