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