From b6b132a9a3f8a2839c2cf33c558c2a3cf70e8cce Mon Sep 17 00:00:00 2001 From: "Denis.Zharkov" Date: Mon, 2 Jan 2023 12:00:14 +0100 Subject: [PATCH] K2: Avoid false-positive overload resolution ambiguity with smart casts The idea is that when we have successful candidates both from smart cast and original type, we should discriminate in the favor of former ones. While this problem (see kt55722.kt) existed before this branch is merged, initially it was recognized on FP Ultimate when we stopped assuming captured types from the same projections as equal (see kt55722Initial.kt). ^KT-55722 Fixed ^KT-55024 Fixed ^KT-56283 Related ^KT-56310 Related --- ...CompilerTestFE10TestdataTestGenerated.java | 36 ++++ ...irOldFrontendDiagnosticsTestGenerated.java | 36 ++++ ...DiagnosticsWithLightTreeTestGenerated.java | 36 ++++ .../session/JsConeConflictResolverFactory.kt | 8 +- .../NativeCallConflictResolverFactory.kt | 8 +- .../jvm/JvmCallConflictResolverFactory.kt | 9 +- .../kotlin/fir/resolve/calls/Candidate.kt | 4 +- .../fir/resolve/calls/CandidateFactory.kt | 6 +- ...enceOfSmartCastConeCallConflictResolver.kt | 76 ++++++++ .../resolve/calls/tower/TowerLevelHandler.kt | 6 +- .../fir/resolve/calls/tower/TowerLevels.kt | 47 +++-- .../resolve/overloadConflicts/kt55722.fir.txt | 18 ++ .../resolve/overloadConflicts/kt55722.kt | 17 ++ .../overloadConflicts/kt55722Initial.fir.txt | 14 ++ .../overloadConflicts/kt55722Initial.kt | 13 ++ .../tests/visibility/kt56283.fir.kt | 26 +++ .../tests/visibility/kt56283.fir.txt | 51 ++++++ .../diagnostics/tests/visibility/kt56283.kt | 26 +++ .../visibility/moreSpecificProtected.fir.kt | 106 +++++++++++ .../visibility/moreSpecificProtected.fir.txt | 171 ++++++++++++++++++ .../tests/visibility/moreSpecificProtected.kt | 106 +++++++++++ .../moreSpecificProtectedSimple.fir.kt | 55 ++++++ .../moreSpecificProtectedSimple.fir.txt | 63 +++++++ .../visibility/moreSpecificProtectedSimple.kt | 55 ++++++ .../smartCastAndSuppressedVisibility.fir.kt | 20 ++ .../smartCastAndSuppressedVisibility.kt | 20 ++ .../test/runners/DiagnosticTestGenerated.java | 36 ++++ 27 files changed, 1034 insertions(+), 35 deletions(-) create mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FilteringOutOriginalInPresenceOfSmartCastConeCallConflictResolver.kt create mode 100644 compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722.fir.txt create mode 100644 compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722.kt create mode 100644 compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722Initial.fir.txt create mode 100644 compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722Initial.kt create mode 100644 compiler/testData/diagnostics/tests/visibility/kt56283.fir.kt create mode 100644 compiler/testData/diagnostics/tests/visibility/kt56283.fir.txt create mode 100644 compiler/testData/diagnostics/tests/visibility/kt56283.kt create mode 100644 compiler/testData/diagnostics/tests/visibility/moreSpecificProtected.fir.kt create mode 100644 compiler/testData/diagnostics/tests/visibility/moreSpecificProtected.fir.txt create mode 100644 compiler/testData/diagnostics/tests/visibility/moreSpecificProtected.kt create mode 100644 compiler/testData/diagnostics/tests/visibility/moreSpecificProtectedSimple.fir.kt create mode 100644 compiler/testData/diagnostics/tests/visibility/moreSpecificProtectedSimple.fir.txt create mode 100644 compiler/testData/diagnostics/tests/visibility/moreSpecificProtectedSimple.kt create mode 100644 compiler/testData/diagnostics/tests/visibility/smartCastAndSuppressedVisibility.fir.kt create mode 100644 compiler/testData/diagnostics/tests/visibility/smartCastAndSuppressedVisibility.kt diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index cd12372aa54..b22355a4e3d 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -27371,6 +27371,18 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt37692.kt"); } + @Test + @TestMetadata("kt55722.kt") + public void testKt55722() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722.kt"); + } + + @Test + @TestMetadata("kt55722Initial.kt") + public void testKt55722Initial() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722Initial.kt"); + } + @Test @TestMetadata("numberOfDefaults.kt") public void testNumberOfDefaults() throws Exception { @@ -34694,12 +34706,30 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/visibility/javaInterfaceFieldDirectAccess.kt"); } + @Test + @TestMetadata("kt56283.kt") + public void testKt56283() throws Exception { + runTest("compiler/testData/diagnostics/tests/visibility/kt56283.kt"); + } + @Test @TestMetadata("lackOfInvisibleSetterOfJavaClassInSamePackage.kt") public void testLackOfInvisibleSetterOfJavaClassInSamePackage() throws Exception { runTest("compiler/testData/diagnostics/tests/visibility/lackOfInvisibleSetterOfJavaClassInSamePackage.kt"); } + @Test + @TestMetadata("moreSpecificProtected.kt") + public void testMoreSpecificProtected() throws Exception { + runTest("compiler/testData/diagnostics/tests/visibility/moreSpecificProtected.kt"); + } + + @Test + @TestMetadata("moreSpecificProtectedSimple.kt") + public void testMoreSpecificProtectedSimple() throws Exception { + runTest("compiler/testData/diagnostics/tests/visibility/moreSpecificProtectedSimple.kt"); + } + @Test @TestMetadata("notOverridingInternal.kt") public void testNotOverridingInternal() throws Exception { @@ -34801,6 +34831,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag public void testProtectedVisibilityAndSmartcast_overrideSameType() throws Exception { runTest("compiler/testData/diagnostics/tests/visibility/protectedVisibilityAndSmartcast_overrideSameType.kt"); } + + @Test + @TestMetadata("smartCastAndSuppressedVisibility.kt") + public void testSmartCastAndSuppressedVisibility() throws Exception { + runTest("compiler/testData/diagnostics/tests/visibility/smartCastAndSuppressedVisibility.kt"); + } } @Nested diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index 591dc323423..dd923bf68c3 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -27383,6 +27383,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt37692.kt"); } + @Test + @TestMetadata("kt55722.kt") + public void testKt55722() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722.kt"); + } + + @Test + @TestMetadata("kt55722Initial.kt") + public void testKt55722Initial() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722Initial.kt"); + } + @Test @TestMetadata("numberOfDefaults.kt") public void testNumberOfDefaults() throws Exception { @@ -34790,12 +34802,30 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/visibility/javaInterfaceFieldDirectAccess.kt"); } + @Test + @TestMetadata("kt56283.kt") + public void testKt56283() throws Exception { + runTest("compiler/testData/diagnostics/tests/visibility/kt56283.kt"); + } + @Test @TestMetadata("lackOfInvisibleSetterOfJavaClassInSamePackage.kt") public void testLackOfInvisibleSetterOfJavaClassInSamePackage() throws Exception { runTest("compiler/testData/diagnostics/tests/visibility/lackOfInvisibleSetterOfJavaClassInSamePackage.kt"); } + @Test + @TestMetadata("moreSpecificProtected.kt") + public void testMoreSpecificProtected() throws Exception { + runTest("compiler/testData/diagnostics/tests/visibility/moreSpecificProtected.kt"); + } + + @Test + @TestMetadata("moreSpecificProtectedSimple.kt") + public void testMoreSpecificProtectedSimple() throws Exception { + runTest("compiler/testData/diagnostics/tests/visibility/moreSpecificProtectedSimple.kt"); + } + @Test @TestMetadata("notOverridingInternal.kt") public void testNotOverridingInternal() throws Exception { @@ -34897,6 +34927,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti public void testProtectedVisibilityAndSmartcast_overrideSameType() throws Exception { runTest("compiler/testData/diagnostics/tests/visibility/protectedVisibilityAndSmartcast_overrideSameType.kt"); } + + @Test + @TestMetadata("smartCastAndSuppressedVisibility.kt") + public void testSmartCastAndSuppressedVisibility() throws Exception { + runTest("compiler/testData/diagnostics/tests/visibility/smartCastAndSuppressedVisibility.kt"); + } } @Nested diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index 4e860627d17..00bc021ee3e 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -27371,6 +27371,18 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt37692.kt"); } + @Test + @TestMetadata("kt55722.kt") + public void testKt55722() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722.kt"); + } + + @Test + @TestMetadata("kt55722Initial.kt") + public void testKt55722Initial() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722Initial.kt"); + } + @Test @TestMetadata("numberOfDefaults.kt") public void testNumberOfDefaults() throws Exception { @@ -34694,12 +34706,30 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/visibility/javaInterfaceFieldDirectAccess.kt"); } + @Test + @TestMetadata("kt56283.kt") + public void testKt56283() throws Exception { + runTest("compiler/testData/diagnostics/tests/visibility/kt56283.kt"); + } + @Test @TestMetadata("lackOfInvisibleSetterOfJavaClassInSamePackage.kt") public void testLackOfInvisibleSetterOfJavaClassInSamePackage() throws Exception { runTest("compiler/testData/diagnostics/tests/visibility/lackOfInvisibleSetterOfJavaClassInSamePackage.kt"); } + @Test + @TestMetadata("moreSpecificProtected.kt") + public void testMoreSpecificProtected() throws Exception { + runTest("compiler/testData/diagnostics/tests/visibility/moreSpecificProtected.kt"); + } + + @Test + @TestMetadata("moreSpecificProtectedSimple.kt") + public void testMoreSpecificProtectedSimple() throws Exception { + runTest("compiler/testData/diagnostics/tests/visibility/moreSpecificProtectedSimple.kt"); + } + @Test @TestMetadata("notOverridingInternal.kt") public void testNotOverridingInternal() throws Exception { @@ -34801,6 +34831,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac public void testProtectedVisibilityAndSmartcast_overrideSameType() throws Exception { runTest("compiler/testData/diagnostics/tests/visibility/protectedVisibilityAndSmartcast_overrideSameType.kt"); } + + @Test + @TestMetadata("smartCastAndSuppressedVisibility.kt") + public void testSmartCastAndSuppressedVisibility() throws Exception { + runTest("compiler/testData/diagnostics/tests/visibility/smartCastAndSuppressedVisibility.kt"); + } } @Nested diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/JsConeConflictResolverFactory.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/JsConeConflictResolverFactory.kt index 437bce620bc..510fc7677d1 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/JsConeConflictResolverFactory.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/JsConeConflictResolverFactory.kt @@ -7,10 +7,7 @@ package org.jetbrains.kotlin.fir.session import org.jetbrains.kotlin.fir.NoMutableState import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents -import org.jetbrains.kotlin.fir.resolve.calls.ConeCallConflictResolverFactory -import org.jetbrains.kotlin.fir.resolve.calls.ConeCompositeConflictResolver -import org.jetbrains.kotlin.fir.resolve.calls.ConeIntegerOperatorConflictResolver -import org.jetbrains.kotlin.fir.resolve.calls.ConeOverloadConflictResolver +import org.jetbrains.kotlin.fir.resolve.calls.* import org.jetbrains.kotlin.fir.resolve.calls.jvm.ConeEquivalentCallConflictResolver import org.jetbrains.kotlin.fir.resolve.inference.InferenceComponents import org.jetbrains.kotlin.fir.types.typeContext @@ -28,7 +25,8 @@ object JsCallConflictResolverFactory : ConeCallConflictResolverFactory() { return ConeCompositeConflictResolver( ConeOverloadConflictResolver(specificityComparator, components, transformerComponents), ConeEquivalentCallConflictResolver(specificityComparator, components, transformerComponents), - ConeIntegerOperatorConflictResolver(specificityComparator, components, transformerComponents) + ConeIntegerOperatorConflictResolver(specificityComparator, components, transformerComponents), + FilteringOutOriginalInPresenceOfSmartCastConeCallConflictResolver ) } } diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/NativeCallConflictResolverFactory.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/NativeCallConflictResolverFactory.kt index 7f8974d0f38..0a618b79070 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/NativeCallConflictResolverFactory.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/NativeCallConflictResolverFactory.kt @@ -6,10 +6,7 @@ package org.jetbrains.kotlin.fir.session import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents -import org.jetbrains.kotlin.fir.resolve.calls.ConeCallConflictResolverFactory -import org.jetbrains.kotlin.fir.resolve.calls.ConeCompositeConflictResolver -import org.jetbrains.kotlin.fir.resolve.calls.ConeIntegerOperatorConflictResolver -import org.jetbrains.kotlin.fir.resolve.calls.ConeOverloadConflictResolver +import org.jetbrains.kotlin.fir.resolve.calls.* import org.jetbrains.kotlin.fir.resolve.calls.jvm.ConeEquivalentCallConflictResolver import org.jetbrains.kotlin.fir.resolve.inference.InferenceComponents import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator @@ -24,7 +21,8 @@ object NativeCallConflictResolverFactory : ConeCallConflictResolverFactory() { return ConeCompositeConflictResolver( ConeOverloadConflictResolver(specificityComparator, components, transformerComponents), ConeEquivalentCallConflictResolver(specificityComparator, components, transformerComponents), - ConeIntegerOperatorConflictResolver(specificityComparator, components, transformerComponents) + ConeIntegerOperatorConflictResolver(specificityComparator, components, transformerComponents), + FilteringOutOriginalInPresenceOfSmartCastConeCallConflictResolver ) } } diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/resolve/calls/jvm/JvmCallConflictResolverFactory.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/resolve/calls/jvm/JvmCallConflictResolverFactory.kt index 9fcfe132549..6b17a347bc4 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/resolve/calls/jvm/JvmCallConflictResolverFactory.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/resolve/calls/jvm/JvmCallConflictResolverFactory.kt @@ -7,12 +7,8 @@ package org.jetbrains.kotlin.fir.resolve.calls.jvm import org.jetbrains.kotlin.fir.NoMutableState import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents -import org.jetbrains.kotlin.fir.resolve.calls.ConeCallConflictResolverFactory -import org.jetbrains.kotlin.fir.resolve.calls.ConeCompositeConflictResolver -import org.jetbrains.kotlin.fir.resolve.calls.ConeIntegerOperatorConflictResolver -import org.jetbrains.kotlin.fir.resolve.calls.ConeOverloadConflictResolver +import org.jetbrains.kotlin.fir.resolve.calls.* import org.jetbrains.kotlin.fir.resolve.inference.InferenceComponents -import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformer import org.jetbrains.kotlin.fir.types.typeContext import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator import org.jetbrains.kotlin.resolve.jvm.JvmTypeSpecificityComparator @@ -29,7 +25,8 @@ object JvmCallConflictResolverFactory : ConeCallConflictResolverFactory() { ConeOverloadConflictResolver(specificityComparator, components, transformerComponents), ConeEquivalentCallConflictResolver(specificityComparator, components, transformerComponents), JvmPlatformOverloadsConflictResolver(specificityComparator, components, transformerComponents), - ConeIntegerOperatorConflictResolver(specificityComparator, components, transformerComponents) + ConeIntegerOperatorConflictResolver(specificityComparator, components, transformerComponents), + FilteringOutOriginalInPresenceOfSmartCastConeCallConflictResolver ) } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt index bfb1be619e5..dc866dd356a 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt @@ -39,7 +39,9 @@ class Candidate( private val baseSystem: ConstraintStorage, override val callInfo: CallInfo, val originScope: FirScope?, - val isFromCompanionObjectTypeScope: Boolean = false + val isFromCompanionObjectTypeScope: Boolean = false, + // It's only true if we're in the member scope of smart cast receiver and this particular candidate came from original type + val isFromOriginalTypeInPresenceOfSmartCast: Boolean = false, ) : AbstractCandidate() { var systemInitialized: Boolean = false diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt index 1e1448ff2b4..54bc3eb2c93 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt @@ -54,7 +54,8 @@ class CandidateFactory private constructor( scope: FirScope?, dispatchReceiverValue: ReceiverValue? = null, givenExtensionReceiverOptions: List = emptyList(), - objectsByName: Boolean = false + objectsByName: Boolean = false, + isFromOriginalTypeInPresenceOfSmartCast: Boolean = false, ): Candidate { @Suppress("NAME_SHADOWING") val symbol = symbol.unwrapIntegerOperatorSymbolIfNeeded(callInfo) @@ -74,7 +75,8 @@ class CandidateFactory private constructor( ExplicitReceiverKind.DISPATCH_RECEIVER -> dispatchReceiverValue.isCandidateFromCompanionObjectTypeScope() // The following cases are not applicable for companion objects. ExplicitReceiverKind.NO_EXPLICIT_RECEIVER, ExplicitReceiverKind.BOTH_RECEIVERS -> false - } + }, + isFromOriginalTypeInPresenceOfSmartCast, ) // The counterpart in FE 1.0 checks if the given descriptor is VariableDescriptor yet not PropertyDescriptor. diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FilteringOutOriginalInPresenceOfSmartCastConeCallConflictResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FilteringOutOriginalInPresenceOfSmartCastConeCallConflictResolver.kt new file mode 100644 index 00000000000..c809e67e8cf --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FilteringOutOriginalInPresenceOfSmartCastConeCallConflictResolver.kt @@ -0,0 +1,76 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.resolve.calls + +import org.jetbrains.kotlin.utils.sure + +/** + * In case of MemberScopeTowerLevel with smart cast dispatch receiver, we may create candidates both from smart cast type and from + * the member scope of original expression's type (without smart cast). + * + * It might be necessary because the ones from smart cast might be invisible (e.g., because they are protected in other class). + * + * open class A { + * open protected fun foo(a: Derived) {} + * fun f(a: A, d: Derived) { + * when (a) { + * is B -> { + * a.foo(d) // should be resolved to A::foo, not the public B::foo + * } + * } + * } + * } + * class B : A() { + * override fun foo(a: Derived) {} + * public fun foo(a: Base) {} + * } + * + * If we would just resolve `a.foo(d)` if `a` had a type B, then we would choose a public B::foo, because the other one `foo` is protected in B, + * so we can't call it outside the B subclasses. + * + * But that resolution result would be less precise result that the one before smart-cast applied (A::foo has more specific parameters), + * so at MemberScopeTowerLevel we create candidates both from A's and B's scopes on the same level. + * + * But in case when there would be successful candidates from both types, we discriminate ones from original type, thus sticking to the candidates + * from smart cast type. + * + * See more details at KT-51460, KT-55722, KT-56310 and relevant tests + * - testData/diagnostics/tests/visibility/moreSpecificProtectedSimple.kt + * - testData/diagnostics/tests/smartCasts/kt51460.kt + */ +object FilteringOutOriginalInPresenceOfSmartCastConeCallConflictResolver : ConeCallConflictResolver() { + override fun chooseMaximallySpecificCandidates( + candidates: Set, + discriminateGenerics: Boolean, + discriminateAbstracts: Boolean + ): Set { + val (originalIfSmartCastPresent, other) = candidates.partition { it.isFromOriginalTypeInPresenceOfSmartCast } + + // If we have both successful candidates from smart cast and original, use the former one as they might have more correct return type + if (originalIfSmartCastPresent.isNotEmpty() && other.isNotEmpty()) return other.toSet().discriminateByInvokeVariablePriority() + + return candidates.discriminateByInvokeVariablePriority() + } + + // See the relevant test at testData/diagnostics/tests/resolve/invoke/kt9517.kt + private fun Set.discriminateByInvokeVariablePriority(): Set { + if (size <= 1) return this + + // Resulting successful candidates should always belong to the same tower group. + // Thus, if one of them is not variable + invoke, it should be applied to others, too. + if (first().callInfo.candidateForCommonInvokeReceiver == null) return this + + val (originalIfSmartCastPresent, other) = partition { + it.callInfo.candidateForCommonInvokeReceiver.sure { + "If one candidate within a group is variable+invoke, other should be the same, but $it found" + }.isFromOriginalTypeInPresenceOfSmartCast + } + + if (originalIfSmartCastPresent.isNotEmpty() && other.isNotEmpty()) return other.toSet() + + return this + } +} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevelHandler.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevelHandler.kt index 3e8714d9cf6..a02837bb152 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevelHandler.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevelHandler.kt @@ -76,7 +76,8 @@ private class TowerScopeLevelProcessor( dispatchReceiverValue: ReceiverValue?, givenExtensionReceiverOptions: List, scope: FirScope, - objectsByName: Boolean + objectsByName: Boolean, + isFromOriginalTypeInPresenceOfSmartCast: Boolean ) { resultCollector.consumeCandidate( group, candidateFactory.createCandidate( @@ -86,7 +87,8 @@ private class TowerScopeLevelProcessor( scope, dispatchReceiverValue, givenExtensionReceiverOptions, - objectsByName + objectsByName, + isFromOriginalTypeInPresenceOfSmartCast ), candidateFactory.context ) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevels.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevels.kt index 651d125718b..3f7d642d682 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevels.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevels.kt @@ -9,9 +9,6 @@ import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.fakeElement import org.jetbrains.kotlin.fir.* -import org.jetbrains.kotlin.fir.declarations.ContextReceiverGroup -import org.jetbrains.kotlin.fir.declarations.FirConstructor -import org.jetbrains.kotlin.fir.declarations.getAnnotationByClassId import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.isInner import org.jetbrains.kotlin.fir.expressions.FirSmartCastExpression @@ -59,7 +56,8 @@ abstract class TowerScopeLevel { dispatchReceiverValue: ReceiverValue?, givenExtensionReceiverOptions: List, scope: FirScope, - objectsByName: Boolean = false + objectsByName: Boolean = false, + isFromOriginalTypeInPresenceOfSmartCast: Boolean = false, ) } } @@ -87,9 +85,7 @@ class MemberScopeTowerLevel( val scope = dispatchReceiverValue.scope(session, scopeSession) ?: return ProcessResult.SCOPE_EMPTY var (empty, candidates) = scope.collectCandidates(processScopeMembers) - val scopeWithoutSmartcast = (dispatchReceiverValue.receiverExpression as? FirSmartCastExpression) - ?.takeIf { it.isStable } - ?.originalExpression?.typeRef + val scopeWithoutSmartcast = getOriginalReceiverExpressionIfStableSmartCast()?.typeRef ?.coneType ?.scope( session, @@ -169,12 +165,16 @@ class MemberScopeTowerLevel( candidatesFromSmartcast.forEach { put(it, true) } } + val candidates = mutableListOf>() + + // The code below is assumed to be for sake of optimization only. + // It helps to avoid creating candidates both for some member in the original type and for its override in the smart cast + // when they are both visible. + // But semantically it should be OK to declare `val candidates` as `candidatesMapping.keys.toList()` val overridableGroups = session.overrideService.createOverridableGroups( candidatesFromOriginalType + candidatesFromSmartcast, FirIntersectionScopeOverrideChecker(session) ) - - val candidates = mutableListOf>() for (group in overridableGroups) { val visibleCandidates = group.filter { visibilityChecker.isVisible(it.member.fir, callInfo, dispatchReceiverValue) @@ -183,7 +183,7 @@ class MemberScopeTowerLevel( val visibleCandidatesFromSmartcast = visibleCandidates.filter { candidatesMapping.getValue(it) } candidates += visibleCandidatesFromSmartcast.ifEmpty { group } } - consumeCandidates(output, candidates) + consumeCandidates(output, candidates, candidatesMapping) } private fun > FirTypeScope.collectCandidates( @@ -206,20 +206,39 @@ class MemberScopeTowerLevel( private fun > consumeCandidates( output: TowerScopeLevelProcessor, - candidatesWithScope: List> + candidatesWithScope: List>, + // The map is not null only if there's a smart cast type on a dispatch receiver + // and candidates are present both in smart cast and original types. + // isFromSmartCast[candidate] == true iff exactly that member is present in smart cast type + isFromSmartCast: Map, Boolean>? = null ) { - for ((candidate, scope) in candidatesWithScope) { + for (candidateWithScope in candidatesWithScope) { + val (candidate, scope) = candidateWithScope if (candidate.hasConsistentExtensionReceiver(givenExtensionReceiverOptions)) { + val isFromOriginalTypeInPresenceOfSmartCast = isFromSmartCast != null && !isFromSmartCast.getValue(candidateWithScope) + + val dispatchReceiverToUse = when { + isFromOriginalTypeInPresenceOfSmartCast -> + getOriginalReceiverExpressionIfStableSmartCast()?.let(::ExpressionReceiverValue) + else -> dispatchReceiverValue + } + output.consumeCandidate( candidate, - dispatchReceiverValue, + dispatchReceiverToUse, givenExtensionReceiverOptions, - scope + scope, + isFromOriginalTypeInPresenceOfSmartCast = isFromOriginalTypeInPresenceOfSmartCast ) } } } + private fun getOriginalReceiverExpressionIfStableSmartCast() = + (dispatchReceiverValue.receiverExpression as? FirSmartCastExpression) + ?.takeIf { it.isStable } + ?.originalExpression + override fun processFunctionsByName( info: CallInfo, processor: TowerScopeLevelProcessor> diff --git a/compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722.fir.txt b/compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722.fir.txt new file mode 100644 index 00000000000..668ea26322a --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722.fir.txt @@ -0,0 +1,18 @@ +FILE: kt55722.kt + public abstract interface A : R|kotlin/Any| { + public open fun foo(x: R|T?|): R|kotlin/Unit| { + } + + } + public abstract interface B : R|A| { + public abstract override fun foo(x: R|F?|): R|kotlin/Unit| + + } + public final fun bar(x: R|A|): R|kotlin/Unit| { + when () { + (R|/x| is R|B|) -> { + R|/x|.R|SubstitutionOverride|(Null(null)) + } + } + + } diff --git a/compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722.kt b/compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722.kt new file mode 100644 index 00000000000..877b6a11464 --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722.kt @@ -0,0 +1,17 @@ +// FIR_IDENTICAL +// FIR_DUMP +// SKIP_TXT + +interface A { + fun foo(x: T?) {} +} + +interface B : A { + override fun foo(x: F?) +} + +fun bar(x: A) { + if (x is B) { + x.foo(null) // Shouldn't be OVERLOAD_RESOLUTION_AMBIGUITY + } +} diff --git a/compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722Initial.fir.txt b/compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722Initial.fir.txt new file mode 100644 index 00000000000..5ef92d2dc13 --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722Initial.fir.txt @@ -0,0 +1,14 @@ +FILE: kt55722Initial.kt + public abstract interface MyConsumer : R|kotlin/Any| { + public open fun consume(x: R|T|): R|kotlin/Unit| { + } + + } + public final fun foo(x: R|MyConsumer?|, v: R|kotlin/CharSequence|): R|kotlin/Unit| { + when () { + !=(R|/x|, Null(null)) -> { + R|/x|.R|SubstitutionOverride|(R|/v|) + } + } + + } diff --git a/compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722Initial.kt b/compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722Initial.kt new file mode 100644 index 00000000000..c67175836a1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722Initial.kt @@ -0,0 +1,13 @@ +// FIR_IDENTICAL +// FIR_DUMP +// SKIP_TXT + +interface MyConsumer { + fun consume(x: T) {} +} + +fun foo(x: MyConsumer?, v: CharSequence) { + if (x != null) { + x.consume(v) + } +} diff --git a/compiler/testData/diagnostics/tests/visibility/kt56283.fir.kt b/compiler/testData/diagnostics/tests/visibility/kt56283.fir.kt new file mode 100644 index 00000000000..9aba365b5fd --- /dev/null +++ b/compiler/testData/diagnostics/tests/visibility/kt56283.fir.kt @@ -0,0 +1,26 @@ +// SKIP_TXT +// FIR_DUMP +open class Base +class Derived : Base() + +open class A(protected open val foo: Base) { + + protected open fun bar(): Base = Base() + + fun f(other: A) { + other.foo // OK in K1 and K2 + other.bar() // OK in K1 and K2 + + when (other) { + is B -> { + // OK in K2, INVISIBLE_MEMBER (B::foo) in K1 + other.foo + other.bar() + } + } + } +} + +class B(override val foo: Derived): A(foo) { + override fun bar(): Derived = Derived() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/visibility/kt56283.fir.txt b/compiler/testData/diagnostics/tests/visibility/kt56283.fir.txt new file mode 100644 index 00000000000..2eddf6fa77d --- /dev/null +++ b/compiler/testData/diagnostics/tests/visibility/kt56283.fir.txt @@ -0,0 +1,51 @@ +FILE: kt56283.fir.kt + public open class Base : R|kotlin/Any| { + public constructor(): R|Base| { + super() + } + + } + public final class Derived : R|Base| { + public constructor(): R|Derived| { + super() + } + + } + public open class A : R|kotlin/Any| { + public constructor(foo: R|Base|): R|A| { + super() + } + + protected open val foo: R|Base| = R|/foo| + protected get(): R|Base| + + protected open fun bar(): R|Base| { + ^bar R|/Base.Base|() + } + + public final fun f(other: R|A|): R|kotlin/Unit| { + R|/other|.R|/A.foo| + R|/other|.R|/A.bar|() + when (R|/other|) { + ($subj$ is R|B|) -> { + R|/other|.R|/A.foo| + R|/other|.R|/A.bar|() + } + } + + } + + } + public final class B : R|A| { + public constructor(foo: R|Derived|): R|B| { + super(R|/foo|) + } + + protected open override val foo: R|Derived| = R|/foo| + protected get(): R|Derived| + + protected open override fun bar(): R|Derived| { + ^bar R|/Derived.Derived|() + } + + } diff --git a/compiler/testData/diagnostics/tests/visibility/kt56283.kt b/compiler/testData/diagnostics/tests/visibility/kt56283.kt new file mode 100644 index 00000000000..9c484a9e897 --- /dev/null +++ b/compiler/testData/diagnostics/tests/visibility/kt56283.kt @@ -0,0 +1,26 @@ +// SKIP_TXT +// FIR_DUMP +open class Base +class Derived : Base() + +open class A(protected open val foo: Base) { + + protected open fun bar(): Base = Base() + + fun f(other: A) { + other.foo // OK in K1 and K2 + other.bar() // OK in K1 and K2 + + when (other) { + is B -> { + // OK in K2, INVISIBLE_MEMBER (B::foo) in K1 + other.foo + other.bar() + } + } + } +} + +class B(override val foo: Derived): A(foo) { + override fun bar(): Derived = Derived() +} diff --git a/compiler/testData/diagnostics/tests/visibility/moreSpecificProtected.fir.kt b/compiler/testData/diagnostics/tests/visibility/moreSpecificProtected.fir.kt new file mode 100644 index 00000000000..e0491e65720 --- /dev/null +++ b/compiler/testData/diagnostics/tests/visibility/moreSpecificProtected.fir.kt @@ -0,0 +1,106 @@ +// SKIP_TXT +// FIR_DUMP +// CHECK_TYPE + +open class Base +class Derived : Base() + +interface M1 +interface M2 +interface M2Sub : M2 +interface M3 +interface M3Sub : M3 +interface M4 + + +interface M5 +interface M5Sub : M5 +interface M5SubSub : M5Sub +interface M6 + +open class A { + public fun foo(a1: Base, a2: Base): M1 = TODO() + protected open fun foo(a1: Base, a2: Derived): M2 = TODO() + protected open fun foo(a1: Derived, a2: Derived): M3 = TODO() + + public fun baz(a1: Base, a2: Base): M4 = TODO() + protected open fun baz(a1: Derived, a2: Derived): M5 = TODO() +} + +open class B : A() { + public val fromB: Any = Any() + + override fun foo(a1: Base, a2: Derived): M2Sub = TODO() + + override fun baz(a1: Derived, a2: Derived): M5Sub = TODO() + + fun f(a: A, b: B, c: C, d: Derived) { + // Only visible is M1 + a.foo(d, d) checkType { _() } + // We may call M3 and it's the most specific by its parameters + b.foo(d, d) checkType { _() } + // We can't call M3 (and M3Sub) as it's protected overriden in C, so it's invisible because + // we only allow to call protected members on dispatch receiver values that are subtypes of a dispatch receiver parameter + // So, the next visible and specific member is M2Sub + c.foo(d, d) checkType { _() } + + // Only visible is M4 + a.baz(d, d) checkType { _() } + // M5Sub is more specific and visible for `b` receiver + b.baz(d, d) checkType { _() } + + // M5SubSub is invisible because it's protected in something that is not our super-class + // M6 and M1 are visible, but M6 is more specific + c.baz(d, d) checkType { _() } + + when (a) { + is C -> { + // Make sure smart cast works + a.fromC + // The same logic as for `c.foo` + a.foo(d, d) checkType { _() } + + // The same logic as for `c.baz` + a.baz(d, d) checkType { _() } + } + is B -> { + // Make sure smart cast works + a.fromB + // The same logic as `b.foo` + a.foo(d, d) checkType { _() } + + // The same logic as for `b.baz` + a.baz(d, d) checkType { _() } + } + } + + when (b) { + is C -> { + b.fromC + // In K1, it works just as `c.foo` + b.foo(d, d) checkType { _() } + // In K2, M3Sub is invisible, but we have candidate M3 from original receiver and we choose it + // Unlike the case of `c.foo` when we choose M2Sub because we don't have more special M3 there in the scope of C + // (in the meaning of overload comparison by the value parameter types) + b.foo(d, d) checkType { _() } + + // In K1, it works just as `c.foo` + b.baz(d, d) checkType { _() } + // In K2, M5SubSub is invisible, but we have candidate M5Sub from original receiver and we choose it + // Unlike the case of `c.baz` when we choose M6 because we don't have more special M5Sub there in the scope of C + // (in the meaning of overload comparison by the value parameter types) + b.baz(d, d) checkType { _() } + } + } + } +} + +class C : B() { + public val fromC: Any = Any() + + override fun foo(a1: Derived, a2: Derived): M3Sub = TODO() + + override fun baz(a1: Derived, a2: Derived): M5SubSub = TODO() + // public fallback + public fun baz(a1: Derived, a2: Base): M6 = TODO() +} diff --git a/compiler/testData/diagnostics/tests/visibility/moreSpecificProtected.fir.txt b/compiler/testData/diagnostics/tests/visibility/moreSpecificProtected.fir.txt new file mode 100644 index 00000000000..5080fde5416 --- /dev/null +++ b/compiler/testData/diagnostics/tests/visibility/moreSpecificProtected.fir.txt @@ -0,0 +1,171 @@ +FILE: moreSpecificProtected.fir.kt + public open class Base : R|kotlin/Any| { + public constructor(): R|Base| { + super() + } + + } + public final class Derived : R|Base| { + public constructor(): R|Derived| { + super() + } + + } + public abstract interface M1 : R|kotlin/Any| { + } + public abstract interface M2 : R|kotlin/Any| { + } + public abstract interface M2Sub : R|M2| { + } + public abstract interface M3 : R|kotlin/Any| { + } + public abstract interface M3Sub : R|M3| { + } + public abstract interface M4 : R|kotlin/Any| { + } + public abstract interface M5 : R|kotlin/Any| { + } + public abstract interface M5Sub : R|M5| { + } + public abstract interface M5SubSub : R|M5Sub| { + } + public abstract interface M6 : R|kotlin/Any| { + } + public open class A : R|kotlin/Any| { + public constructor(): R|A| { + super() + } + + public final fun foo(a1: R|Base|, a2: R|Base|): R|M1| { + ^foo R|kotlin/TODO|() + } + + protected open fun foo(a1: R|Base|, a2: R|Derived|): R|M2| { + ^foo R|kotlin/TODO|() + } + + protected open fun foo(a1: R|Derived|, a2: R|Derived|): R|M3| { + ^foo R|kotlin/TODO|() + } + + public final fun baz(a1: R|Base|, a2: R|Base|): R|M4| { + ^baz R|kotlin/TODO|() + } + + protected open fun baz(a1: R|Derived|, a2: R|Derived|): R|M5| { + ^baz R|kotlin/TODO|() + } + + } + public open class B : R|A| { + public constructor(): R|B| { + super() + } + + public final val fromB: R|kotlin/Any| = R|kotlin/Any.Any|() + public get(): R|kotlin/Any| + + protected open override fun foo(a1: R|Base|, a2: R|Derived|): R|M2Sub| { + ^foo R|kotlin/TODO|() + } + + protected open override fun baz(a1: R|Derived|, a2: R|Derived|): R|M5Sub| { + ^baz R|kotlin/TODO|() + } + + public final fun f(a: R|A|, b: R|B|, c: R|C|, d: R|Derived|): R|kotlin/Unit| { + R|/a|.R|/A.foo|(R|/d|, R|/d|).R|/checkType|(checkType@fun R|CheckTypeInv|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|/_|() + } + ) + R|/b|.R|/A.foo|(R|/d|, R|/d|).R|/checkType|(checkType@fun R|CheckTypeInv|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|/_|() + } + ) + R|/c|.R|/B.foo|(R|/d|, R|/d|).R|/checkType|(checkType@fun R|CheckTypeInv|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|/_|() + } + ) + R|/a|.R|/A.baz|(R|/d|, R|/d|).R|/checkType|(checkType@fun R|CheckTypeInv|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|/_|() + } + ) + R|/b|.R|/B.baz|(R|/d|, R|/d|).R|/checkType|(checkType@fun R|CheckTypeInv|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|/_|() + } + ) + R|/c|.R|/C.baz|(R|/d|, R|/d|).R|/checkType|(checkType@fun R|CheckTypeInv|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|/_|() + } + ) + when (R|/a|) { + ($subj$ is R|C|) -> { + R|/a|.R|/C.fromC| + R|/a|.R|/B.foo|(R|/d|, R|/d|).R|/checkType|(checkType@fun R|CheckTypeInv|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|/_|() + } + ) + R|/a|.R|/C.baz|(R|/d|, R|/d|).R|/checkType|(checkType@fun R|CheckTypeInv|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|/_|() + } + ) + } + ($subj$ is R|B|) -> { + R|/a|.R|/B.fromB| + R|/a|.R|/A.foo|(R|/d|, R|/d|).R|/checkType|(checkType@fun R|CheckTypeInv|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|/_|() + } + ) + R|/a|.R|/B.baz|(R|/d|, R|/d|).R|/checkType|(checkType@fun R|CheckTypeInv|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|/_|() + } + ) + } + } + + when (R|/b|) { + ($subj$ is R|C|) -> { + R|/b|.R|/C.fromC| + R|/b|.R|/A.foo|(R|/d|, R|/d|).R|/checkType|(checkType@fun R|CheckTypeInv|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|/_#|() + } + ) + R|/b|.R|/A.foo|(R|/d|, R|/d|).R|/checkType|(checkType@fun R|CheckTypeInv|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|/_|() + } + ) + R|/b|.R|/B.baz|(R|/d|, R|/d|).R|/checkType|(checkType@fun R|CheckTypeInv|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|/_#|() + } + ) + R|/b|.R|/B.baz|(R|/d|, R|/d|).R|/checkType|(checkType@fun R|CheckTypeInv|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|/_|() + } + ) + } + } + + } + + } + public final class C : R|B| { + public constructor(): R|C| { + super() + } + + public final val fromC: R|kotlin/Any| = R|kotlin/Any.Any|() + public get(): R|kotlin/Any| + + protected open override fun foo(a1: R|Derived|, a2: R|Derived|): R|M3Sub| { + ^foo R|kotlin/TODO|() + } + + protected open override fun baz(a1: R|Derived|, a2: R|Derived|): R|M5SubSub| { + ^baz R|kotlin/TODO|() + } + + public final fun baz(a1: R|Derived|, a2: R|Base|): R|M6| { + ^baz R|kotlin/TODO|() + } + + } diff --git a/compiler/testData/diagnostics/tests/visibility/moreSpecificProtected.kt b/compiler/testData/diagnostics/tests/visibility/moreSpecificProtected.kt new file mode 100644 index 00000000000..0ac4c0ca947 --- /dev/null +++ b/compiler/testData/diagnostics/tests/visibility/moreSpecificProtected.kt @@ -0,0 +1,106 @@ +// SKIP_TXT +// FIR_DUMP +// CHECK_TYPE + +open class Base +class Derived : Base() + +interface M1 +interface M2 +interface M2Sub : M2 +interface M3 +interface M3Sub : M3 +interface M4 + + +interface M5 +interface M5Sub : M5 +interface M5SubSub : M5Sub +interface M6 + +open class A { + public fun foo(a1: Base, a2: Base): M1 = TODO() + protected open fun foo(a1: Base, a2: Derived): M2 = TODO() + protected open fun foo(a1: Derived, a2: Derived): M3 = TODO() + + public fun baz(a1: Base, a2: Base): M4 = TODO() + protected open fun baz(a1: Derived, a2: Derived): M5 = TODO() +} + +open class B : A() { + public val fromB: Any = Any() + + override fun foo(a1: Base, a2: Derived): M2Sub = TODO() + + override fun baz(a1: Derived, a2: Derived): M5Sub = TODO() + + fun f(a: A, b: B, c: C, d: Derived) { + // Only visible is M1 + a.foo(d, d) checkType { _() } + // We may call M3 and it's the most specific by its parameters + b.foo(d, d) checkType { _() } + // We can't call M3 (and M3Sub) as it's protected overriden in C, so it's invisible because + // we only allow to call protected members on dispatch receiver values that are subtypes of a dispatch receiver parameter + // So, the next visible and specific member is M2Sub + c.foo(d, d) checkType { _() } + + // Only visible is M4 + a.baz(d, d) checkType { _() } + // M5Sub is more specific and visible for `b` receiver + b.baz(d, d) checkType { _() } + + // M5SubSub is invisible because it's protected in something that is not our super-class + // M6 and M1 are visible, but M6 is more specific + c.baz(d, d) checkType { _() } + + when (a) { + is C -> { + // Make sure smart cast works + a.fromC + // The same logic as for `c.foo` + a.foo(d, d) checkType { _() } + + // The same logic as for `c.baz` + a.baz(d, d) checkType { _() } + } + is B -> { + // Make sure smart cast works + a.fromB + // The same logic as `b.foo` + a.foo(d, d) checkType { _() } + + // The same logic as for `b.baz` + a.baz(d, d) checkType { _() } + } + } + + when (b) { + is C -> { + b.fromC + // In K1, it works just as `c.foo` + b.foo(d, d) checkType { _() } + // In K2, M3Sub is invisible, but we have candidate M3 from original receiver and we choose it + // Unlike the case of `c.foo` when we choose M2Sub because we don't have more special M3 there in the scope of C + // (in the meaning of overload comparison by the value parameter types) + b.foo(d, d) checkType { _() } + + // In K1, it works just as `c.foo` + b.baz(d, d) checkType { _() } + // In K2, M5SubSub is invisible, but we have candidate M5Sub from original receiver and we choose it + // Unlike the case of `c.baz` when we choose M6 because we don't have more special M5Sub there in the scope of C + // (in the meaning of overload comparison by the value parameter types) + b.baz(d, d) checkType { _() } + } + } + } +} + +class C : B() { + public val fromC: Any = Any() + + override fun foo(a1: Derived, a2: Derived): M3Sub = TODO() + + override fun baz(a1: Derived, a2: Derived): M5SubSub = TODO() + // public fallback + public fun baz(a1: Derived, a2: Base): M6 = TODO() +} diff --git a/compiler/testData/diagnostics/tests/visibility/moreSpecificProtectedSimple.fir.kt b/compiler/testData/diagnostics/tests/visibility/moreSpecificProtectedSimple.fir.kt new file mode 100644 index 00000000000..edee2312405 --- /dev/null +++ b/compiler/testData/diagnostics/tests/visibility/moreSpecificProtectedSimple.fir.kt @@ -0,0 +1,55 @@ +// SKIP_TXT +// FIR_DUMP +// ISSUE: KT-56310 + +interface Base +interface Derived : Base + +interface M1 { + val success: Boolean +} +interface M1Sub : M1 + +open class A { + open protected fun baz(a: Derived): M1 = TODO() + open protected fun foo(a: Derived): M1 = TODO() + + fun f(a: A, b: B, d: Derived) { + a.baz(d).success // OK in K1 and K2 + a.foo(d).success // OK in K1 and K2 + + // Both K1 and K2 resolves calls to B's members with String return type because other's members in B type are invisible + b.baz(d).length + b.foo(d).length + + when (a) { + is B -> { + // OK in K1 because `a.baz(d)` resolved to String returning methid (just as `b.baz(d)`) + a.baz(d).length + // OK in K2, because we have two visible candidates: + // - A::baz from original (after unwrapping smart cast) receiver + // - B::baz that returns String + // But the first one is more specific via its parameter types + a.baz(d).success // Unresolved reference in K1, going to be OK in K2 + + // Works in K2 for the same reasons as `a.baz(d)` + // In K1, works by coincidence because we bind override groups for members from original and smart cast receiver, + // and as return type from B is the same (not more specific), we choose the member from A as a group representative. + // Thus, we have successful candidates + // - A::foo + // - B::foo returning String + // But A::foo is more specific, so we choose it + a.foo(d).success + } + } + } +} + +class B : A() { + override fun baz(a: Derived): M1Sub = TODO() + public fun baz(a: Base): String = TODO() + + // The only difference between `baz` and `foo` is that the former has more specific covariant return type (M1Sub) + override fun foo(a: Derived): M1 = TODO() + public fun foo(a: Base): String = TODO() +} diff --git a/compiler/testData/diagnostics/tests/visibility/moreSpecificProtectedSimple.fir.txt b/compiler/testData/diagnostics/tests/visibility/moreSpecificProtectedSimple.fir.txt new file mode 100644 index 00000000000..6d42b31f367 --- /dev/null +++ b/compiler/testData/diagnostics/tests/visibility/moreSpecificProtectedSimple.fir.txt @@ -0,0 +1,63 @@ +FILE: moreSpecificProtectedSimple.fir.kt + public abstract interface Base : R|kotlin/Any| { + } + public abstract interface Derived : R|Base| { + } + public abstract interface M1 : R|kotlin/Any| { + public abstract val success: R|kotlin/Boolean| + public get(): R|kotlin/Boolean| + + } + public abstract interface M1Sub : R|M1| { + } + public open class A : R|kotlin/Any| { + public constructor(): R|A| { + super() + } + + protected open fun baz(a: R|Derived|): R|M1| { + ^baz R|kotlin/TODO|() + } + + protected open fun foo(a: R|Derived|): R|M1| { + ^foo R|kotlin/TODO|() + } + + public final fun f(a: R|A|, b: R|B|, d: R|Derived|): R|kotlin/Unit| { + R|/a|.R|/A.baz|(R|/d|).R|/M1.success| + R|/a|.R|/A.foo|(R|/d|).R|/M1.success| + R|/b|.R|/B.baz|(R|/d|).R|kotlin/String.length| + R|/b|.R|/B.foo|(R|/d|).R|kotlin/String.length| + when (R|/a|) { + ($subj$ is R|B|) -> { + R|/a|.R|/A.baz|(R|/d|).# + R|/a|.R|/A.baz|(R|/d|).R|/M1.success| + R|/a|.R|/A.foo|(R|/d|).R|/M1.success| + } + } + + } + + } + public final class B : R|A| { + public constructor(): R|B| { + super() + } + + protected open override fun baz(a: R|Derived|): R|M1Sub| { + ^baz R|kotlin/TODO|() + } + + public final fun baz(a: R|Base|): R|kotlin/String| { + ^baz R|kotlin/TODO|() + } + + protected open override fun foo(a: R|Derived|): R|M1| { + ^foo R|kotlin/TODO|() + } + + public final fun foo(a: R|Base|): R|kotlin/String| { + ^foo R|kotlin/TODO|() + } + + } diff --git a/compiler/testData/diagnostics/tests/visibility/moreSpecificProtectedSimple.kt b/compiler/testData/diagnostics/tests/visibility/moreSpecificProtectedSimple.kt new file mode 100644 index 00000000000..7f8b206c3cf --- /dev/null +++ b/compiler/testData/diagnostics/tests/visibility/moreSpecificProtectedSimple.kt @@ -0,0 +1,55 @@ +// SKIP_TXT +// FIR_DUMP +// ISSUE: KT-56310 + +interface Base +interface Derived : Base + +interface M1 { + val success: Boolean +} +interface M1Sub : M1 + +open class A { + open protected fun baz(a: Derived): M1 = TODO() + open protected fun foo(a: Derived): M1 = TODO() + + fun f(a: A, b: B, d: Derived) { + a.baz(d).success // OK in K1 and K2 + a.foo(d).success // OK in K1 and K2 + + // Both K1 and K2 resolves calls to B's members with String return type because other's members in B type are invisible + b.baz(d).length + b.foo(d).length + + when (a) { + is B -> { + // OK in K1 because `a.baz(d)` resolved to String returning methid (just as `b.baz(d)`) + a.baz(d).length + // OK in K2, because we have two visible candidates: + // - A::baz from original (after unwrapping smart cast) receiver + // - B::baz that returns String + // But the first one is more specific via its parameter types + a.baz(d).success // Unresolved reference in K1, going to be OK in K2 + + // Works in K2 for the same reasons as `a.baz(d)` + // In K1, works by coincidence because we bind override groups for members from original and smart cast receiver, + // and as return type from B is the same (not more specific), we choose the member from A as a group representative. + // Thus, we have successful candidates + // - A::foo + // - B::foo returning String + // But A::foo is more specific, so we choose it + a.foo(d).success + } + } + } +} + +class B : A() { + override fun baz(a: Derived): M1Sub = TODO() + public fun baz(a: Base): String = TODO() + + // The only difference between `baz` and `foo` is that the former has more specific covariant return type (M1Sub) + override fun foo(a: Derived): M1 = TODO() + public fun foo(a: Base): String = TODO() +} diff --git a/compiler/testData/diagnostics/tests/visibility/smartCastAndSuppressedVisibility.fir.kt b/compiler/testData/diagnostics/tests/visibility/smartCastAndSuppressedVisibility.fir.kt new file mode 100644 index 00000000000..c936cd06c2b --- /dev/null +++ b/compiler/testData/diagnostics/tests/visibility/smartCastAndSuppressedVisibility.fir.kt @@ -0,0 +1,20 @@ +// SKIP_TXT +// ISSUE: KT-55024 +// MODULE: a +interface A { + fun foo() +} + +internal sealed class B(val x: A) : A { + override fun foo() {} + fun bar() {} +} + +// MODULE: b(a) +@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") +private fun test_1(x: A) { + if (x is B) { + x.foo() + x.bar() + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/visibility/smartCastAndSuppressedVisibility.kt b/compiler/testData/diagnostics/tests/visibility/smartCastAndSuppressedVisibility.kt new file mode 100644 index 00000000000..ee86ce47938 --- /dev/null +++ b/compiler/testData/diagnostics/tests/visibility/smartCastAndSuppressedVisibility.kt @@ -0,0 +1,20 @@ +// SKIP_TXT +// ISSUE: KT-55024 +// MODULE: a +interface A { + fun foo() +} + +internal sealed class B(val x: A) : A { + override fun foo() {} + fun bar() {} +} + +// MODULE: b(a) +@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") +private fun test_1(x: A) { + if (x is B) { + x.foo() + x.bar() + } +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 374d95de6ba..76af32fc8eb 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -27383,6 +27383,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt37692.kt"); } + @Test + @TestMetadata("kt55722.kt") + public void testKt55722() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722.kt"); + } + + @Test + @TestMetadata("kt55722Initial.kt") + public void testKt55722Initial() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/kt55722Initial.kt"); + } + @Test @TestMetadata("numberOfDefaults.kt") public void testNumberOfDefaults() throws Exception { @@ -34790,12 +34802,30 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/visibility/javaInterfaceFieldDirectAccess.kt"); } + @Test + @TestMetadata("kt56283.kt") + public void testKt56283() throws Exception { + runTest("compiler/testData/diagnostics/tests/visibility/kt56283.kt"); + } + @Test @TestMetadata("lackOfInvisibleSetterOfJavaClassInSamePackage.kt") public void testLackOfInvisibleSetterOfJavaClassInSamePackage() throws Exception { runTest("compiler/testData/diagnostics/tests/visibility/lackOfInvisibleSetterOfJavaClassInSamePackage.kt"); } + @Test + @TestMetadata("moreSpecificProtected.kt") + public void testMoreSpecificProtected() throws Exception { + runTest("compiler/testData/diagnostics/tests/visibility/moreSpecificProtected.kt"); + } + + @Test + @TestMetadata("moreSpecificProtectedSimple.kt") + public void testMoreSpecificProtectedSimple() throws Exception { + runTest("compiler/testData/diagnostics/tests/visibility/moreSpecificProtectedSimple.kt"); + } + @Test @TestMetadata("notOverridingInternal.kt") public void testNotOverridingInternal() throws Exception { @@ -34897,6 +34927,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { public void testProtectedVisibilityAndSmartcast_overrideSameType() throws Exception { runTest("compiler/testData/diagnostics/tests/visibility/protectedVisibilityAndSmartcast_overrideSameType.kt"); } + + @Test + @TestMetadata("smartCastAndSuppressedVisibility.kt") + public void testSmartCastAndSuppressedVisibility() throws Exception { + runTest("compiler/testData/diagnostics/tests/visibility/smartCastAndSuppressedVisibility.kt"); + } } @Nested