FIR: Rework loading overrides of special built-in methods from Java

Some of the changed data is correct, but some diagnostics are incorrect
Corner cases like having both contains(Object) and contains(String)
within implementation of Collection<String> is not supported
This commit is contained in:
Denis.Zharkov
2021-02-17 18:31:57 +03:00
parent 4b0aeb7105
commit 45018ea468
25 changed files with 725 additions and 372 deletions
@@ -43,6 +43,9 @@ import org.jetbrains.kotlin.util.OperatorNameConventions.ITERATOR
import org.jetbrains.kotlin.util.OperatorNameConventions.NEXT
import org.jetbrains.kotlin.util.OperatorNameConventions.SET
import org.jetbrains.kotlin.util.OperatorNameConventions.UNARY_OPERATION_NAMES
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.properties.Delegates
@OptIn(FirImplementationDetail::class)
@@ -227,3 +230,29 @@ class FirJavaMethodBuilder : FirFunctionBuilder, FirTypeParametersOwnerBuilder,
inline fun buildJavaMethod(init: FirJavaMethodBuilder.() -> Unit): FirJavaMethod {
return FirJavaMethodBuilder().apply(init).build()
}
@OptIn(ExperimentalContracts::class)
inline fun buildJavaMethodCopy(original: FirSimpleFunction, init: FirJavaMethodBuilder.() -> Unit): FirJavaMethod {
contract {
callsInPlace(init, InvocationKind.EXACTLY_ONCE)
}
val copyBuilder = FirJavaMethodBuilder()
copyBuilder.source = original.source
copyBuilder.session = original.session
copyBuilder.resolvePhase = original.resolvePhase
copyBuilder.attributes = original.attributes.copy()
copyBuilder.returnTypeRef = original.returnTypeRef
copyBuilder.valueParameters.addAll(original.valueParameters)
copyBuilder.body = original.body
copyBuilder.status = original.status
copyBuilder.dispatchReceiverType = original.dispatchReceiverType
copyBuilder.name = original.name
copyBuilder.symbol = original.symbol
copyBuilder.annotations.addAll(original.annotations)
copyBuilder.typeParameters.addAll(original.typeParameters)
val annotations = original.annotations
copyBuilder.annotationBuilder = { annotations }
return copyBuilder
.apply(init)
.build()
}
@@ -20,6 +20,9 @@ import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.fir.visitors.FirVisitor
import org.jetbrains.kotlin.fir.visitors.transformSingle
import org.jetbrains.kotlin.name.Name
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
@OptIn(FirImplementationDetail::class)
class FirJavaValueParameter @FirImplementationDetail constructor(
@@ -174,3 +177,21 @@ class FirJavaValueParameterBuilder {
inline fun buildJavaValueParameter(init: FirJavaValueParameterBuilder.() -> Unit): FirJavaValueParameter {
return FirJavaValueParameterBuilder().apply(init).build()
}
@OptIn(ExperimentalContracts::class)
inline fun buildJavaValueParameterCopy(original: FirValueParameter, init: FirJavaValueParameterBuilder.() -> Unit): FirValueParameter {
contract {
callsInPlace(init, InvocationKind.EXACTLY_ONCE)
}
val copyBuilder = FirJavaValueParameterBuilder()
copyBuilder.source = original.source
copyBuilder.session = original.session
copyBuilder.attributes = original.attributes.copy()
copyBuilder.returnTypeRef = original.returnTypeRef
copyBuilder.name = original.name
val annotations = original.annotations
copyBuilder.annotationBuilder = { annotations }
copyBuilder.defaultValue = original.defaultValue
copyBuilder.isVararg = original.isVararg
return copyBuilder.apply(init).build()
}
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.fir.declarations.synthetic.buildSyntheticProperty
import org.jetbrains.kotlin.fir.initialSignatureAttr
import org.jetbrains.kotlin.fir.java.JavaTypeParameterStack
import org.jetbrains.kotlin.fir.java.declarations.*
import org.jetbrains.kotlin.fir.java.toConeKotlinTypeProbablyFlexible
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.scopes.jvm.computeJvmDescriptor
import org.jetbrains.kotlin.fir.symbols.CallableId
@@ -165,7 +166,10 @@ class FirSignatureEnhancement(
val memberContext = context.copyWithNewDefaultTypeQualifiers(typeQualifierResolver, jsr305State, firMethod.annotations)
val predefinedEnhancementInfo =
SignatureBuildingComponents.signature(owner.symbol.classId, firMethod.computeJvmDescriptor()).let { signature ->
SignatureBuildingComponents.signature(
owner.symbol.classId,
firMethod.computeJvmDescriptor { it.toConeKotlinTypeProbablyFlexible(session, javaTypeParameterStack) }
).let { signature ->
PREDEFINED_FUNCTION_ENHANCEMENT_INFO_BY_SIGNATURE[signature]
}
@@ -308,6 +312,9 @@ class FirSignatureEnhancement(
ownerParameter: FirJavaValueParameter,
index: Int
): FirResolvedTypeRef {
if (ownerParameter.returnTypeRef is FirResolvedTypeRef) {
return ownerParameter.returnTypeRef as FirResolvedTypeRef
}
val signatureParts = ownerFunction.partsForValueParameter(
typeQualifierResolver,
overriddenMembers,
@@ -5,31 +5,17 @@
package org.jetbrains.kotlin.fir.java.scopes
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirCallableMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.java.enhancement.FirSignatureEnhancement
import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.impl.FirFakeOverrideGenerator
import org.jetbrains.kotlin.fir.scopes.jvm.computeJvmDescriptorReplacingKotlinToJava
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.load.java.SpecialGenericSignatures
import org.jetbrains.kotlin.load.java.SpecialGenericSignatures.Companion.ERASED_COLLECTION_PARAMETER_SIGNATURES
import org.jetbrains.kotlin.load.java.SpecialGenericSignatures.Companion.ERASED_VALUE_PARAMETERS_SHORT_NAMES
import org.jetbrains.kotlin.load.java.SpecialGenericSignatures.Companion.ERASED_VALUE_PARAMETERS_SIGNATURES
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class JavaClassMembersEnhancementScope(
session: FirSession,
@@ -62,91 +48,10 @@ class JavaClassMembersEnhancementScope(
return super.processPropertiesByName(name, processor)
}
private fun FirSimpleFunction.changeSignatureIfErasedValueParameter(): FirSimpleFunction {
val typeParameters = owner.fir.typeParameters
if (typeParameters.isEmpty() || name !in ERASED_VALUE_PARAMETERS_SHORT_NAMES) {
return this
}
val jvmDescriptor = this.computeJvmDescriptorReplacingKotlinToJava().replace(
"kotlin/collections/Collection",
"java/util/Collection"
)
if (ERASED_VALUE_PARAMETERS_SIGNATURES.none { it.endsWith(jvmDescriptor) }) {
return this
}
val superClassIds = listOfNotNull(symbol.callableId.classId) +
lookupSuperTypes(owner, lookupInterfaces = true, deep = true, useSiteSession = session).map { it.lookupTag.classId }
for (superClassId in superClassIds) {
val javaClassId = JavaToKotlinClassMap.mapKotlinToJava(superClassId.asSingleFqName().toUnsafe()) ?: superClassId
val newParameterTypes: List<ConeKotlinType?> = when (val fqJvmDescriptor = "${javaClassId.asString()}.$jvmDescriptor") {
in ERASED_COLLECTION_PARAMETER_SIGNATURES -> {
valueParameters.map {
val typeParameter = typeParameters.first()
ConeClassLikeLookupTagImpl(ClassId.topLevel(StandardNames.FqNames.collection)).constructClassType(
arrayOf(
ConeTypeParameterLookupTag(typeParameter.symbol).constructType(emptyArray(), isNullable = false)
), isNullable = false
)
}
}
in ERASED_VALUE_PARAMETERS_SIGNATURES -> {
val specialSignatureInfo = SpecialGenericSignatures.getSpecialSignatureInfo(fqJvmDescriptor)
if (!specialSignatureInfo.isObjectReplacedWithTypeParameter) {
return this
}
valueParameters.mapIndexed { i, valueParameter ->
val classLikeType =
valueParameter.returnTypeRef.coneTypeSafe<ConeKotlinType>()?.lowerBoundIfFlexible().safeAs<ConeClassLikeType>()
if (classLikeType?.lookupTag?.classId == StandardClassIds.Any) {
val typeParameterIndex = if (name.asString() == "containsValue") 1 else i
val typeParameter = typeParameters.getOrNull(typeParameterIndex) ?: typeParameters.first()
val type = ConeTypeParameterLookupTag(typeParameter.symbol).constructType(
emptyArray(), valueParameter.returnTypeRef.isMarkedNullable == true
)
if (valueParameter.returnTypeRef.coneType is ConeFlexibleType) {
ConeFlexibleType(
type.withAttributes(
type.attributes.withFlexibleUnless {
it.hasEnhancedNullability
}
),
type.withNullability(ConeNullability.NULLABLE)
)
} else {
type
}
} else {
null
}
}
}
else -> {
continue
}
}
if (newParameterTypes.none { it != null }) {
return this
}
return FirFakeOverrideGenerator.createCopyForFirFunction(
FirNamedFunctionSymbol(symbol.callableId),
this,
session,
FirDeclarationOrigin.Enhancement,
newParameterTypes = valueParameters.zip(newParameterTypes).map { (valueParameter, newType) ->
newType ?: valueParameter.returnTypeRef.coneType
},
newDispatchReceiverType = dispatchReceiverType,
)
}
return this
}
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
useSiteMemberScope.processFunctionsByName(name) process@{ original ->
val symbol = signatureEnhancement.enhancedFunction(original, name)
val enhancedFunction = (symbol.fir as? FirSimpleFunction)?.changeSignatureIfErasedValueParameter()
val enhancedFunction = (symbol.fir as? FirSimpleFunction)
val enhancedFunctionSymbol = enhancedFunction?.symbol ?: symbol
if (enhancedFunctionSymbol is FirNamedFunctionSymbol) {
@@ -162,7 +67,6 @@ class JavaClassMembersEnhancementScope(
private fun FirCallableMemberDeclaration<*>.overriddenMembers(name: Name): List<FirCallableMemberDeclaration<*>> {
val backMap = overrideBindCache.getOrPut(name) {
useSiteMemberScope.bindOverrides(name)
useSiteMemberScope
.overrideByBase
.toList()
@@ -5,26 +5,30 @@
package org.jetbrains.kotlin.fir.java.scopes
import com.intellij.lang.jvm.types.JvmPrimitiveTypeKind
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty
import org.jetbrains.kotlin.fir.declarations.synthetic.buildSyntheticProperty
import org.jetbrains.kotlin.fir.java.declarations.*
import org.jetbrains.kotlin.fir.resolve.FirJavaSyntheticNamesProvider
import org.jetbrains.kotlin.fir.resolve.calls.syntheticNamesProvider
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.scopes.getContainingCallableNamesIfPresent
import org.jetbrains.kotlin.fir.scopes.getContainingClassifierNamesIfPresent
import org.jetbrains.kotlin.fir.java.toConeKotlinTypeProbablyFlexible
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.scopes.*
import org.jetbrains.kotlin.fir.scopes.impl.AbstractFirUseSiteMemberScope
import org.jetbrains.kotlin.fir.scopes.jvm.computeJvmDescriptor
import org.jetbrains.kotlin.fir.scopes.jvm.computeJvmSignature
import org.jetbrains.kotlin.fir.symbols.CallableId
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.isUnit
import org.jetbrains.kotlin.fir.types.jvm.FirJavaTypeRef
import org.jetbrains.kotlin.load.java.structure.JavaPrimitiveType
import org.jetbrains.kotlin.load.java.structure.impl.JavaPrimitiveTypeImpl
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.load.java.BuiltinSpecialProperties
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.SpecialGenericSignatures
import org.jetbrains.kotlin.load.java.SpecialGenericSignatures.Companion.ERASED_COLLECTION_PARAMETER_NAMES
import org.jetbrains.kotlin.load.java.SpecialGenericSignatures.Companion.sameAsBuiltinMethodWithErasedValueParameters
import org.jetbrains.kotlin.load.java.getPropertyNamesCandidatesByAccessorName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
class JavaClassUseSiteMemberScope(
klass: FirJavaClass,
@@ -37,15 +41,8 @@ class JavaClassUseSiteMemberScope(
superTypesScope,
declaredMemberScope
) {
internal fun bindOverrides(name: Name) {
val overrideCandidates = mutableSetOf<FirNamedFunctionSymbol>()
declaredMemberScope.processFunctionsByName(name) {
overrideCandidates += it
}
superTypesScope.processFunctionsByName(name) {
it.getOverridden(overrideCandidates)
}
}
private val typeParameterStack = klass.javaTypeParameterStack
private val specialFunctions = hashMapOf<Name, Collection<FirNamedFunctionSymbol>>()
override fun getCallableNames(): Set<Name> {
return declaredMemberScope.getContainingCallableNamesIfPresent() + superTypesScope.getCallableNames()
@@ -72,99 +69,378 @@ class JavaClassUseSiteMemberScope(
}.symbol
}
private fun processAccessorFunctionsAndPropertiesByName(
propertyName: Name,
getterNames: List<Name>,
processor: (FirVariableSymbol<*>) -> Unit
) {
val overrideCandidates = mutableSetOf<FirCallableSymbol<*>>()
declaredMemberScope.processPropertiesByName(propertyName) { variableSymbol ->
if (variableSymbol.isStatic) return@processPropertiesByName
overrideCandidates += variableSymbol
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
val fields = mutableSetOf<FirCallableSymbol<*>>()
val fieldNames = mutableSetOf<Name>()
// fields
declaredMemberScope.processPropertiesByName(name) processor@{ variableSymbol ->
if (variableSymbol.isStatic) return@processor
fields += variableSymbol
fieldNames += variableSymbol.fir.name
processor(variableSymbol)
}
for (getterName in getterNames) {
var getterSymbol: FirNamedFunctionSymbol? = null
var setterSymbol: FirNamedFunctionSymbol? = null
declaredMemberScope.processFunctionsByName(getterName) { functionSymbol ->
if (getterSymbol == null) {
val function = functionSymbol.fir
if (!function.isStatic && function.valueParameters.isEmpty()) {
getterSymbol = functionSymbol
}
}
}
val setterName = session.syntheticNamesProvider.setterNameByGetterName(getterName)
if (getterSymbol != null && setterName != null) {
declaredMemberScope.processFunctionsByName(setterName) { functionSymbol ->
if (setterSymbol == null) {
val function = functionSymbol.fir
if (!function.isStatic && function.valueParameters.size == 1) {
val returnTypeRef = function.returnTypeRef
if (returnTypeRef.isUnit) {
// Unit return type
setterSymbol = functionSymbol
} else if (returnTypeRef is FirJavaTypeRef) {
// Void/void return type
when (val returnType = returnTypeRef.type) {
is JavaPrimitiveTypeImpl ->
if (returnType.psi.kind == JvmPrimitiveTypeKind.VOID) {
setterSymbol = functionSymbol
}
is JavaPrimitiveType ->
if (returnType.type == null) {
setterSymbol = functionSymbol
}
}
}
}
}
}
val accessorSymbol = generateAccessorSymbol(getterSymbol!!, setterSymbol, propertyName)
overrideCandidates += accessorSymbol
}
}
val fromSupertypes = superTypesScope.getProperties(name)
superTypesScope.processPropertiesByName(propertyName) {
when (val overriddenBy = it.getOverridden(overrideCandidates)) {
null -> processor(it)
is FirAccessorSymbol -> processor(overriddenBy)
is FirPropertySymbol -> if (it is FirPropertySymbol) {
directOverriddenProperties.getOrPut(overriddenBy) { mutableListOf() }.add(it)
for (propertyFromSupertype in fromSupertypes) {
if (propertyFromSupertype is FirFieldSymbol) {
if (propertyFromSupertype.fir.name !in fieldNames) {
processor(propertyFromSupertype)
}
continue
}
if (propertyFromSupertype !is FirPropertySymbol) continue
val overrideInClass = propertyFromSupertype.createOverridePropertyIfExists(declaredMemberScope)
when {
overrideInClass != null -> {
directOverriddenProperties.getOrPut(overrideInClass) { mutableListOf() }.add(propertyFromSupertype)
overrideByBase[propertyFromSupertype] = overrideInClass
processor(overrideInClass)
}
else -> processor(propertyFromSupertype)
}
}
}
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
val getterNames = FirJavaSyntheticNamesProvider.possibleGetterNamesByPropertyName(name)
return processAccessorFunctionsAndPropertiesByName(name, getterNames, processor)
private fun FirVariableSymbol<*>.createOverridePropertyIfExists(scope: FirScope): FirPropertySymbol? {
if (this !is FirPropertySymbol || !doesClassOverridesProperty(this, scope)) return null
val getterSymbol = this.findGetterOverride(scope)!!
val setterSymbol =
if (this.fir.isVar)
this.findSetterOverride(scope)!!
else
null
return generateAccessorSymbol(getterSymbol, setterSymbol, fir.name)
}
private fun doesClassOverridesProperty(
propertySymbolFromSupertype: FirPropertySymbol,
scope: FirScope,
): Boolean {
val getter = propertySymbolFromSupertype.findGetterOverride(scope)
val setter = propertySymbolFromSupertype.findSetterOverride(scope)
if (getter == null) return false
if (!propertySymbolFromSupertype.fir.isVar) return true
return setter != null && setter.fir.modality == getter.fir.modality
}
private fun FirPropertySymbol.findGetterOverride(
scope: FirScope,
): FirNamedFunctionSymbol? {
val specialGetterName = getBuiltinSpecialPropertyGetterName()
if (specialGetterName != null) {
return findGetterByName(specialGetterName.asString(), scope)
}
return findGetterByName(JvmAbi.getterName(fir.name.asString()), scope)
}
private fun FirPropertySymbol.findGetterByName(
getterName: String,
scope: FirScope,
): FirNamedFunctionSymbol? {
val propertyFromSupertype = fir
val expectedReturnType = propertyFromSupertype.returnTypeRef.coneTypeSafe<ConeKotlinType>()
return scope.getFunctions(Name.identifier(getterName)).firstNotNullResult factory@{ candidateSymbol ->
val candidate = candidateSymbol.fir
if (candidate.valueParameters.isNotEmpty()) return@factory null
val candidateReturnType = candidate.returnTypeRef.toConeKotlinTypeProbablyFlexible(session, typeParameterStack)
candidateSymbol.takeIf {
// TODO: Decide something for the case when property type is not computed yet
expectedReturnType == null || AbstractTypeChecker.isSubtypeOf(session.typeContext, candidateReturnType, expectedReturnType)
}
}
}
private fun FirPropertySymbol.findSetterOverride(
scope: FirScope,
): FirNamedFunctionSymbol? {
val propertyType = fir.returnTypeRef.coneTypeSafe<ConeKotlinType>() ?: return null
return scope.getFunctions(Name.identifier(JvmAbi.setterName(fir.name.asString()))).firstNotNullResult factory@{ candidateSymbol ->
val candidate = candidateSymbol.fir
if (candidate.valueParameters.size != 1) return@factory null
if (!candidate.returnTypeRef.toConeKotlinTypeProbablyFlexible(session, typeParameterStack).isUnit) return@factory null
val parameterType =
candidate.valueParameters.single().returnTypeRef.toConeKotlinTypeProbablyFlexible(session, typeParameterStack)
candidateSymbol.takeIf {
AbstractTypeChecker.equalTypes(session.typeContext, parameterType, propertyType)
}
}
}
private fun FirPropertySymbol.getBuiltinSpecialPropertyGetterName(): Name? {
var result: Name? = null
superTypesScope.processOverriddenPropertiesAndSelf(this) { overridden ->
val fqName = overridden.fir.containingClass()?.classId?.asSingleFqName()?.child(overridden.fir.name)
BuiltinSpecialProperties.PROPERTY_FQ_NAME_TO_JVM_GETTER_NAME_MAP[fqName]?.let { name ->
result = name
return@processOverriddenPropertiesAndSelf ProcessorAction.STOP
}
ProcessorAction.NEXT
}
return result
}
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
val potentialPropertyNames = session.syntheticNamesProvider.possiblePropertyNamesByAccessorName(name)
val accessors = mutableListOf<FirAccessorSymbol>()
val getterName by lazy { session.syntheticNamesProvider.getterNameBySetterName(name) ?: name }
for (potentialPropertyName in potentialPropertyNames) {
processAccessorFunctionsAndPropertiesByName(potentialPropertyName, listOf(getterName)) {
if (it is FirAccessorSymbol) {
accessors += it
}
}
}
if (accessors.isEmpty()) {
val potentialPropertyNames = getPropertyNamesCandidatesByAccessorName(name)
val renamedSpecialBuiltInNames = SpecialGenericSignatures.getBuiltinFunctionNamesByJvmName(name)
if (potentialPropertyNames.isEmpty() && renamedSpecialBuiltInNames.isEmpty() &&
!name.sameAsBuiltinMethodWithErasedValueParameters
) {
return super.processFunctionsByName(name, processor)
}
super.processFunctionsByName(name) { functionSymbol ->
if (accessors.none { accessorSymbol ->
val syntheticProperty = accessorSymbol.fir as FirSyntheticProperty
syntheticProperty.getter.delegate === functionSymbol.fir ||
syntheticProperty.setter?.delegate === functionSymbol.fir
}
val overriddenProperties = potentialPropertyNames.flatMap(this::getProperties).filterIsInstance<FirPropertySymbol>()
specialFunctions.getOrPut(name) {
doProcessSpecialFunctions(name, overriddenProperties, renamedSpecialBuiltInNames)
}.forEach {
processor(it)
}
}
private fun doProcessSpecialFunctions(
name: Name,
overriddenProperties: List<FirPropertySymbol>,
renamedSpecialBuiltInNames: List<Name>
): List<FirNamedFunctionSymbol> {
val result = mutableListOf<FirNamedFunctionSymbol>()
declaredMemberScope.processFunctionsByName(name) { functionSymbol ->
if (functionSymbol.isStatic) return@processFunctionsByName
if (overriddenProperties.none { it.isOverriddenInClassBy(functionSymbol) } &&
!functionSymbol.doesOverrideRenamedBuiltins(renamedSpecialBuiltInNames) &&
!functionSymbol.shouldBeVisibleAsOverrideOfBuiltInWithErasedValueParameters()
) {
processor(functionSymbol)
result += functionSymbol
}
}
addOverriddenSpecialMethods(name, result, declaredMemberScope)
val overrideCandidates = result.toMutableSet()
superTypesScope.processFunctionsByName(name) {
val overriddenBy = it.getOverridden(overrideCandidates)
if (overriddenBy == null) {
result += it
}
}
return result
}
private fun FirPropertySymbol.isOverriddenInClassBy(functionSymbol: FirNamedFunctionSymbol): Boolean {
val fir = fir as? FirSyntheticProperty ?: return false
return fir.getter.delegate.symbol == functionSymbol || fir.setter?.delegate?.symbol == functionSymbol
}
private fun addOverriddenSpecialMethods(
name: Name,
result: MutableList<FirNamedFunctionSymbol>,
scope: FirScope,
) {
superTypesScope.processFunctionsByName(name) { fromSupertype ->
obtainOverrideForBuiltinWithDifferentJvmName(fromSupertype, scope, name)?.let {
directOverriddenFunctions[it] = listOf(fromSupertype)
overrideByBase[fromSupertype] = it
result += it
}
obtainOverrideForBuiltInWithErasedValueParametersInJava(fromSupertype, scope)?.let {
directOverriddenFunctions[it] = listOf(fromSupertype)
overrideByBase[fromSupertype] = it
result += it
}
}
}
private fun obtainOverrideForBuiltinWithDifferentJvmName(
symbol: FirNamedFunctionSymbol,
scope: FirScope,
name: Name,
): FirNamedFunctionSymbol? {
val overriddenBuiltin = symbol.getOverriddenBuiltinWithDifferentJvmName() ?: return null
val nameInJava =
SpecialGenericSignatures.SIGNATURE_TO_JVM_REPRESENTATION_NAME[overriddenBuiltin.fir.computeJvmSignature() ?: return null]
?: return null
for (candidateSymbol in scope.getFunctions(nameInJava)) {
val candidateFir = candidateSymbol.fir
val renamedCopy = buildJavaMethodCopy(candidateFir) {
this.name = name
this.symbol = FirNamedFunctionSymbol(CallableId(candidateFir.symbol.callableId.classId!!, name))
}.apply {
initialSignatureAttr = candidateFir
}
if (overrideChecker.isOverriddenFunction(renamedCopy, overriddenBuiltin.fir)) {
return renamedCopy.symbol
}
}
return null
}
private fun obtainOverrideForBuiltInWithErasedValueParametersInJava(
symbol: FirNamedFunctionSymbol,
scope: FirScope,
): FirNamedFunctionSymbol? {
val overriddenBuiltin =
symbol.getOverriddenBuiltinFunctionWithErasedValueParametersInJava()
?: return null
return createOverrideForBuiltinFunctionWithErasedParameterIfNeeded(symbol, overriddenBuiltin, scope)
}
private fun createOverrideForBuiltinFunctionWithErasedParameterIfNeeded(
fromSupertype: FirNamedFunctionSymbol,
overriddenBuiltin: FirNamedFunctionSymbol,
scope: FirScope,
): FirNamedFunctionSymbol? {
return scope.getFunctions(overriddenBuiltin.fir.name).firstOrNull { candidateOverride ->
candidateOverride.fir.computeJvmDescriptor() == overriddenBuiltin.fir.computeJvmDescriptor() &&
candidateOverride.hasErasedParameters()
}?.let { override ->
buildJavaMethodCopy(override.fir) {
this.valueParameters.clear()
override.fir.valueParameters.zip(fromSupertype.fir.valueParameters)
.mapTo(this.valueParameters) { (overrideParameter, parameterFromSupertype) ->
buildJavaValueParameterCopy(overrideParameter) {
this@buildJavaValueParameterCopy.returnTypeRef = parameterFromSupertype.returnTypeRef
}
}
symbol = FirNamedFunctionSymbol(override.callableId)
}.apply {
initialSignatureAttr = override.fir
}.symbol
}
}
// It's either overrides Collection.contains(Object) or Collection.containsAll(Collection<?>) or similar methods
private fun FirNamedFunctionSymbol.hasErasedParameters(): Boolean {
val valueParameter = fir.valueParameters.first()
val parameterType = valueParameter.returnTypeRef.toConeKotlinTypeProbablyFlexible(session, typeParameterStack)
val upperBound = parameterType.upperBoundIfFlexible()
if (upperBound !is ConeClassLikeType) return false
if (fir.name.asString() in ERASED_COLLECTION_PARAMETER_NAMES) {
require(upperBound.lookupTag.classId == StandardClassIds.Collection) {
"Unexpected type: ${upperBound.lookupTag.classId}"
}
return upperBound.typeArguments.singleOrNull() is ConeStarProjection
}
return upperBound.classId == StandardClassIds.Any
}
private fun FirNamedFunctionSymbol.doesOverrideRenamedBuiltins(renamedSpecialBuiltInNames: List<Name>): Boolean {
return renamedSpecialBuiltInNames.any {
// e.g. 'removeAt' or 'toInt'
builtinName ->
val builtinSpecialFromSuperTypes =
getFunctionsFromSupertypes(builtinName).filter { it.getOverriddenBuiltinWithDifferentJvmName() != null }
if (builtinSpecialFromSuperTypes.isEmpty()) return@any false
val currentJvmDescriptor = fir.computeJvmDescriptor(customName = builtinName.asString())
builtinSpecialFromSuperTypes.any { builtinSpecial ->
builtinSpecial.fir.computeJvmDescriptor() == currentJvmDescriptor
}
}
}
private fun FirFunction<*>.computeJvmSignature(): String? {
return computeJvmSignature { it.toConeKotlinTypeProbablyFlexible(session, typeParameterStack) }
}
private fun FirFunction<*>.computeJvmDescriptor(customName: String? = null, includeReturnType: Boolean = false): String {
return computeJvmDescriptor(customName, includeReturnType) {
it.toConeKotlinTypeProbablyFlexible(
session,
typeParameterStack
)
}
}
private fun getFunctionsFromSupertypes(name: Name): List<FirNamedFunctionSymbol> {
val result = mutableListOf<FirNamedFunctionSymbol>()
superTypesScope.processFunctionsByName(name) {
result += it
}
return result
}
private fun FirNamedFunctionSymbol.getOverriddenBuiltinWithDifferentJvmName(): FirNamedFunctionSymbol? {
var result: FirNamedFunctionSymbol? = null
superTypesScope.processOverriddenFunctions(this) {
if (!it.isFromBuiltInClass(session)) return@processOverriddenFunctions ProcessorAction.NEXT
if (SpecialGenericSignatures.SIGNATURE_TO_JVM_REPRESENTATION_NAME.containsKey(
it.fir.computeJvmSignature()
)
) {
result = it
return@processOverriddenFunctions ProcessorAction.STOP
}
ProcessorAction.NEXT
}
return result
}
private fun FirNamedFunctionSymbol.shouldBeVisibleAsOverrideOfBuiltInWithErasedValueParameters(): Boolean {
val name = fir.name
if (!name.sameAsBuiltinMethodWithErasedValueParameters) return false
val candidatesToOverride =
getFunctionsFromSupertypes(name).mapNotNull {
it.getOverriddenBuiltinFunctionWithErasedValueParametersInJava()
}
val jvmDescriptor = fir.computeJvmDescriptor()
return candidatesToOverride.any { candidate ->
candidate.fir.computeJvmDescriptor() == jvmDescriptor && this.hasErasedParameters()
}
}
private fun FirNamedFunctionSymbol.getOverriddenBuiltinFunctionWithErasedValueParametersInJava(): FirNamedFunctionSymbol? {
var result: FirNamedFunctionSymbol? = null
superTypesScope.processOverriddenFunctionsAndSelf(this) {
if (it.fir.computeJvmSignature() in SpecialGenericSignatures.ERASED_VALUE_PARAMETERS_SIGNATURES) {
result = it
return@processOverriddenFunctionsAndSelf ProcessorAction.STOP
}
ProcessorAction.NEXT
}
return result
}
}
private fun FirCallableSymbol<*>.isFromBuiltInClass(session: FirSession) =
dispatchReceiverClassOrNull()?.toSymbol(session)?.fir?.origin == FirDeclarationOrigin.BuiltIns
@@ -15,11 +15,9 @@ import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
import org.jetbrains.kotlin.fir.scopes.impl.FirAbstractOverrideChecker
import org.jetbrains.kotlin.fir.scopes.jvm.computeJvmDescriptor
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.load.java.SpecialGenericSignatures
class JavaOverrideChecker internal constructor(
private val session: FirSession,
@@ -30,11 +28,10 @@ class JavaOverrideChecker internal constructor(
private fun isEqualTypes(
candidateType: ConeKotlinType,
baseType: ConeKotlinType,
substitutor: ConeSubstitutor,
mayBeSpecialBuiltIn: Boolean
substitutor: ConeSubstitutor
): Boolean {
if (candidateType is ConeFlexibleType) return isEqualTypes(candidateType.lowerBound, baseType, substitutor, mayBeSpecialBuiltIn)
if (baseType is ConeFlexibleType) return isEqualTypes(candidateType, baseType.lowerBound, substitutor, mayBeSpecialBuiltIn)
if (candidateType is ConeFlexibleType) return isEqualTypes(candidateType.lowerBound, baseType, substitutor)
if (baseType is ConeFlexibleType) return isEqualTypes(candidateType, baseType.lowerBound, substitutor)
if (candidateType is ConeClassLikeType && baseType is ConeClassLikeType) {
val candidateTypeClassId = candidateType.fullyExpandedType(session).lookupTag.classId.let { it.readOnlyToMutable() ?: it }
val baseTypeClassId = baseType.fullyExpandedType(session).lookupTag.classId.let { it.readOnlyToMutable() ?: it }
@@ -49,22 +46,11 @@ class JavaOverrideChecker internal constructor(
return isEqualArrayElementTypeProjections(
candidateType.typeArguments.single(),
baseType.typeArguments.single(),
substitutor,
mayBeSpecialBuiltIn
substitutor
)
}
return true
}
// TODO: handle the situation in more proper way
// Typical case: class EnumMap<K extends Enum, V> implements Map<K, V>
// We have containsKey(Object) in Map which is enhanced to containsKey(K) in supertype scope
// In EnumMap we have overridden containsKey(Object) which is not yet enhanced
// K may be substituted but after that we will get containsKey(Enum) from supertype, which still does not match...
if (mayBeSpecialBuiltIn && baseType is ConeTypeParameterType &&
candidateType is ConeClassLikeType && candidateType.classId == StandardClassIds.Any
) {
return true
}
return with(context) {
areEqualTypeConstructors(
substitutor.substituteOrSelf(candidateType).typeConstructor(),
@@ -76,25 +62,22 @@ class JavaOverrideChecker internal constructor(
private fun isEqualTypes(
candidateTypeRef: FirTypeRef,
baseTypeRef: FirTypeRef,
substitutor: ConeSubstitutor,
mayBeSpecialBuiltIn: Boolean = false
substitutor: ConeSubstitutor
) = isEqualTypes(
candidateTypeRef.toConeKotlinTypeProbablyFlexible(session, javaTypeParameterStack),
baseTypeRef.toConeKotlinTypeProbablyFlexible(session, javaTypeParameterStack),
substitutor,
mayBeSpecialBuiltIn
substitutor
)
private fun isEqualArrayElementTypeProjections(
candidateTypeProjection: ConeTypeProjection,
baseTypeProjection: ConeTypeProjection,
substitutor: ConeSubstitutor,
mayBeSpecialBuiltIn: Boolean
substitutor: ConeSubstitutor
): Boolean =
when {
candidateTypeProjection is ConeKotlinTypeProjection && baseTypeProjection is ConeKotlinTypeProjection ->
candidateTypeProjection.kind == baseTypeProjection.kind &&
isEqualTypes(candidateTypeProjection.type, baseTypeProjection.type, substitutor, mayBeSpecialBuiltIn)
isEqualTypes(candidateTypeProjection.type, baseTypeProjection.type, substitutor)
candidateTypeProjection is ConeStarProjection && baseTypeProjection is ConeStarProjection -> true
else -> false
}
@@ -169,13 +152,8 @@ class JavaOverrideChecker internal constructor(
if (overrideCandidate.valueParameters.size != baseParameterTypes.size) return false
val substitutor = buildTypeParametersSubstitutorIfCompatible(overrideCandidate, baseDeclaration) ?: return false
val jvmDescriptor by lazy { baseDeclaration.computeJvmDescriptor() }
val mayBeSpecialBuiltIn =
baseDeclaration.name in SpecialGenericSignatures.ERASED_VALUE_PARAMETERS_SHORT_NAMES &&
SpecialGenericSignatures.ERASED_VALUE_PARAMETERS_SIGNATURES.any { it.endsWith(jvmDescriptor) }
return overrideCandidate.valueParameters.zip(baseParameterTypes).all { (paramFromJava, baseType) ->
isEqualTypes(paramFromJava.returnTypeRef, baseType, substitutor, mayBeSpecialBuiltIn)
isEqualTypes(paramFromJava.returnTypeRef, baseType, substitutor)
}
}