[IrFakeOverrideBuilder] Consolidate visibility handling

Now fake overrides declarations invisible for override
are totally ignored by FakeOverrideBuilder, instead of
creating fake overrides for them, and than filtering them
out later.

As side-effect it fixes KT-64972.

^KT-64974
This commit is contained in:
Pavel Kunyavskiy
2024-01-10 16:27:38 +01:00
committed by Space Team
parent 104e6a9b7c
commit e6fd523431
5 changed files with 109 additions and 114 deletions
@@ -11,13 +11,11 @@ import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.IrTypeProjection
import org.jetbrains.kotlin.ir.types.extractTypeParameters
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.getPackageFragment
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.types.Variance
/**
@@ -37,10 +35,15 @@ abstract class FakeOverrideBuilderStrategy(
private val unimplementedOverridesStrategy: IrUnimplementedOverridesStrategy
) {
/**
* Creates a fake override for [member] from [superType] to be added to the class [clazz]
* Creates a fake override for [member] from [superType] to be added to the class [clazz] or returns null,
* if no fake override should be created for this member
*/
fun fakeOverrideMember(superType: IrType, member: IrOverridableMember, clazz: IrClass): IrOverridableMember =
buildFakeOverrideMember(superType, member, clazz, friendModules, unimplementedOverridesStrategy)
fun fakeOverrideMember(superType: IrType, member: IrOverridableMember, clazz: IrClass): IrOverridableMember? {
return if (isVisibleForOverrideInClass(member, clazz))
buildFakeOverrideMember(superType, member, clazz, unimplementedOverridesStrategy)
else
null
}
/**
* This function is a callback for fake override creation finish.
@@ -62,6 +65,41 @@ abstract class FakeOverrideBuilderStrategy(
}
}
private fun isInFriendModules(
fromModule: ModuleDescriptor,
toModule: ModuleDescriptor,
): Boolean {
val fromModuleName = fromModule.name.asStringStripSpecialMarkers()
val toModuleName = toModule.name.asStringStripSpecialMarkers()
return fromModuleName == toModuleName || friendModules[fromModuleName]?.contains(toModuleName) == true
}
private fun isVisibleForOverrideInClass(original: IrOverridableMember, clazz: IrClass) : Boolean {
return when {
DescriptorVisibilities.isPrivate(original.visibility) -> false
original.visibility == DescriptorVisibilities.INVISIBLE_FAKE -> false
original.visibility == DescriptorVisibilities.INTERNAL -> {
val thisModule = clazz.getPackageFragment().moduleDescriptor
val memberModule = original.getPackageFragment().moduleDescriptor
when {
thisModule == memberModule -> true
isInFriendModules(thisModule, memberModule) -> true
// TODO: this is very questionable - KT-63381
original.hasAnnotation(StandardClassIds.Annotations.PublishedApi) -> true
else -> false
}
}
else -> {
original.visibility.visibleFromPackage(
clazz.getPackageFragment().packageFqName,
original.getPackageFragment().packageFqName
)
}
}
}
/**
* Most implementations need [file] in which they are working now.
*
@@ -93,33 +131,10 @@ abstract class FakeOverrideBuilderStrategy(
protected abstract fun linkPropertyFakeOverride(property: IrPropertyWithLateBinding, manglerCompatibleMode: Boolean)
}
private fun IrOverridableMember.isPrivateToThisModule(
thisClass: IrClass, memberClass: IrClass, friendModules: Map<String, Collection<String>>,
): Boolean {
if (visibility != DescriptorVisibilities.INTERNAL) return false
val thisModule = thisClass.getPackageFragment().moduleDescriptor
val memberModule = memberClass.getPackageFragment().moduleDescriptor
return thisModule != memberModule && !isInFriendModules(thisModule, memberModule, friendModules)
}
private fun isInFriendModules(
fromModule: ModuleDescriptor,
toModule: ModuleDescriptor,
friendModules: Map<String, Collection<String>>,
): Boolean {
val fromModuleName = fromModule.name.asStringStripSpecialMarkers()
val toModuleName = toModule.name.asStringStripSpecialMarkers()
return fromModuleName == toModuleName || friendModules[fromModuleName]?.contains(toModuleName) == true
}
fun buildFakeOverrideMember(
superType: IrType,
member: IrOverridableMember,
clazz: IrClass,
friendModules: Map<String, Collection<String>> = emptyMap(),
unimplementedOverridesStrategy: IrUnimplementedOverridesStrategy = IrUnimplementedOverridesStrategy.ProcessAsFakeOverrides,
): IrOverridableMember {
require(superType is IrSimpleType) { "superType is $superType, expected IrSimpleType" }
@@ -144,13 +159,7 @@ fun buildFakeOverrideMember(
return CopyIrTreeWithSymbolsForFakeOverrides(member, substitutionMap, clazz, unimplementedOverridesStrategy)
.copy()
.apply {
makeExternal(clazz.isExternal)
val isInvisible = isPrivateToThisModule(clazz, classifier.owner, friendModules)
if (isInvisible && !member.annotations.hasAnnotation(StandardNames.FqNames.publishedApi))
visibility = DescriptorVisibilities.INVISIBLE_FAKE
}
.apply { makeExternal(clazz.isExternal) }
}
// TODO: this is JS-specific functionality which should be moved out of the common ir.tree.
@@ -65,27 +65,24 @@ class IrFakeOverrideBuilder(
*/
fun buildFakeOverridesForClass(clazz: IrClass, oldSignatures: Boolean) {
strategy.inFile(clazz.fileOrNull) {
val superTypes = clazz.superTypes
val fromCurrent = clazz.declarations.filterIsInstance<IrOverridableMember>()
val allFromSuper = superTypes.flatMap { superType ->
val allFromSuper = clazz.superTypes.flatMap { superType ->
val superClass = superType.getClass() ?: error("Unexpected super type: $superType")
superClass.declarations
.filter { it.isOverridableMemberOrAccessor() }
.map {
.mapNotNull {
val overriddenMember = it as IrOverridableMember
val fakeOverride = strategy.fakeOverrideMember(superType, overriddenMember, clazz)
val fakeOverride = strategy.fakeOverrideMember(superType, overriddenMember, clazz) ?: return@mapNotNull null
FakeOverride(fakeOverride, overriddenMember)
}
}
val allFromSuperByName = allFromSuper.groupBy { it.override.name }
val allFromCurrentByName = clazz.declarations.filterIsInstanceAnd<IrOverridableMember> { !it.isStaticMember }.groupBy { it.name }
allFromSuperByName.forEach { group ->
allFromSuperByName.forEach { (name, superMembers) ->
generateOverridesInFunctionGroup(
group.value,
fromCurrent.filter { it.name == group.key && !it.isStaticMember },
superMembers,
allFromCurrentByName[name] ?: emptyList(),
clazz,
oldSignatures
)
@@ -113,10 +110,10 @@ class IrFakeOverrideBuilder(
val superClass = superType.getClass() ?: error("Unexpected super type: $superType")
superClass.declarations
.filterIsInstanceAnd<IrOverridableMember> {
it !in overriddenMembers && it.symbol !in ignoredParentSymbols && !it.isStaticMember && !DescriptorVisibilities.isPrivate(it.visibility)
it !in overriddenMembers && it.symbol !in ignoredParentSymbols && !it.isStaticMember
}
.map { overriddenMember ->
val fakeOverride = strategy.fakeOverrideMember(superType, overriddenMember, clazz)
.mapNotNull { overriddenMember ->
val fakeOverride = strategy.fakeOverrideMember(superType, overriddenMember, clazz) ?: return@mapNotNull null
FakeOverride(fakeOverride, overriddenMember)
}
}
@@ -168,10 +165,7 @@ class IrFakeOverrideBuilder(
// Note: We do allow overriding multiple FOs at once one of which is `isInline=true`.
when (overrideChecker.isOverridableBy(fromSupertype.override, fromCurrent, checkIsInlineFlag = true).result) {
OverrideCompatibilityInfo.Result.OVERRIDABLE -> {
val isVisibleFake = fromSupertype.override.visibility != DescriptorVisibilities.INVISIBLE_FAKE
if (isVisibleFake && isVisibleForOverride(fromCurrent, fromSupertype.original)) {
overridden += fromSupertype
}
overridden += fromSupertype
bound += fromSupertype
}
OverrideCompatibilityInfo.Result.CONFLICT -> {
@@ -181,7 +175,11 @@ class IrFakeOverrideBuilder(
}
}
fromCurrent.overriddenSymbols = overridden.memoryOptimizedMap { it.original.symbol }
// because of binary incompatible changes, it's possible to have private member colliding with fake override
// In that case we shouldn't generate fake override, but also shouldn't mark them as overridden
if (!DescriptorVisibilities.isPrivate(fromCurrent.visibility)) {
fromCurrent.overriddenSymbols = overridden.memoryOptimizedMap { it.original.symbol }
}
return bound
}
@@ -295,7 +293,7 @@ class IrFakeOverrideBuilder(
"Unexpected fake override accessor kind: $this"
}
// For descriptors it gets INVISIBLE_FAKE.
if (this.visibility == DescriptorVisibilities.PRIVATE) return null
if (DescriptorVisibilities.isPrivate(this.visibility)) return null
this.visibility = newVisibility
this.modality = newModality
@@ -308,15 +306,9 @@ class IrFakeOverrideBuilder(
addedFakeOverrides: MutableList<IrOverridableMember>,
compatibilityMode: Boolean
) {
val effectiveOverridden = overridables.filter { it.override.isVisibleInClass(currentClass) }
// The descriptor based algorithm goes further building invisible fakes here,
// but we don't use invisible fakes in IR
if (effectiveOverridden.isEmpty()) return
val modality = determineModalityForFakeOverride(effectiveOverridden, currentClass)
val visibility = findMemberWithMaxVisibility(effectiveOverridden).override.visibility
val mostSpecific = selectMostSpecificMember(effectiveOverridden)
val modality = determineModalityForFakeOverride(overridables, currentClass)
val visibility = findMemberWithMaxVisibility(overridables).override.visibility
val mostSpecific = selectMostSpecificMember(overridables)
val fakeOverride = mostSpecific.override.apply {
when (this) {
@@ -334,7 +326,7 @@ class IrFakeOverrideBuilder(
}
}
fakeOverride.overriddenSymbols = effectiveOverridden.memoryOptimizedMap { it.original.symbol }
fakeOverride.overriddenSymbols = overridables.memoryOptimizedMap { it.original.symbol }
require(
fakeOverride.overriddenSymbols.isNotEmpty()
@@ -510,10 +502,10 @@ private val IrOverridableMember.returnType: IrType
}
fun IrSimpleFunction.isOverridableFunction(): Boolean =
visibility != DescriptorVisibilities.PRIVATE && hasDispatchReceiver
!DescriptorVisibilities.isPrivate(visibility) && hasDispatchReceiver
fun IrProperty.isOverridableProperty(): Boolean =
visibility != DescriptorVisibilities.PRIVATE && (getter.hasDispatchReceiver || setter.hasDispatchReceiver)
!DescriptorVisibilities.isPrivate(visibility) && (getter.hasDispatchReceiver || setter.hasDispatchReceiver)
fun IrDeclaration.isOverridableMemberOrAccessor(): Boolean = when (this) {
is IrSimpleFunction -> isOverridableFunction()
@@ -11,10 +11,6 @@ import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.util.*
fun isVisibleForOverride(overriding: IrOverridableMember, fromSuper: IrOverridableMember): Boolean {
return fromSuper.isVisibleInClass(overriding.parentAsClass)
}
fun IrDeclarationWithVisibility.isEffectivelyPrivate(): Boolean {
fun DescriptorVisibility.isNonPrivate(): Boolean =
this == DescriptorVisibilities.PUBLIC
@@ -35,26 +31,4 @@ fun IrDeclarationWithVisibility.isEffectivelyPrivate(): Boolean {
else -> true
}
}
internal fun IrOverridableMember.isVisibleInClass(klass: IrClass): Boolean {
if (DescriptorVisibilities.isPrivate(visibility) || visibility == DescriptorVisibilities.INVISIBLE_FAKE) return false
// OverridingUtil.isVisibleForOverride just calls DescriptorVisibilities.isVisible here. However, we can't use descriptors. Moreover,
// the current member's parent has already been reassigned to the current class (in OverridingUtil, it is still the original class where
// the member was declared at this point). So we load the original class from the dispatch receiver.
val dispatchReceiver = when (this) {
is IrSimpleFunction -> dispatchReceiverParameter
is IrProperty -> getter?.dispatchReceiverParameter
else -> error("Unsupported member: ${render()}")
} ?: error("Members without dispatch receiver are not possible here: ${render()} (klass=${klass.fqNameWhenAvailable}")
// Package-private Java members are only overridable within the same package.
val originalClass = dispatchReceiver.type.getClass()!!
if (!visibility.visibleFromPackage(klass.getPackageFragment().packageFqName, originalClass.getPackageFragment().packageFqName))
return false
// TODO (KT-61384): also check internal.
return true
}
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: WASM
// WITH_STDLIB
// MODULE: lib1
@@ -3,43 +3,64 @@
// VERSION: 1
open class X {
private fun foo() = "private in super"
fun bar() = foo()
private fun foo1() = "private in super"
fun testX1() = foo1()
private fun foo2() = "private in super"
fun testX2() = foo2()
private val zeg = "private in super"
fun lim() = zeg
private val val1 = "private in super"
fun testX3() = val1
private val val2 = "private in super"
fun testX4() = val2
}
// FILE: B.kt
// VERSION: 2
open class X {
public fun foo() = "public in super"
fun bar() = foo()
public fun foo1() = "public in super"
fun testX1() = foo1()
open public fun foo2() = "public in super"
fun testX2() = foo2()
private val zeg = "public in super"
fun lim() = zeg
public val val1 = "public in super"
fun testX3() = val1
open public val val2 = "public in super"
fun testX4() = val2
}
// MODULE: mainLib(lib)
// FILE: mainLib.kt
class Y: X() {
private fun foo() = "private in derived"
fun qux() = foo()
private fun foo1() = "private in derived"
fun testY1() = foo1()
private fun foo2() = "private in derived"
fun testY2() = foo2()
private val val1 = "private in derived"
fun testY3() = val1
private val val2 = "private in derived"
fun testY4() = val2
private val zeg = "private in derived"
fun tes() = zeg
}
fun lib(): String = when {
X().bar() != "public in super" -> "fail 1"
Y().bar() != "public in super" -> "fail 2"
Y().qux() != "private in derived" -> "fail 3"
X().testX1() != "public in super" -> "fail X().testX1()"
Y().testX1() != "public in super" -> "fail Y().testX1()"
Y().testY1() != "private in derived" -> "fail Y().testY1()"
X().lim() != "public in super" -> "fail 4"
Y().lim() != "public in super" -> "fail 5"
Y().tes() != "private in derived" -> "fail 6"
X().testX2() != "public in super" -> "fail X().testX2()"
Y().testX2() != "public in super" -> "fail Y().testX2()"
Y().testY2() != "private in derived" -> "fail Y().testY2()"
X().testX3() != "public in super" -> "fail X().testX3()"
Y().testX3() != "public in super" -> "fail Y().testX3()"
Y().testY3() != "private in derived" -> "fail Y().testY3()"
X().testX4() != "public in super" -> "fail X().testX4()"
Y().testX4() != "public in super" -> "fail Y().testX4()"
Y().testY4() != "private in derived" -> "fail Y().testY4()"
else -> "OK"
}