mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-03-10 08:31:29 +00:00
[Commonizer] Implement alis type substitution as CirNodeTransformer
This has the advantage, that the substitution only has to run on functions or properties that are 'incomplete' (missing at least one other target declaration) ^KT-47433 Verification Pending
This commit is contained in:
committed by
Space
parent
7c450f9884
commit
c4d90dc744
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.commonizer.mergedtree.CirNode.Companion.indexOfCommo
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirRootNode
|
||||
import org.jetbrains.kotlin.commonizer.metadata.CirTreeSerializer
|
||||
import org.jetbrains.kotlin.commonizer.transformer.InlineTypeAliasCirNodeTransformer
|
||||
import org.jetbrains.kotlin.commonizer.transformer.TypeSubstitutionCirNodeTransformer
|
||||
import org.jetbrains.kotlin.commonizer.tree.CirTreeRoot
|
||||
import org.jetbrains.kotlin.commonizer.tree.defaultCirTreeRootDeserializer
|
||||
import org.jetbrains.kotlin.commonizer.tree.mergeCirTree
|
||||
@@ -56,7 +57,10 @@ internal fun commonizeTarget(
|
||||
)
|
||||
|
||||
val mergedTree = mergeCirTree(parameters.storageManager, classifiers, availableTrees)
|
||||
|
||||
InlineTypeAliasCirNodeTransformer(parameters.storageManager, classifiers).invoke(mergedTree)
|
||||
TypeSubstitutionCirNodeTransformer(parameters.storageManager, classifiers, availableTrees).invoke(mergedTree)
|
||||
|
||||
mergedTree.accept(CommonizationVisitor(classifiers, mergedTree), Unit)
|
||||
|
||||
return mergedTree
|
||||
|
||||
@@ -7,7 +7,7 @@ package org.jetbrains.kotlin.commonizer.mergedtree
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirClass
|
||||
|
||||
internal class CirMemberContext private constructor(val classes: List<CirClass>) {
|
||||
class CirMemberContext private constructor(internal val classes: List<CirClass>) {
|
||||
|
||||
companion object {
|
||||
val empty = CirMemberContext(emptyList())
|
||||
|
||||
@@ -3,11 +3,15 @@
|
||||
* 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.commonizer.mergedtree
|
||||
package org.jetbrains.kotlin.commonizer.transformer
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.cir.*
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirClassType.Companion.copyInterned
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirTypeAliasType.Companion.copyInterned
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirClassifierIndex
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirProvidedClassifiers
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirTypeSubstitutor
|
||||
import org.jetbrains.kotlin.commonizer.tree.CirTreeTypeAlias
|
||||
|
||||
internal class CirAliasTypeSubstitutor(
|
||||
private val commonDependencies: CirProvidedClassifiers,
|
||||
@@ -33,7 +37,6 @@ internal class CirAliasTypeSubstitutor(
|
||||
}
|
||||
|
||||
private fun substituteClassOrTypeAliasType(targetIndex: Int, type: CirClassOrTypeAliasType): CirClassOrTypeAliasType {
|
||||
|
||||
/*
|
||||
Classifier id is available in all platforms or is provided by common dependencies.
|
||||
The classifier itself does not require substitution, but type arguments potentially do!
|
||||
@@ -46,7 +49,6 @@ internal class CirAliasTypeSubstitutor(
|
||||
} else type
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Yet, we do not have any evidence that it is worth trying to substitute types with arguments when the type itself is
|
||||
* not common at all. This substitutor could try to substitute the type & it's arguments.
|
||||
@@ -70,28 +72,34 @@ internal class CirAliasTypeSubstitutor(
|
||||
private fun substituteClassType(targetIndex: Int, type: CirClassType): CirClassOrTypeAliasType {
|
||||
if (type.arguments.isNotEmpty()) return type
|
||||
if (type.outerType != null) return type
|
||||
if (isCommon(type.classifierId)) return type
|
||||
val classifierIndex = classifierIndices[targetIndex]
|
||||
|
||||
var typeAliases = classifierIndex.findTypeAliasesWithUnderlyingType(type.classifierId)
|
||||
val typeAliases = classifierIndex.findTypeAliasesWithUnderlyingType(type.classifierId)
|
||||
val commonTypeAlias = findSuitableCommonTypeAlias(classifierIndex, typeAliases) ?: return type
|
||||
|
||||
while (typeAliases.isNotEmpty()) {
|
||||
val commonTypeAlias = typeAliases.firstOrNull { (id, typeAlias) -> typeAlias.typeParameters.isEmpty() && isCommon(id) }
|
||||
return CirTypeAliasType.createInterned(
|
||||
typeAliasId = commonTypeAlias.id,
|
||||
underlyingType = commonTypeAlias.typeAlias.underlyingType,
|
||||
arguments = emptyList(),
|
||||
isMarkedNullable = type.isMarkedNullable
|
||||
)
|
||||
}
|
||||
|
||||
if (commonTypeAlias != null) {
|
||||
return CirTypeAliasType.createInterned(
|
||||
typeAliasId = commonTypeAlias.id,
|
||||
underlyingType = commonTypeAlias.typeAlias.underlyingType,
|
||||
arguments = emptyList(),
|
||||
isMarkedNullable = type.isMarkedNullable
|
||||
)
|
||||
}
|
||||
private tailrec fun findSuitableCommonTypeAlias(
|
||||
index: CirClassifierIndex,
|
||||
typeAliases: List<CirTreeTypeAlias>
|
||||
): CirTreeTypeAlias? {
|
||||
if (typeAliases.isEmpty()) return null
|
||||
|
||||
typeAliases = typeAliases.flatMap { (id, _) ->
|
||||
classifierIndex.findTypeAliasesWithUnderlyingType(id)
|
||||
}
|
||||
val commonTypeAlias = typeAliases.firstOrNull { (id, typeAlias) -> typeAlias.typeParameters.isEmpty() && isCommon(id) }
|
||||
if (commonTypeAlias != null) {
|
||||
return commonTypeAlias
|
||||
}
|
||||
|
||||
return type
|
||||
return findSuitableCommonTypeAlias(
|
||||
index, typeAliases.flatMap { (id, _) -> index.findTypeAliasesWithUnderlyingType(id) }
|
||||
)
|
||||
}
|
||||
|
||||
private fun isCommon(id: CirEntityId): Boolean =
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.commonizer.transformer
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.*
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirNodeRelationship.ParentNode
|
||||
import org.jetbrains.kotlin.commonizer.tree.CirTreeRoot
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
|
||||
internal fun TypeSubstitutionCirNodeTransformer(
|
||||
storageManager: StorageManager, classifiers: CirKnownClassifiers, roots: Iterable<CirTreeRoot>
|
||||
): TypeSubstitutionCirNodeTransformer {
|
||||
val typeSubstitutor = CirAliasTypeSubstitutor(classifiers.commonDependencies, roots.map(::CirClassifierIndex))
|
||||
return TypeSubstitutionCirNodeTransformer(storageManager, classifiers, typeSubstitutor)
|
||||
}
|
||||
|
||||
internal class TypeSubstitutionCirNodeTransformer(
|
||||
private val storageManager: StorageManager,
|
||||
private val classifiers: CirKnownClassifiers,
|
||||
private val typeSubstitutor: CirTypeSubstitutor
|
||||
) : CirNodeTransformer {
|
||||
|
||||
override fun invoke(root: CirRootNode) {
|
||||
for (index in 0 until root.targetDeclarations.size) {
|
||||
root.modules.forEach { (_, module) -> this(module, index) }
|
||||
}
|
||||
}
|
||||
|
||||
private operator fun invoke(module: CirModuleNode, index: Int) {
|
||||
module.packages.forEach { (_, pkg) -> this(pkg, index) }
|
||||
}
|
||||
|
||||
private operator fun invoke(pkg: CirPackageNode, index: Int) {
|
||||
pkg.functions.values.toList().forEach { function -> this(pkg, function, index, CirMemberContext.empty) }
|
||||
pkg.properties.values.toList().forEach { property -> this(pkg, property, index, CirMemberContext.empty) }
|
||||
pkg.classes.values.toList().forEach { clazz -> this(clazz, index, CirMemberContext.empty) }
|
||||
}
|
||||
|
||||
private operator fun invoke(clazz: CirClassNode, index: Int, context: CirMemberContext) {
|
||||
val contextWithClass = context.withContextOf(clazz.targetDeclarations[index] ?: return)
|
||||
clazz.functions.values.toList().forEach { function -> this(clazz, function, index, contextWithClass) }
|
||||
clazz.properties.values.toList().forEach { property -> this(clazz, property, index, contextWithClass) }
|
||||
clazz.classes.values.toList().forEach { innerClass -> this(innerClass, index, contextWithClass) }
|
||||
}
|
||||
|
||||
private operator fun invoke(parent: CirNodeWithMembers<*, *>, function: CirFunctionNode, index: Int, context: CirMemberContext) {
|
||||
/* Only perform type substitution to nodes that are not 'complete' */
|
||||
if (function.targetDeclarations.none { it == null }) return
|
||||
val originalFunction = function.targetDeclarations[index] ?: return
|
||||
val newFunction = typeSubstitutor.substitute(index, originalFunction)
|
||||
if (originalFunction == newFunction) return
|
||||
|
||||
val approximationKey = FunctionApproximationKey.create(context, newFunction)
|
||||
val newNode = parent.functions.getOrPut(approximationKey) {
|
||||
buildFunctionNode(storageManager, parent.targetDeclarations.size, classifiers, ParentNode(parent))
|
||||
}
|
||||
|
||||
newNode.targetDeclarations.setAllowingOverride(index, newFunction)
|
||||
}
|
||||
|
||||
private operator fun invoke(parent: CirNodeWithMembers<*, *>, property: CirPropertyNode, index: Int, context: CirMemberContext) {
|
||||
val originalProperty = property.targetDeclarations[index] ?: return
|
||||
val newProperty = typeSubstitutor.substitute(index, originalProperty)
|
||||
if (originalProperty == newProperty) return
|
||||
|
||||
val approximationKey = PropertyApproximationKey.create(context, newProperty)
|
||||
val newNode = parent.properties.getOrPut(approximationKey) {
|
||||
buildPropertyNode(storageManager, parent.targetDeclarations.size, classifiers, ParentNode(parent))
|
||||
}
|
||||
|
||||
newNode.targetDeclarations.setAllowingOverride(index, newProperty)
|
||||
}
|
||||
}
|
||||
@@ -8,13 +8,14 @@ package org.jetbrains.kotlin.commonizer.tree
|
||||
import org.jetbrains.kotlin.commonizer.TargetDependent
|
||||
import org.jetbrains.kotlin.commonizer.cir.*
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.*
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirNodeRelationship.Companion.ParentNode
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirNodeRelationship.ParentNode
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
|
||||
internal data class TargetBuildingContext(
|
||||
val storageManager: StorageManager,
|
||||
val classifiers: CirKnownClassifiers,
|
||||
val memberContext: CirMemberContext = CirMemberContext.empty,
|
||||
val typeSubstitutor: CirTypeSubstitutor,
|
||||
val targets: Int, val targetIndex: Int
|
||||
) {
|
||||
fun withMemberContextOf(clazz: CirClass) = copy(memberContext = memberContext.withContextOf(clazz))
|
||||
@@ -31,10 +32,6 @@ internal fun mergeCirTree(
|
||||
storageManager = storageManager,
|
||||
classifiers = classifiers,
|
||||
memberContext = CirMemberContext.empty,
|
||||
typeSubstitutor = CirAliasTypeSubstitutor(
|
||||
commonDependencies = classifiers.commonDependencies,
|
||||
classifierIndices = roots.map(::CirClassifierIndex)
|
||||
),
|
||||
targets = roots.size,
|
||||
targetIndex = targetIndex
|
||||
), roots[target].modules
|
||||
@@ -70,7 +67,7 @@ internal fun CirNodeWithMembers<*, *>.buildClass(
|
||||
context: TargetBuildingContext, treeClass: CirTreeClass, parent: CirNode<*, *>? = null
|
||||
) {
|
||||
val classNode = classes.getOrPut(treeClass.clazz.name) {
|
||||
buildClassNode(context.storageManager, context.targets, context.classifiers, CirNodeRelationship.ParentNode(parent), treeClass.id)
|
||||
buildClassNode(context.storageManager, context.targets, context.classifiers, ParentNode(parent), treeClass.id)
|
||||
}
|
||||
classNode.targetDeclarations[context.targetIndex] = treeClass.clazz
|
||||
val contextWithClass = context.withMemberContextOf(treeClass.clazz)
|
||||
@@ -83,28 +80,26 @@ internal fun CirNodeWithMembers<*, *>.buildClass(
|
||||
internal fun CirNodeWithMembers<*, *>.buildFunction(
|
||||
context: TargetBuildingContext, function: CirFunction, parent: CirNode<*, *>? = null
|
||||
) {
|
||||
val newFunction = context.typeSubstitutor.substitute(context.targetIndex, function)
|
||||
val functionNode = functions.getOrPut(FunctionApproximationKey.create(context.memberContext, newFunction)) {
|
||||
buildFunctionNode(context.storageManager, context.targets, context.classifiers, CirNodeRelationship.ParentNode(parent))
|
||||
val functionNode = functions.getOrPut(FunctionApproximationKey.create(context.memberContext, function)) {
|
||||
buildFunctionNode(context.storageManager, context.targets, context.classifiers, ParentNode(parent))
|
||||
}
|
||||
functionNode.targetDeclarations[context.targetIndex] = newFunction
|
||||
functionNode.targetDeclarations[context.targetIndex] = function
|
||||
}
|
||||
|
||||
internal fun CirNodeWithMembers<*, *>.buildProperty(
|
||||
context: TargetBuildingContext, property: CirProperty, parent: CirNode<*, *>? = null
|
||||
) {
|
||||
val newProperty = context.typeSubstitutor.substitute(context.targetIndex, property)
|
||||
val propertyNode = properties.getOrPut(PropertyApproximationKey.create(context.memberContext, newProperty)) {
|
||||
buildPropertyNode(context.storageManager, context.targets, context.classifiers, CirNodeRelationship.ParentNode(parent))
|
||||
val propertyNode = properties.getOrPut(PropertyApproximationKey.create(context.memberContext, property)) {
|
||||
buildPropertyNode(context.storageManager, context.targets, context.classifiers, ParentNode(parent))
|
||||
}
|
||||
propertyNode.targetDeclarations[context.targetIndex] = newProperty
|
||||
propertyNode.targetDeclarations[context.targetIndex] = property
|
||||
}
|
||||
|
||||
internal fun CirClassNode.buildConstructor(
|
||||
context: TargetBuildingContext, constructor: CirClassConstructor, parent: CirNode<*, *>
|
||||
) {
|
||||
val constructorNode = constructors.getOrPut(ConstructorApproximationKey.create(context.memberContext, constructor)) {
|
||||
buildClassConstructorNode(context.storageManager, context.targets, context.classifiers, CirNodeRelationship.ParentNode(parent))
|
||||
buildClassConstructorNode(context.storageManager, context.targets, context.classifiers, ParentNode(parent))
|
||||
}
|
||||
constructorNode.targetDeclarations[context.targetIndex] = constructor
|
||||
}
|
||||
|
||||
@@ -31,7 +31,9 @@ class CommonizedGroup<T : Any>(
|
||||
|
||||
elements[index] = value
|
||||
}
|
||||
|
||||
internal fun setAllowingOverride(index: Int, value: T?) {
|
||||
elements[index] = value
|
||||
}
|
||||
}
|
||||
|
||||
interface I<T>
|
||||
|
||||
|
||||
@@ -182,9 +182,40 @@ class HierarchicalPropertyCommonizationTest : AbstractInlineSourcesCommonization
|
||||
result.assertCommonized(
|
||||
"(a, b)", """
|
||||
expect class AB expect constructor()
|
||||
expect typealias TA_AB = AB
|
||||
typealias TA_AB = AB
|
||||
// https://youtrack.jetbrains.com/issue/KT-47100
|
||||
expect val x: TA_AB
|
||||
expect val x: AB
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
fun `test single typeAliased property and double typeAliased property - with reversed order`() {
|
||||
val result = commonize {
|
||||
outputTarget("(a, b)")
|
||||
simpleSingleSourceTarget(
|
||||
"a", """
|
||||
class AB
|
||||
typealias TA_AB = AB
|
||||
typealias TA_B = TA_AB
|
||||
val x: TA_B = TA_B()
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
simpleSingleSourceTarget(
|
||||
"b", """
|
||||
class AB
|
||||
typealias TA_AB = AB
|
||||
val x: TA_AB = TA_AB()
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
result.assertCommonized(
|
||||
"(a, b)", """
|
||||
expect class AB expect constructor()
|
||||
typealias TA_AB = AB
|
||||
// https://youtrack.jetbrains.com/issue/KT-47100
|
||||
expect val x: AB
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import org.jetbrains.kotlin.commonizer.assertCommonized
|
||||
|
||||
class HierarchicalTypeSubstitutionCommonizationTest : AbstractInlineSourcesCommonizationTest() {
|
||||
|
||||
fun `test boxed function using TA and expanded type`() {
|
||||
fun `test function with boxed parameter`() {
|
||||
val result = commonize {
|
||||
outputTarget("(a, b)")
|
||||
|
||||
@@ -43,7 +43,7 @@ class HierarchicalTypeSubstitutionCommonizationTest : AbstractInlineSourcesCommo
|
||||
}
|
||||
|
||||
|
||||
fun `test boxed function using TA and expanded type - with box from dependencies`() {
|
||||
fun `test function with boxed parameter - with box from dependencies`() {
|
||||
val result = commonize {
|
||||
outputTarget("(a, b)")
|
||||
|
||||
@@ -76,7 +76,7 @@ class HierarchicalTypeSubstitutionCommonizationTest : AbstractInlineSourcesCommo
|
||||
)
|
||||
}
|
||||
|
||||
fun `test parameters with non-commonized TA expanding to a commonized type`() {
|
||||
fun `test function parameter with suitable typealias`() {
|
||||
val result = commonize {
|
||||
outputTarget("(a, b)")
|
||||
|
||||
@@ -103,4 +103,36 @@ class HierarchicalTypeSubstitutionCommonizationTest : AbstractInlineSourcesCommo
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
fun `test boxed property return type`() {
|
||||
val result = commonize {
|
||||
outputTarget("(a, b)")
|
||||
|
||||
simpleSingleSourceTarget(
|
||||
"a", """
|
||||
class Box<T>
|
||||
class A
|
||||
typealias X = A
|
||||
val x: Box<X>
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
simpleSingleSourceTarget(
|
||||
"b", """
|
||||
class Box<T>
|
||||
class B
|
||||
typealias X = B
|
||||
val x: Box<B>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
result.assertCommonized(
|
||||
"(a, b)", """
|
||||
expect class Box<T> expect constructor()
|
||||
expect class X expect constructor()
|
||||
expect val x: Box<X>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user