Fixed type checking recursive problem.

#KT-11995 Fixed
This commit is contained in:
Stanislav Erokhin
2016-05-19 15:49:40 +03:00
parent 1c8272d3f1
commit c4778bfe5a
9 changed files with 97 additions and 38 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* Copyright 2010-2016 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.
@@ -16,11 +16,7 @@
package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.OverridingUtil.OverrideCompatibilityInfo
object DescriptorEquivalenceForOverrides {
@@ -33,8 +29,8 @@ object DescriptorEquivalenceForOverrides {
a is TypeParameterDescriptor &&
b is TypeParameterDescriptor -> areTypeParametersEquivalent(a, b)
a is CallableMemberDescriptor &&
b is CallableMemberDescriptor -> areCallableMemberDescriptorsEquivalent(a, b)
a is CallableDescriptor &&
b is CallableDescriptor -> areCallableDescriptorsEquivalent(a, b)
a is PackageFragmentDescriptor &&
b is PackageFragmentDescriptor -> (a).fqName == (b).fqName
@@ -61,7 +57,11 @@ object DescriptorEquivalenceForOverrides {
return a.index == b.index // We ignore type parameter names
}
private fun areCallableMemberDescriptorsEquivalent(a: CallableMemberDescriptor, b: CallableMemberDescriptor): Boolean {
fun areCallableDescriptorsEquivalent(
a: CallableDescriptor,
b: CallableDescriptor,
ignoreReturnType: Boolean = false
): Boolean {
if (a == b) return true
if (a.name != b.name) return false
if (a.containingDeclaration == b.containingDeclaration) return false
@@ -83,8 +83,8 @@ object DescriptorEquivalenceForOverrides {
areTypeParametersEquivalent(d1, d2, {x, y -> x == a && y == b})
}
return overridingUtil.isOverridableByIncludingReturnType(a, b).result == OverrideCompatibilityInfo.Result.OVERRIDABLE
&& overridingUtil.isOverridableByIncludingReturnType(b, a).result == OverrideCompatibilityInfo.Result.OVERRIDABLE
return overridingUtil.isOverridableBy(a, b, null, !ignoreReturnType).result == OverrideCompatibilityInfo.Result.OVERRIDABLE
&& overridingUtil.isOverridableBy(b, a, null, !ignoreReturnType).result == OverrideCompatibilityInfo.Result.OVERRIDABLE
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* Copyright 2010-2016 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.
@@ -47,6 +47,7 @@ import org.jetbrains.kotlin.utils.HashSetUtil;
import java.util.*;
import static kotlin.collections.CollectionsKt.sortedBy;
import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.*;
import static org.jetbrains.kotlin.diagnostics.Errors.*;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.classCanHaveAbstractMembers;
@@ -146,21 +147,27 @@ public class OverrideResolver {
return filterOverrides(candidateSet, transform, Filtering.RETAIN_OVERRIDING);
}
@NotNull
private static <D> Set<D> filterOverrides(
@NotNull Set<D> candidateSet,
@NotNull final Function<? super D, ? extends CallableDescriptor> transform,
@NotNull Filtering filtering
) {
if (candidateSet.size() <= 1) return candidateSet;
// In a multi-module project different "copies" of the same class may be present in different libraries,
// that's why we use structural equivalence for members (DescriptorEquivalenceForOverrides).
// Here we filter out structurally equivalent descriptors before processing overrides, because such descriptors
// "override" each other (overrides(f, g) = overrides(g, f) = true) and the code below removes them all from the
// candidates, unless we first compute noDuplicates
Set<D> noDuplicates = HashSetUtil.linkedHashSet(
candidateSet,
// In a multi-module project different "copies" of the same class may be present in different libraries,
// that's why we use structural equivalence for members (DescriptorEquivalenceForOverrides).
//
// Sometimes we should compare "copies" from sources and from binary files.
// But we cannot compare return types for such copies, because it may lead us to recursive problem (see KT-11995).
// Because of this we compare them without return type and choose descriptor from source if we found duplicate.
@NotNull
private static <D> Set<D> noDuplicates(
@NotNull Set<D> candidateSet,
@NotNull final Function<? super D, ? extends CallableDescriptor> transform
) {
List<D> fromSourcesGoesFirst = sortedBy(candidateSet, new Function1<D, Integer>() {
@Override
public Integer invoke(D d) {
return DescriptorToSourceUtils.descriptorToDeclaration(transform.fun(d)) != null ? 0 : 1;
}
});
return HashSetUtil.linkedHashSet(
fromSourcesGoesFirst,
new EqualityPolicy<D>() {
@Override
public int getHashCode(D d) {
@@ -169,11 +176,29 @@ public class OverrideResolver {
@Override
public boolean isEqual(D d1, D d2) {
CallableDescriptor f = transform.fun(d1);
CallableDescriptor g = transform.fun(d2);
return DescriptorEquivalenceForOverrides.INSTANCE.areEquivalent(f.getOriginal(), g.getOriginal());
CallableDescriptor f = transform.fun(d1).getOriginal();
CallableDescriptor g = transform.fun(d2).getOriginal();
boolean ignoreReturnType = (DescriptorToSourceUtils.descriptorToDeclaration(f) == null) !=
(DescriptorToSourceUtils.descriptorToDeclaration(g) == null);
return DescriptorEquivalenceForOverrides.INSTANCE.areCallableDescriptorsEquivalent(f, g, ignoreReturnType);
}
});
}
@NotNull
private static <D> Set<D> filterOverrides(
@NotNull Set<D> candidateSet,
@NotNull Function<? super D, ? extends CallableDescriptor> transform,
@NotNull Filtering filtering
) {
if (candidateSet.size() <= 1) return candidateSet;
// Here we filter out structurally equivalent descriptors before processing overrides, because such descriptors
// "override" each other (overrides(f, g) = overrides(g, f) = true) and the code below removes them all from the
// candidates, unless we first compute noDuplicates
Set<D> noDuplicates = noDuplicates(candidateSet, transform);
Set<D> candidates = Sets.newLinkedHashSet();
outerLoop:
Binary file not shown.
@@ -0,0 +1,11 @@
OUT:
Buildfile: [TestData]/build.xml
build:
[kotlinc] Compiling [[TestData]/test.kt] => [[Temp]/test.jar]
[exec] foo(Int)
BUILD SUCCESSFUL
Total time: [time]
Return code: 0
+12
View File
@@ -0,0 +1,12 @@
<project name="Ant Task Test" default="build">
<taskdef resource="org/jetbrains/kotlin/ant/antlib.xml" classpath="${kotlin.lib}/kotlin-ant.jar"/>
<target name="build">
<kotlinc src="${test.data}/test.kt" classpath="${test.data}/Kt11995.jar" output="${temp}/test.jar" nowarn="true" />
<exec executable="java">
<arg line="-classpath ${temp}/test.jar foo.TestKt"/>
</exec>
</target>
</project>
+9
View File
@@ -0,0 +1,9 @@
package foo
fun foo(a: Any) = foo(1)
fun foo(i: Int) = "foo(Int)"
fun main(args: Array<String>) {
println(foo(""))
}
@@ -71,6 +71,12 @@ public class AntTaskTestGenerated extends AbstractAntTaskTest {
doTest(fileName);
}
@TestMetadata("kt11995")
public void testKt11995() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/integration/ant/jvm/kt11995/");
doTest(fileName);
}
@TestMetadata("mainInFiles")
public void testMainInFiles() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/integration/ant/jvm/mainInFiles/");
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* Copyright 2010-2016 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.
@@ -21,12 +21,13 @@ import com.intellij.util.containers.hash.HashSet;
import com.intellij.util.containers.hash.LinkedHashMap;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
public class HashSetUtil {
@NotNull
public static <T> Set<T> linkedHashSet(@NotNull Set<T> set, @NotNull EqualityPolicy<T> policy) {
public static <T> Set<T> linkedHashSet(@NotNull Collection<T> set, @NotNull EqualityPolicy<T> policy) {
// this implementation of LinkedHashMap doesn't admit nulls as values
Map<T, String> map = new LinkedHashMap<T, String>(policy);
for (T t : set) {
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* Copyright 2010-2016 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.
@@ -73,12 +73,7 @@ public class OverridingUtil {
}
@NotNull
public OverrideCompatibilityInfo isOverridableByIncludingReturnType(@NotNull CallableDescriptor superDescriptor, @NotNull CallableDescriptor subDescriptor) {
return isOverridableBy(superDescriptor, subDescriptor, null, true);
}
@NotNull
private OverrideCompatibilityInfo isOverridableBy(
public OverrideCompatibilityInfo isOverridableBy(
@NotNull CallableDescriptor superDescriptor,
@NotNull CallableDescriptor subDescriptor,
@Nullable ClassDescriptor subClassDescriptor,