Refactoring: Improve createExpectActualTypeParameterSubstitutor API
Review: https://jetbrains.team/p/kt/reviews/12279/files Motivation: make sure that cases like KT-62027 won't happen again Review: https://jetbrains.team/p/kt/reviews/12279/files Now it's responsibility of the `createExpectActualTypeParameterSubstitutor` calller to think about the case when parameters size isn't equal. You must not be able to create a substitutor if type parameters sizes are not equal Improvement in `createExpectActualTypeParameterSubstitutor` API also improves `AbstractExpectActualCompatibilityChecker.getCallablesCompatibility` API. Because suppose that you accidentally created a redundant wrapping substitutor => you need to handle the case of not equal type parameters size on the call site => you start thinking why you should do that on the call site? It must be a responsibility of `getCallablesCompatibility` => you realize that you created a redundant wrapping substitutor
This commit is contained in:
+9
-4
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.types.areCompatibleExpectActualTypes
|
||||
import org.jetbrains.kotlin.fir.types.createExpectActualTypeParameterSubstitutor
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
|
||||
import org.jetbrains.kotlin.utils.zipIfSizesAreEqual
|
||||
|
||||
object FirActualCallableDeclarationChecker : FirCallableDeclarationChecker() {
|
||||
override fun check(declaration: FirCallableDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
@@ -34,12 +35,16 @@ object FirActualCallableDeclarationChecker : FirCallableDeclarationChecker() {
|
||||
val expectTypeParameters = expectFunctionSymbol.getContainingClassSymbol(expectFunctionSymbol.moduleData.session)
|
||||
?.typeParameterSymbols.orEmpty()
|
||||
val actualClassTypeParameters = actualFunctionSymbol.getContainingClassSymbol(context.session)?.typeParameterSymbols.orEmpty()
|
||||
val parentSubstitutor =
|
||||
createExpectActualTypeParameterSubstitutor(expectTypeParameters, actualClassTypeParameters, context.session)
|
||||
|
||||
val parentSubstitutor = createExpectActualTypeParameterSubstitutor(
|
||||
// It's responsibility of AbstractExpectActualCompatibilityChecker to report that
|
||||
(expectTypeParameters zipIfSizesAreEqual actualClassTypeParameters) ?: return,
|
||||
context.session
|
||||
)
|
||||
|
||||
val substitutor = createExpectActualTypeParameterSubstitutor(
|
||||
expectFunctionSymbol.typeParameterSymbols,
|
||||
actualFunctionSymbol.typeParameterSymbols,
|
||||
// It's responsibility of AbstractExpectActualCompatibilityChecker to check that
|
||||
(expectFunctionSymbol.typeParameterSymbols zipIfSizesAreEqual actualFunctionSymbol.typeParameterSymbols) ?: return,
|
||||
context.session,
|
||||
parentSubstitutor
|
||||
)
|
||||
|
||||
@@ -13,12 +13,11 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
|
||||
fun createExpectActualTypeParameterSubstitutor(
|
||||
expectedTypeParameters: List<FirTypeParameterSymbol>,
|
||||
actualTypeParameters: List<FirTypeParameterSymbol>,
|
||||
expectActualTypeParameters: List<Pair<FirTypeParameterSymbol, FirTypeParameterSymbol>>,
|
||||
useSiteSession: FirSession,
|
||||
parentSubstitutor: ConeSubstitutor? = null
|
||||
): ConeSubstitutor {
|
||||
val substitution = expectedTypeParameters.zip(actualTypeParameters).associate { (expectedParameterSymbol, actualParameterSymbol) ->
|
||||
val substitution = expectActualTypeParameters.associate { (expectedParameterSymbol, actualParameterSymbol) ->
|
||||
expectedParameterSymbol to actualParameterSymbol.toLookupTag().constructType(emptyArray(), isNullable = false)
|
||||
}
|
||||
val substitutor = ConeSubstitutorByMap(
|
||||
|
||||
+3
-7
@@ -36,8 +36,6 @@ import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.SimpleTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeSystemContext
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.UnsafeCastFunction
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.castAll
|
||||
|
||||
class FirExpectActualMatchingContextImpl private constructor(
|
||||
private val actualSession: FirSession,
|
||||
@@ -147,15 +145,13 @@ class FirExpectActualMatchingContextImpl private constructor(
|
||||
override val PropertySymbolMarker.setter: FunctionSymbolMarker?
|
||||
get() = asSymbol().setterSymbol
|
||||
|
||||
@OptIn(UnsafeCastFunction::class)
|
||||
override fun createExpectActualTypeParameterSubstitutor(
|
||||
expectTypeParameters: List<TypeParameterSymbolMarker>,
|
||||
actualTypeParameters: List<TypeParameterSymbolMarker>,
|
||||
expectActualTypeParameters: List<Pair<TypeParameterSymbolMarker, TypeParameterSymbolMarker>>,
|
||||
parentSubstitutor: TypeSubstitutorMarker?,
|
||||
): TypeSubstitutorMarker {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return createExpectActualTypeParameterSubstitutor(
|
||||
expectTypeParameters.castAll<FirTypeParameterSymbol>(),
|
||||
actualTypeParameters.castAll<FirTypeParameterSymbol>(),
|
||||
expectActualTypeParameters as List<Pair<FirTypeParameterSymbol, FirTypeParameterSymbol>>,
|
||||
actualSession,
|
||||
parentSubstitutor as ConeSubstitutor?
|
||||
)
|
||||
|
||||
+3
-8
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.mpp.CallableSymbolMarker
|
||||
import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualCompatibilityChecker
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
|
||||
import org.jetbrains.kotlin.utils.zipIfSizesAreEqual
|
||||
|
||||
object FirExpectActualResolver {
|
||||
fun findExpectForActual(
|
||||
@@ -46,14 +47,8 @@ object FirExpectActualResolver {
|
||||
val expectTypeParameters = expectContainingClass?.typeParameterSymbols.orEmpty()
|
||||
val actualTypeParameters = actualContainingClass?.typeParameterSymbols.orEmpty()
|
||||
|
||||
parentSubstitutor = when (expectTypeParameters.size == actualTypeParameters.size) {
|
||||
true -> createExpectActualTypeParameterSubstitutor(
|
||||
expectTypeParameters,
|
||||
actualTypeParameters,
|
||||
useSiteSession,
|
||||
)
|
||||
false -> null
|
||||
}
|
||||
parentSubstitutor = (expectTypeParameters zipIfSizesAreEqual actualTypeParameters)
|
||||
?.let { createExpectActualTypeParameterSubstitutor(it, useSiteSession) }
|
||||
|
||||
when (actualSymbol) {
|
||||
is FirConstructorSymbol -> expectContainingClass?.getConstructors(scopeSession)
|
||||
|
||||
+5
-9
@@ -37,8 +37,6 @@ import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeSystemContext
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.UnsafeCastFunction
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.castAll
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
|
||||
|
||||
internal abstract class IrExpectActualMatchingContext(
|
||||
@@ -238,16 +236,14 @@ internal abstract class IrExpectActualMatchingContext(
|
||||
override val PropertySymbolMarker.setter: FunctionSymbolMarker?
|
||||
get() = asIr().setter?.symbol
|
||||
|
||||
@OptIn(UnsafeCastFunction::class)
|
||||
override fun createExpectActualTypeParameterSubstitutor(
|
||||
expectTypeParameters: List<TypeParameterSymbolMarker>,
|
||||
actualTypeParameters: List<TypeParameterSymbolMarker>,
|
||||
expectActualTypeParameters: List<Pair<TypeParameterSymbolMarker, TypeParameterSymbolMarker>>,
|
||||
parentSubstitutor: TypeSubstitutorMarker?,
|
||||
): TypeSubstitutorMarker {
|
||||
val expectParameters = expectTypeParameters.castAll<IrTypeParameterSymbol>()
|
||||
val actualParameters = actualTypeParameters.castAll<IrTypeParameterSymbol>()
|
||||
val actualTypes = actualParameters.map { it.owner.defaultType }
|
||||
val substitutor = IrTypeSubstitutor(expectParameters, actualTypes, allowEmptySubstitution = true)
|
||||
val typeParametersToArguments = expectActualTypeParameters.associate { (expect, actual) ->
|
||||
(expect as IrTypeParameterSymbol) to (actual as IrTypeParameterSymbol).owner.defaultType
|
||||
}
|
||||
val substitutor = IrTypeSubstitutor(typeParametersToArguments, allowEmptySubstitution = true)
|
||||
return when (parentSubstitutor) {
|
||||
null -> substitutor
|
||||
is AbstractIrTypeSubstitutor -> IrChainedSubstitutor(parentSubstitutor, substitutor)
|
||||
|
||||
+3
-5
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrTypeArgument
|
||||
import org.jetbrains.kotlin.ir.types.IrTypeSubstitutor
|
||||
import org.jetbrains.kotlin.ir.util.SYNTHETIC_OFFSET
|
||||
@@ -50,11 +51,8 @@ class WrapInlineDeclarationsWithReifiedTypeParametersLowering(val context: Backe
|
||||
if (!owner.isInlineFunWithReifiedParameter()) {
|
||||
return expression
|
||||
}
|
||||
val substitutionMap = expression.typeSubstitutionMap.entries
|
||||
val typeSubstitutor = IrTypeSubstitutor(
|
||||
substitutionMap.map { it.key },
|
||||
substitutionMap.map { it.value as IrTypeArgument },
|
||||
)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val typeSubstitutor = IrTypeSubstitutor(expression.typeSubstitutionMap as Map<IrTypeParameterSymbol, IrTypeArgument>)
|
||||
|
||||
val function = irFactory.buildFun {
|
||||
name = Name.identifier("${owner.name}${"$"}wrap")
|
||||
|
||||
@@ -65,12 +65,15 @@ abstract class BaseIrTypeSubstitutor : AbstractIrTypeSubstitutor() {
|
||||
}
|
||||
|
||||
class IrTypeSubstitutor(
|
||||
typeParameters: List<IrTypeParameterSymbol>,
|
||||
typeArguments: List<IrTypeArgument>,
|
||||
private val allowEmptySubstitution: Boolean = false,
|
||||
private val substitution: Map<IrTypeParameterSymbol, IrTypeArgument>,
|
||||
private val allowEmptySubstitution: Boolean = false
|
||||
) : BaseIrTypeSubstitutor() {
|
||||
init {
|
||||
assert(typeParameters.size == typeArguments.size) {
|
||||
constructor(
|
||||
typeParameters: List<IrTypeParameterSymbol>,
|
||||
typeArguments: List<IrTypeArgument>,
|
||||
allowEmptySubstitution: Boolean = false,
|
||||
) : this(typeParameters.zip(typeArguments).toMap(), allowEmptySubstitution) {
|
||||
check(typeParameters.size == typeArguments.size) {
|
||||
"Unexpected number of type arguments: ${typeArguments.size}\n" +
|
||||
"Type parameters are:\n" +
|
||||
typeParameters.joinToString(separator = "\n") { it.owner.render() } +
|
||||
@@ -79,8 +82,6 @@ class IrTypeSubstitutor(
|
||||
}
|
||||
}
|
||||
|
||||
private val substitution = typeParameters.zip(typeArguments).toMap()
|
||||
|
||||
override fun getSubstitutionArgument(typeParameter: IrTypeParameterSymbol): IrTypeArgument =
|
||||
substitution[typeParameter]
|
||||
?: typeParameter.takeIf { allowEmptySubstitution }?.owner?.defaultType
|
||||
|
||||
@@ -595,17 +595,12 @@ interface IrTypeSystemContext : TypeSystemContext, TypeSystemCommonSuperTypesCon
|
||||
}
|
||||
|
||||
override fun typeSubstitutorByTypeConstructor(map: Map<TypeConstructorMarker, KotlinTypeMarker>): TypeSubstitutorMarker {
|
||||
val typeParameters = mutableListOf<IrTypeParameterSymbol>()
|
||||
val typeArguments = mutableListOf<IrTypeArgument>()
|
||||
for ((key, value) in map) {
|
||||
typeParameters += key as IrTypeParameterSymbol
|
||||
typeArguments += value as IrTypeArgument
|
||||
}
|
||||
return IrTypeSubstitutor(typeParameters, typeArguments)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return IrTypeSubstitutor(map as Map<IrTypeParameterSymbol, IrTypeArgument>)
|
||||
}
|
||||
|
||||
override fun createEmptySubstitutor(): TypeSubstitutorMarker {
|
||||
return IrTypeSubstitutor(emptyList(), emptyList())
|
||||
return IrTypeSubstitutor(emptyMap())
|
||||
}
|
||||
|
||||
override fun TypeSubstitutorMarker.safeSubstitute(type: KotlinTypeMarker): KotlinTypeMarker {
|
||||
|
||||
+3
-5
@@ -208,8 +208,6 @@ class IrBodyDeserializer(
|
||||
return IrSimpleTypeBuilder().apply { classifier = klass.symbol }.buildSimpleType()
|
||||
}
|
||||
|
||||
val typeArguments = ArrayList<IrTypeArgument>(typeParameters.size)
|
||||
val typeParameterSymbols = ArrayList<IrTypeParameterSymbol>(typeParameters.size)
|
||||
val rawType = with(IrSimpleTypeBuilder()) {
|
||||
arguments = typeParameters.memoryOptimizedMap {
|
||||
classifier = it.symbol
|
||||
@@ -219,15 +217,15 @@ class IrBodyDeserializer(
|
||||
buildSimpleType()
|
||||
}
|
||||
|
||||
val typeParametersToArguments = HashMap<IrTypeParameterSymbol, IrTypeArgument>(typeParameters.size)
|
||||
for (i in typeParameters.indices) {
|
||||
val typeParameter = typeParameters[i]
|
||||
val callTypeArgument = constructorCall.getTypeArgument(i) ?: error("No type argument for id $i")
|
||||
val typeArgument = makeTypeProjection(callTypeArgument, typeParameter.variance)
|
||||
typeArguments.add(typeArgument)
|
||||
typeParameterSymbols.add(typeParameter.symbol)
|
||||
typeParametersToArguments[typeParameter.symbol] = typeArgument
|
||||
}
|
||||
|
||||
val substitutor = IrTypeSubstitutor(typeParameterSymbols, typeArguments)
|
||||
val substitutor = IrTypeSubstitutor(typeParametersToArguments)
|
||||
return substitutor.substitute(rawType) as IrSimpleType
|
||||
}
|
||||
}
|
||||
|
||||
+5
-4
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.utils.SmartList
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.enumMapOf
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.enumSetOf
|
||||
import org.jetbrains.kotlin.utils.keysToMap
|
||||
import org.jetbrains.kotlin.utils.zipIfSizesAreEqual
|
||||
import java.util.*
|
||||
|
||||
object AbstractExpectActualCompatibilityChecker {
|
||||
@@ -124,8 +125,8 @@ object AbstractExpectActualCompatibilityChecker {
|
||||
}
|
||||
|
||||
val substitutor = createExpectActualTypeParameterSubstitutor(
|
||||
expectTypeParameterSymbols,
|
||||
actualTypeParameterSymbols,
|
||||
(expectTypeParameterSymbols zipIfSizesAreEqual actualTypeParameterSymbols)
|
||||
?: error("expect/actual type parameters sizes are checked above"),
|
||||
parentSubstitutor
|
||||
)
|
||||
|
||||
@@ -348,8 +349,8 @@ object AbstractExpectActualCompatibilityChecker {
|
||||
}
|
||||
|
||||
val substitutor = createExpectActualTypeParameterSubstitutor(
|
||||
expectedTypeParameters,
|
||||
actualTypeParameters,
|
||||
(expectedTypeParameters zipIfSizesAreEqual actualTypeParameters)
|
||||
?: error("expect/actual type parameters sizes are checked above"),
|
||||
parentSubstitutor
|
||||
)
|
||||
|
||||
|
||||
+1
-2
@@ -103,8 +103,7 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemC
|
||||
val PropertySymbolMarker.setter: FunctionSymbolMarker?
|
||||
|
||||
fun createExpectActualTypeParameterSubstitutor(
|
||||
expectTypeParameters: List<TypeParameterSymbolMarker>,
|
||||
actualTypeParameters: List<TypeParameterSymbolMarker>,
|
||||
expectActualTypeParameters: List<Pair<TypeParameterSymbolMarker, TypeParameterSymbolMarker>>,
|
||||
parentSubstitutor: TypeSubstitutorMarker?
|
||||
): TypeSubstitutorMarker
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ fun <K, V> Iterable<K>.keysToMap(value: (K) -> V): Map<K, V> {
|
||||
return associateBy({ it }, value)
|
||||
}
|
||||
|
||||
infix fun <A, B> Collection<A>.zipIfSizesAreEqual(other: Collection<B>): List<Pair<A, B>>? =
|
||||
if (size == other.size) zip(other) else null
|
||||
|
||||
fun <K, V : Any> Iterable<K>.keysToMapExceptNulls(value: (K) -> V?): Map<K, V> {
|
||||
val map = LinkedHashMap<K, V>()
|
||||
for (k in this) {
|
||||
|
||||
Reference in New Issue
Block a user