[Commonizer] Implement new ClassOrTypeAliasTypeCommonizer

^KT-47432 Verification Pending
^KT-47434 Verification Pending
This commit is contained in:
sebastian.sellmair
2021-06-29 14:43:37 +02:00
committed by Space
parent 8e2780345c
commit 8f73df0f85
9 changed files with 141 additions and 71 deletions

View File

@@ -25,9 +25,12 @@ object CirClassRecursionMarker : CirClass, CirRecursionMarker {
set(_) = unsupported()
}
object CirClassifierRecursionMarker : CirClassifier, CirRecursionMarker {
object CirTypeAliasRecursionMarker : CirTypeAlias, CirRecursionMarker {
override val underlyingType: CirClassOrTypeAliasType get() = unsupported()
override val expandedType: CirClassType get() = unsupported()
override val annotations get() = unsupported()
override val name get() = unsupported()
override val typeParameters get() = unsupported()
override val visibility get() = unsupported()
override val isLiftedUp: Boolean get() = unsupported()
}

View File

@@ -0,0 +1,63 @@
/*
* 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.core
import org.jetbrains.kotlin.commonizer.cir.CirClassOrTypeAliasType
import org.jetbrains.kotlin.commonizer.cir.CirClassType
import org.jetbrains.kotlin.commonizer.cir.CirTypeAliasType
import org.jetbrains.kotlin.commonizer.mergedtree.CirKnownClassifiers
internal class ClassOrTypeAliasTypeStatelessCommonizer(
private val classifiers: CirKnownClassifiers
) : StatelessCommonizer<CirClassOrTypeAliasType, CirClassOrTypeAliasType> {
override fun commonize(values: List<CirClassOrTypeAliasType>): CirClassOrTypeAliasType? {
if (values.isEmpty()) return null
values.singleOrNull()?.let { return it }
val classTypes = values.filterIsInstance<CirClassType>()
val typeAliasTypes = values.filterIsInstance<CirTypeAliasType>()
val expandedTypeAliasTypes = typeAliasTypes.map { it.expandedType() }
if (values.all { it is CirClassType }) {
return ClassTypeCommonizer(classifiers).commonize(classTypes)
}
if (values.all { it is CirTypeAliasType }) {
/*
In case regular type-alias-type commonization fails, we try to expand all type-aliases and
try our luck with commonizing those class types
*/
return TypeAliasTypeCommonizer(classifiers).commonize(typeAliasTypes)
?: ClassTypeCommonizer(classifiers).commonize(expandedTypeAliasTypes)
}
/*
There are type-alias types & class types enqueued for commonization.
We reduce all classes to a common representation and all type aliases to a common representation.
*/
val commonizedClass = ClassTypeCommonizer(classifiers).commonize(classTypes) ?: return null
val commonizedTypeAlias = TypeAliasTypeCommonizer(classifiers).commonize(typeAliasTypes)
if (commonizedTypeAlias != null) {
return ClassTypeCommonizer(classifiers).commonize(listOf(commonizedClass, commonizedTypeAlias.expandedType()))
}
/*
If type-alias type commonization failed:
Last attempt: Try to commonize all type-alias expansions with the commonized class
*/
return ClassTypeCommonizer(classifiers).commonize(expandedTypeAliasTypes + commonizedClass)
}
}
internal class ClassOrTypeAliasTypeCommonizer(classifiers: CirKnownClassifiers) :
StatelessCommonizerAdapter<CirClassOrTypeAliasType, CirClassOrTypeAliasType>(ClassOrTypeAliasTypeStatelessCommonizer(classifiers))
internal tailrec fun CirClassOrTypeAliasType.expandedType(): CirClassType = when (this) {
is CirClassType -> this
is CirTypeAliasType -> this.underlyingType.expandedType()
}

View File

