Fixed type checking recursive problem.
#KT-11995 Fixed
This commit is contained in:
+11
-11
@@ -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
|
||||
@@ -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>
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user