org.jetbrains.kotlin.fir.java.enhancement.SignatureEnhancement.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-compiler-embeddable Show documentation
Show all versions of kotlin-compiler-embeddable Show documentation
the Kotlin compiler embeddable
/*
* Copyright 2010-2023 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.fir.java.enhancement
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.builtins.StandardNames.DEFAULT_VALUE_PARAMETER
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fakeElement
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.caches.FirCache
import org.jetbrains.kotlin.fir.caches.FirCachesFactory
import org.jetbrains.kotlin.fir.caches.createCache
import org.jetbrains.kotlin.fir.caches.firCachesFactory
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.*
import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty
import org.jetbrains.kotlin.fir.declarations.synthetic.buildSyntheticProperty
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.expressions.FirLiteralExpression
import org.jetbrains.kotlin.fir.expressions.unexpandedClassId
import org.jetbrains.kotlin.fir.java.FirJavaTypeConversionMode
import org.jetbrains.kotlin.fir.java.JavaTypeParameterStack
import org.jetbrains.kotlin.fir.java.declarations.FirJavaClass
import org.jetbrains.kotlin.fir.java.declarations.FirJavaExternalAnnotation
import org.jetbrains.kotlin.fir.java.declarations.FirJavaField
import org.jetbrains.kotlin.fir.java.declarations.buildJavaField
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.CallableCopyTypeCalculator
import org.jetbrains.kotlin.fir.scopes.DeferredCallableCopyReturnType
import org.jetbrains.kotlin.fir.scopes.deferredCallableCopyReturnType
import org.jetbrains.kotlin.fir.scopes.jvm.computeJvmDescriptor
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.fir.types.impl.FirImplicitTypeRefImplWithoutSource
import org.jetbrains.kotlin.fir.types.jvm.FirJavaTypeRef
import org.jetbrains.kotlin.fir.utils.exceptions.withFirEntry
import org.jetbrains.kotlin.load.java.*
import org.jetbrains.kotlin.load.java.AnnotationQualifierApplicabilityType.VALUE_PARAMETER
import org.jetbrains.kotlin.load.java.typeEnhancement.*
import org.jetbrains.kotlin.load.kotlin.SignatureBuildingComponents
import org.jetbrains.kotlin.name.*
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeParameterMarker
import org.jetbrains.kotlin.types.model.TypeSystemContext
import org.jetbrains.kotlin.util.PrivateForInline
import org.jetbrains.kotlin.utils.exceptions.errorWithAttachment
class FirSignatureEnhancement(
private val owner: FirRegularClass,
private val session: FirSession,
private val overridden: FirCallableDeclaration.() -> List,
) {
/*
* FirSignatureEnhancement may be created with library session which doesn't have single module data,
* so owner is a only place where module data can be obtained. However it's guaranteed that `owner`
* was created for same session as one passed to constructor, so it's safe to use owners module data
*/
private val moduleData get() = owner.moduleData
private val javaTypeParameterStack: JavaTypeParameterStack =
if (owner is FirJavaClass) owner.javaTypeParameterStack else JavaTypeParameterStack.EMPTY
private val typeQualifierResolver = session.javaAnnotationTypeQualifierResolver
// This property is assumed to be initialized only after annotations for the class are initialized
// While in one of the cases FirSignatureEnhancement is created just one step before annotations resolution
private val contextQualifiers: JavaTypeQualifiersByElementType? by lazy(LazyThreadSafetyMode.NONE) {
typeQualifierResolver.extractDefaultQualifiers(owner)
}
private val enhancementsCache = session.enhancedSymbolStorage.cacheByOwner.getValue(owner.symbol, null)
fun enhancedFunction(
function: FirFunctionSymbol<*>,
name: Name?,
precomputedOverridden: List? = null,
): FirFunctionSymbol<*> {
return enhancementsCache.enhancedFunctions.getValue(
function,
FirEnhancedSymbolsStorage.FunctionEnhancementContext(this, name, precomputedOverridden)
)
}
fun enhancedProperty(property: FirVariableSymbol<*>, name: Name): FirVariableSymbol<*> {
return enhancementsCache.enhancedVariables.getValue(property, this to name)
}
private fun FirDeclaration.computeDefaultQualifiers() =
typeQualifierResolver.extractAndMergeDefaultQualifiers(contextQualifiers, annotations)
@PrivateForInline
internal fun enhance(
original: FirVariableSymbol<*>,
name: Name,
): FirVariableSymbol<*> {
when (val firElement = original.fir) {
is FirEnumEntry -> {
if (firElement.returnTypeRef !is FirJavaTypeRef) return original
val predefinedInfo =
PredefinedFunctionEnhancementInfo(
TypeEnhancementInfo(0 to JavaTypeQualifiers(NullabilityQualifier.NOT_NULL, null, false)),
emptyList()
)
val newReturnTypeRef = enhanceReturnType(firElement, firElement.computeDefaultQualifiers(), predefinedInfo)
return buildEnumEntryCopy(firElement) {
symbol = FirEnumEntrySymbol(firElement.symbol.callableId)
returnTypeRef = newReturnTypeRef
origin = FirDeclarationOrigin.Enhancement
}.symbol
}
is FirField -> {
if (firElement.returnTypeRef !is FirJavaTypeRef) return original
val newReturnTypeRef = enhanceReturnType(
firElement, firElement.computeDefaultQualifiers(),
predefinedEnhancementInfo = null
).let {
val coneTypeOrNull = it.coneTypeOrNull
val lowerBound = coneTypeOrNull?.lowerBoundIfFlexible()
if (lowerBound != null && lowerBound.isString && firElement.isStatic && firElement.hasConstantInitializer) {
it.withReplacedConeType(coneTypeOrNull.withNullability(ConeNullability.NOT_NULL, session.typeContext))
} else {
it
}
}
val symbol = FirFieldSymbol(original.callableId)
buildJavaField {
source = firElement.source
moduleData = [email protected]
this.symbol = symbol
this.name = name
returnTypeRef = newReturnTypeRef
isFromSource = original.origin.fromSource
// TODO: Use some kind of copy mechanism
visibility = firElement.visibility
modality = firElement.modality
isVar = firElement.isVar
annotationBuilder = { firElement.annotations }
status = firElement.status
if (firElement is FirJavaField) {
lazyInitializer = firElement.lazyInitializer
lazyHasConstantInitializer = firElement.lazyHasConstantInitializer
} else {
initializer = firElement.initializer
}
dispatchReceiverType = firElement.dispatchReceiverType
attributes = firElement.attributes.copy()
}
return symbol
}
is FirSyntheticProperty -> {
val propertySymbol = firElement.symbol as FirJavaOverriddenSyntheticPropertySymbol
val getterDelegate = firElement.getter.delegate
val overridden = firElement.overridden()
val enhancedGetterSymbol = getterDelegate.enhanceAccessorOrNull(overridden)
val setterDelegate = firElement.setter?.delegate
val enhancedSetterSymbol = setterDelegate?.enhanceAccessorOrNull(overridden)
if (enhancedGetterSymbol == null && enhancedSetterSymbol == null) {
return original
}
return buildSyntheticProperty {
moduleData = [email protected]
this.name = name
symbol = FirJavaOverriddenSyntheticPropertySymbol(propertySymbol.callableId, propertySymbol.getterId)
delegateGetter = enhancedGetterSymbol?.fir as FirSimpleFunction? ?: getterDelegate
delegateSetter = enhancedSetterSymbol?.fir as FirSimpleFunction? ?: setterDelegate
status = firElement.status
deprecationsProvider = getDeprecationsProviderFromAccessors(session, delegateGetter, delegateSetter)
dispatchReceiverType = firElement.dispatchReceiverType
}.symbol
}
else -> {
if (original is FirPropertySymbol) return original
errorWithAttachment("Can't make enhancement for ${original::class.java}") {
withFirEntry("firElement", firElement)
}
}
}
}
private fun FirSimpleFunction.enhanceAccessorOrNull(overriddenProperties: List): FirFunctionSymbol<*>? {
if (!symbol.isEnhanceable()) return null
return enhancedFunction(symbol, name, overriddenProperties)
}
@PrivateForInline
internal fun enhance(
original: FirFunctionSymbol<*>,
name: Name?,
precomputedOverridden: List?,
): FirFunctionSymbol<*> {
if (!original.isEnhanceable()) {
return original
}
val firMethod = original.fir
val enhancedParameters = enhanceTypeParameterBoundsForMethod(firMethod)
return enhanceMethod(
firMethod,
original.callableId,
name,
enhancedParameters,
original is FirIntersectionOverrideFunctionSymbol,
precomputedOverridden
)
}
private fun FirCallableSymbol<*>.isEnhanceable(): Boolean {
return origin is FirDeclarationOrigin.Java || isEnhanceableIntersection()
}
/**
* Intersection overrides with Java and Kotlin overridden symbols need to be enhanced so that we get non-flexible types
* in the signature.
* This is required for @PurelyImplements to work properly.
*
* We only enhance intersection overrides if their dispatch receiver is equal to [owner], i.e. we don't enhance inherited
* intersection overrides.
*
* See compiler/testData/codegen/box/fakeOverride/javaInheritsKotlinIntersectionOverride.kt.
*/
private fun FirCallableSymbol<*>.isEnhanceableIntersection(): Boolean {
return this is FirIntersectionCallableSymbol &&
dispatchReceiverClassLookupTagOrNull() == owner.symbol.toLookupTag() &&
unwrapFakeOverrides>().origin is FirDeclarationOrigin.Enhancement
}
/**
* @param enhancedTypeParameters pass enhanced type parameters that will be used instead of original ones.
* **null** means that [enhanceMethod] will use the original type parameters to create an enhanced function
*/
private fun enhanceMethod(
firMethod: FirFunction,
methodId: CallableId,
name: Name?,
enhancedTypeParameters: List?,
isIntersectionOverride: Boolean,
precomputedOverridden: List?,
): FirFunctionSymbol<*> {
val fakeSource = firMethod.source?.fakeElement(KtFakeSourceElementKind.Enhancement)
val predefinedEnhancementInfo =
SignatureBuildingComponents.signature(
owner.symbol.classId,
firMethod.computeJvmDescriptor { it.toConeKotlinTypeProbablyFlexible(session, javaTypeParameterStack, fakeSource) }
).let { signature ->
PREDEFINED_FUNCTION_ENHANCEMENT_INFO_BY_SIGNATURE[signature]?.useWarningsIfErrorModeIsNotEnabledYet()
}
predefinedEnhancementInfo?.let {
assert(it.parametersInfo.size == firMethod.valueParameters.size) {
"Predefined enhancement info for $this has ${it.parametersInfo.size}, but ${firMethod.valueParameters.size} expected"
}
}
val defaultQualifiers = firMethod.computeDefaultQualifiers()
val overriddenMembers = precomputedOverridden ?: (firMethod as? FirSimpleFunction)?.overridden().orEmpty()
// TODO(KT-66195) handle context receivers
val hasReceiver = overriddenMembers.any { it.receiverParameter != null }
val newReceiverTypeRef = if (firMethod is FirSimpleFunction && hasReceiver) {
enhanceReceiverType(firMethod, overriddenMembers, defaultQualifiers)
} else null
val (newReturnTypeRef, deferredCalc) = if (firMethod is FirSimpleFunction) {
enhanceReturnType(firMethod, overriddenMembers, defaultQualifiers, predefinedEnhancementInfo)
} else {
firMethod.returnTypeRef to null
}
val enhancedValueParameterTypes = mutableListOf()
for ((index, valueParameter) in firMethod.valueParameters.withIndex()) {
if (hasReceiver && index == 0) continue
enhancedValueParameterTypes += enhanceValueParameterType(
firMethod, overriddenMembers, hasReceiver,
defaultQualifiers, predefinedEnhancementInfo, valueParameter,
if (hasReceiver) index - 1 else index
)
}
val functionSymbol: FirFunctionSymbol<*>
var isJavaRecordComponent = false
val typeParameterSubstitutionMap = mutableMapOf()
var typeParameterSubstitutor: ConeSubstitutor? = null
val declarationOrigin =
if (isIntersectionOverride) FirDeclarationOrigin.IntersectionOverride else FirDeclarationOrigin.Enhancement
val function = when (firMethod) {
is FirConstructor -> {
val symbol = FirConstructorSymbol(methodId).also { functionSymbol = it }
if (firMethod.isPrimary) {
FirPrimaryConstructorBuilder().apply {
returnTypeRef = newReturnTypeRef!! // Constructors don't have overriddens, newReturnTypeRef is never null
val resolvedStatus = firMethod.status as? FirResolvedDeclarationStatus
status = if (resolvedStatus != null) {
FirResolvedDeclarationStatusImpl(
resolvedStatus.visibility,
Modality.FINAL,
resolvedStatus.effectiveVisibility
)
} else {
FirDeclarationStatusImpl(firMethod.visibility, Modality.FINAL)
}.apply {
isInner = firMethod.isInner
// Java annotation class constructors have stable names, copy flag.
hasStableParameterNames = firMethod.hasStableParameterNames
}
this.symbol = symbol
dispatchReceiverType = firMethod.dispatchReceiverType
attributes = firMethod.attributes.copy()
}
} else {
FirConstructorBuilder().apply {
returnTypeRef = newReturnTypeRef!! // Constructors don't have overriddens, newReturnTypeRef is never null
status = firMethod.status
this.symbol = symbol
dispatchReceiverType = firMethod.dispatchReceiverType
attributes = firMethod.attributes.copy()
}
}.apply {
source = firMethod.source
moduleData = [email protected]
resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES
origin = declarationOrigin
// TODO: we should set a new origin / containing declaration to type parameters (KT-60440)
this.typeParameters += (enhancedTypeParameters ?: firMethod.typeParameters)
}
}
is FirSimpleFunction -> {
isJavaRecordComponent = firMethod.isJavaRecordComponent == true
FirSimpleFunctionBuilder().apply {
source = firMethod.source
moduleData = [email protected]
origin = declarationOrigin
this.name = name!!
status = firMethod.status
symbol = if (isIntersectionOverride) {
FirIntersectionOverrideFunctionSymbol(
methodId, overriddenMembers.map { it.symbol },
containsMultipleNonSubsumed = (firMethod.symbol as? FirIntersectionCallableSymbol)?.containsMultipleNonSubsumed == true,
)
} else {
FirNamedFunctionSymbol(methodId)
}.also { functionSymbol = it }
resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES
typeParameters += (enhancedTypeParameters ?: firMethod.typeParameters).map { typeParameter ->
// FirJavaMethod contains only FirTypeParameter so [enhancedTypeParameters] must have the same type
require(typeParameter is FirTypeParameter) {
"Unexpected type parameter type: ${typeParameter::class.simpleName}"
}
// TODO: we probably shouldn't build a copy second time. See performFirstRoundOfBoundsResolution (KT-60446)
val newTypeParameter = buildTypeParameterCopy(typeParameter) {
origin = declarationOrigin
symbol = FirTypeParameterSymbol()
containingDeclarationSymbol = functionSymbol
}
typeParameterSubstitutionMap[typeParameter.symbol] = ConeTypeParameterTypeImpl(
newTypeParameter.symbol.toLookupTag(), isNullable = false
)
newTypeParameter
}
if (typeParameterSubstitutionMap.isNotEmpty()) {
typeParameterSubstitutor = ConeSubstitutorByMap.create(typeParameterSubstitutionMap, session)
}
returnTypeRef = if (typeParameterSubstitutor != null && newReturnTypeRef is FirResolvedTypeRef) {
newReturnTypeRef.withReplacedConeType(
typeParameterSubstitutor?.substituteOrNull(newReturnTypeRef.coneType)
)
} else {
newReturnTypeRef ?: FirImplicitTypeRefImplWithoutSource
}
val substitutedReceiverTypeRef = newReceiverTypeRef?.withReplacedConeType(
typeParameterSubstitutor?.substituteOrNull(newReceiverTypeRef.coneType)
)
receiverParameter = substitutedReceiverTypeRef?.let { receiverType ->
buildReceiverParameter {
typeRef = receiverType
annotations += firMethod.valueParameters.first().annotations
source = receiverType.source?.fakeElement(KtFakeSourceElementKind.ReceiverFromType)
}
}
typeParameters.forEach { typeParameter ->
typeParameter.replaceBounds(
typeParameter.bounds.map { boundTypeRef ->
boundTypeRef.withReplacedConeType(typeParameterSubstitutor?.substituteOrNull(boundTypeRef.coneType))
}
)
}
dispatchReceiverType = firMethod.dispatchReceiverType
attributes = firMethod.attributes.copy().apply {
if (deferredCalc != null) {
deferredCallableCopyReturnType = if (typeParameterSubstitutor != null) {
DelegatingDeferredReturnTypeWithSubstitution(deferredCalc, typeParameterSubstitutor!!)
} else {
deferredCalc
}
}
}
}
}
else -> errorWithAttachment("Unknown Java method to enhance: ${firMethod::class.java}") {
withFirEntry("firMethod", firMethod)
}
}.apply {
val newValueParameters = firMethod.valueParameters.zip(enhancedValueParameterTypes) { valueParameter, enhancedReturnType ->
valueParameter.defaultValue?.replaceConeTypeOrNull(enhancedReturnType.coneType)
buildValueParameter {
source = valueParameter.source
containingFunctionSymbol = functionSymbol
moduleData = [email protected]
origin = declarationOrigin
returnTypeRef = enhancedReturnType.withReplacedConeType(
typeParameterSubstitutor?.substituteOrNull(enhancedReturnType.coneType)
)
this.name = valueParameter.name
symbol = FirValueParameterSymbol(this.name)
defaultValue = valueParameter.defaultValue
isCrossinline = valueParameter.isCrossinline
isNoinline = valueParameter.isNoinline
isVararg = valueParameter.isVararg
resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES
annotations += valueParameter.annotations
}
}
this.valueParameters += newValueParameters
annotations += firMethod.annotations
deprecationsProvider = annotations.getDeprecationsProviderFromAnnotations(session, fromJava = true)
}.build().apply {
if (isJavaRecordComponent) {
this.isJavaRecordComponent = true
}
updateIsOperatorFlagIfNeeded(this)
}
return function.symbol
}
private fun PredefinedFunctionEnhancementInfo.useWarningsIfErrorModeIsNotEnabledYet(): PredefinedFunctionEnhancementInfo {
val stringVersionRepresentation = errorsSinceLanguageVersion ?: return this
val fromVersionString =
LanguageVersion.fromVersionString(stringVersionRepresentation) ?: error("Unexpected LV: $stringVersionRepresentation")
if (session.languageVersionSettings.languageVersion >= fromVersionString) return this
return warningModeClone ?: error("For not null LV $errorsSinceLanguageVersion, `warningModeClone` should not be null")
}
private fun updateIsOperatorFlagIfNeeded(function: FirFunction) {
if (function !is FirSimpleFunction) return
val isOperator = OperatorFunctionChecks.isOperator(function, session, scopeSession = null).isSuccess
if (!isOperator) return
val newStatus = function.status.copy(isOperator = true)
function.replaceStatus(newStatus)
}
/**
* Perform first time initialization of bounds with FirResolvedTypeRef instances
* But after that bounds are still not enhanced and more over might have not totally correct raw types bounds
* (see the next step in the method performSecondRoundOfBoundsResolution)
*
* In case of A, or similar cases, the bound is converted to the flexible version A<*>..A<*>?,
* while in the end it's assumed to be A>..A<*>?
*
* That's necessary because at this stage it's not quite easy to come just to the final version since for that
* we would the need upper bounds of all the type parameters that might not yet be initialized at the moment
*
* See the usages of FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND_FIRST_ROUND
*/
fun performFirstRoundOfBoundsResolution(
typeParameters: List,
source: KtSourceElement?,
): Pair>, List> {
val initialBounds: MutableList> = mutableListOf()
val typeParametersCopy = ArrayList(typeParameters.size)
for (typeParameter in typeParameters) {
typeParametersCopy += if (typeParameter is FirTypeParameter) {
initialBounds.add(typeParameter.bounds.toList())
buildTypeParameterCopy(typeParameter) {
// TODO: we should create a new symbol to avoid clashing (KT-60445)
bounds.clear()
typeParameter.bounds.mapTo(bounds) {
it.resolveIfJavaType(
session, javaTypeParameterStack, source, FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND_FIRST_ROUND
)
}
}
} else {
typeParameter
}
}
return initialBounds to typeParametersCopy
}
/**
* In most cases that method doesn't change anything
*
* But the cases like A
* After the first step we've got all bounds are initialized to potentially approximated version of raw types
* And here, we compute the final version using previously initialized bounds
*
* So, mostly it works just as the first step, but assumes that bounds already contain FirResolvedTypeRef
*/
private fun performSecondRoundOfBoundsResolution(
typeParameters: List,
initialBounds: List