From 42113b5e58a8a0f29f2bfae552dfd83ab4895631 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Thu, 25 Dec 2014 14:45:50 +0300 Subject: [PATCH] Filtering error types and accounting for platform types when computing intersections KT-6508 Build impossible because of NoSuchElementException #KT-6508 Fixed --- .../tests/platformTypes/intersection/map.kt | 11 +++++++++ .../tests/platformTypes/intersection/map.txt | 4 ++++ .../diagnostics/tests/regressions/kt6508.kt | 24 +++++++++++++++++++ .../diagnostics/tests/regressions/kt6508.txt | 3 +++ .../checkers/JetDiagnosticsTestGenerated.java | 23 +++++++++++++++++- .../types/IntersectionTypeConstructor.java | 2 ++ .../jetbrains/jet/lang/types/TypeUtils.java | 21 +++++++++++++++- .../jetbrains/jet/lang/types/flexibleTypes.kt | 2 +- 8 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/platformTypes/intersection/map.kt create mode 100644 compiler/testData/diagnostics/tests/platformTypes/intersection/map.txt create mode 100644 compiler/testData/diagnostics/tests/regressions/kt6508.kt create mode 100644 compiler/testData/diagnostics/tests/regressions/kt6508.txt diff --git a/compiler/testData/diagnostics/tests/platformTypes/intersection/map.kt b/compiler/testData/diagnostics/tests/platformTypes/intersection/map.kt new file mode 100644 index 00000000000..7781837573a --- /dev/null +++ b/compiler/testData/diagnostics/tests/platformTypes/intersection/map.kt @@ -0,0 +1,11 @@ +// !EXPLICIT_FLEXIBLE_TYPES + +fun foo( + p1: ft, Map?>, + p2: Map +) = p1 == p2 + +fun foo( + p1: ft, + p2: String +) = p1 == p2 \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/platformTypes/intersection/map.txt b/compiler/testData/diagnostics/tests/platformTypes/intersection/map.txt new file mode 100644 index 00000000000..f628823b268 --- /dev/null +++ b/compiler/testData/diagnostics/tests/platformTypes/intersection/map.txt @@ -0,0 +1,4 @@ +package + +internal fun foo(/*0*/ p1: kotlin.(Mutable)Map!, /*1*/ p2: kotlin.Map): kotlin.Boolean +internal fun foo(/*0*/ p1: kotlin.String!, /*1*/ p2: kotlin.String): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/regressions/kt6508.kt b/compiler/testData/diagnostics/tests/regressions/kt6508.kt new file mode 100644 index 00000000000..1e1e885f325 --- /dev/null +++ b/compiler/testData/diagnostics/tests/regressions/kt6508.kt @@ -0,0 +1,24 @@ +// !DIAGNOSTICS: -UNUSED_EXPRESSION + +// FILE: foo/View.java + +package foo; + +public class View {} + +// FILE: foo/TextView.java + +package foo; + +public class TextView extends View {} + +// FILE: k.kt + +import foo.View +//import foo.TextView + +fun String.gah(view:View ?) { + if (view is TextView) + view + else TextView() as foo.TextView +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/regressions/kt6508.txt b/compiler/testData/diagnostics/tests/regressions/kt6508.txt new file mode 100644 index 00000000000..62e00043489 --- /dev/null +++ b/compiler/testData/diagnostics/tests/regressions/kt6508.txt @@ -0,0 +1,3 @@ +package + +internal fun kotlin.String.gah(/*0*/ view: foo.View?): kotlin.Unit diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index aa9245e9439..4945f49fe8c 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -8043,7 +8043,7 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { @TestMetadata("compiler/testData/diagnostics/tests/platformTypes") @TestDataPath("$PROJECT_ROOT") - @InnerTestClasses({PlatformTypes.CommonSupertype.class, PlatformTypes.MethodCall.class}) + @InnerTestClasses({PlatformTypes.CommonSupertype.class, PlatformTypes.Intersection.class, PlatformTypes.MethodCall.class}) @RunWith(JUnit3RunnerWithInners.class) public static class PlatformTypes extends AbstractJetDiagnosticsTest { public void testAllFilesPresentInPlatformTypes() throws Exception { @@ -8185,6 +8185,21 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { } } + @TestMetadata("compiler/testData/diagnostics/tests/platformTypes/intersection") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Intersection extends AbstractJetDiagnosticsTest { + public void testAllFilesPresentInIntersection() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/platformTypes/intersection"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("map.kt") + public void testMap() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/intersection/map.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/diagnostics/tests/platformTypes/methodCall") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -8953,6 +8968,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("kt6508.kt") + public void testKt6508() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/regressions/kt6508.kt"); + doTest(fileName); + } + @TestMetadata("kt688.kt") public void testKt688() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/regressions/kt688.kt"); diff --git a/core/descriptors/src/org/jetbrains/jet/lang/types/IntersectionTypeConstructor.java b/core/descriptors/src/org/jetbrains/jet/lang/types/IntersectionTypeConstructor.java index e46915e39a2..dd98be4095b 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/types/IntersectionTypeConstructor.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/types/IntersectionTypeConstructor.java @@ -30,6 +30,8 @@ public class IntersectionTypeConstructor extends AnnotatedImpl implements TypeCo public IntersectionTypeConstructor(Annotations annotations, Collection typesToIntersect) { super(annotations); + assert !typesToIntersect.isEmpty() : "Attempt to create an empty intersection"; + this.intersectedTypes = new LinkedHashSet(typesToIntersect); this.hashCode = intersectedTypes.hashCode(); } diff --git a/core/descriptors/src/org/jetbrains/jet/lang/types/TypeUtils.java b/core/descriptors/src/org/jetbrains/jet/lang/types/TypeUtils.java index 7780f4d33d7..070e4177757 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/types/TypeUtils.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/types/TypeUtils.java @@ -172,6 +172,8 @@ public class TypeUtils { boolean nothingTypePresent = false; List nullabilityStripped = new ArrayList(types.size()); for (JetType type : types) { + if (type.isError()) continue; + nothingTypePresent |= KotlinBuiltIns.isNothingOrNullableNothing(type); allNullable &= type.isMarkedNullable(); nullabilityStripped.add(makeNotNullable(type)); @@ -181,6 +183,11 @@ public class TypeUtils { return allNullable ? KotlinBuiltIns.getInstance().getNullableNothingType() : KotlinBuiltIns.getInstance().getNothingType(); } + if (nullabilityStripped.isEmpty()) { + // All types were errors + return ErrorUtils.createErrorType("Intersection of errors types: " + types); + } + // Now we remove types that have subtypes in the list List resultingTypes = new ArrayList(); outer: @@ -213,11 +220,23 @@ public class TypeUtils { resultingTypes.add(type); } + if (resultingTypes.isEmpty()) { + // If we ended up here, it means that all types from `nullabilityStripped` were excluded by the code above + // most likely, this is because they are all semantically interchangeable (e.g. List! and List), + // in that case, we can safely select the best representative out of that set and return it + // TODO: maybe return the most specific among the types that are subtypes to all others in the `nullabilityStripped`? + // TODO: e.g. among {Int, Int?, Int!}, return `Int` (now it returns `Int!`). + JetType bestRepresentative = TypesPackage.singleBestRepresentative(nullabilityStripped); + if (bestRepresentative == null) { + throw new AssertionError("Empty intersection for types " + types); + } + return makeNullableAsSpecified(bestRepresentative, allNullable); + } + if (resultingTypes.size() == 1) { return makeNullableAsSpecified(resultingTypes.get(0), allNullable); } - TypeConstructor constructor = new IntersectionTypeConstructor(Annotations.EMPTY, resultingTypes); JetScope[] scopes = new JetScope[resultingTypes.size()]; diff --git a/core/descriptors/src/org/jetbrains/jet/lang/types/flexibleTypes.kt b/core/descriptors/src/org/jetbrains/jet/lang/types/flexibleTypes.kt index 0b76e32371d..653b0455b7f 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/types/flexibleTypes.kt +++ b/core/descriptors/src/org/jetbrains/jet/lang/types/flexibleTypes.kt @@ -67,7 +67,7 @@ public fun JetType.isNullabilityFlexible(): Boolean { } // This function is intended primarily for sets: since JetType.equals() represents _syntactical_ equality of types, -// whereas JetTypeChecker.DEFAULT.equalsTypes() represents semantical equality +// whereas JetTypeChecker.DEFAULT.equalsTypes() represents semantic equality // A set of types (e.g. exact bounds etc) may contain, for example, X, X? and X! // These are not equal syntactically (by JetType.equals()), but X! is _compatible_ with others as exact bounds, // moreover, X! is a better fit.