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<K, V> to ConcurrentHashMap
ConcurrentHashMap<K, V> extends ConcurrentMap<K!, V!> that extends java.util.Map<K!, V!> (mapped to kotlin.MutableMap<K!, V!>)

So we want in that case to use refined (more specific) version when checking subtypes:
ConcurrentHashMap<String, Int> should not be a subtype Map<String!, Int!> (and respectively Map<String?, Int?>)
It should be pure non-platform Map<String, Int> that can be found only with BFS
This commit is contained in:
Denis Zharkov
2015-07-01 14:48:51 +03:00
parent 0a19fb7df2
commit da416f1caf
5 changed files with 97 additions and 11 deletions
@@ -0,0 +1,10 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE
interface X<T>
interface A: X<String>
interface B : <!INCONSISTENT_TYPE_PARAMETER_VALUES!>A, X<Int><!>
fun foo(x: B) {
// Checks that when checking subtypes we search closes corresponding constructor (e.g. with BFS)
val y: X<Int> = x
}
@@ -0,0 +1,21 @@
package
internal fun foo(/*0*/ x: B): kotlin.Unit
internal interface A : X<kotlin.String> {
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<kotlin.Int> {
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</*0*/ T> {
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
}
@@ -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");
@@ -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) {
@@ -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<SubtypePathNode>()
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
}