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 +}