[FIR] Never create ConeSubstitutorByMap with empty substitution

Relates to KT-66323
This commit is contained in:
Dmitriy Novozhilov
2024-03-04 08:51:37 +02:00
committed by Space Team
parent 6c691b497a
commit 624bea3ecf
16 changed files with 57 additions and 27 deletions
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.analysis.api.components.KtSubstitutorBuilder
import org.jetbrains.kotlin.analysis.api.components.KtSubstitutorFactory import org.jetbrains.kotlin.analysis.api.components.KtSubstitutorFactory
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirTypeParameterSymbol import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirTypeParameterSymbol
import org.jetbrains.kotlin.analysis.api.fir.types.KtFirGenericSubstitutor
import org.jetbrains.kotlin.analysis.api.fir.types.KtFirMapBackedSubstitutor import org.jetbrains.kotlin.analysis.api.fir.types.KtFirMapBackedSubstitutor
import org.jetbrains.kotlin.analysis.api.fir.types.KtFirType import org.jetbrains.kotlin.analysis.api.fir.types.KtFirType
import org.jetbrains.kotlin.analysis.api.types.KtSubstitutor import org.jetbrains.kotlin.analysis.api.types.KtSubstitutor
@@ -29,7 +30,9 @@ internal class KtFirSubstitutorFactory(
} }
} }
val coneSubstitutor = ConeSubstitutorByMap(firSubstitution, analysisSession.useSiteSession) return when (val coneSubstitutor = ConeSubstitutorByMap.create(firSubstitution, analysisSession.useSiteSession)) {
return KtFirMapBackedSubstitutor(coneSubstitutor, analysisSession.firSymbolBuilder) is ConeSubstitutorByMap -> KtFirMapBackedSubstitutor(coneSubstitutor, analysisSession.firSymbolBuilder)
else -> KtFirGenericSubstitutor(coneSubstitutor, analysisSession.firSymbolBuilder)
}
} }
} }
@@ -80,7 +80,7 @@ object FirQualifiedAccessJavaNullabilityWarningChecker : FirQualifiedAccessExpre
} }
} }
return ConeSubstitutorByMap(substitutionMap, session) return ConeSubstitutorByMap.create(substitutionMap, session)
} }
} }
@@ -279,7 +279,7 @@ fun findStaticallyKnownSubtype(
// At this point we have values for all type parameters of List // At this point we have values for all type parameters of List
// Let's make a type by substituting them: List<T> -> List<Foo> // Let's make a type by substituting them: List<T> -> List<Foo>
val substitutor = ConeSubstitutorByMap(resultSubstitution, session) val substitutor = ConeSubstitutorByMap.create(resultSubstitution, session)
return substitutor.substituteOrSelf(subtypeWithVariablesType) return substitutor.substituteOrSelf(subtypeWithVariablesType)
} }
@@ -69,7 +69,7 @@ private fun buildDeepSubstitutionMultimap(
for (index in 0 until count) { for (index in 0 until count) {
val typeArgument = typeArguments[index] val typeArgument = typeArguments[index]
val substitutedArgument = ConeSubstitutorByMap(substitution, session) val substitutedArgument = ConeSubstitutorByMap.create(substitution, session)
.substituteArgument(typeArgument, index) .substituteArgument(typeArgument, index)
?: typeArgument ?: typeArgument
val substitutedType = substitutedArgument.type ?: continue val substitutedType = substitutedArgument.type ?: continue
@@ -6,11 +6,13 @@
package org.jetbrains.kotlin.fir.resolve.substitution package org.jetbrains.kotlin.fir.resolve.substitution
import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.ConeTypeProjection
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
abstract class ConeSubstitutor : TypeSubstitutorMarker { abstract class ConeSubstitutor : TypeSubstitutorMarker {
open fun substituteOrSelf(type: ConeKotlinType): ConeKotlinType = substituteOrNull(type) ?: type open fun substituteOrSelf(type: ConeKotlinType): ConeKotlinType = substituteOrNull(type) ?: type
abstract fun substituteOrNull(type: ConeKotlinType): ConeKotlinType? abstract fun substituteOrNull(type: ConeKotlinType): ConeKotlinType?
abstract fun substituteArgument(projection: ConeTypeProjection, index: Int): ConeTypeProjection?
object Empty : ConeSubstitutor() { object Empty : ConeSubstitutor() {
override fun substituteOrSelf(type: ConeKotlinType): ConeKotlinType { override fun substituteOrSelf(type: ConeKotlinType): ConeKotlinType {
@@ -21,6 +23,10 @@ abstract class ConeSubstitutor : TypeSubstitutorMarker {
return null return null
} }
override fun substituteArgument(projection: ConeTypeProjection, index: Int): ConeTypeProjection? {
return null
}
override fun toString(): String = "Empty" override fun toString(): String = "Empty"
} }
} }
@@ -900,7 +900,7 @@ internal fun FirQualifiedAccessExpression.buildSubstitutorByCalledCallable(): Co
val typeProjection = typeArguments.getOrNull(index) as? FirTypeProjectionWithVariance ?: continue val typeProjection = typeArguments.getOrNull(index) as? FirTypeProjectionWithVariance ?: continue
map[typeParameter.symbol] = typeProjection.typeRef.coneType map[typeParameter.symbol] = typeProjection.typeRef.coneType
} }
return ConeSubstitutorByMap(map, session) return ConeSubstitutorByMap.create(map, session)
} }
val augmentedArrayAssignSourceKindToIrStatementOrigin = mapOf( val augmentedArrayAssignSourceKindToIrStatementOrigin = mapOf(
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.fir.java.resolveIfJavaType
import org.jetbrains.kotlin.fir.java.symbols.FirJavaOverriddenSyntheticPropertySymbol import org.jetbrains.kotlin.fir.java.symbols.FirJavaOverriddenSyntheticPropertySymbol
import org.jetbrains.kotlin.fir.java.toConeKotlinTypeProbablyFlexible import org.jetbrains.kotlin.fir.java.toConeKotlinTypeProbablyFlexible
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
import org.jetbrains.kotlin.fir.scopes.jvm.computeJvmDescriptor import org.jetbrains.kotlin.fir.scopes.jvm.computeJvmDescriptor
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
@@ -279,7 +280,7 @@ class FirSignatureEnhancement(
var isJavaRecordComponent = false var isJavaRecordComponent = false
val typeParameterSubstitutionMap = mutableMapOf<FirTypeParameterSymbol, ConeKotlinType>() val typeParameterSubstitutionMap = mutableMapOf<FirTypeParameterSymbol, ConeKotlinType>()
var typeParameterSubstitutor: ConeSubstitutorByMap? = null var typeParameterSubstitutor: ConeSubstitutor? = null
val declarationOrigin = val declarationOrigin =
if (isIntersectionOverride) FirDeclarationOrigin.IntersectionOverride else FirDeclarationOrigin.Enhancement if (isIntersectionOverride) FirDeclarationOrigin.IntersectionOverride else FirDeclarationOrigin.Enhancement
@@ -360,7 +361,7 @@ class FirSignatureEnhancement(
newTypeParameter newTypeParameter
} }
if (typeParameterSubstitutionMap.isNotEmpty()) { if (typeParameterSubstitutionMap.isNotEmpty()) {
typeParameterSubstitutor = ConeSubstitutorByMap(typeParameterSubstitutionMap, session) typeParameterSubstitutor = ConeSubstitutorByMap.create(typeParameterSubstitutionMap, session)
} }
returnTypeRef = newReturnTypeRef.withReplacedConeType( returnTypeRef = newReturnTypeRef.withReplacedConeType(
typeParameterSubstitutor?.substituteOrNull(newReturnTypeRef.coneType) typeParameterSubstitutor?.substituteOrNull(newReturnTypeRef.coneType)
@@ -415,7 +415,7 @@ class JvmMappedScope(
* @returns {T1 -> F1, T2 -> F2} substitution * @returns {T1 -> F1, T2 -> F2} substitution
*/ */
private fun createMappingSubstitutor(fromClass: FirRegularClass, toClass: FirRegularClass, session: FirSession): ConeSubstitutor = private fun createMappingSubstitutor(fromClass: FirRegularClass, toClass: FirRegularClass, session: FirSession): ConeSubstitutor =
ConeSubstitutorByMap( ConeSubstitutorByMap.create(
fromClass.typeParameters.zip(toClass.typeParameters).associate { (fromTypeParameter, toTypeParameter) -> fromClass.typeParameters.zip(toClass.typeParameters).associate { (fromTypeParameter, toTypeParameter) ->
fromTypeParameter.symbol to ConeTypeParameterTypeImpl( fromTypeParameter.symbol to ConeTypeParameterTypeImpl(
ConeTypeParameterLookupTag(toTypeParameter.symbol), ConeTypeParameterLookupTag(toTypeParameter.symbol),
@@ -307,7 +307,7 @@ fun createSubstitutionForSupertype(superType: ConeLookupTagBasedType, session: F
it as? ConeKotlinType ?: ConeErrorType(ConeSimpleDiagnostic("illegal projection usage", DiagnosticKind.IllegalProjectionUsage)) it as? ConeKotlinType ?: ConeErrorType(ConeSimpleDiagnostic("illegal projection usage", DiagnosticKind.IllegalProjectionUsage))
} }
val mapping = klass.typeParameters.map { it.symbol }.zip(arguments).toMap() val mapping = klass.typeParameters.map { it.symbol }.zip(arguments).toMap()
return ConeSubstitutorByMap(mapping, session) return ConeSubstitutorByMap.create(mapping, session)
} }
fun FirRegularClassSymbol.getSuperClassSymbolOrAny(session: FirSession): FirRegularClassSymbol { fun FirRegularClassSymbol.getSuperClassSymbolOrAny(session: FirSession): FirRegularClassSymbol {
@@ -67,7 +67,7 @@ fun wrapProjection(old: ConeTypeProjection, newType: ConeKotlinType): ConeTypePr
abstract class AbstractConeSubstitutor(protected val typeContext: ConeTypeContext) : ConeSubstitutor() { abstract class AbstractConeSubstitutor(protected val typeContext: ConeTypeContext) : ConeSubstitutor() {
abstract fun substituteType(type: ConeKotlinType): ConeKotlinType? abstract fun substituteType(type: ConeKotlinType): ConeKotlinType?
open fun substituteArgument(projection: ConeTypeProjection, index: Int): ConeTypeProjection? { override fun substituteArgument(projection: ConeTypeProjection, index: Int): ConeTypeProjection? {
val type = (projection as? ConeKotlinTypeProjection)?.type ?: return null val type = (projection as? ConeKotlinTypeProjection)?.type ?: return null
val newType = substituteOrNull(type) ?: return null val newType = substituteOrNull(type) ?: return null
return wrapProjection(projection, newType) return wrapProjection(projection, newType)
@@ -212,12 +212,7 @@ abstract class AbstractConeSubstitutor(protected val typeContext: ConeTypeContex
} }
fun substitutorByMap(substitution: Map<FirTypeParameterSymbol, ConeKotlinType>, useSiteSession: FirSession): ConeSubstitutor { fun substitutorByMap(substitution: Map<FirTypeParameterSymbol, ConeKotlinType>, useSiteSession: FirSession): ConeSubstitutor {
// If all arguments match parameters, then substitutor isn't needed return ConeSubstitutorByMap.create(substitution, useSiteSession, allowIdenticalSubstitution = false)
if (substitution.all { (parameterSymbol, argumentType) ->
(argumentType as? ConeTypeParameterType)?.lookupTag?.typeParameterSymbol == parameterSymbol && !argumentType.isMarkedNullable
}
) return ConeSubstitutor.Empty
return ConeSubstitutorByMap(substitution, useSiteSession)
} }
data class ChainedSubstitutor(val first: ConeSubstitutor, val second: ConeSubstitutor) : ConeSubstitutor() { data class ChainedSubstitutor(val first: ConeSubstitutor, val second: ConeSubstitutor) : ConeSubstitutor() {
@@ -226,6 +221,11 @@ data class ChainedSubstitutor(val first: ConeSubstitutor, val second: ConeSubsti
return second.substituteOrNull(type) return second.substituteOrNull(type)
} }
override fun substituteArgument(projection: ConeTypeProjection, index: Int): ConeTypeProjection? {
first.substituteArgument(projection, index)?.let { return second.substituteArgument(projection, index) }
return second.substituteArgument(projection, index)
}
override fun toString(): String { override fun toString(): String {
return "$first then $second" return "$first then $second"
} }
@@ -237,11 +237,31 @@ fun ConeSubstitutor.chain(other: ConeSubstitutor): ConeSubstitutor {
return ChainedSubstitutor(this, other) return ChainedSubstitutor(this, other)
} }
class ConeSubstitutorByMap( class ConeSubstitutorByMap private constructor(
// Used only for sake of optimizations at org.jetbrains.kotlin.analysis.api.fir.types.KtFirMapBackedSubstitutor // Used only for sake of optimizations at org.jetbrains.kotlin.analysis.api.fir.types.KtFirMapBackedSubstitutor
val substitution: Map<FirTypeParameterSymbol, ConeKotlinType>, val substitution: Map<FirTypeParameterSymbol, ConeKotlinType>,
private val useSiteSession: FirSession private val useSiteSession: FirSession
) : AbstractConeSubstitutor(useSiteSession.typeContext) { ) : AbstractConeSubstitutor(useSiteSession.typeContext) {
companion object {
fun create(
substitution: Map<FirTypeParameterSymbol, ConeKotlinType>,
useSiteSession: FirSession,
allowIdenticalSubstitution: Boolean = true,
): ConeSubstitutor {
if (substitution.isEmpty()) return Empty
if (!allowIdenticalSubstitution) {
// If all arguments match parameters, then substitutor isn't needed
val substitutionIsIdentical = substitution.all { (parameterSymbol, argumentType) ->
(argumentType as? ConeTypeParameterType)?.lookupTag?.typeParameterSymbol == parameterSymbol && !argumentType.isMarkedNullable
}
if (substitutionIsIdentical) {
return Empty
}
}
return ConeSubstitutorByMap(substitution, useSiteSession)
}
}
private val hashCode by lazy(LazyThreadSafetyMode.PUBLICATION) { private val hashCode by lazy(LazyThreadSafetyMode.PUBLICATION) {
substitution.hashCode() substitution.hashCode()
@@ -79,7 +79,7 @@ class FirLocalScope private constructor(
val klass = classes[name] val klass = classes[name]
if (klass != null) { if (klass != null) {
val substitution = klass.typeParameterSymbols.associateWith { it.toConeType() } val substitution = klass.typeParameterSymbols.associateWith { it.toConeType() }
processor(klass, ConeSubstitutorByMap(substitution, useSiteSession)) processor(klass, ConeSubstitutorByMap.create(substitution, useSiteSession))
} }
} }
@@ -37,7 +37,7 @@ abstract class FirNestedClassifierScope(val klass: FirClass, val useSiteSession:
val substitution = klass.typeParameters.associate { val substitution = klass.typeParameters.associate {
it.symbol to it.toConeType() it.symbol to it.toConeType()
} }
ConeSubstitutorByMap(substitution, useSiteSession) ConeSubstitutorByMap.create(substitution, useSiteSession)
} }
processor(matchedClass, substitutor) processor(matchedClass, substitutor)
} }
@@ -77,8 +77,8 @@ class FirScriptDeclarationsScope(
) { ) {
val matchedClass = classIndex[name] ?: return val matchedClass = classIndex[name] ?: return
val substitution = matchedClass.typeParameterSymbols.associateWith { it.toConeType() } val substitution = matchedClass.typeParameterSymbols.associateWith { it.toConeType() }
processor(matchedClass, ConeSubstitutorByMap(substitution, useSiteSession)) processor(matchedClass, ConeSubstitutorByMap.create(substitution, useSiteSession))
} }
override fun getClassifierNames(): Set<Name> = classIndex.keys override fun getClassifierNames(): Set<Name> = classIndex.keys
} }
@@ -424,7 +424,7 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
val typeParameterErasureMap = this.extractTypeParameters() val typeParameterErasureMap = this.extractTypeParameters()
.map { (it as ConeTypeParameterLookupTag).typeParameterSymbol } .map { (it as ConeTypeParameterLookupTag).typeParameterSymbol }
.eraseToUpperBoundsAssociated(session) .eraseToUpperBoundsAssociated(session)
val substitutor by lazy { ConeSubstitutorByMap(typeParameterErasureMap, session) } val substitutor by lazy { ConeSubstitutorByMap.create(typeParameterErasureMap, session) }
val typeWithErasedTypeParameters = if (argumentsCount() != 0) { val typeWithErasedTypeParameters = if (argumentsCount() != 0) {
replaceArgumentsDeeply { replaceArgumentsDeeply {
val type = it.getType() val type = it.getType()
@@ -19,7 +19,7 @@ fun createExpectActualTypeParameterSubstitutor(
val substitution = expectActualTypeParameters.associate { (expectedParameterSymbol, actualParameterSymbol) -> val substitution = expectActualTypeParameters.associate { (expectedParameterSymbol, actualParameterSymbol) ->
expectedParameterSymbol to actualParameterSymbol.toLookupTag().constructType(emptyArray(), isNullable = false) expectedParameterSymbol to actualParameterSymbol.toLookupTag().constructType(emptyArray(), isNullable = false)
} }
val substitutor = ConeSubstitutorByMap( val substitutor = ConeSubstitutorByMap.create(
substitution, substitution,
useSiteSession useSiteSession
) )
@@ -1024,7 +1024,7 @@ abstract class FirDataFlowAnalyzer(
val substitutionFromArguments = typeParameters.zip(qualifiedAccess.typeArguments).map { (typeParameterRef, typeArgument) -> val substitutionFromArguments = typeParameters.zip(qualifiedAccess.typeArguments).map { (typeParameterRef, typeArgument) ->
typeParameterRef.symbol to typeArgument.toConeTypeProjection().type typeParameterRef.symbol to typeArgument.toConeTypeProjection().type
}.filter { it.second != null }.toMap() as Map<FirTypeParameterSymbol, ConeKotlinType> }.filter { it.second != null }.toMap() as Map<FirTypeParameterSymbol, ConeKotlinType>
ConeSubstitutorByMap(substitutionFromArguments, components.session) ConeSubstitutorByMap.create(substitutionFromArguments, components.session)
} else { } else {
ConeSubstitutor.Empty ConeSubstitutor.Empty
} }
@@ -1034,7 +1034,7 @@ abstract class FirDataFlowAnalyzer(
typeArgumentsSubstitutor typeArgumentsSubstitutor
} else { } else {
val map = originalFunction.symbol.typeParameterSymbols.zip(typeParameters.map { it.symbol.toConeType() }).toMap() val map = originalFunction.symbol.typeParameterSymbols.zip(typeParameters.map { it.symbol.toConeType() }).toMap()
ConeSubstitutorByMap(map, components.session).chain(typeArgumentsSubstitutor) ConeSubstitutorByMap.create(map, components.session).chain(typeArgumentsSubstitutor)
} }
for (conditionalEffect in conditionalEffects) { for (conditionalEffect in conditionalEffects) {