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 edbe66821c1..83ee4713460 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 @@ -578,7 +578,7 @@ public class CandidateResolver { CallResolutionContext newContext = context.replaceBindingTrace(traceToResolveArgument).replaceExpectedType(expectedType); JetTypeInfo typeInfoForCall = argumentTypeResolver.getArgumentTypeInfo(argumentExpression, newContext, resolveFunctionArgumentBodies, traceToResolveArgument); - JetType type = typeInfoForCall.getType(); + JetType type = updateResultTypeForSmartCasts(typeInfoForCall.getType(), argumentExpression, context); constraintSystem.addSubtypeConstraint(type, effectiveExpectedType, ConstraintPosition.getValueParameterPosition( valueParameterDescriptor.getIndex())); if (isErrorType != null) { @@ -586,6 +586,22 @@ public class CandidateResolver { } } + @Nullable + private static JetType updateResultTypeForSmartCasts( + @Nullable JetType type, + @Nullable JetExpression argumentExpression, + @NotNull CallCandidateResolutionContext context + ) { + if (argumentExpression == null || type == null) return type; + + DataFlowValue dataFlowValue = + DataFlowValueFactory.INSTANCE.createDataFlowValue(argumentExpression, type, context.trace.getBindingContext()); + Set possibleTypes = context.dataFlowInfo.getPossibleTypes(dataFlowValue); + if (possibleTypes.isEmpty()) return type; + + return TypeUtils.intersect(JetTypeChecker.INSTANCE, possibleTypes); + } + private ValueArgumentsCheckingResult checkAllValueArguments( @NotNull CallCandidateResolutionContext context, @NotNull CallResolverUtil.ResolveArgumentsMode resolveFunctionArgumentBodies) { 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 c5bc25246dc..fb0deafb134 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 @@ -110,6 +110,12 @@ public class ConstraintsUtil { @Nullable private static JetType commonSupertype(@NotNull Collection lowerBounds) { if (lowerBounds.isEmpty()) return null; + if (lowerBounds.size() == 1) { + JetType type = lowerBounds.iterator().next(); + if (type.getConstructor() instanceof IntersectionTypeConstructor) { + return commonSupertype(type.getConstructor().getSupertypes()); + } + } return CommonSupertypes.commonSupertype(lowerBounds); } @@ -124,6 +130,7 @@ public class ConstraintsUtil { @NotNull TypeConstraints typeConstraints ) { if (suggestion == null) return false; + if (!suggestion.getConstructor().isDenotable()) return false; if (typeConstraints.getExactBounds().size() > 1) return false; for (JetType exactBound : typeConstraints.getExactBounds()) { diff --git a/compiler/testData/diagnostics/tests/smartCasts/inference/intersectionTypes.kt b/compiler/testData/diagnostics/tests/smartCasts/inference/intersectionTypes.kt new file mode 100644 index 00000000000..cdb34a140d5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/inference/intersectionTypes.kt @@ -0,0 +1,56 @@ +package a + +fun id(t: T): T = t + +fun two(u: T, v: T): T = u + +fun three(a: T, b: T, c: T): T = c + +trait A +trait B: A +trait C: A + +fun test(a: A, b: B, c: C) { + if (a is B && a is C) { + val d: C = id(a) + val e: Any = id(a) + val f = id(a) + val g = two(a, b) + g: B + g: A + + val h: Any = two(a, b) + + val k = three(a, b, c) + k: A + k: B + val l: Int = three(a, b, c) + + use(d, e, f, g, h, k, l) + } +} + +fun foo(t: T, l: MutableList): T = t + +fun testErrorMessages(a: A, ml: MutableList) { + if (a is B && a is C) { + foo(a, ml) + } + + if(a is C) { + foo(a, ml) + } +} + +fun rr(s: String?) { + if (s != null) { + val l = arrayListOf("", s) + l: MutableList + l: MutableList + } +} + +//from library +fun arrayListOf(vararg values: T): MutableList = throw Exception() + +fun use(vararg a: Any) = a \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/inference/kt1355.kt b/compiler/testData/diagnostics/tests/smartCasts/inference/kt1355.kt new file mode 100644 index 00000000000..a4bd1ee4c63 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/inference/kt1355.kt @@ -0,0 +1,40 @@ +//KT-1355 Type inference fails with autocast and generic function +//tests for Map.set +package a + +import java.util.HashMap + +fun foo(map: MutableMap, value: String?) { + if (value != null) { + map.put(1, value) //ok + map.set(1, value) //type inference failed + map[1] = value //type inference failed + } +} + +//--------------------------- + +public data open class Tag(public var tagName: String) { + public val attributes: MutableMap = HashMap() + public val contents: MutableList = arrayListOf() + + public var id: String? + get() = attributes["id"] + set(value) { + if(value == null) { + attributes.remove("id") + } + else { + attributes["id"] = value!! + attributes["id"] = value + } + } +} + + +//from library +fun MutableMap.set(key : K, value : V) = this.put(key, value) + +fun arrayListOf(vararg values: T): MutableList = throw Exception() + + diff --git a/compiler/testData/diagnostics/tests/smartCasts/inference/kt2746.kt b/compiler/testData/diagnostics/tests/smartCasts/inference/kt2746.kt new file mode 100644 index 00000000000..4bddbccb38c --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/inference/kt2746.kt @@ -0,0 +1,19 @@ +//KT-2746 Do autocasts in inference +import java.util.HashMap + +class C(t :T) + +fun test1(a: Any) { + if (a is String) { + val c: C = C(a) + } +} + + +fun f(t :T): C = C(t) + +fun test2(a: Any) { + if (a is String) { + val c1: C = f(a) + } +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/inference/kt2851.kt b/compiler/testData/diagnostics/tests/smartCasts/inference/kt2851.kt new file mode 100644 index 00000000000..e6147254056 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/inference/kt2851.kt @@ -0,0 +1,20 @@ +//KT-2851 Type inference failed passing in not-null after smart-cast value in Pair +package a + +fun main(args: Array) { + val value: String? = "" + if (value != null) { + foo(Pair("val", value)) + foo(Pair("val", value!!)) + foo(Pair("val", value)) + } +} + +fun foo(map: Pair) {} + + +//from library +public class Pair ( + public val first: A, + public val second: B +) diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index 89ecf10b247..cd4c849cd16 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -5070,6 +5070,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage } @TestMetadata("compiler/testData/diagnostics/tests/smartCasts") + @InnerTestClasses({SmartCasts.Inference.class}) public static class SmartCasts extends AbstractDiagnosticsTestWithEagerResolve { public void testAllFilesPresentInSmartCasts() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/smartCasts"), Pattern.compile("^(.+)\\.kt$"), true); @@ -5115,6 +5116,40 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage doTest("compiler/testData/diagnostics/tests/smartCasts/thisWithLabelAsReceiverPart.kt"); } + @TestMetadata("compiler/testData/diagnostics/tests/smartCasts/inference") + 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/smartCasts/inference"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("intersectionTypes.kt") + public void testIntersectionTypes() throws Exception { + doTest("compiler/testData/diagnostics/tests/smartCasts/inference/intersectionTypes.kt"); + } + + @TestMetadata("kt1355.kt") + public void testKt1355() throws Exception { + doTest("compiler/testData/diagnostics/tests/smartCasts/inference/kt1355.kt"); + } + + @TestMetadata("kt2746.kt") + public void testKt2746() throws Exception { + doTest("compiler/testData/diagnostics/tests/smartCasts/inference/kt2746.kt"); + } + + @TestMetadata("kt2851.kt") + public void testKt2851() throws Exception { + doTest("compiler/testData/diagnostics/tests/smartCasts/inference/kt2851.kt"); + } + + } + + public static Test innerSuite() { + TestSuite suite = new TestSuite("SmartCasts"); + suite.addTestSuite(SmartCasts.class); + suite.addTestSuite(Inference.class); + return suite; + } } @TestMetadata("compiler/testData/diagnostics/tests/substitutions") @@ -5345,7 +5380,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage suite.addTestSuite(Scopes.class); suite.addTestSuite(SenselessComparison.class); suite.addTestSuite(Shadowing.class); - suite.addTestSuite(SmartCasts.class); + suite.addTest(SmartCasts.innerSuite()); suite.addTestSuite(Substitutions.class); suite.addTestSuite(Subtyping.class); suite.addTestSuite(ThisAndSuper.class);