@@ -10,6 +10,16 @@ interface Commonizer<T, R> {
fun commonizeWith(next: T): Boolean
}
fun <T, R> Commonizer<T, R>.commonize(values: List<T>): R? {
/* Fast path: If commonizer supports StatelessCommonizer interface */
if (this is StatelessCommonizerAdapter) {
return commonize(values)
}
values.forEach { value -> if (!commonizeWith(value)) return null }
return result
}
abstract class AbstractStandardCommonizer<T, R> : Commonizer<T, R> {
private enum class State {
EMPTY,

View File

@@ -0,0 +1,31 @@
/*
* 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.core
interface StatelessCommonizer<T, R> {
fun commonize(values: List<T>): R?
}
open class StatelessCommonizerAdapter<T, R : Any>(
private val commonizer: StatelessCommonizer<T, R>
) : AbstractStandardCommonizer<T, R>(), StatelessCommonizer<T, R> by commonizer {
private val values = mutableListOf<T>()
override fun commonizationResult(): R {
try {
return checkNotNull(commonize(values.toList()))
} finally {
values.clear()
}
}
override fun initialize(first: T) = Unit
override fun doCommonizeWith(next: T): Boolean {
values.add(next)
return commonize(values.toList()) != null
}
}

View File

@@ -17,11 +17,11 @@ import org.jetbrains.kotlin.descriptors.Modality
* Secondary (less optimistic) branch:
* - Make sure that all TAs are identical, so the resulting TA can be lifted up into "common" fragment.
*/
class TypeAliasCommonizer(classifiers: CirKnownClassifiers) : AbstractStandardCommonizer<CirTypeAlias, CirClassifier>() {
class TypeAliasCommonizer(classifiers: CirKnownClassifiers) : AbstractStandardCommonizer<CirTypeAlias, CirTypeAlias>() {
private val primary = TypeAliasShortCircuitingCommonizer(classifiers)
private val secondary = TypeAliasLiftingUpCommonizer(classifiers)
override fun commonizationResult(): CirClassifier = primary.resultOrNull ?: secondary.result
override fun commonizationResult(): CirTypeAlias = primary.resultOrNull ?: secondary.result
override fun initialize(first: CirTypeAlias) = Unit

View File

@@ -14,61 +14,30 @@ import org.jetbrains.kotlin.commonizer.utils.isUnderKotlinNativeSyntheticPackage
import org.jetbrains.kotlin.descriptors.Visibility
class TypeCommonizer(private val classifiers: CirKnownClassifiers) : AbstractStandardCommonizer<CirType, CirType>() {
private var typeCommonizer: AbstractStandardCommonizer<*, CirType>? = null
private var underlyingTypeCommonizer: TypeCommonizer? = null
private lateinit var typeCommonizer: Commonizer<*, CirType>
override fun commonizationResult() = typeCommonizer?.resultOrNull
?: underlyingTypeCommonizer?.result
?: typeCommonizer?.result
?: failInErrorState()
override fun commonizationResult() = typeCommonizer.result
override fun initialize(first: CirType) {
@Suppress("UNCHECKED_CAST")
typeCommonizer = when (first) {
is CirClassType -> ClassTypeCommonizer(classifiers)
is CirTypeAliasType -> TypeAliasTypeCommonizer(classifiers)
is CirClassOrTypeAliasType -> ClassOrTypeAliasTypeCommonizer(classifiers)
is CirTypeParameterType -> TypeParameterTypeCommonizer()
is CirFlexibleType -> FlexibleTypeCommonizer(classifiers)
} as AbstractStandardCommonizer<*, CirType>
if (first is CirTypeAliasType) {
underlyingTypeCommonizer = TypeCommonizer(classifiers)
}
} as Commonizer<*, CirType>
}
override fun doCommonizeWith(next: CirType): Boolean {
return when (next) {
is CirClassType -> doCommonizeWithClassType(next)
is CirTypeAliasType -> doCommonizeWithTypeAliasType(next)
is CirClassOrTypeAliasType -> (typeCommonizer as? ClassOrTypeAliasTypeCommonizer)?.commonizeWith(next) == true
is CirTypeParameterType -> (typeCommonizer as? TypeParameterTypeCommonizer)?.commonizeWith(next) == true
is CirFlexibleType -> (typeCommonizer as? FlexibleTypeCommonizer)?.commonizeWith(next) == true
}
}
private fun doCommonizeWithClassType(next: CirClassType): Boolean {
if ((typeCommonizer as? ClassTypeCommonizer) == null) {
typeCommonizer = null
}
val firstAnswer = (typeCommonizer as? ClassTypeCommonizer)?.commonizeWith(next) == true
val secondAnswer = underlyingTypeCommonizer?.commonizeWith(next) == true
return firstAnswer || secondAnswer
}
private fun doCommonizeWithTypeAliasType(next: CirTypeAliasType): Boolean {
if (typeCommonizer != null && underlyingTypeCommonizer == null) {
underlyingTypeCommonizer = TypeCommonizer(classifiers)
underlyingTypeCommonizer?.commonizeWith(typeCommonizer?.resultOrNull ?: return false)
typeCommonizer = null
}
val firstAnswer = (typeCommonizer as? TypeAliasTypeCommonizer)?.commonizeWith(next) == true
val secondAnswer = underlyingTypeCommonizer?.commonizeWith(next.underlyingType) == true
return firstAnswer || secondAnswer
}
}
private class ClassTypeCommonizer(private val classifiers: CirKnownClassifiers) : AbstractStandardCommonizer<CirClassType, CirClassType>() {
internal class ClassTypeCommonizer(private val classifiers: CirKnownClassifiers) :
AbstractStandardCommonizer<CirClassType, CirClassType>() {
private lateinit var classId: CirEntityId
private val outerType = OuterClassTypeCommonizer(classifiers)
private lateinit var anyVisibility: Visibility
@@ -101,14 +70,15 @@ private class ClassTypeCommonizer(private val classifiers: CirKnownClassifiers)
&& arguments.commonizeWith(next.arguments)
}
private class OuterClassTypeCommonizer(classifiers: CirKnownClassifiers) :
// TODO NOW
internal class OuterClassTypeCommonizer(classifiers: CirKnownClassifiers) :
AbstractNullableCommonizer<CirClassType, CirClassType, CirClassType, CirClassType>(
wrappedCommonizerFactory = { ClassTypeCommonizer(classifiers) },
extractor = { it },
builder = { it }
)
private class TypeAliasTypeCommonizer(private val classifiers: CirKnownClassifiers) :
internal class TypeAliasTypeCommonizer(private val classifiers: CirKnownClassifiers) :
AbstractStandardCommonizer<CirTypeAliasType, CirClassOrTypeAliasType>() {
private lateinit var typeAliasId: CirEntityId
@@ -125,11 +95,11 @@ private class TypeAliasTypeCommonizer(private val classifiers: CirKnownClassifie
override fun initialize(first: CirTypeAliasType) {
typeAliasId = first.classifierId
isMarkedNullable = first.isMarkedNullable
isMarkedNullable = first.expandedType().isMarkedNullable
}
override fun doCommonizeWith(next: CirTypeAliasType): Boolean {
if (isMarkedNullable != next.isMarkedNullable || typeAliasId != next.classifierId)
if (isMarkedNullable != next.expandedType().isMarkedNullable || typeAliasId != next.classifierId)
return false
if (commonizedTypeBuilder == null) {
@@ -152,7 +122,7 @@ private class TypeAliasTypeCommonizer(private val classifiers: CirKnownClassifie
}
// builds a new type for "common" library fragment for the given combination of type alias types in "platform" fragments
private interface CommonizedTypeAliasTypeBuilder {
internal interface CommonizedTypeAliasTypeBuilder {
fun build(typeAliasId: CirEntityId, arguments: List<CirTypeProjection>, isMarkedNullable: Boolean): CirClassOrTypeAliasType
companion object {
@@ -161,7 +131,7 @@ private class TypeAliasTypeCommonizer(private val classifiers: CirKnownClassifie
override fun build(typeAliasId: CirEntityId, arguments: List<CirTypeProjection>, isMarkedNullable: Boolean) =
CirClassType.createInterned(
classId = typeAliasId,
outerType = null, // there can't be outer type
outerType = null, // there can't be outer type // TODO NOW!!!
visibility = commonClass.visibility,
arguments = arguments,
isMarkedNullable = isMarkedNullable
@@ -257,7 +227,7 @@ private fun commonizeTypeAlias(typeAliasId: CirEntityId, classifiers: CirKnownCl
return create(typeAliasNode?.commonDeclaration?.invoke() ?: classNode?.commonDeclaration?.invoke())
}
private class CommonizedTypeAliasAnswer(val commonized: Boolean, val commonClassifier: CirClassifier?) {
internal class CommonizedTypeAliasAnswer(val commonized: Boolean, val commonClassifier: CirClassifier?) {
companion object {
val SUCCESS_FROM_DEPENDENCY_LIBRARY = CommonizedTypeAliasAnswer(true, null)
val FAILURE_MISSING_IN_SOME_TARGET = CommonizedTypeAliasAnswer(false, null)

View File

@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.commonizer.mergedtree
import org.jetbrains.kotlin.commonizer.cir.CirClassifier
import org.jetbrains.kotlin.commonizer.cir.CirEntityId
import org.jetbrains.kotlin.commonizer.cir.CirTypeAlias
import org.jetbrains.kotlin.commonizer.utils.CommonizedGroup
@@ -14,8 +13,8 @@ import org.jetbrains.kotlin.storage.NullableLazyValue
class CirTypeAliasNode(
val id: CirEntityId,
override val targetDeclarations: CommonizedGroup<CirTypeAlias>,
override val commonDeclaration: NullableLazyValue<CirClassifier>,
) : CirClassifierNode<CirTypeAlias, CirClassifier>, CirNodeWithLiftingUp<CirTypeAlias, CirClassifier> {
override val commonDeclaration: NullableLazyValue<CirTypeAlias>,
) : CirClassifierNode<CirTypeAlias, CirTypeAlias>, CirNodeWithLiftingUp<CirTypeAlias, CirTypeAlias> {
override fun <T, R> accept(visitor: CirNodeVisitor<T, R>, data: T): R =
visitor.visitTypeAliasNode(this, data)

View File

@@ -6,9 +6,9 @@
package org.jetbrains.kotlin.commonizer.mergedtree
import org.jetbrains.kotlin.commonizer.cir.CirClassRecursionMarker
import org.jetbrains.kotlin.commonizer.cir.CirClassifierRecursionMarker
import org.jetbrains.kotlin.commonizer.cir.CirDeclaration
import org.jetbrains.kotlin.commonizer.cir.CirEntityId
import org.jetbrains.kotlin.commonizer.cir.CirTypeAliasRecursionMarker
import org.jetbrains.kotlin.commonizer.core.*
import org.jetbrains.kotlin.commonizer.utils.CommonizedGroup
import org.jetbrains.kotlin.storage.NullableLazyValue
@@ -116,7 +116,7 @@ internal fun buildTypeAliasNode(
size = size,
nodeRelationship = null,
commonizerProducer = { TypeAliasCommonizer(classifiers) },
recursionMarker = CirClassifierRecursionMarker,
recursionMarker = CirTypeAliasRecursionMarker,
nodeProducer = { targetDeclarations, commonDeclaration ->
CirTypeAliasNode(typeAliasId, targetDeclarations, commonDeclaration).also {
classifiers.commonizedNodes.addTypeAliasNode(typeAliasId, it)
@@ -148,12 +148,8 @@ internal fun <T : Any, R> commonize(
targetDeclarations: CommonizedGroup<T>,
commonizer: Commonizer<T, R>
): R? {
for (targetDeclaration in targetDeclarations) {
if (targetDeclaration == null || !commonizer.commonizeWith(targetDeclaration))
return null
}
return commonizer.result
if (targetDeclarations.any { it == null }) return null
return commonizer.commonize(targetDeclarations.filterNotNull())
}
@Suppress("NOTHING_TO_INLINE")
@@ -176,4 +172,4 @@ private fun CirNodeRelationship?.shouldCommonize(): Boolean {
is CirNodeRelationship.PreferredNode -> node.commonDeclaration() == null
is CirNodeRelationship.Composite -> relationships.all { it.shouldCommonize() }
}
}
}

View File

@@ -103,21 +103,21 @@ class TypeCommonizerTest : AbstractCommonizerTest<CirType, CirType>() {
fun classTypesInUserPackageWithDifferentNames1() = doTestFailure(
mockClassType("org/sample/Foo"),
mockClassType("org/fictitiousPackageName/Foo"),
shouldFailOnFirstVariant = true
shouldFailOnFirstVariant = false
)
@Test(expected = IllegalCommonizerStateException::class)
fun classTypesInUserPackageWithDifferentNames2() = doTestFailure(
mockClassType("org/sample/Foo"),
mockClassType("org/sample/Bar"),
shouldFailOnFirstVariant = true
shouldFailOnFirstVariant = false
)
@Test(expected = IllegalCommonizerStateException::class)
fun classTypesInUserPackageWithDifferentNames3() = doTestFailure(
mockClassType("org/sample/Foo"),
mockClassType("kotlin/String"),
shouldFailOnFirstVariant = true
shouldFailOnFirstVariant = false
)
@Test
@@ -364,19 +364,17 @@ class TypeCommonizerTest : AbstractCommonizerTest<CirType, CirType>() {
mockTAType("kotlin/sequences/SequenceBuilder", nullable = false) { mockClassType("kotlin/sequences/SequenceScope") }
)
@Test
// why success: nullability of underlying type does not matter if typealias belongs to one of the standard Kotlin packages
fun taTypesInKotlinPackageWithDifferentNullability3() = doTestSuccess(
expected = mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = false) },
@Test(expected = IllegalCommonizerStateException::class)
// why failure: Different nullability in type-expansion can't be commonized by now
fun taTypesInKotlinPackageWithDifferentNullability3() = doTestFailure(
mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = false) },
mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = false) },
mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = true) }
)
@Test
// why success: nullability of underlying type does not matter if typealias belongs to one of the standard Kotlin packages
fun taTypesInKotlinPackageWithDifferentNullability4() = doTestSuccess(
expected = mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = true) },
@Test(expected = IllegalCommonizerStateException::class)
// why failure: Different nullability in type-expansion can't be commonized by now
fun taTypesInKotlinPackageWithDifferentNullability4() = doTestFailure(
mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = true) },
mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = true) },
mockTAType("kotlin/sequences/SequenceBuilder") { mockClassType("kotlin/sequences/SequenceScope", nullable = false) }