From 624bea3ecf1e027454f39b4c2a4843ba6c426be8 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Mon, 4 Mar 2024 08:51:37 +0200 Subject: [PATCH] [FIR] Never create ConeSubstitutorByMap with empty substitution Relates to KT-66323 --- .../fir/components/KtFirSubstitutorFactory.kt | 9 +++-- ...xpressionJavaNullabilityWarningCheckers.kt | 2 +- .../checkers/FirCastDiagnosticsHelpers.kt | 2 +- .../FirInconsistentTypeParameterHelpers.kt | 2 +- .../resolve/substitution/ConeSubstitutor.kt | 6 ++++ .../kotlin/fir/backend/ConversionUtils.kt | 2 +- .../java/enhancement/SignatureEnhancement.kt | 5 +-- .../kotlin/fir/scopes/jvm/JvmMappedScope.kt | 2 +- .../kotlin/fir/resolve/SupertypeUtils.kt | 2 +- .../fir/resolve/substitution/Substitutors.kt | 36 ++++++++++++++----- .../kotlin/fir/scopes/impl/FirLocalScope.kt | 2 +- .../scopes/impl/FirNestedClassifierScope.kt | 2 +- .../scopes/impl/FirScriptDeclarationsScope.kt | 4 +-- .../kotlin/fir/types/ConeInferenceContext.kt | 2 +- .../kotlin/fir/types/ExpectActualUtils.kt | 2 +- .../fir/resolve/dfa/FirDataFlowAnalyzer.kt | 4 +-- 16 files changed, 57 insertions(+), 27 deletions(-) diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSubstitutorFactory.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSubstitutorFactory.kt index 7f62f6fdc67..ac0c7f42c08 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSubstitutorFactory.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSubstitutorFactory.kt @@ -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.fir.KtFirAnalysisSession 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.KtFirType import org.jetbrains.kotlin.analysis.api.types.KtSubstitutor @@ -29,7 +30,9 @@ internal class KtFirSubstitutorFactory( } } - val coneSubstitutor = ConeSubstitutorByMap(firSubstitution, analysisSession.useSiteSession) - return KtFirMapBackedSubstitutor(coneSubstitutor, analysisSession.firSymbolBuilder) + return when (val coneSubstitutor = ConeSubstitutorByMap.create(firSubstitution, analysisSession.useSiteSession)) { + is ConeSubstitutorByMap -> KtFirMapBackedSubstitutor(coneSubstitutor, analysisSession.firSymbolBuilder) + else -> KtFirGenericSubstitutor(coneSubstitutor, analysisSession.firSymbolBuilder) + } } -} \ No newline at end of file +} diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirExpressionJavaNullabilityWarningCheckers.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirExpressionJavaNullabilityWarningCheckers.kt index d33e166f089..1b1018408e4 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirExpressionJavaNullabilityWarningCheckers.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirExpressionJavaNullabilityWarningCheckers.kt @@ -80,7 +80,7 @@ object FirQualifiedAccessJavaNullabilityWarningChecker : FirQualifiedAccessExpre } } - return ConeSubstitutorByMap(substitutionMap, session) + return ConeSubstitutorByMap.create(substitutionMap, session) } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirCastDiagnosticsHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirCastDiagnosticsHelpers.kt index 9052891a079..1cfd7c64d9d 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirCastDiagnosticsHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirCastDiagnosticsHelpers.kt @@ -279,7 +279,7 @@ fun findStaticallyKnownSubtype( // At this point we have values for all type parameters of List // Let's make a type by substituting them: List -> List - val substitutor = ConeSubstitutorByMap(resultSubstitution, session) + val substitutor = ConeSubstitutorByMap.create(resultSubstitution, session) return substitutor.substituteOrSelf(subtypeWithVariablesType) } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirInconsistentTypeParameterHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirInconsistentTypeParameterHelpers.kt index 109f7ea1f28..a5714f8340f 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirInconsistentTypeParameterHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirInconsistentTypeParameterHelpers.kt @@ -69,7 +69,7 @@ private fun buildDeepSubstitutionMultimap( for (index in 0 until count) { val typeArgument = typeArguments[index] - val substitutedArgument = ConeSubstitutorByMap(substitution, session) + val substitutedArgument = ConeSubstitutorByMap.create(substitution, session) .substituteArgument(typeArgument, index) ?: typeArgument val substitutedType = substitutedArgument.type ?: continue diff --git a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/resolve/substitution/ConeSubstitutor.kt b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/resolve/substitution/ConeSubstitutor.kt index 8d17f6372f8..b1b0084bf1b 100644 --- a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/resolve/substitution/ConeSubstitutor.kt +++ b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/resolve/substitution/ConeSubstitutor.kt @@ -6,11 +6,13 @@ package org.jetbrains.kotlin.fir.resolve.substitution import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.ConeTypeProjection import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker abstract class ConeSubstitutor : TypeSubstitutorMarker { open fun substituteOrSelf(type: ConeKotlinType): ConeKotlinType = substituteOrNull(type) ?: type abstract fun substituteOrNull(type: ConeKotlinType): ConeKotlinType? + abstract fun substituteArgument(projection: ConeTypeProjection, index: Int): ConeTypeProjection? object Empty : ConeSubstitutor() { override fun substituteOrSelf(type: ConeKotlinType): ConeKotlinType { @@ -21,6 +23,10 @@ abstract class ConeSubstitutor : TypeSubstitutorMarker { return null } + override fun substituteArgument(projection: ConeTypeProjection, index: Int): ConeTypeProjection? { + return null + } + override fun toString(): String = "Empty" } } diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt index 3c3bbaac34d..4ae5816967b 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt @@ -900,7 +900,7 @@ internal fun FirQualifiedAccessExpression.buildSubstitutorByCalledCallable(): Co val typeProjection = typeArguments.getOrNull(index) as? FirTypeProjectionWithVariance ?: continue map[typeParameter.symbol] = typeProjection.typeRef.coneType } - return ConeSubstitutorByMap(map, session) + return ConeSubstitutorByMap.create(map, session) } val augmentedArrayAssignSourceKindToIrStatementOrigin = mapOf( diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/SignatureEnhancement.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/SignatureEnhancement.kt index 736097d06f7..93fb6d34645 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/SignatureEnhancement.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/SignatureEnhancement.kt @@ -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.toConeKotlinTypeProbablyFlexible 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.scopes.jvm.computeJvmDescriptor import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag @@ -279,7 +280,7 @@ class FirSignatureEnhancement( var isJavaRecordComponent = false val typeParameterSubstitutionMap = mutableMapOf() - var typeParameterSubstitutor: ConeSubstitutorByMap? = null + var typeParameterSubstitutor: ConeSubstitutor? = null val declarationOrigin = if (isIntersectionOverride) FirDeclarationOrigin.IntersectionOverride else FirDeclarationOrigin.Enhancement @@ -360,7 +361,7 @@ class FirSignatureEnhancement( newTypeParameter } if (typeParameterSubstitutionMap.isNotEmpty()) { - typeParameterSubstitutor = ConeSubstitutorByMap(typeParameterSubstitutionMap, session) + typeParameterSubstitutor = ConeSubstitutorByMap.create(typeParameterSubstitutionMap, session) } returnTypeRef = newReturnTypeRef.withReplacedConeType( typeParameterSubstitutor?.substituteOrNull(newReturnTypeRef.coneType) diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/scopes/jvm/JvmMappedScope.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/scopes/jvm/JvmMappedScope.kt index 146c54d7f50..42de232a879 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/scopes/jvm/JvmMappedScope.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/scopes/jvm/JvmMappedScope.kt @@ -415,7 +415,7 @@ class JvmMappedScope( * @returns {T1 -> F1, T2 -> F2} substitution */ private fun createMappingSubstitutor(fromClass: FirRegularClass, toClass: FirRegularClass, session: FirSession): ConeSubstitutor = - ConeSubstitutorByMap( + ConeSubstitutorByMap.create( fromClass.typeParameters.zip(toClass.typeParameters).associate { (fromTypeParameter, toTypeParameter) -> fromTypeParameter.symbol to ConeTypeParameterTypeImpl( ConeTypeParameterLookupTag(toTypeParameter.symbol), diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/SupertypeUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/SupertypeUtils.kt index 8be1be195f4..bcc90d55901 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/SupertypeUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/SupertypeUtils.kt @@ -307,7 +307,7 @@ fun createSubstitutionForSupertype(superType: ConeLookupTagBasedType, session: F it as? ConeKotlinType ?: ConeErrorType(ConeSimpleDiagnostic("illegal projection usage", DiagnosticKind.IllegalProjectionUsage)) } 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 { diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/substitution/Substitutors.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/substitution/Substitutors.kt index 2ef357b8f19..3a778036034 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/substitution/Substitutors.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/substitution/Substitutors.kt @@ -67,7 +67,7 @@ fun wrapProjection(old: ConeTypeProjection, newType: ConeKotlinType): ConeTypePr abstract class AbstractConeSubstitutor(protected val typeContext: ConeTypeContext) : ConeSubstitutor() { 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 newType = substituteOrNull(type) ?: return null return wrapProjection(projection, newType) @@ -212,12 +212,7 @@ abstract class AbstractConeSubstitutor(protected val typeContext: ConeTypeContex } fun substitutorByMap(substitution: Map, useSiteSession: FirSession): ConeSubstitutor { - // If all arguments match parameters, then substitutor isn't needed - if (substitution.all { (parameterSymbol, argumentType) -> - (argumentType as? ConeTypeParameterType)?.lookupTag?.typeParameterSymbol == parameterSymbol && !argumentType.isMarkedNullable - } - ) return ConeSubstitutor.Empty - return ConeSubstitutorByMap(substitution, useSiteSession) + return ConeSubstitutorByMap.create(substitution, useSiteSession, allowIdenticalSubstitution = false) } 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) } + 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 { return "$first then $second" } @@ -237,11 +237,31 @@ fun ConeSubstitutor.chain(other: ConeSubstitutor): ConeSubstitutor { 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 val substitution: Map, private val useSiteSession: FirSession ) : AbstractConeSubstitutor(useSiteSession.typeContext) { + companion object { + fun create( + substitution: Map, + 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) { substitution.hashCode() diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirLocalScope.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirLocalScope.kt index 9f4407f6e7a..91fb4ec088d 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirLocalScope.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirLocalScope.kt @@ -79,7 +79,7 @@ class FirLocalScope private constructor( val klass = classes[name] if (klass != null) { val substitution = klass.typeParameterSymbols.associateWith { it.toConeType() } - processor(klass, ConeSubstitutorByMap(substitution, useSiteSession)) + processor(klass, ConeSubstitutorByMap.create(substitution, useSiteSession)) } } diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirNestedClassifierScope.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirNestedClassifierScope.kt index bb7f5fec7a5..897b3f4cdb1 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirNestedClassifierScope.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirNestedClassifierScope.kt @@ -37,7 +37,7 @@ abstract class FirNestedClassifierScope(val klass: FirClass, val useSiteSession: val substitution = klass.typeParameters.associate { it.symbol to it.toConeType() } - ConeSubstitutorByMap(substitution, useSiteSession) + ConeSubstitutorByMap.create(substitution, useSiteSession) } processor(matchedClass, substitutor) } diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirScriptDeclarationsScope.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirScriptDeclarationsScope.kt index e6ce237f6b2..55dde24cf14 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirScriptDeclarationsScope.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirScriptDeclarationsScope.kt @@ -77,8 +77,8 @@ class FirScriptDeclarationsScope( ) { val matchedClass = classIndex[name] ?: return val substitution = matchedClass.typeParameterSymbols.associateWith { it.toConeType() } - processor(matchedClass, ConeSubstitutorByMap(substitution, useSiteSession)) + processor(matchedClass, ConeSubstitutorByMap.create(substitution, useSiteSession)) } override fun getClassifierNames(): Set = classIndex.keys -} \ No newline at end of file +} diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt index 52a7b21db90..1c7eeff4a19 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt @@ -424,7 +424,7 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo val typeParameterErasureMap = this.extractTypeParameters() .map { (it as ConeTypeParameterLookupTag).typeParameterSymbol } .eraseToUpperBoundsAssociated(session) - val substitutor by lazy { ConeSubstitutorByMap(typeParameterErasureMap, session) } + val substitutor by lazy { ConeSubstitutorByMap.create(typeParameterErasureMap, session) } val typeWithErasedTypeParameters = if (argumentsCount() != 0) { replaceArgumentsDeeply { val type = it.getType() diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ExpectActualUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ExpectActualUtils.kt index 65c9ef02481..227c6bde5ed 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ExpectActualUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ExpectActualUtils.kt @@ -19,7 +19,7 @@ fun createExpectActualTypeParameterSubstitutor( val substitution = expectActualTypeParameters.associate { (expectedParameterSymbol, actualParameterSymbol) -> expectedParameterSymbol to actualParameterSymbol.toLookupTag().constructType(emptyArray(), isNullable = false) } - val substitutor = ConeSubstitutorByMap( + val substitutor = ConeSubstitutorByMap.create( substitution, useSiteSession ) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt index d564b4d7cb9..d44f02bf82f 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt @@ -1024,7 +1024,7 @@ abstract class FirDataFlowAnalyzer( val substitutionFromArguments = typeParameters.zip(qualifiedAccess.typeArguments).map { (typeParameterRef, typeArgument) -> typeParameterRef.symbol to typeArgument.toConeTypeProjection().type }.filter { it.second != null }.toMap() as Map - ConeSubstitutorByMap(substitutionFromArguments, components.session) + ConeSubstitutorByMap.create(substitutionFromArguments, components.session) } else { ConeSubstitutor.Empty } @@ -1034,7 +1034,7 @@ abstract class FirDataFlowAnalyzer( typeArgumentsSubstitutor } else { 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) {