mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-03-10 08:31:29 +00:00
[Commonizer] Implement CirAliasTypeSubstitutor
This will substitute non-commonizable classifiers with known type-aliases (which might be commonizable). A simple example depending on this substitution comes from posix: Most function and properties use the `FILE` typealias which is available across all platforms. Some linux platforms use `__IO_FILE` in their signature, which is just linux specific. This type substitution will figure out, that this type can be substituted with `FILE`. ^KT-47433 Verification Pending
This commit is contained in:
committed by
Space
parent
906346b7d9
commit
7c450f9884
@@ -127,6 +127,22 @@ abstract class CirClassType : CirClassOrTypeAliasType(), CirHasVisibility {
|
||||
)
|
||||
)
|
||||
|
||||
fun CirClassType.copyInterned(
|
||||
classifierId: CirEntityId = this.classifierId,
|
||||
outerType: CirClassType? = this.outerType,
|
||||
visibility: Visibility = this.visibility,
|
||||
arguments: List<CirTypeProjection> = this.arguments,
|
||||
isMarkedNullable: Boolean = this.isMarkedNullable
|
||||
): CirClassType {
|
||||
return createInterned(
|
||||
classId = classifierId,
|
||||
outerType = outerType,
|
||||
visibility = visibility,
|
||||
arguments = arguments,
|
||||
isMarkedNullable = isMarkedNullable
|
||||
)
|
||||
}
|
||||
|
||||
private val interner = Interner<CirClassTypeInternedImpl>()
|
||||
}
|
||||
}
|
||||
@@ -170,6 +186,20 @@ abstract class CirTypeAliasType : CirClassOrTypeAliasType() {
|
||||
)
|
||||
)
|
||||
|
||||
fun CirTypeAliasType.copyInterned(
|
||||
classifierId: CirEntityId = this.classifierId,
|
||||
underlyingType: CirClassOrTypeAliasType = this.underlyingType,
|
||||
arguments: List<CirTypeProjection> = this.arguments,
|
||||
isMarkedNullable: Boolean = this.isMarkedNullable
|
||||
): CirTypeAliasType {
|
||||
return createInterned(
|
||||
typeAliasId = classifierId,
|
||||
underlyingType = underlyingType,
|
||||
arguments = arguments,
|
||||
isMarkedNullable = isMarkedNullable
|
||||
)
|
||||
}
|
||||
|
||||
private val interner = Interner<CirTypeAliasTypeInternedImpl>()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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.mergedtree
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.cir.*
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirClassType.Companion.copyInterned
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirTypeAliasType.Companion.copyInterned
|
||||
|
||||
internal class CirAliasTypeSubstitutor(
|
||||
private val commonDependencies: CirProvidedClassifiers,
|
||||
private val classifierIndices: List<CirClassifierIndex>
|
||||
) : CirTypeSubstitutor {
|
||||
|
||||
override fun substitute(targetIndex: Int, type: CirType): CirType {
|
||||
return when (type) {
|
||||
is CirFlexibleType -> type
|
||||
is CirTypeParameterType -> type
|
||||
is CirClassOrTypeAliasType -> substituteClassOrTypeAliasType(targetIndex, type)
|
||||
}
|
||||
}
|
||||
|
||||
private fun substituteTypeProjection(targetIndex: Int, projection: CirTypeProjection): CirTypeProjection {
|
||||
return when (projection) {
|
||||
is CirRegularTypeProjection -> {
|
||||
val newType = substitute(targetIndex, projection.type)
|
||||
if (newType != projection.type) projection.copy(type = newType) else projection
|
||||
}
|
||||
is CirStarTypeProjection -> projection
|
||||
}
|
||||
}
|
||||
|
||||
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!
|
||||
*/
|
||||
if (isCommon(type.classifierId)) {
|
||||
val newArguments = type.arguments.map { argument -> substituteTypeProjection(targetIndex, argument) }
|
||||
return if (newArguments != type.arguments) when (type) {
|
||||
is CirTypeAliasType -> type.copyInterned(arguments = newArguments)
|
||||
is CirClassType -> type.copyInterned(arguments = newArguments)
|
||||
} 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.
|
||||
*
|
||||
* Without any evidence of libraries that would benefit from this higher effort, we will just skip this case.
|
||||
*/
|
||||
if (type.arguments.isNotEmpty()) {
|
||||
return type
|
||||
}
|
||||
|
||||
return when (type) {
|
||||
is CirClassType -> substituteClassType(targetIndex, type)
|
||||
is CirTypeAliasType -> substituteClassType(targetIndex, type.unabbreviate())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find a suitable type alias pointing to the given class [type] which is available in 'common'
|
||||
* This function does *not* support types with arguments!
|
||||
*/
|
||||
private fun substituteClassType(targetIndex: Int, type: CirClassType): CirClassOrTypeAliasType {
|
||||
if (type.arguments.isNotEmpty()) return type
|
||||
if (type.outerType != null) return type
|
||||
val classifierIndex = classifierIndices[targetIndex]
|
||||
|
||||
var typeAliases = classifierIndex.findTypeAliasesWithUnderlyingType(type.classifierId)
|
||||
|
||||
while (typeAliases.isNotEmpty()) {
|
||||
val commonTypeAlias = typeAliases.firstOrNull { (id, typeAlias) -> typeAlias.typeParameters.isEmpty() && isCommon(id) }
|
||||
|
||||
if (commonTypeAlias != null) {
|
||||
return CirTypeAliasType.createInterned(
|
||||
typeAliasId = commonTypeAlias.id,
|
||||
underlyingType = commonTypeAlias.typeAlias.underlyingType,
|
||||
arguments = emptyList(),
|
||||
isMarkedNullable = type.isMarkedNullable
|
||||
)
|
||||
}
|
||||
|
||||
typeAliases = typeAliases.flatMap { (id, _) ->
|
||||
classifierIndex.findTypeAliasesWithUnderlyingType(id)
|
||||
}
|
||||
}
|
||||
|
||||
return type
|
||||
}
|
||||
|
||||
private fun isCommon(id: CirEntityId): Boolean =
|
||||
commonDependencies.hasClassifier(id) || classifierIndices.all { index -> id in index.allClassifierIds }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.mergedtree
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirEntityId
|
||||
import org.jetbrains.kotlin.commonizer.tree.*
|
||||
import org.jetbrains.kotlin.commonizer.utils.compact
|
||||
import org.jetbrains.kotlin.commonizer.utils.compactMapValues
|
||||
|
||||
internal fun CirClassifierIndex(tree: CirTreeRoot): CirClassifierIndex {
|
||||
return CirUnderlyingTypeIndexBuilder().apply { invoke(tree) }.build()
|
||||
}
|
||||
|
||||
internal interface CirClassifierIndex {
|
||||
val allClassifierIds: Set<CirEntityId>
|
||||
fun findTypeAliasesWithUnderlyingType(underlyingClassifier: CirEntityId): List<CirTreeTypeAlias>
|
||||
}
|
||||
|
||||
private class CirClassifierIndexImpl(
|
||||
override val allClassifierIds: Set<CirEntityId>,
|
||||
private val typeAliasesByUnderlyingType: Map<CirEntityId, List<CirTreeTypeAlias>>
|
||||
) : CirClassifierIndex {
|
||||
|
||||
override fun findTypeAliasesWithUnderlyingType(underlyingClassifier: CirEntityId): List<CirTreeTypeAlias> {
|
||||
return typeAliasesByUnderlyingType[underlyingClassifier].orEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
private class CirUnderlyingTypeIndexBuilder {
|
||||
private val index = mutableMapOf<CirEntityId, MutableList<CirTreeTypeAlias>>()
|
||||
private val classifierIds = mutableSetOf<CirEntityId>()
|
||||
|
||||
operator fun invoke(tree: CirTreeRoot) {
|
||||
tree.modules.forEach { module -> this(module) }
|
||||
}
|
||||
|
||||
operator fun invoke(module: CirTreeModule) {
|
||||
module.packages.forEach { pkg -> this(pkg) }
|
||||
}
|
||||
|
||||
operator fun invoke(pkg: CirTreePackage) {
|
||||
pkg.typeAliases.forEach { typeAlias -> this(typeAlias) }
|
||||
pkg.classes.forEach { clazz -> this(clazz) }
|
||||
}
|
||||
|
||||
operator fun invoke(typeAlias: CirTreeTypeAlias) {
|
||||
classifierIds.add(typeAlias.id)
|
||||
index.getOrPut(typeAlias.typeAlias.underlyingType.classifierId) { mutableListOf() }.add(typeAlias)
|
||||
}
|
||||
|
||||
operator fun invoke(clazz: CirTreeClass) {
|
||||
classifierIds.add(clazz.id)
|
||||
clazz.classes.forEach { innerClazz -> this(innerClazz) }
|
||||
}
|
||||
|
||||
fun build(): CirClassifierIndex {
|
||||
return CirClassifierIndexImpl(
|
||||
allClassifierIds = classifierIds.toSet(),
|
||||
typeAliasesByUnderlyingType = index.compactMapValues { (_, list) -> list.compact() }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.mergedtree
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirFunction
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirProperty
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirType
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirValueParameter.Companion.copyInterned
|
||||
|
||||
interface CirTypeSubstitutor {
|
||||
fun substitute(targetIndex: Int, type: CirType): CirType
|
||||
}
|
||||
|
||||
fun CirTypeSubstitutor.substitute(index: Int, property: CirProperty): CirProperty {
|
||||
val newReturnType = substitute(index, property.returnType)
|
||||
return if (newReturnType != property.returnType) property.copy(returnType = newReturnType) else property
|
||||
}
|
||||
|
||||
fun CirTypeSubstitutor.substitute(index: Int, function: CirFunction): CirFunction {
|
||||
val newExtensionReceiverType = function.extensionReceiver?.type?.let { substitute(index, it) }
|
||||
|
||||
val newExtensionReceiver = if (newExtensionReceiverType != function.extensionReceiver?.type)
|
||||
function.extensionReceiver?.copy(type = checkNotNull(newExtensionReceiverType))
|
||||
else function.extensionReceiver
|
||||
|
||||
val newValueParameters = function.valueParameters.map { valueParameter ->
|
||||
val newReturnType = substitute(index, valueParameter.returnType)
|
||||
if (newReturnType != valueParameter.returnType) valueParameter.copyInterned(returnType = newReturnType) else valueParameter
|
||||
}
|
||||
|
||||
val newReturnType = substitute(index, function.returnType)
|
||||
|
||||
return if (
|
||||
newExtensionReceiver != function.extensionReceiver ||
|
||||
newValueParameters != function.valueParameters ||
|
||||
newReturnType != function.returnType
|
||||
) function.copy(
|
||||
extensionReceiver = newExtensionReceiver,
|
||||
valueParameters = newValueParameters,
|
||||
returnType = newReturnType
|
||||
) else function
|
||||
}
|
||||
@@ -11,7 +11,10 @@ import org.jetbrains.kotlin.commonizer.mergedtree.*
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
|
||||
internal data class TargetBuildingContext(
|
||||
val storageManager: StorageManager, val classifiers: CirKnownClassifiers, val memberContext: CirMemberContext = CirMemberContext.empty,
|
||||
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))
|
||||
@@ -24,7 +27,17 @@ internal fun mergeCirTree(
|
||||
roots.targets.withIndex().forEach { (targetIndex, target) ->
|
||||
node.targetDeclarations[targetIndex] = CirRoot.create(target)
|
||||
node.buildModules(
|
||||
TargetBuildingContext(storageManager, classifiers, CirMemberContext.empty, roots.size, targetIndex), roots[target].modules
|
||||
TargetBuildingContext(
|
||||
storageManager = storageManager,
|
||||
classifiers = classifiers,
|
||||
memberContext = CirMemberContext.empty,
|
||||
typeSubstitutor = CirAliasTypeSubstitutor(
|
||||
commonDependencies = classifiers.commonDependencies,
|
||||
classifierIndices = roots.map(::CirClassifierIndex)
|
||||
),
|
||||
targets = roots.size,
|
||||
targetIndex = targetIndex
|
||||
), roots[target].modules
|
||||
)
|
||||
}
|
||||
return node
|
||||
@@ -70,19 +83,21 @@ internal fun CirNodeWithMembers<*, *>.buildClass(
|
||||
internal fun CirNodeWithMembers<*, *>.buildFunction(
|
||||
context: TargetBuildingContext, function: CirFunction, parent: CirNode<*, *>? = null
|
||||
) {
|
||||
val functionNode = functions.getOrPut(FunctionApproximationKey.create(context.memberContext, function)) {
|
||||
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))
|
||||
}
|
||||
functionNode.targetDeclarations[context.targetIndex] = function
|
||||
functionNode.targetDeclarations[context.targetIndex] = newFunction
|
||||
}
|
||||
|
||||
internal fun CirNodeWithMembers<*, *>.buildProperty(
|
||||
context: TargetBuildingContext, property: CirProperty, parent: CirNode<*, *>? = null
|
||||
) {
|
||||
val propertyNode = properties.getOrPut(PropertyApproximationKey.create(context.memberContext, property)) {
|
||||
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))
|
||||
}
|
||||
propertyNode.targetDeclarations[context.targetIndex] = property
|
||||
propertyNode.targetDeclarations[context.targetIndex] = newProperty
|
||||
}
|
||||
|
||||
internal fun CirClassNode.buildConstructor(
|
||||
|
||||
@@ -23,9 +23,9 @@ class CommonizedGroup<T : Any>(
|
||||
return elements[index] as T?
|
||||
}
|
||||
|
||||
operator fun set(index: Int, value: T) {
|
||||
operator fun set(index: Int, value: T?) {
|
||||
val oldValue = this[index]
|
||||
check(oldValue == null) {
|
||||
check(oldValue == null || value == null) {
|
||||
"$oldValue can not be overwritten with $value at index $index"
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.commonizer.hierarchical
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.AbstractInlineSourcesCommonizationTest
|
||||
import org.jetbrains.kotlin.commonizer.assertCommonized
|
||||
import org.junit.Test
|
||||
|
||||
class HierarchicalClassAndTypeAliasCommonizationTest : AbstractInlineSourcesCommonizationTest() {
|
||||
|
||||
@@ -573,66 +572,4 @@ class HierarchicalClassAndTypeAliasCommonizationTest : AbstractInlineSourcesComm
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
fun `ignored KT-47433 - test boxed function using TA and expanded type`() {
|
||||
val result = commonize {
|
||||
outputTarget("(a, b)")
|
||||
|
||||
simpleSingleSourceTarget(
|
||||
"a", """
|
||||
class Box<T>
|
||||
class A
|
||||
typealias X = A
|
||||
fun x(x: Box<X>)
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
simpleSingleSourceTarget(
|
||||
"b", """
|
||||
class Box<T>
|
||||
class B
|
||||
typealias X = B
|
||||
fun x(x: Box<B>)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
result.assertCommonized(
|
||||
"(a, b)", """
|
||||
expect class Box<T> expect constructor()
|
||||
expect class X expect constructor()
|
||||
expect fun x(x: Box<X>)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
fun `ignored KT-47433 - test parameters with non-commonized TA expanding to a commonized type`() {
|
||||
val result = commonize {
|
||||
outputTarget("(a, b)")
|
||||
|
||||
simpleSingleSourceTarget(
|
||||
"a", """
|
||||
class X
|
||||
fun useX(x: X) = Unit
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
simpleSingleSourceTarget(
|
||||
"b", """
|
||||
class X
|
||||
typealias B = X
|
||||
fun useX(x: B) = Unit
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
result.assertCommonized(
|
||||
"(a, b)", """
|
||||
expect class X expect constructor()
|
||||
expect fun useX(x: X)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ class HierarchicalPropertyCommonizationTest : AbstractInlineSourcesCommonization
|
||||
expect class AB expect constructor()
|
||||
expect typealias TA_AB = AB
|
||||
// https://youtrack.jetbrains.com/issue/KT-47100
|
||||
expect val x: AB
|
||||
expect val x: TA_AB
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.hierarchical
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.AbstractInlineSourcesCommonizationTest
|
||||
import org.jetbrains.kotlin.commonizer.assertCommonized
|
||||
|
||||
class HierarchicalTypeSubstitutionCommonizationTest : AbstractInlineSourcesCommonizationTest() {
|
||||
|
||||
fun `test boxed function using TA and expanded type`() {
|
||||
val result = commonize {
|
||||
outputTarget("(a, b)")
|
||||
|
||||
simpleSingleSourceTarget(
|
||||
"a", """
|
||||
class Box<T>
|
||||
class A
|
||||
typealias X = A
|
||||
fun x(x: Box<X>)
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
simpleSingleSourceTarget(
|
||||
"b", """
|
||||
class Box<T>
|
||||
class B
|
||||
typealias X = B
|
||||
fun x(x: Box<B>)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
result.assertCommonized(
|
||||
"(a, b)", """
|
||||
expect class Box<T> expect constructor()
|
||||
expect class X expect constructor()
|
||||
expect fun x(x: Box<X>)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
fun `test boxed function using TA and expanded type - with box from dependencies`() {
|
||||
val result = commonize {
|
||||
outputTarget("(a, b)")
|
||||
|
||||
registerDependency("a", "b", "(a, b)") {
|
||||
source("""class Box<T>""")
|
||||
}
|
||||
|
||||
simpleSingleSourceTarget(
|
||||
"a", """
|
||||
class A
|
||||
typealias X = A
|
||||
fun x(x: Box<X>)
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
simpleSingleSourceTarget(
|
||||
"b", """
|
||||
class B
|
||||
typealias X = B
|
||||
fun x(x: Box<B>)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
result.assertCommonized(
|
||||
"(a, b)", """
|
||||
expect class X expect constructor()
|
||||
expect fun x(x: Box<X>)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
fun `test parameters with non-commonized TA expanding to a commonized type`() {
|
||||
val result = commonize {
|
||||
outputTarget("(a, b)")
|
||||
|
||||
simpleSingleSourceTarget(
|
||||
"a", """
|
||||
class X
|
||||
fun useX(x: X) = Unit
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
simpleSingleSourceTarget(
|
||||
"b", """
|
||||
class X
|
||||
typealias B = X
|
||||
fun useX(x: B) = Unit
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
result.assertCommonized(
|
||||
"(a, b)", """
|
||||
expect class X expect constructor()
|
||||
expect fun useX(x: X)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user