From c4778bfe5a4fe97826eae6ade149be95b30ad2c2 Mon Sep 17 00:00:00 2001 From: Stanislav Erokhin Date: Thu, 19 May 2016 15:49:40 +0300 Subject: [PATCH] Fixed type checking recursive problem. #KT-11995 Fixed --- .../DescriptorEquivalenceForOverrides.kt | 22 +++---- .../kotlin/resolve/OverrideResolver.java | 61 ++++++++++++------ .../integration/ant/jvm/kt11995/Kt11995.jar | Bin 0 -> 987 bytes .../ant/jvm/kt11995/build.log.expected | 11 ++++ .../integration/ant/jvm/kt11995/build.xml | 12 ++++ .../integration/ant/jvm/kt11995/test.kt | 9 +++ .../integration/AntTaskTestGenerated.java | 6 ++ .../jetbrains/kotlin/utils/HashSetUtil.java | 5 +- .../kotlin/resolve/OverridingUtil.java | 9 +-- 9 files changed, 97 insertions(+), 38 deletions(-) create mode 100644 compiler/testData/integration/ant/jvm/kt11995/Kt11995.jar create mode 100644 compiler/testData/integration/ant/jvm/kt11995/build.log.expected create mode 100644 compiler/testData/integration/ant/jvm/kt11995/build.xml create mode 100644 compiler/testData/integration/ant/jvm/kt11995/test.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorEquivalenceForOverrides.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorEquivalenceForOverrides.kt index 56f6e304944..3d33a21361d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorEquivalenceForOverrides.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorEquivalenceForOverrides.kt @@ -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 } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverrideResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverrideResolver.java index 4d17abf0189..ed736df6834 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverrideResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverrideResolver.java @@ -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 Set filterOverrides( - @NotNull Set candidateSet, - @NotNull final Function 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 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 Set noDuplicates( + @NotNull Set candidateSet, + @NotNull final Function transform + ) { + List fromSourcesGoesFirst = sortedBy(candidateSet, new Function1() { + @Override + public Integer invoke(D d) { + return DescriptorToSourceUtils.descriptorToDeclaration(transform.fun(d)) != null ? 0 : 1; + } + }); + + return HashSetUtil.linkedHashSet( + fromSourcesGoesFirst, new EqualityPolicy() { @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 Set filterOverrides( + @NotNull Set candidateSet, + @NotNull Function 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 noDuplicates = noDuplicates(candidateSet, transform); Set candidates = Sets.newLinkedHashSet(); outerLoop: diff --git a/compiler/testData/integration/ant/jvm/kt11995/Kt11995.jar b/compiler/testData/integration/ant/jvm/kt11995/Kt11995.jar new file mode 100644 index 0000000000000000000000000000000000000000..0da95c187b1e59c426fde3610ab40f49fd33449f GIT binary patch literal 987 zcmWIWW@h1H0D<<7%^qL|l;C7wVeoYgan$wnbJPEKih&^js8oc30|=YZl*$3Mz?FKJ z7#dnyn(Afem*iyT#pmXyl;)%+CnO}K0dY&BUYy>uEq`hhw@tY6Na(!~&{TGgRcE_` zq=06~12M=J><(f9%BJP#>*IEZ08~XtYH^8oiC%I}VsUY9pm+Xd2Z4Xr{3c&%G2!jM zBy!7P{!7Nm3Vfmdp{h$(C@Mrfopkxu&1J7|?4Go@^N6l}yR!QcMpv1ifgOcOalDt_ z)EM8lu`&Ml@A2ty23;8=i|bLl!<7uhmoXd*;QZ0DTQS`E`&x01`4O`ZF4JjiUVQHD zi_FLRuPz-4;`Ns-v$m4k)o2iRW~o=qx+!LNJbcT-XTP)H(d_=H)WvgKIoE0Bodlts zL6U;na=UV;e)C!~U*v&7&JXiL@_Qc}_OqmJnY@Pk@WcAFD7lpvwHLPR=6ko`%m%;P zGILk*_x>tfv$*QOVWMV-Fi7iUM_yThbw?8V!?!g2D$ z3Nu~n%`1~bjTB60Upv)e`kFQSskYd{zi*T6riX2bxV5Mtan{+%lfzRae<_}KZ&NH^ z$F3yn``hNt$A}_*k?@sC5wAT|cRfBKTzD*Jue`Rh^2IRWnI4PI{gauO_`dARciaD- z#d=>Yz8%e;S$5}4R+abP3oWfOVz={?SSxR=nEZWG#Vz*L$FE*JAF+B}y%&G;@+IEg z3|%XP>=sW~SJS<7_6^fRxxGxMWw?JA?`>v2RGjqw;`Vpn6Ssd%P^|cI`nJNs#b&+1 zubWkG9C~aQHYsGDipIIG-~2&|=O$CP++9WnhJQ?;#KXwM#ehBefYLK6fG1f<>SkmT zVL+sBJ=YT=MzyJWU+e%&l literal 0 HcmV?d00001 diff --git a/compiler/testData/integration/ant/jvm/kt11995/build.log.expected b/compiler/testData/integration/ant/jvm/kt11995/build.log.expected new file mode 100644 index 00000000000..0f6d9bf29c2 --- /dev/null +++ b/compiler/testData/integration/ant/jvm/kt11995/build.log.expected @@ -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 diff --git a/compiler/testData/integration/ant/jvm/kt11995/build.xml b/compiler/testData/integration/ant/jvm/kt11995/build.xml new file mode 100644 index 00000000000..48b401de372 --- /dev/null +++ b/compiler/testData/integration/ant/jvm/kt11995/build.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/compiler/testData/integration/ant/jvm/kt11995/test.kt b/compiler/testData/integration/ant/jvm/kt11995/test.kt new file mode 100644 index 00000000000..bf70284213b --- /dev/null +++ b/compiler/testData/integration/ant/jvm/kt11995/test.kt @@ -0,0 +1,9 @@ +package foo + +fun foo(a: Any) = foo(1) + +fun foo(i: Int) = "foo(Int)" + +fun main(args: Array) { + println(foo("")) +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/integration/AntTaskTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/integration/AntTaskTestGenerated.java index 7ee03388c43..f56299fe74c 100644 --- a/compiler/tests/org/jetbrains/kotlin/integration/AntTaskTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/integration/AntTaskTestGenerated.java @@ -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/"); diff --git a/compiler/util/src/org/jetbrains/kotlin/utils/HashSetUtil.java b/compiler/util/src/org/jetbrains/kotlin/utils/HashSetUtil.java index 5a0dbc19ea9..fdd6e283aa1 100644 --- a/compiler/util/src/org/jetbrains/kotlin/utils/HashSetUtil.java +++ b/compiler/util/src/org/jetbrains/kotlin/utils/HashSetUtil.java @@ -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 Set linkedHashSet(@NotNull Set set, @NotNull EqualityPolicy policy) { + public static Set linkedHashSet(@NotNull Collection set, @NotNull EqualityPolicy policy) { // this implementation of LinkedHashMap doesn't admit nulls as values Map map = new LinkedHashMap(policy); for (T t : set) { diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java b/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java index 2d01941100d..ddc432a4fce 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java @@ -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,