From a4f378d04d297ff7850dda254c436ec1c098ace0 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Sat, 9 Sep 2017 14:59:41 +0300 Subject: [PATCH] Decrease resolution priority of declarations from kotlin-stdlib-jre7/8 In order to allow "overload resolution ambiguity" when both kotlin-stdlib-jre7/8 and kotlin-stdlib-jdk7/8 are in the dependencies --- .../kotlin/resolve/calls/tower/TowerLevels.kt | 5 +- .../resolve/calls/util/isFromStdlibJre7Or8.kt | 60 +++++++++++++++++++ .../kotlin/resolve/sinceKotlinUtil.kt | 6 +- .../build.log.expected | 10 ++++ .../jvm/stdlibJre78AndStdlibJdk78/build.xml | 15 +++++ .../ant/jvm/stdlibJre78AndStdlibJdk78/main.kt | 15 +++++ .../integration/AbstractAntTaskTest.java | 4 ++ .../integration/AntTaskTestGenerated.java | 6 ++ 8 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/util/isFromStdlibJre7Or8.kt rename compiler/{frontend => resolution}/src/org/jetbrains/kotlin/resolve/sinceKotlinUtil.kt (95%) create mode 100644 compiler/testData/integration/ant/jvm/stdlibJre78AndStdlibJdk78/build.log.expected create mode 100644 compiler/testData/integration/ant/jvm/stdlibJre78AndStdlibJdk78/build.xml create mode 100644 compiler/testData/integration/ant/jvm/stdlibJre78AndStdlibJdk78/main.kt diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/TowerLevels.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/TowerLevels.kt index a1bae96f1be..ad87add0171 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/TowerLevels.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/TowerLevels.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.resolve.calls.USE_NEW_INFERENCE import org.jetbrains.kotlin.resolve.calls.smartcasts.getReceiverValueWithSmartCast import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForTypeAliasObject +import org.jetbrains.kotlin.resolve.calls.util.isLowPriorityFromStdlibJre7Or8 import org.jetbrains.kotlin.resolve.descriptorUtil.HIDES_MEMBERS_NAME_LIST import org.jetbrains.kotlin.resolve.descriptorUtil.hasClassValueDescriptor import org.jetbrains.kotlin.resolve.descriptorUtil.hasHidesMembersAnnotation @@ -59,7 +60,9 @@ internal abstract class AbstractScopeTowerLevel( diagnostics.add(ErrorDescriptorDiagnostic) } else { - if (descriptor.hasLowPriorityInOverloadResolution()) diagnostics.add(LowPriorityDescriptorDiagnostic) + if (descriptor.hasLowPriorityInOverloadResolution() || descriptor.isLowPriorityFromStdlibJre7Or8()) { + diagnostics.add(LowPriorityDescriptorDiagnostic) + } if (dispatchReceiverSmartCastType != null) diagnostics.add(UsedSmartCastForDispatchReceiver(dispatchReceiverSmartCastType)) if (!USE_NEW_INFERENCE) { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/util/isFromStdlibJre7Or8.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/util/isFromStdlibJre7Or8.kt new file mode 100644 index 00000000000..abada9fa740 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/util/isFromStdlibJre7Or8.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2010-2017 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.resolve.calls.util + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.config.LanguageVersion +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.SINCE_KOTLIN_FQ_NAME + +private val kotlin: FqName = KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME +private val kotlinText: FqName = KotlinBuiltIns.TEXT_PACKAGE_FQ_NAME +private val kotlinCollections: FqName = KotlinBuiltIns.COLLECTIONS_PACKAGE_FQ_NAME +private val kotlinStreams: FqName = kotlin.child(Name.identifier("streams")) + +private val use: Name = Name.identifier("use") +private val get: Name = Name.identifier("get") +private val getOrDefault: Name = Name.identifier("getOrDefault") +private val remove: Name = Name.identifier("remove") + +// We treat all declarations in deprecated libraries kotlin-stdlib-jre7 and kotlin-stdlib-jre8 as if they were +// annotated with @LowPriorityInOverloadResolution. +// This is needed in order to avoid reporting "overload resolution ambiguity" in case when there's both kotlin-stdlib-jreN and +// kotlin-stdlib-jdkN in dependencies, to allow smooth migration from the former to the latter. The ambiguity here would be incorrect +// because there's really no ambiguity in the bytecode since all declarations in -jdk7/8 have been moved to another JVM package. +// We locate declarations in kotlin-stdlib-jre{7,8} by FQ name and @SinceKotlin annotation value, which should be 1.1. +fun CallableDescriptor.isLowPriorityFromStdlibJre7Or8(): Boolean { + val containingPackage = containingDeclaration as? PackageFragmentDescriptor ?: return false + val packageFqName = containingPackage.fqName + if (!packageFqName.startsWith(KotlinBuiltIns.BUILT_INS_PACKAGE_NAME)) return false + + val isFromStdlibJre7Or8 = + packageFqName == kotlin && name == use || + packageFqName == kotlinText && name == get || + packageFqName == kotlinCollections && (name == getOrDefault || name == remove) || + packageFqName == kotlinStreams + + if (!isFromStdlibJre7Or8) return false + + val sinceKotlin = annotations.findAnnotation(SINCE_KOTLIN_FQ_NAME) ?: return false + val version = sinceKotlin.allValueArguments.values.singleOrNull()?.value as? String ?: return false + + return version == LanguageVersion.KOTLIN_1_1.versionString +} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/sinceKotlinUtil.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/sinceKotlinUtil.kt similarity index 95% rename from compiler/frontend/src/org/jetbrains/kotlin/resolve/sinceKotlinUtil.kt rename to compiler/resolution/src/org/jetbrains/kotlin/resolve/sinceKotlinUtil.kt index 7f8bf956f55..ccc62b48016 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/sinceKotlinUtil.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/sinceKotlinUtil.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2016 JetBrains s.r.o. + * Copyright 2010-2017 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. @@ -23,13 +23,13 @@ import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForTypeAliasObject -internal val SINCE_KOTLIN_FQ_NAME = FqName("kotlin.SinceKotlin") +val SINCE_KOTLIN_FQ_NAME = FqName("kotlin.SinceKotlin") /** * @return true if the descriptor is accessible according to [languageVersionSettings], or false otherwise. The [actionIfInaccessible] * callback is called with the version specified in the [SinceKotlin] annotation if the descriptor is inaccessible. */ -internal fun DeclarationDescriptor.checkSinceKotlinVersionAccessibility( +fun DeclarationDescriptor.checkSinceKotlinVersionAccessibility( languageVersionSettings: LanguageVersionSettings, actionIfInaccessible: ((ApiVersion) -> Unit)? = null ): Boolean { diff --git a/compiler/testData/integration/ant/jvm/stdlibJre78AndStdlibJdk78/build.log.expected b/compiler/testData/integration/ant/jvm/stdlibJre78AndStdlibJdk78/build.log.expected new file mode 100644 index 00000000000..e7e2f376871 --- /dev/null +++ b/compiler/testData/integration/ant/jvm/stdlibJre78AndStdlibJdk78/build.log.expected @@ -0,0 +1,10 @@ +OUT: +Buildfile: [TestData]/build.xml + +build: + [kotlinc] Compiling [[TestData]/main.kt] => [[Temp]/test.jar] + +BUILD SUCCESSFUL +Total time: [time] + +Return code: 0 diff --git a/compiler/testData/integration/ant/jvm/stdlibJre78AndStdlibJdk78/build.xml b/compiler/testData/integration/ant/jvm/stdlibJre78AndStdlibJdk78/build.xml new file mode 100644 index 00000000000..4f774169ae6 --- /dev/null +++ b/compiler/testData/integration/ant/jvm/stdlibJre78AndStdlibJdk78/build.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/compiler/testData/integration/ant/jvm/stdlibJre78AndStdlibJdk78/main.kt b/compiler/testData/integration/ant/jvm/stdlibJre78AndStdlibJdk78/main.kt new file mode 100644 index 00000000000..67acb61dcb3 --- /dev/null +++ b/compiler/testData/integration/ant/jvm/stdlibJre78AndStdlibJdk78/main.kt @@ -0,0 +1,15 @@ +import java.util.stream.Stream +import kotlin.streams.* + +// kotlin-stdlib-jdk7 + +fun testUse(c: AutoCloseable) = c.use {} + +// kotlin-stdlib-jdk8 + +fun testGet(c: MatchGroupCollection) = c.get("") + +fun testGetOrDefault(c: Map) = c.getOrDefault(42, "") +fun testRemove(c: MutableMap) = c.remove(42, "") + +fun testAsSequence(c: Stream) = c.asSequence() diff --git a/compiler/tests-common/org/jetbrains/kotlin/integration/AbstractAntTaskTest.java b/compiler/tests-common/org/jetbrains/kotlin/integration/AbstractAntTaskTest.java index de201fdce89..91d261f58fc 100644 --- a/compiler/tests-common/org/jetbrains/kotlin/integration/AbstractAntTaskTest.java +++ b/compiler/tests-common/org/jetbrains/kotlin/integration/AbstractAntTaskTest.java @@ -33,6 +33,10 @@ public abstract class AbstractAntTaskTest extends KotlinIntegrationTestBase { "-Dkotlin.lib=" + KotlinIntegrationTestBase.getCompilerLib(), "-Dkotlin.runtime.jar=" + ForTestCompileRuntime.runtimeJarForTests().getAbsolutePath(), "-Dkotlin.reflect.jar=" + ForTestCompileRuntime.reflectJarForTests().getAbsolutePath(), + "-Dkotlin.stdlib.jre7.jar=" + new File("dist/kotlinc/lib/kotlin-stdlib-jre7.jar").getAbsolutePath(), + "-Dkotlin.stdlib.jre8.jar=" + new File("dist/kotlinc/lib/kotlin-stdlib-jre8.jar").getAbsolutePath(), + "-Dkotlin.stdlib.jdk7.jar=" + new File("dist/kotlinc/lib/kotlin-stdlib-jdk7.jar").getAbsolutePath(), + "-Dkotlin.stdlib.jdk8.jar=" + new File("dist/kotlinc/lib/kotlin-stdlib-jdk8.jar").getAbsolutePath(), "-Dtest.data=" + testDataDir, "-Dtemp=" + tmpdir, "-f", "build.xml" diff --git a/compiler/tests/org/jetbrains/kotlin/integration/AntTaskTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/integration/AntTaskTestGenerated.java index 20768ce7bdc..68712f50144 100644 --- a/compiler/tests/org/jetbrains/kotlin/integration/AntTaskTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/integration/AntTaskTestGenerated.java @@ -132,6 +132,12 @@ public class AntTaskTestGenerated extends AbstractAntTaskTest { doTest(fileName); } + @TestMetadata("stdlibJre78AndStdlibJdk78") + public void testStdlibJre78AndStdlibJdk78() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/integration/ant/jvm/stdlibJre78AndStdlibJdk78/"); + doTest(fileName); + } + @TestMetadata("suppressWarnings") public void testSuppressWarnings() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/integration/ant/jvm/suppressWarnings/");