From da416f1cafda8877f280ecba49592715f1f6acdd Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Wed, 1 Jul 2015 14:48:51 +0300 Subject: [PATCH] Change traversal order for finding corresponding supertype from DFS to BFS In most cases order doesn't matter as in supertype tree built from real code types with same type constructors should be completely equal. The only case when order does matter is when we artificially add more specific supertype closer to the root. For example specific annotation adding non-platform supertype MutableMap to ConcurrentHashMap ConcurrentHashMap extends ConcurrentMap that extends java.util.Map (mapped to kotlin.MutableMap) So we want in that case to use refined (more specific) version when checking subtypes: ConcurrentHashMap should not be a subtype Map (and respectively Map) It should be pure non-platform Map that can be found only with BFS --- .../findClosestCorrespondingSupertype.kt | 10 ++++ .../findClosestCorrespondingSupertype.txt | 21 +++++++ .../checkers/JetDiagnosticsTestGenerated.java | 6 ++ .../types/checker/TypeCheckingProcedure.java | 12 +--- .../jetbrains/kotlin/types/checker/utils.kt | 59 +++++++++++++++++++ 5 files changed, 97 insertions(+), 11 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/subtyping/findClosestCorrespondingSupertype.kt create mode 100644 compiler/testData/diagnostics/tests/subtyping/findClosestCorrespondingSupertype.txt create mode 100644 core/descriptors/src/org/jetbrains/kotlin/types/checker/utils.kt diff --git a/compiler/testData/diagnostics/tests/subtyping/findClosestCorrespondingSupertype.kt b/compiler/testData/diagnostics/tests/subtyping/findClosestCorrespondingSupertype.kt new file mode 100644 index 00000000000..a0123248f44 --- /dev/null +++ b/compiler/testData/diagnostics/tests/subtyping/findClosestCorrespondingSupertype.kt @@ -0,0 +1,10 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE + +interface X +interface A: X +interface B : A, X + +fun foo(x: B) { + // Checks that when checking subtypes we search closes corresponding constructor (e.g. with BFS) + val y: X = x +} diff --git a/compiler/testData/diagnostics/tests/subtyping/findClosestCorrespondingSupertype.txt b/compiler/testData/diagnostics/tests/subtyping/findClosestCorrespondingSupertype.txt new file mode 100644 index 00000000000..f0dfc7c8267 --- /dev/null +++ b/compiler/testData/diagnostics/tests/subtyping/findClosestCorrespondingSupertype.txt @@ -0,0 +1,21 @@ +package + +internal fun foo(/*0*/ x: B): kotlin.Unit + +internal interface A : X { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +internal interface B : A, X { + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String +} + +internal interface X { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index 345a5ccaf38..20b1715069e 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -13371,6 +13371,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/subtyping"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("findClosestCorrespondingSupertype.kt") + public void testFindClosestCorrespondingSupertype() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/subtyping/findClosestCorrespondingSupertype.kt"); + doTest(fileName); + } + @TestMetadata("kt2069.kt") public void testKt2069() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/subtyping/kt2069.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/TypeCheckingProcedure.java b/core/descriptors/src/org/jetbrains/kotlin/types/checker/TypeCheckingProcedure.java index f017d890f14..0844dc4548d 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/TypeCheckingProcedure.java +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/TypeCheckingProcedure.java @@ -40,17 +40,7 @@ public class TypeCheckingProcedure { // as the second parameter, applying the substitution of type arguments to it @Nullable public static JetType findCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype, @NotNull TypeCheckingProcedureCallbacks typeCheckingProcedureCallbacks) { - TypeConstructor constructor = subtype.getConstructor(); - if (typeCheckingProcedureCallbacks.assertEqualTypeConstructors(constructor, supertype.getConstructor())) { - return subtype; - } - for (JetType immediateSupertype : constructor.getSupertypes()) { - JetType correspondingSupertype = findCorrespondingSupertype(immediateSupertype, supertype, typeCheckingProcedureCallbacks); - if (correspondingSupertype != null) { - return TypeSubstitutor.create(subtype).safeSubstitute(correspondingSupertype, Variance.INVARIANT); - } - } - return null; + return CheckerPackage.findCorrespondingSupertype(subtype, supertype, typeCheckingProcedureCallbacks); } public static JetType getOutType(TypeParameterDescriptor parameter, TypeProjection argument) { diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/utils.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/utils.kt new file mode 100644 index 00000000000..bcd0681b245 --- /dev/null +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/utils.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.types.checker + +import org.jetbrains.kotlin.types.JetType +import org.jetbrains.kotlin.types.TypeSubstitutor +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.checker.TypeCheckingProcedureCallbacks +import java.util.* + +private class SubtypePathNode(val type: JetType, val previous: SubtypePathNode?) + +public fun findCorrespondingSupertype( + subtype: JetType, supertype: JetType, + typeCheckingProcedureCallbacks: TypeCheckingProcedureCallbacks +): JetType? { + val queue = ArrayDeque() + queue.add(SubtypePathNode(subtype, null)) + + val supertypeConstructor = supertype.getConstructor() + + while (!queue.isEmpty()) { + val lastPathNode = queue.poll() + val currentSubtype = lastPathNode.type + val constructor = currentSubtype.getConstructor() + + if (typeCheckingProcedureCallbacks.assertEqualTypeConstructors(constructor, supertypeConstructor)) { + var substituted = currentSubtype + var currentPathNode = lastPathNode.previous + + while (currentPathNode != null) { + substituted = TypeSubstitutor.create(currentPathNode.type).safeSubstitute(substituted, Variance.INVARIANT) + currentPathNode = currentPathNode.previous + } + + return substituted + } + + for (immediateSupertype in constructor.getSupertypes()) { + queue.add(SubtypePathNode(immediateSupertype, lastPathNode)) + } + } + + return null +}