From da6006b7d005d60011bd3249a214dbc1a3de2c03 Mon Sep 17 00:00:00 2001 From: Nikolay Lunyak Date: Tue, 20 Feb 2024 14:48:49 +0200 Subject: [PATCH] [FIR] Relax intersection overrides visibility requirements for Java Allow multiple bases with default implementations as long as there's a non-abstract symbol from a class. Our rules for Kotlin are stricter than those in Java. --- .../diagnostics/KtFirDataClassConverters.kt | 1 - .../api/fir/diagnostics/KtFirDiagnostics.kt | 3 +- .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 3 +- .../diagnostics/FirDiagnosticsList.kt | 3 +- .../fir/analysis/diagnostics/FirErrors.kt | 3 +- .../java/scopes/JavaOverridabilityRules.kt | 9 +++ .../fir/java/scopes/JavaOverrideChecker.kt | 36 ++++++++++++ .../native/FirNativeOverrideChecker.kt | 9 +++ .../FirIntersectionScopeOverrideChecker.kt | 11 ++++ .../kotlin/fir/scopes/FirOverrideChecker.kt | 7 +++ .../PlatformSpecificOverridabilityRules.kt | 8 +++ .../fir/scopes/impl/FirOverrideUtils.kt | 57 +++++++++++++++++++ .../scopes/impl/FirStandardOverrideChecker.kt | 13 +++++ .../impl/FirTypeIntersectionScopeContext.kt | 55 +----------------- .../diagnostics/PositioningStrategies.kt | 6 +- ...ersectionWithMultipleDefaultsInJava.fir.kt | 34 ----------- .../intersectionWithMultipleDefaultsInJava.kt | 1 + 17 files changed, 159 insertions(+), 100 deletions(-) delete mode 100644 compiler/testData/diagnostics/tests/intersectionWithMultipleDefaultsInJava.fir.kt diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt index e8b3a7d6988..97795825f1b 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.analysis.api.fir.diagnostics import com.intellij.psi.PsiElement -import com.intellij.psi.PsiNameIdentifierOwner import com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.KtPsiSourceElement import org.jetbrains.kotlin.diagnostics.KtPsiDiagnostic diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index dc6fe8160f6..628557966c2 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.analysis.api.fir.diagnostics import com.intellij.psi.PsiElement -import com.intellij.psi.PsiNameIdentifierOwner import com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.analysis.api.diagnostics.KtDiagnosticWithPsi import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol @@ -1928,7 +1927,7 @@ sealed interface KtFirDiagnostic : KtDiagnosticWithPsi { val containingClassName: Name } - interface CannotInferVisibility : KtFirDiagnostic { + interface CannotInferVisibility : KtFirDiagnostic { override val diagnosticClass get() = CannotInferVisibility::class val callable: KtCallableSymbol } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index e720780bd97..1ee83ff95fe 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.analysis.api.fir.diagnostics import com.intellij.psi.PsiElement -import com.intellij.psi.PsiNameIdentifierOwner import com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol @@ -2320,7 +2319,7 @@ internal class CannotInferVisibilityImpl( override val callable: KtCallableSymbol, firDiagnostic: KtPsiDiagnostic, token: KtLifetimeToken, -) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.CannotInferVisibility +) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.CannotInferVisibility internal class MultipleDefaultsInheritedFromSupertypesImpl( override val name: Name, diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 74f2e7bed40..57992456ae0 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.fir.checkers.generator.diagnostics import com.intellij.psi.PsiElement -import com.intellij.psi.PsiNameIdentifierOwner import com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.builtins.functions.FunctionTypeKind @@ -927,7 +926,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { parameter>("overridden") parameter("containingClassName") } - val CANNOT_INFER_VISIBILITY by error(PositioningStrategy.DECLARATION_NAME) { + val CANNOT_INFER_VISIBILITY by error(PositioningStrategy.DECLARATION_NAME) { parameter>("callable") } diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index a116e852069..6bf2cb3e1b7 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.fir.analysis.diagnostics import com.intellij.psi.PsiElement -import com.intellij.psi.PsiNameIdentifierOwner import com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.builtins.functions.FunctionTypeKind @@ -519,7 +518,7 @@ object FirErrors { val DATA_CLASS_OVERRIDE_DEFAULT_VALUES: KtDiagnosticFactory2, FirClassSymbol<*>> by error2, FirClassSymbol<*>>(SourceElementPositioningStrategies.DATA_MODIFIER) val CANNOT_WEAKEN_ACCESS_PRIVILEGE: KtDiagnosticFactory3, Name> by error3, Name>(SourceElementPositioningStrategies.VISIBILITY_MODIFIER) val CANNOT_CHANGE_ACCESS_PRIVILEGE: KtDiagnosticFactory3, Name> by error3, Name>(SourceElementPositioningStrategies.VISIBILITY_MODIFIER) - val CANNOT_INFER_VISIBILITY: KtDiagnosticFactory1> by error1>(SourceElementPositioningStrategies.DECLARATION_NAME) + val CANNOT_INFER_VISIBILITY: KtDiagnosticFactory1> by error1>(SourceElementPositioningStrategies.DECLARATION_NAME) val MULTIPLE_DEFAULTS_INHERITED_FROM_SUPERTYPES: KtDiagnosticFactory3>> by error3>>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) val MULTIPLE_DEFAULTS_INHERITED_FROM_SUPERTYPES_WHEN_NO_EXPLICIT_OVERRIDE: KtDiagnosticFactory3>> by error3>>(SourceElementPositioningStrategies.DECLARATION_NAME) val MULTIPLE_DEFAULTS_INHERITED_FROM_SUPERTYPES_DEPRECATION: KtDiagnosticFactoryForDeprecation3>> by deprecationError3>>(ProhibitAllMultipleDefaultsInheritedFromSupertypes, SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaOverridabilityRules.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaOverridabilityRules.kt index f96fba355f9..66d91177c37 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaOverridabilityRules.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaOverridabilityRules.kt @@ -5,13 +5,17 @@ package org.jetbrains.kotlin.fir.java.scopes +import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin import org.jetbrains.kotlin.fir.declarations.FirProperty import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.java.JavaTypeParameterStack +import org.jetbrains.kotlin.fir.scopes.MemberWithBaseScope import org.jetbrains.kotlin.fir.scopes.PlatformSpecificOverridabilityRules +import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.fir.unwrapFakeOverrides class JavaOverridabilityRules(session: FirSession) : PlatformSpecificOverridabilityRules { @@ -46,4 +50,9 @@ class JavaOverridabilityRules(session: FirSession) : PlatformSpecificOverridabil } private fun FirCallableDeclaration.isOriginallyFromJava(): Boolean = unwrapFakeOverrides().origin == FirDeclarationOrigin.Enhancement + + override fun > chooseIntersectionVisibility( + extractedOverrides: Collection>, + dispatchClassSymbol: FirRegularClassSymbol?, + ): Visibility = javaOverrideChecker.chooseIntersectionVisibility(extractedOverrides, dispatchClassSymbol) } diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaOverrideChecker.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaOverrideChecker.kt index 2ccecf52103..25cd697afdb 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaOverrideChecker.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaOverrideChecker.kt @@ -5,23 +5,36 @@ package org.jetbrains.kotlin.fir.java.scopes +import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.analysis.checkers.classKind import org.jetbrains.kotlin.fir.containingClassLookupTag import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.isStatic import org.jetbrains.kotlin.fir.declarations.utils.modality +import org.jetbrains.kotlin.fir.dispatchReceiverClassLookupTagOrNull import org.jetbrains.kotlin.fir.java.JavaTypeParameterStack +import org.jetbrains.kotlin.fir.java.declarations.FirJavaClass import org.jetbrains.kotlin.fir.java.enhancement.readOnlyToMutable import org.jetbrains.kotlin.fir.java.toConeKotlinTypeProbablyFlexible import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap +import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.scopes.FirTypeScope +import org.jetbrains.kotlin.fir.scopes.MemberWithBaseScope import org.jetbrains.kotlin.fir.scopes.ProcessorAction import org.jetbrains.kotlin.fir.scopes.impl.FirAbstractOverrideChecker +import org.jetbrains.kotlin.fir.scopes.impl.chooseIntersectionVisibilityOrNull +import org.jetbrains.kotlin.fir.scopes.impl.filterOutDuplicates +import org.jetbrains.kotlin.fir.scopes.impl.isAbstract import org.jetbrains.kotlin.fir.scopes.jvm.computeJvmDescriptorRepresentation import org.jetbrains.kotlin.fir.scopes.processOverriddenFunctions +import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.unwrapFakeOverrides @@ -357,4 +370,27 @@ class JavaOverrideChecker internal constructor( return overridesMutableCollectionRemove } + + override fun > chooseIntersectionVisibility( + extractedOverrides: Collection>, + dispatchClassSymbol: FirRegularClassSymbol?, + ): Visibility { + val overridesWithoutIntersections = extractedOverrides.flatMap { it.flattenIntersectionsRecursively() } + val nonSubsumed = overridesWithoutIntersections.nonSubsumed().filterOutDuplicates() + + // In Java it's OK to inherit multiple implementations of the same function + // from the supertypes as long as there's an implementation from a class. + // We shouldn't reject green Java code. + if (dispatchClassSymbol?.fir is FirJavaClass) { + val nonAbstractFromClass = nonSubsumed.find { + !it.isAbstract && it.member.dispatchReceiverClassLookupTagOrNull() + ?.toSymbol(session)?.classKind == ClassKind.CLASS + } + if (nonAbstractFromClass != null) { + return nonAbstractFromClass.member.rawStatus.visibility + } + } + + return chooseIntersectionVisibilityOrNull(nonSubsumed) ?: Visibilities.Unknown + } } diff --git a/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/FirNativeOverrideChecker.kt b/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/FirNativeOverrideChecker.kt index 7b0fd2ebfce..bf5a8a6e094 100644 --- a/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/FirNativeOverrideChecker.kt +++ b/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/FirNativeOverrideChecker.kt @@ -5,13 +5,17 @@ package org.jetbrains.kotlin.fir.backend.native +import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.backend.native.interop.decodeObjCMethodAnnotation import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration import org.jetbrains.kotlin.fir.declarations.FirProperty import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.scopes.FirOverrideChecker +import org.jetbrains.kotlin.fir.scopes.MemberWithBaseScope import org.jetbrains.kotlin.fir.scopes.impl.FirStandardOverrideChecker +import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol /** * The same code, as in this class also exists in [org.jetbrains.kotlin.ir.objcinterop.ObjCOverridabilityCondition] @@ -30,6 +34,11 @@ class FirNativeOverrideChecker(private val session: FirSession) : FirOverrideChe // KT-57640: There's no necessity to implement platform-dependent overridability check for properties standardOverrideChecker.isOverriddenProperty(overrideCandidate, baseDeclaration) + override fun > chooseIntersectionVisibility( + extractedOverrides: Collection>, + dispatchClassSymbol: FirRegularClassSymbol?, + ): Visibility = standardOverrideChecker.chooseIntersectionVisibility(extractedOverrides, dispatchClassSymbol) + /** * mimics ObjCOverridabilityCondition.isOverridable */ diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirIntersectionScopeOverrideChecker.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirIntersectionScopeOverrideChecker.kt index ca2f5fc471f..b16122fc072 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirIntersectionScopeOverrideChecker.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirIntersectionScopeOverrideChecker.kt @@ -5,10 +5,13 @@ package org.jetbrains.kotlin.fir.scopes +import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration import org.jetbrains.kotlin.fir.declarations.FirProperty import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction +import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol /** * That class is expected to work just the same as FirStandardOverrideChecker for regular members, @@ -31,4 +34,12 @@ class FirIntersectionScopeOverrideChecker(session: FirSession) : FirOverrideChec platformSpecificOverridabilityRules?.isOverriddenProperty(overrideCandidate, baseDeclaration)?.let { return it } return standardOverrideChecker.isOverriddenProperty(overrideCandidate, baseDeclaration) } + + override fun > chooseIntersectionVisibility( + extractedOverrides: Collection>, + dispatchClassSymbol: FirRegularClassSymbol?, + ): Visibility { + platformSpecificOverridabilityRules?.chooseIntersectionVisibility(extractedOverrides, dispatchClassSymbol)?.let { return it } + return standardOverrideChecker.chooseIntersectionVisibility(extractedOverrides, dispatchClassSymbol) + } } diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirOverrideChecker.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirOverrideChecker.kt index 04863273112..bb35b2ee012 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirOverrideChecker.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirOverrideChecker.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.fir.scopes +import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSessionComponent import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration @@ -13,6 +14,7 @@ import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol interface FirOverrideChecker : FirSessionComponent { fun isOverriddenFunction( @@ -24,6 +26,11 @@ interface FirOverrideChecker : FirSessionComponent { overrideCandidate: FirCallableDeclaration, // NB: in Java it can be a function which overrides accessor baseDeclaration: FirProperty ): Boolean + + fun > chooseIntersectionVisibility( + extractedOverrides: Collection>, + dispatchClassSymbol: FirRegularClassSymbol?, + ): Visibility } fun FirOverrideChecker.isOverriddenFunction( diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/PlatformSpecificOverridabilityRules.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/PlatformSpecificOverridabilityRules.kt index 465cce4a1c3..19ec7958050 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/PlatformSpecificOverridabilityRules.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/PlatformSpecificOverridabilityRules.kt @@ -5,11 +5,14 @@ package org.jetbrains.kotlin.fir.scopes +import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSessionComponent import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration import org.jetbrains.kotlin.fir.declarations.FirProperty import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction +import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol interface PlatformSpecificOverridabilityRules : FirSessionComponent { // Thus functions return "null" in case the status should be defined via standard platform-independent rules @@ -22,6 +25,11 @@ interface PlatformSpecificOverridabilityRules : FirSessionComponent { overrideCandidate: FirCallableDeclaration, baseDeclaration: FirProperty ): Boolean? + + fun > chooseIntersectionVisibility( + extractedOverrides: Collection>, + dispatchClassSymbol: FirRegularClassSymbol?, + ): Visibility? } val FirSession.platformSpecificOverridabilityRules: PlatformSpecificOverridabilityRules? by FirSession.nullableSessionComponentAccessor() diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirOverrideUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirOverrideUtils.kt index ae3dda4e543..774b9cc801f 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirOverrideUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirOverrideUtils.kt @@ -5,10 +5,18 @@ package org.jetbrains.kotlin.fir.scopes.impl +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.descriptors.Visibility +import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration +import org.jetbrains.kotlin.fir.declarations.FirResolvedDeclarationStatus +import org.jetbrains.kotlin.fir.declarations.utils.visibility import org.jetbrains.kotlin.fir.scopes.* import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol +import org.jetbrains.kotlin.fir.unwrapSubstitutionOverrides +import org.jetbrains.kotlin.name.StandardClassIds fun filterOutOverriddenFunctions(extractedOverridden: Collection>): Collection> { return filterOutOverridden(extractedOverridden, FirTypeScope::processDirectOverriddenFunctionsWithBaseScope) @@ -55,3 +63,52 @@ fun > overrides( return result } + +fun chooseIntersectionVisibilityOrNull( + nonSubsumedOverrides: List>>, +): Visibility? { + val nonAbstract = nonSubsumedOverrides.filter { + // Kotlin's Cloneable interface contains phantom `protected open fun clone()`. + !it.isAbstract && it.member.callableId != StandardClassIds.Callables.clone + } + val allAreAbstract = nonAbstract.isEmpty() + + if (allAreAbstract) { + return findMaxVisibilityOrNull(nonSubsumedOverrides) + } + + return nonAbstract.singleOrNull()?.member?.rawStatus?.visibility +} + +val MemberWithBaseScope>.isAbstract: Boolean + get() { + // This function is expected to be called during FirResolvePhase.STATUS, + // meaning we can't yet access `resolvedStatus`, because it would require + // the same phase, but by this time we expect the statuses to have been + // calculated de-facto. + require(member.rawStatus is FirResolvedDeclarationStatus) + // Kotlin's Cloneable interface contains phantom `protected open fun clone()`. + return member.rawStatus.modality == Modality.ABSTRACT + } + +fun > List>.filterOutDuplicates(): List> { + val uniqueSymbols = mutableSetOf>() + return filter { uniqueSymbols.add(it.member.fir.unwrapSubstitutionOverrides().symbol) } +} + +fun > findMaxVisibilityOrNull( + extractedOverrides: Collection> +): Visibility? { + var maxVisibility: Visibility = Visibilities.Private + + for ((override) in extractedOverrides) { + val visibility = (override.fir as FirMemberDeclaration).visibility + val compare = Visibilities.compare(visibility, maxVisibility) ?: return null + + if (compare > 0) { + maxVisibility = visibility + } + } + + return maxVisibility +} diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirStandardOverrideChecker.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirStandardOverrideChecker.kt index 707c6670b43..988148047bc 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirStandardOverrideChecker.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirStandardOverrideChecker.kt @@ -6,12 +6,16 @@ package org.jetbrains.kotlin.fir.scopes.impl import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.isSuspend import org.jetbrains.kotlin.fir.declarations.utils.visibility import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.transformers.ensureResolvedTypeDeclaration +import org.jetbrains.kotlin.fir.scopes.MemberWithBaseScope +import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.types.AbstractTypeChecker @@ -157,4 +161,13 @@ class FirStandardOverrideChecker(private val session: FirSession) : FirAbstractO isEqualTypes(memberParam.typeRef, selfParam.typeRef, substitutor) } } + + override fun > chooseIntersectionVisibility( + extractedOverrides: Collection>, + dispatchClassSymbol: FirRegularClassSymbol?, + ): Visibility { + val overridesWithoutIntersections = extractedOverrides.flatMap { it.flattenIntersectionsRecursively() } + val nonSubsumed = overridesWithoutIntersections.nonSubsumed().filterOutDuplicates() + return chooseIntersectionVisibilityOrNull(nonSubsumed) ?: Visibilities.Unknown + } } diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirTypeIntersectionScopeContext.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirTypeIntersectionScopeContext.kt index 02a794d1faa..90645c47601 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirTypeIntersectionScopeContext.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirTypeIntersectionScopeContext.kt @@ -13,7 +13,6 @@ import org.jetbrains.kotlin.fir.caches.* import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.isExpect import org.jetbrains.kotlin.fir.declarations.utils.modality -import org.jetbrains.kotlin.fir.declarations.utils.visibility import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculatorForFullBodyResolve import org.jetbrains.kotlin.fir.scopes.* @@ -22,7 +21,6 @@ import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.utils.addToStdlib.runIf import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract @@ -207,7 +205,7 @@ class FirTypeIntersectionScopeContext( extractedOverrides: List>, ): MemberWithBaseScope> { val newModality = chooseIntersectionOverrideModality(extractedOverrides.flatMap { it.flattenIntersectionsRecursively() }.nonSubsumed()) - val newVisibility = chooseIntersectionVisibility(extractedOverrides) + val newVisibility = overrideChecker.chooseIntersectionVisibility(extractedOverrides, dispatchClassSymbol) val mostSpecificSymbols = mostSpecific.map { it.member } val extractedOverridesSymbols = extractedOverrides.map { it.member } val key = mostSpecific.first() @@ -320,57 +318,6 @@ class FirTypeIntersectionScopeContext( } } - private fun > chooseIntersectionVisibility( - extractedOverrides: Collection> - ): Visibility = chooseIntersectionVisibilityOrNull(extractedOverrides) ?: Visibilities.Unknown - - private fun > chooseIntersectionVisibilityOrNull( - extractedOverrides: Collection> - ): Visibility? { - val overridesWithoutIntersections = extractedOverrides.flatMap { it.flattenIntersectionsRecursively() } - val nonSubsumed = overridesWithoutIntersections.nonSubsumed().filterOutDuplicates() - val nonAbstract = nonSubsumed.filter { - require(it.member.rawStatus is FirResolvedDeclarationStatus) { - "We expect that to be true already, but we can't yet call resolvedStatus" - } - // Kotlin's Cloneable interface contains phantom `protected open fun clone()`. - it.member.rawStatus.modality != Modality.ABSTRACT && it.member.callableId != StandardClassIds.Callables.clone - } - val allAreAbstract = nonAbstract.isEmpty() - - if (allAreAbstract) { - return findMaxVisibilityOrNull(nonSubsumed) - } - - if (nonAbstract.size >= 2) { - return null - } - - return nonAbstract.single().member.rawStatus.visibility - } - - private fun > List>.filterOutDuplicates(): List> { - val uniqueSymbols = mutableSetOf>() - return filter { uniqueSymbols.add(it.member.fir.unwrapSubstitutionOverrides().symbol) } - } - - private fun > findMaxVisibilityOrNull( - extractedOverrides: Collection> - ): Visibility? { - var maxVisibility: Visibility = Visibilities.Private - - for ((override) in extractedOverrides) { - val visibility = (override.fir as FirMemberDeclaration).visibility - val compare = Visibilities.compare(visibility, maxVisibility) ?: return null - - if (compare > 0) { - maxVisibility = visibility - } - } - - return maxVisibility - } - private fun createIntersectionOverrideFunction( mostSpecific: Collection>, overrides: Collection>, diff --git a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt index 72e50a851e1..610e50fbc35 100644 --- a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt +++ b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt @@ -22,7 +22,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.sure object PositioningStrategies { - open class DeclarationHeader : PositioningStrategy() { + open class DeclarationHeader : PositioningStrategy() { override fun isValid(element: T): Boolean { if (element is KtNamedDeclaration && element !is KtObjectDeclaration && @@ -142,8 +142,8 @@ object PositioningStrategies { } @JvmField - val DECLARATION_NAME: PositioningStrategy = object : DeclarationHeader() { - override fun mark(element: PsiNameIdentifierOwner): List { + val DECLARATION_NAME: PositioningStrategy = object : DeclarationHeader() { + override fun mark(element: KtNamedDeclaration): List { val nameIdentifier = element.nameIdentifier if (nameIdentifier != null) { if (element is KtClassOrObject) { diff --git a/compiler/testData/diagnostics/tests/intersectionWithMultipleDefaultsInJava.fir.kt b/compiler/testData/diagnostics/tests/intersectionWithMultipleDefaultsInJava.fir.kt deleted file mode 100644 index 6ac12129809..00000000000 --- a/compiler/testData/diagnostics/tests/intersectionWithMultipleDefaultsInJava.fir.kt +++ /dev/null @@ -1,34 +0,0 @@ -// TARGET_BACKEND: JVM - -// FILE: IntCollection.java -interface IntCollection { - public boolean add(int key); -} - -// FILE: IntSet.java -interface IntSet extends IntCollection { - public default boolean add(Integer it) { return true; } - - // from the supertype - // public boolean add(int key); -} - -// FILE: AbstractCollection.java -abstract class AbstractCollection { - public boolean add(E it) { return true; } -} - -// FILE: AbstractIntCollection.java -abstract class AbstractIntCollection extends AbstractCollection { - public boolean add(int it) { return true; } - - // from the supertype - // public default boolen add(Integer it) { return true; } -} - -// FILE: AbstractIntSet.java -public abstract class AbstractIntSet extends AbstractIntCollection implements IntSet {} - -// FILE: Main.kt - -class KotlinClass : AbstractIntSet() diff --git a/compiler/testData/diagnostics/tests/intersectionWithMultipleDefaultsInJava.kt b/compiler/testData/diagnostics/tests/intersectionWithMultipleDefaultsInJava.kt index 7c0bd32b2b1..17048d1fce7 100644 --- a/compiler/testData/diagnostics/tests/intersectionWithMultipleDefaultsInJava.kt +++ b/compiler/testData/diagnostics/tests/intersectionWithMultipleDefaultsInJava.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // TARGET_BACKEND: JVM // FILE: IntCollection.java