[Commonizer] Respect artificial supertypes when loading dependencies

CirProvided.ExportedForwardDeclarationClass will now correctly
report artificial superclasses.

The InlineTypeAliasCirNodeTransformer does not need to know anything
about artificial supertypes anymore. When inlining any
type alias, it is enough to resolve the type-alias's expansions
supertypes.

^KT-47430
This commit is contained in:
sebastian.sellmair
2021-08-13 10:04:36 +02:00
committed by Space
parent 26ede5d885
commit f64a0efa8b
7 changed files with 249 additions and 158 deletions

View File

@@ -82,6 +82,8 @@ internal fun <T> TargetDependent(keys: Iterable<CommonizerTarget>, factory: (tar
return FactoryBasedTargetDependent(keys.toList(), factory)
}
internal fun <T> TargetDependent(vararg pairs: Pair<CommonizerTarget, T>) = pairs.toMap().toTargetDependent()
internal fun <T> EagerTargetDependent(keys: Iterable<CommonizerTarget>, factory: (target: CommonizerTarget) -> T): TargetDependent<T> {
return keys.associateWith(factory).toTargetDependent()
}

View File

@@ -7,6 +7,10 @@ package org.jetbrains.kotlin.commonizer.mergedtree
import org.jetbrains.kotlin.commonizer.ModulesProvider
import org.jetbrains.kotlin.commonizer.cir.CirEntityId
import org.jetbrains.kotlin.commonizer.mergedtree.ArtificialSupertypes.artificialSupertypes
import org.jetbrains.kotlin.commonizer.utils.CNAMES_STRUCTS_PACKAGE
import org.jetbrains.kotlin.commonizer.utils.OBJCNAMES_CLASSES_PACKAGE
import org.jetbrains.kotlin.commonizer.utils.OBJCNAMES_PROTOCOLS_PACKAGE
import org.jetbrains.kotlin.commonizer.utils.isUnderKotlinNativeSyntheticPackages
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Visibilities
@@ -88,8 +92,7 @@ object CirProvided {
override val typeParameters: List<TypeParameter> get() = emptyList()
override val visibility: Visibility get() = Visibilities.Public
override val supertypes: List<Type>
get() = emptyList() // TODO!!
override val supertypes: List<Type> = syntheticClassId.artificialSupertypes()
}
data class TypeAlias(
@@ -129,3 +132,28 @@ object CirProvided {
data class RegularTypeProjection(val variance: Variance, val type: Type) : TypeProjection
}
/**
* Analog to "KlibResolvedModuleDescriptorsFactoryImpl.createForwardDeclarationsModule" which also
* automatically assumes relevant supertypes for forward declarations based upon the package they are in.
*/
private object ArtificialSupertypes {
private fun createType(classId: String): CirProvided.ClassType {
return CirProvided.ClassType(
classId = CirEntityId.create(classId),
outerType = null, arguments = emptyList(), isMarkedNullable = false
)
}
private val cOpaqueType = listOf(createType("kotlinx/cinterop/COpaque"))
private val objcObjectBase = listOf(createType("kotlinx/cinterop/ObjCObjectBase"))
private val objcCObject = listOf(createType("kotlinx/cinterop/ObjCObject"))
fun CirEntityId.artificialSupertypes(): List<CirProvided.Type> {
return when (packageName) {
CNAMES_STRUCTS_PACKAGE -> cOpaqueType
OBJCNAMES_CLASSES_PACKAGE -> objcObjectBase
OBJCNAMES_PROTOCOLS_PACKAGE -> objcCObject
else -> emptyList()
}
}
}

View File

@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.metadata.deserialization.*
import org.jetbrains.kotlin.serialization.deserialization.ProtoEnumFlags
import org.jetbrains.kotlin.types.Variance
internal class CirProvidedClassifiersByModules private constructor(
internal class CirProvidedClassifiersByModules internal constructor(
private val hasForwardDeclarations: Boolean,
private val classifiers: Map<CirEntityId, CirProvided.Classifier>,
) : CirProvidedClassifiers {
@@ -33,11 +33,8 @@ internal class CirProvidedClassifiersByModules private constructor(
}
override fun classifier(classifierId: CirEntityId) =
if (classifierId.packageName.isUnderKotlinNativeSyntheticPackages) {
if (hasForwardDeclarations) FALLBACK_FORWARD_DECLARATION_CLASS else null
} else {
classifiers[classifierId]
}
classifiers[classifierId] ?: if (hasForwardDeclarations && classifierId.packageName.isUnderKotlinNativeSyntheticPackages)
FALLBACK_FORWARD_DECLARATION_CLASS else null
companion object {
fun load(modulesProvider: ModulesProvider): CirProvidedClassifiers {

View File

@@ -10,18 +10,13 @@ import org.jetbrains.kotlin.commonizer.mergedtree.*
import org.jetbrains.kotlin.commonizer.mergedtree.CirNodeRelationship.Composite.Companion.plus
import org.jetbrains.kotlin.commonizer.mergedtree.CirNodeRelationship.ParentNode
import org.jetbrains.kotlin.commonizer.mergedtree.CirNodeRelationship.PreferredNode
import org.jetbrains.kotlin.commonizer.transformer.ArtificialSupertypes.artificialSupertypes
import org.jetbrains.kotlin.commonizer.utils.CNAMES_STRUCTS_PACKAGE
import org.jetbrains.kotlin.commonizer.utils.OBJCNAMES_CLASSES_PACKAGE
import org.jetbrains.kotlin.commonizer.utils.OBJCNAMES_PROTOCOLS_PACKAGE
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.storage.StorageManager
internal class InlineTypeAliasCirNodeTransformer(
private val storageManager: StorageManager,
private val classifiers: CirKnownClassifiers
private val classifiers: CirKnownClassifiers,
) : CirNodeTransformer {
override fun invoke(root: CirRootNode) {
root.modules.values.forEach(::invoke)
@@ -70,7 +65,7 @@ internal class InlineTypeAliasCirNodeTransformer(
val intoArtificialClass = ArtificialAliasedCirClass(
pointingTypeAlias = fromTypeAlias,
pointedClass = fromAliasedClassNode?.targetDeclarations?.get(targetIndex) ?: fromTypeAlias.toArtificialCirClass()
pointedClass = fromAliasedClassNode?.targetDeclarations?.get(targetIndex) ?: fromTypeAlias.toArtificialCirClass(targetIndex)
)
intoClassNode.targetDeclarations[targetIndex] = intoArtificialClass
@@ -125,6 +120,23 @@ internal class InlineTypeAliasCirNodeTransformer(
this.classes[typeAliasNode.classifierName] = classNode
return classNode
}
private fun CirTypeAlias.toArtificialCirClass(targetIndex: Int): CirClass = CirClass.create(
annotations = emptyList(), name = name, typeParameters = typeParameters, supertypes = resolveSupertypes(targetIndex),
visibility = this.visibility, modality = Modality.FINAL, kind = ClassKind.CLASS,
companion = null, isCompanion = false, isData = false, isValue = false, isInner = false, isExternal = false
)
private fun CirTypeAlias.resolveSupertypes(targetIndex: Int): List<CirType> {
if (expandedType.isMarkedNullable) return emptyList()
val resolver = SimpleCirSupertypesResolver(
classifiers = classifiers.classifierIndices[targetIndex],
dependencies = CirProvidedClassifiers.of(
classifiers.commonDependencies, classifiers.targetDependencies[targetIndex]
)
)
return resolver.supertypes(expandedType).toList()
}
}
private typealias ClassNodeIndex = Map<CirEntityId, CirClassNode>
@@ -143,38 +155,5 @@ private data class ArtificialAliasedCirClass(
set(_) = throw UnsupportedOperationException("Can't set companion on artificial class (pointed by $pointingTypeAlias)")
}
private fun CirTypeAlias.toArtificialCirClass(): CirClass = CirClass.create(
annotations = emptyList(), name = name, typeParameters = typeParameters, supertypes = artificialSupertypes(),
visibility = this.visibility, modality = Modality.FINAL, kind = ClassKind.CLASS,
companion = null, isCompanion = false, isData = false, isValue = false, isInner = false, isExternal = false
)
/**
* Analog to "KlibResolvedModuleDescriptorsFactoryImpl.createForwardDeclarationsModule" which also
* automatically assumes relevant supertypes for forward declarations based upon the package they are in.
*/
private object ArtificialSupertypes {
private fun createType(classId: String): CirClassType {
return CirClassType.createInterned(
classId = CirEntityId.create(classId),
outerType = null, visibility = Visibilities.Public, arguments = emptyList(), isMarkedNullable = false
)
}
private val cOpaqueType = listOf(createType("kotlinx/cinterop/COpaque"))
private val objcObjectBase = listOf(createType("kotlinx/cinterop/ObjCObjectBase"))
private val objcCObject = listOf(createType("kotlinx/cinterop/ObjCObject"))
fun CirTypeAlias.artificialSupertypes(): List<CirType> {
/* Not supported (yet). No real life examples known (yet), that would benefit */
if (this.expandedType.isMarkedNullable) return emptyList()
return when (underlyingType.classifierId.packageName) {
CNAMES_STRUCTS_PACKAGE -> cOpaqueType
OBJCNAMES_CLASSES_PACKAGE -> objcObjectBase
OBJCNAMES_PROTOCOLS_PACKAGE -> objcCObject
else -> emptyList()
}
}
}

View File

@@ -39,7 +39,7 @@ class HierarchicalClassAndTypeAliasCommonizationTest : AbstractInlineSourcesComm
simpleSingleSourceTarget("d", "typealias X = Short")
}
result.assertCommonized("(a, b)", "expect class X")
result.assertCommonized("(a, b)", "expect class X: Number")
result.assertCommonized("(c, d)", "expect class X")
result.assertCommonized("((a, b), (c, d))", "expect class X")
}
@@ -558,7 +558,7 @@ class HierarchicalClassAndTypeAliasCommonizationTest : AbstractInlineSourcesComm
result.assertCommonized(
"(c, d)", """
expect class Proxy
expect class Proxy: Number
typealias X = Proxy
expect val x: X
""".trimIndent()
@@ -566,7 +566,7 @@ class HierarchicalClassAndTypeAliasCommonizationTest : AbstractInlineSourcesComm
result.assertCommonized(
"(a, b, c, d)", """
expect class Proxy
expect class Proxy: Number
typealias X = Proxy
expect val x: X
""".trimIndent()

View File

@@ -1,123 +1,202 @@
package org.jetbrains.kotlin.commonizer.transformer
import org.jetbrains.kotlin.commonizer.LeafCommonizerTarget
import org.jetbrains.kotlin.commonizer.TargetDependent
import org.jetbrains.kotlin.commonizer.cir.*
import org.jetbrains.kotlin.commonizer.cir.CirClassType
import org.jetbrains.kotlin.commonizer.cir.CirEntityId
import org.jetbrains.kotlin.commonizer.cir.CirName
import org.jetbrains.kotlin.commonizer.cir.CirPackageName
import org.jetbrains.kotlin.commonizer.mapValue
import org.jetbrains.kotlin.commonizer.mergedtree.*
import org.jetbrains.kotlin.commonizer.tree.CirTreeRoot
import org.jetbrains.kotlin.commonizer.tree.mergeCirTree
import org.jetbrains.kotlin.commonizer.utils.InlineSourceBuilder
import org.jetbrains.kotlin.commonizer.utils.KtInlineSourceCommonizerTestCase
import org.jetbrains.kotlin.commonizer.utils.createCirTree
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
class InlineTypeAliasCirNodeTransformerTest {
class InlineTypeAliasCirNodeTransformerTest : KtInlineSourceCommonizerTestCase() {
private val storageManager = LockBasedStorageManager("test")
fun `test inlining typealias to common dependencies`() {
private val classifiers = CirKnownClassifiers(
classifierIndices = TargetDependent.empty(),
targetDependencies = TargetDependent.empty(),
commonizedNodes = CirCommonizedClassifierNodes.default(),
commonDependencies = CirProvidedClassifiers.EMPTY
)
@Test
fun `test artificial supertypes - regular package`() {
val root = setup(CirEntityId.create("regular/package/__X"))
InlineTypeAliasCirNodeTransformer(storageManager, classifiers).invoke(root)
val artificialXClass = root.assertInlinedXClass
assertEquals(
emptyList(), artificialXClass.supertypes,
"Expected no artificial supertype for artificial class X"
)
}
@Test
fun `test artificial supertypes - cnames structs`() {
val root = setup(CirEntityId.create("cnames/structs/__X"))
InlineTypeAliasCirNodeTransformer(storageManager, classifiers).invoke(root)
val artificialXClass = root.assertInlinedXClass
assertEquals(
setOf(CirEntityId.create("kotlinx/cinterop/COpaque")),
artificialXClass.supertypes.map { it as CirClassType }.map { it.classifierId }.toSet(),
"Expected 'COpaque' supertype being attached automatically"
)
}
@Test
fun `test artificial supertypes - objcnames classes`() {
val root = setup(CirEntityId.create("objcnames/classes/__X"))
InlineTypeAliasCirNodeTransformer(storageManager, classifiers).invoke(root)
val artificialXClass = root.assertInlinedXClass
assertEquals(
setOf(CirEntityId.create("kotlinx/cinterop/ObjCObjectBase")),
artificialXClass.supertypes.map { it as CirClassType }.map { it.classifierId }.toSet(),
"Expected 'ObjCObjectBase' supertype being attached automatically"
)
}
@Test
fun `test artificial supertypes - objcnames protocols`() {
val root = setup(CirEntityId.create("objcnames/protocols/__X"))
InlineTypeAliasCirNodeTransformer(storageManager, classifiers).invoke(root)
val artificialXClass = root.assertInlinedXClass
assertEquals(
setOf(CirEntityId.create("kotlinx/cinterop/ObjCObject")),
artificialXClass.supertypes.map { it as CirClassType }.map { it.classifierId }.toSet(),
"Expected 'ObjCObject' supertype being attached automatically"
)
}
private fun setup(typeAliasPointingTo: CirEntityId): CirRootNode {
val root = buildRootNode(storageManager, CirProvidedClassifiers.EMPTY, 1)
root.modules[CirName.create("test-module")] = buildModuleNode(storageManager, 1).apply {
packages[CirPackageName.create("under.test")] = buildPackageNode(storageManager, 2).apply {
typeAliases[CirName.create("X")] = buildTypeAliasNode(
storageManager, 2, classifiers, CirEntityId.create("under/test/X")
).apply {
val underlyingType = CirClassType.createInterned(
classId = typeAliasPointingTo,
outerType = null, visibility = Visibilities.Public,
arguments = emptyList(), isMarkedNullable = false
)
targetDeclarations[0] = CirTypeAlias.create(
annotations = emptyList(),
name = CirName.create("X"),
typeParameters = emptyList(),
visibility = Visibilities.Public,
underlyingType = underlyingType,
expandedType = underlyingType
)
}
classes[CirName.create("X")] = buildClassNode(
storageManager, 2, classifiers, null, CirEntityId.create("under/test/X")
).apply {
targetDeclarations[1] = CirClass.create(
name = CirName.create("X"), typeParameters = emptyList(),
supertypes = emptyList(), visibility = Visibilities.Public,
companion = null, isCompanion = false, isData = false, isExternal = false,
isInner = false, isValue = false, kind = ClassKind.CLASS,
modality = Modality.FINAL, annotations = emptyList(),
)
}
}
fun InlineSourceBuilder.ModuleBuilder.withDependencies() = dependency {
source(
"""
package dep
class ClassA
class ClassB: ClassA()
""".trimIndent()
)
}
return root
val targetARoot = CirTreeRoot(
modules = listOf(
createCirTree {
withDependencies()
source(
"""
package pkg
import dep.*
class X : ClassA()
""".trimIndent()
)
}
)
)
val targetBRoot = CirTreeRoot(
modules = listOf(
createCirTree {
withDependencies()
source(
"""
package pkg
import dep.*
typealias X = ClassB
""".trimIndent()
)
}
)
)
val roots = TargetDependent(
LeafCommonizerTarget("a") to targetARoot,
LeafCommonizerTarget("b") to targetBRoot
)
val classifiers = CirKnownClassifiers(
classifierIndices = roots.mapValue(::CirClassifierIndex),
targetDependencies = roots.mapValue(CirTreeRoot::dependencies),
commonizedNodes = CirCommonizedClassifierNodes.default(),
commonDependencies = CirProvidedClassifiersByModules(
true, mapOf(
CirEntityId.create("dep/ClassA") to CirProvided.RegularClass(
typeParameters = emptyList(),
kind = ClassKind.CLASS,
visibility = Visibilities.Public,
supertypes = emptyList()
),
CirEntityId.create("dep/ClassB") to CirProvided.RegularClass(
typeParameters = emptyList(),
kind = ClassKind.CLASS,
visibility = Visibilities.Public,
supertypes = listOf(
CirProvided.ClassType(
classId = CirEntityId.create("dep/ClassA"),
outerType = null,
arguments = emptyList(),
isMarkedNullable = false
)
)
)
)
)
)
val mergedTree = mergeCirTree(LockBasedStorageManager.NO_LOCKS, classifiers, roots)
InlineTypeAliasCirNodeTransformer(LockBasedStorageManager.NO_LOCKS, classifiers).invoke(mergedTree)
val pkg = mergedTree.modules.values.single().packages.getValue(CirPackageName.create("pkg"))
val xClassNode = kotlin.test.assertNotNull(pkg.classes[CirName.create("X")])
val inlinedXClass = kotlin.test.assertNotNull(xClassNode.targetDeclarations[1])
kotlin.test.assertEquals(
setOf(CirEntityId.create("dep/ClassA")),
inlinedXClass.supertypes.map { (it as? CirClassType)?.classifierId }.toSet()
)
}
private val CirRootNode.xClassNode: CirClassNode
get() = modules.getValue(CirName.create("test-module"))
.packages.getValue(CirPackageName.create("under.test"))
.classes.getValue(CirName.create("X"))
private val CirRootNode.assertInlinedXClass: CirClass
get() = assertNotNull(xClassNode.targetDeclarations[0], "Missing inlined class 'X' at index 0")
fun `test inlining typealias to target dependencies`() {
fun InlineSourceBuilder.ModuleBuilder.withDependencies() = dependency {
source(
"""
package dep
class ClassA
class ClassB: ClassA()
""".trimIndent()
)
}
val targetARoot = CirTreeRoot(
modules = listOf(
createCirTree {
withDependencies()
source(
"""
package pkg
import dep.*
class X : ClassA()
""".trimIndent()
)
}
)
)
val targetBRoot = CirTreeRoot(
dependencies = CirProvidedClassifiersByModules(
true, mapOf(
CirEntityId.create("dep/ClassA") to CirProvided.RegularClass(
typeParameters = emptyList(),
kind = ClassKind.CLASS,
visibility = Visibilities.Public,
supertypes = emptyList()
),
CirEntityId.create("dep/ClassB") to CirProvided.RegularClass(
typeParameters = emptyList(),
kind = ClassKind.CLASS,
visibility = Visibilities.Public,
supertypes = listOf(
CirProvided.ClassType(
classId = CirEntityId.create("dep/ClassA"),
outerType = null,
arguments = emptyList(),
isMarkedNullable = false
)
)
)
)
),
modules = listOf(
createCirTree {
withDependencies()
source(
"""
package pkg
import dep.*
typealias X = ClassB
""".trimIndent()
)
}
)
)
val roots = TargetDependent(
LeafCommonizerTarget("a") to targetARoot,
LeafCommonizerTarget("b") to targetBRoot
)
val classifiers = CirKnownClassifiers(
classifierIndices = roots.mapValue(::CirClassifierIndex),
targetDependencies = roots.mapValue(CirTreeRoot::dependencies),
commonizedNodes = CirCommonizedClassifierNodes.default(),
commonDependencies = CirProvidedClassifiers.EMPTY
)
val mergedTree = mergeCirTree(LockBasedStorageManager.NO_LOCKS, classifiers, roots)
InlineTypeAliasCirNodeTransformer(LockBasedStorageManager.NO_LOCKS, classifiers).invoke(mergedTree)
val pkg = mergedTree.modules.values.single().packages.getValue(CirPackageName.create("pkg"))
val xClassNode = kotlin.test.assertNotNull(pkg.classes[CirName.create("X")])
val inlinedXClass = kotlin.test.assertNotNull(xClassNode.targetDeclarations[1])
kotlin.test.assertEquals(
setOf(CirEntityId.create("dep/ClassA")),
inlinedXClass.supertypes.map { (it as? CirClassType)?.classifierId }.toSet()
)
}
}
}

View File

@@ -8,10 +8,16 @@ package org.jetbrains.kotlin.commonizer.utils
import kotlinx.metadata.klib.KlibModuleMetadata
import kotlinx.metadata.klib.annotations
import org.jetbrains.kotlin.commonizer.CommonizerTarget
import org.jetbrains.kotlin.commonizer.cir.CirFunction
import org.jetbrains.kotlin.commonizer.cir.CirProperty
import org.jetbrains.kotlin.commonizer.identityString
import org.jetbrains.kotlin.commonizer.metadata.utils.MetadataDeclarationsComparator
import org.jetbrains.kotlin.commonizer.metadata.utils.MetadataDeclarationsComparator.*
import org.jetbrains.kotlin.commonizer.metadata.utils.SerializedMetadataLibraryProvider
import org.jetbrains.kotlin.commonizer.tree.CirTreeClass
import org.jetbrains.kotlin.commonizer.tree.CirTreeModule
import org.jetbrains.kotlin.commonizer.tree.CirTreePackage
import org.jetbrains.kotlin.commonizer.tree.CirTreeTypeAlias
import org.jetbrains.kotlin.library.SerializedMetadata
import java.io.File
import kotlin.contracts.ExperimentalContracts