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.NEXT
import org.jetbrains.kotlin.util.OperatorNameConventions.SET import org.jetbrains.kotlin.util.OperatorNameConventions.SET
import org.jetbrains.kotlin.util.OperatorNameConventions.UNARY_OPERATION_NAMES 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 import kotlin.properties.Delegates
@OptIn(FirImplementationDetail::class) @OptIn(FirImplementationDetail::class)
@@ -227,3 +230,29 @@ class FirJavaMethodBuilder : FirFunctionBuilder, FirTypeParametersOwnerBuilder,
inline fun buildJavaMethod(init: FirJavaMethodBuilder.() -> Unit): FirJavaMethod { inline fun buildJavaMethod(init: FirJavaMethodBuilder.() -> Unit): FirJavaMethod {
return FirJavaMethodBuilder().apply(init).build() 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.FirVisitor
import org.jetbrains.kotlin.fir.visitors.transformSingle import org.jetbrains.kotlin.fir.visitors.transformSingle
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
@OptIn(FirImplementationDetail::class) @OptIn(FirImplementationDetail::class)
class FirJavaValueParameter @FirImplementationDetail constructor( class FirJavaValueParameter @FirImplementationDetail constructor(
@@ -174,3 +177,21 @@ class FirJavaValueParameterBuilder {
inline fun buildJavaValueParameter(init: FirJavaValueParameterBuilder.() -> Unit): FirJavaValueParameter { inline fun buildJavaValueParameter(init: FirJavaValueParameterBuilder.() -> Unit): FirJavaValueParameter {
return FirJavaValueParameterBuilder().apply(init).build() 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.initialSignatureAttr
import org.jetbrains.kotlin.fir.java.JavaTypeParameterStack import org.jetbrains.kotlin.fir.java.JavaTypeParameterStack
import org.jetbrains.kotlin.fir.java.declarations.* 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.render
import org.jetbrains.kotlin.fir.scopes.jvm.computeJvmDescriptor import org.jetbrains.kotlin.fir.scopes.jvm.computeJvmDescriptor
import org.jetbrains.kotlin.fir.symbols.CallableId import org.jetbrains.kotlin.fir.symbols.CallableId
@@ -165,7 +166,10 @@ class FirSignatureEnhancement(
val memberContext = context.copyWithNewDefaultTypeQualifiers(typeQualifierResolver, jsr305State, firMethod.annotations) val memberContext = context.copyWithNewDefaultTypeQualifiers(typeQualifierResolver, jsr305State, firMethod.annotations)
val predefinedEnhancementInfo = 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] PREDEFINED_FUNCTION_ENHANCEMENT_INFO_BY_SIGNATURE[signature]
} }
@@ -308,6 +312,9 @@ class FirSignatureEnhancement(
ownerParameter: FirJavaValueParameter, ownerParameter: FirJavaValueParameter,
index: Int index: Int
): FirResolvedTypeRef { ): FirResolvedTypeRef {
if (ownerParameter.returnTypeRef is FirResolvedTypeRef) {
return ownerParameter.returnTypeRef as FirResolvedTypeRef
}
val signatureParts = ownerFunction.partsForValueParameter( val signatureParts = ownerFunction.partsForValueParameter(
typeQualifierResolver, typeQualifierResolver,
overriddenMembers, overriddenMembers,
@@ -5,31 +5,17 @@
package org.jetbrains.kotlin.fir.java.scopes 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.FirSession
import org.jetbrains.kotlin.fir.declarations.FirCallableMemberDeclaration 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.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.java.enhancement.FirSignatureEnhancement 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.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.scopes.FirTypeScope import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction 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.symbols.impl.*
import org.jetbrains.kotlin.fir.types.* 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.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class JavaClassMembersEnhancementScope( class JavaClassMembersEnhancementScope(
session: FirSession, session: FirSession,
@@ -62,91 +48,10 @@ class JavaClassMembersEnhancementScope(
return super.processPropertiesByName(name, processor) 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) { override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
useSiteMemberScope.processFunctionsByName(name) process@{ original -> useSiteMemberScope.processFunctionsByName(name) process@{ original ->
val symbol = signatureEnhancement.enhancedFunction(original, name) val symbol = signatureEnhancement.enhancedFunction(original, name)
val enhancedFunction = (symbol.fir as? FirSimpleFunction)?.changeSignatureIfErasedValueParameter() val enhancedFunction = (symbol.fir as? FirSimpleFunction)
val enhancedFunctionSymbol = enhancedFunction?.symbol ?: symbol val enhancedFunctionSymbol = enhancedFunction?.symbol ?: symbol
if (enhancedFunctionSymbol is FirNamedFunctionSymbol) { if (enhancedFunctionSymbol is FirNamedFunctionSymbol) {
@@ -162,7 +67,6 @@ class JavaClassMembersEnhancementScope(
private fun FirCallableMemberDeclaration<*>.overriddenMembers(name: Name): List<FirCallableMemberDeclaration<*>> { private fun FirCallableMemberDeclaration<*>.overriddenMembers(name: Name): List<FirCallableMemberDeclaration<*>> {
val backMap = overrideBindCache.getOrPut(name) { val backMap = overrideBindCache.getOrPut(name) {
useSiteMemberScope.bindOverrides(name)
useSiteMemberScope useSiteMemberScope
.overrideByBase .overrideByBase
.toList() .toList()
@@ -5,26 +5,30 @@
package org.jetbrains.kotlin.fir.java.scopes package org.jetbrains.kotlin.fir.java.scopes
import com.intellij.lang.jvm.types.JvmPrimitiveTypeKind import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty
import org.jetbrains.kotlin.fir.declarations.synthetic.buildSyntheticProperty import org.jetbrains.kotlin.fir.declarations.synthetic.buildSyntheticProperty
import org.jetbrains.kotlin.fir.java.declarations.* import org.jetbrains.kotlin.fir.java.declarations.*
import org.jetbrains.kotlin.fir.resolve.FirJavaSyntheticNamesProvider import org.jetbrains.kotlin.fir.java.toConeKotlinTypeProbablyFlexible
import org.jetbrains.kotlin.fir.resolve.calls.syntheticNamesProvider import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.scopes.FirScope import org.jetbrains.kotlin.fir.scopes.*
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.scopes.impl.AbstractFirUseSiteMemberScope 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.CallableId
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.isUnit import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.jvm.FirJavaTypeRef import org.jetbrains.kotlin.load.java.BuiltinSpecialProperties
import org.jetbrains.kotlin.load.java.structure.JavaPrimitiveType import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.structure.impl.JavaPrimitiveTypeImpl 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.name.Name
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
class JavaClassUseSiteMemberScope( class JavaClassUseSiteMemberScope(
klass: FirJavaClass, klass: FirJavaClass,
@@ -37,15 +41,8 @@ class JavaClassUseSiteMemberScope(
superTypesScope, superTypesScope,
declaredMemberScope declaredMemberScope
) { ) {
internal fun bindOverrides(name: Name) { private val typeParameterStack = klass.javaTypeParameterStack
val overrideCandidates = mutableSetOf<FirNamedFunctionSymbol>() private val specialFunctions = hashMapOf<Name, Collection<FirNamedFunctionSymbol>>()
declaredMemberScope.processFunctionsByName(name) {
overrideCandidates += it
}
superTypesScope.processFunctionsByName(name) {
it.getOverridden(overrideCandidates)
}
}
override fun getCallableNames(): Set<Name> { override fun getCallableNames(): Set<Name> {
return declaredMemberScope.getContainingCallableNamesIfPresent() + superTypesScope.getCallableNames() return declaredMemberScope.getContainingCallableNamesIfPresent() + superTypesScope.getCallableNames()
@@ -72,99 +69,378 @@ class JavaClassUseSiteMemberScope(
}.symbol }.symbol
} }
private fun processAccessorFunctionsAndPropertiesByName( override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
propertyName: Name, val fields = mutableSetOf<FirCallableSymbol<*>>()
getterNames: List<Name>, val fieldNames = mutableSetOf<Name>()
processor: (FirVariableSymbol<*>) -> Unit
) { // fields
val overrideCandidates = mutableSetOf<FirCallableSymbol<*>>() declaredMemberScope.processPropertiesByName(name) processor@{ variableSymbol ->
declaredMemberScope.processPropertiesByName(propertyName) { variableSymbol -> if (variableSymbol.isStatic) return@processor
if (variableSymbol.isStatic) return@processPropertiesByName fields += variableSymbol
overrideCandidates += variableSymbol fieldNames += variableSymbol.fir.name
processor(variableSymbol) processor(variableSymbol)
} }
for (getterName in getterNames) { val fromSupertypes = superTypesScope.getProperties(name)
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
}
}
superTypesScope.processPropertiesByName(propertyName) { for (propertyFromSupertype in fromSupertypes) {
when (val overriddenBy = it.getOverridden(overrideCandidates)) { if (propertyFromSupertype is FirFieldSymbol) {
null -> processor(it) if (propertyFromSupertype.fir.name !in fieldNames) {
is FirAccessorSymbol -> processor(overriddenBy) processor(propertyFromSupertype)
is FirPropertySymbol -> if (it is FirPropertySymbol) {
directOverriddenProperties.getOrPut(overriddenBy) { mutableListOf() }.add(it)
} }
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) { private fun FirVariableSymbol<*>.createOverridePropertyIfExists(scope: FirScope): FirPropertySymbol? {
val getterNames = FirJavaSyntheticNamesProvider.possibleGetterNamesByPropertyName(name) if (this !is FirPropertySymbol || !doesClassOverridesProperty(this, scope)) return null
return processAccessorFunctionsAndPropertiesByName(name, getterNames, processor)
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) { override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
val potentialPropertyNames = session.syntheticNamesProvider.possiblePropertyNamesByAccessorName(name) val potentialPropertyNames = getPropertyNamesCandidatesByAccessorName(name)
val accessors = mutableListOf<FirAccessorSymbol>()
val getterName by lazy { session.syntheticNamesProvider.getterNameBySetterName(name) ?: name } val renamedSpecialBuiltInNames = SpecialGenericSignatures.getBuiltinFunctionNamesByJvmName(name)
for (potentialPropertyName in potentialPropertyNames) {
processAccessorFunctionsAndPropertiesByName(potentialPropertyName, listOf(getterName)) { if (potentialPropertyNames.isEmpty() && renamedSpecialBuiltInNames.isEmpty() &&
if (it is FirAccessorSymbol) { !name.sameAsBuiltinMethodWithErasedValueParameters
accessors += it ) {
}
}
}
if (accessors.isEmpty()) {
return super.processFunctionsByName(name, processor) return super.processFunctionsByName(name, processor)
} }
super.processFunctionsByName(name) { functionSymbol ->
if (accessors.none { accessorSymbol -> val overriddenProperties = potentialPropertyNames.flatMap(this::getProperties).filterIsInstance<FirPropertySymbol>()
val syntheticProperty = accessorSymbol.fir as FirSyntheticProperty
syntheticProperty.getter.delegate === functionSymbol.fir || specialFunctions.getOrPut(name) {
syntheticProperty.setter?.delegate === functionSymbol.fir 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.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
import org.jetbrains.kotlin.fir.scopes.impl.FirAbstractOverrideChecker 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.symbols.StandardClassIds
import org.jetbrains.kotlin.fir.typeContext import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.load.java.SpecialGenericSignatures
class JavaOverrideChecker internal constructor( class JavaOverrideChecker internal constructor(
private val session: FirSession, private val session: FirSession,
@@ -30,11 +28,10 @@ class JavaOverrideChecker internal constructor(
private fun isEqualTypes( private fun isEqualTypes(
candidateType: ConeKotlinType, candidateType: ConeKotlinType,
baseType: ConeKotlinType, baseType: ConeKotlinType,
substitutor: ConeSubstitutor, substitutor: ConeSubstitutor
mayBeSpecialBuiltIn: Boolean
): Boolean { ): Boolean {
if (candidateType is ConeFlexibleType) return isEqualTypes(candidateType.lowerBound, baseType, substitutor, mayBeSpecialBuiltIn) if (candidateType is ConeFlexibleType) return isEqualTypes(candidateType.lowerBound, baseType, substitutor)
if (baseType is ConeFlexibleType) return isEqualTypes(candidateType, baseType.lowerBound, substitutor, mayBeSpecialBuiltIn) if (baseType is ConeFlexibleType) return isEqualTypes(candidateType, baseType.lowerBound, substitutor)
if (candidateType is ConeClassLikeType && baseType is ConeClassLikeType) { if (candidateType is ConeClassLikeType && baseType is ConeClassLikeType) {
val candidateTypeClassId = candidateType.fullyExpandedType(session).lookupTag.classId.let { it.readOnlyToMutable() ?: it } val candidateTypeClassId = candidateType.fullyExpandedType(session).lookupTag.classId.let { it.readOnlyToMutable() ?: it }
val baseTypeClassId = baseType.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( return isEqualArrayElementTypeProjections(
candidateType.typeArguments.single(), candidateType.typeArguments.single(),
baseType.typeArguments.single(), baseType.typeArguments.single(),
substitutor, substitutor
mayBeSpecialBuiltIn
) )
} }
return true 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) { return with(context) {
areEqualTypeConstructors( areEqualTypeConstructors(
substitutor.substituteOrSelf(candidateType).typeConstructor(), substitutor.substituteOrSelf(candidateType).typeConstructor(),
@@ -76,25 +62,22 @@ class JavaOverrideChecker internal constructor(
private fun isEqualTypes( private fun isEqualTypes(
candidateTypeRef: FirTypeRef, candidateTypeRef: FirTypeRef,
baseTypeRef: FirTypeRef, baseTypeRef: FirTypeRef,
substitutor: ConeSubstitutor, substitutor: ConeSubstitutor
mayBeSpecialBuiltIn: Boolean = false
) = isEqualTypes( ) = isEqualTypes(
candidateTypeRef.toConeKotlinTypeProbablyFlexible(session, javaTypeParameterStack), candidateTypeRef.toConeKotlinTypeProbablyFlexible(session, javaTypeParameterStack),
baseTypeRef.toConeKotlinTypeProbablyFlexible(session, javaTypeParameterStack), baseTypeRef.toConeKotlinTypeProbablyFlexible(session, javaTypeParameterStack),
substitutor, substitutor
mayBeSpecialBuiltIn
) )
private fun isEqualArrayElementTypeProjections( private fun isEqualArrayElementTypeProjections(
candidateTypeProjection: ConeTypeProjection, candidateTypeProjection: ConeTypeProjection,
baseTypeProjection: ConeTypeProjection, baseTypeProjection: ConeTypeProjection,
substitutor: ConeSubstitutor, substitutor: ConeSubstitutor
mayBeSpecialBuiltIn: Boolean
): Boolean = ): Boolean =
when { when {
candidateTypeProjection is ConeKotlinTypeProjection && baseTypeProjection is ConeKotlinTypeProjection -> candidateTypeProjection is ConeKotlinTypeProjection && baseTypeProjection is ConeKotlinTypeProjection ->
candidateTypeProjection.kind == baseTypeProjection.kind && candidateTypeProjection.kind == baseTypeProjection.kind &&
isEqualTypes(candidateTypeProjection.type, baseTypeProjection.type, substitutor, mayBeSpecialBuiltIn) isEqualTypes(candidateTypeProjection.type, baseTypeProjection.type, substitutor)
candidateTypeProjection is ConeStarProjection && baseTypeProjection is ConeStarProjection -> true candidateTypeProjection is ConeStarProjection && baseTypeProjection is ConeStarProjection -> true
else -> false else -> false
} }
@@ -169,13 +152,8 @@ class JavaOverrideChecker internal constructor(
if (overrideCandidate.valueParameters.size != baseParameterTypes.size) return false if (overrideCandidate.valueParameters.size != baseParameterTypes.size) return false
val substitutor = buildTypeParametersSubstitutorIfCompatible(overrideCandidate, baseDeclaration) ?: 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) -> return overrideCandidate.valueParameters.zip(baseParameterTypes).all { (paramFromJava, baseType) ->
isEqualTypes(paramFromJava.returnTypeRef, baseType, substitutor, mayBeSpecialBuiltIn) isEqualTypes(paramFromJava.returnTypeRef, baseType, substitutor)
} }
} }
@@ -5,6 +5,9 @@
package org.jetbrains.kotlin.fir.scopes.jvm package org.jetbrains.kotlin.fir.scopes.jvm
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.fir.containingClass
import org.jetbrains.kotlin.fir.declarations.FirCallableMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFunction import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.symbols.StandardClassIds import org.jetbrains.kotlin.fir.symbols.StandardClassIds
@@ -12,68 +15,44 @@ import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.FirImplicitAnyTypeRef import org.jetbrains.kotlin.fir.types.impl.FirImplicitAnyTypeRef
import org.jetbrains.kotlin.fir.types.impl.FirImplicitNullableAnyTypeRef import org.jetbrains.kotlin.fir.types.impl.FirImplicitNullableAnyTypeRef
import org.jetbrains.kotlin.fir.types.jvm.FirJavaTypeRef import org.jetbrains.kotlin.fir.types.jvm.FirJavaTypeRef
import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.load.java.structure.JavaClassifierType
import org.jetbrains.kotlin.load.java.structure.JavaPrimitiveType import org.jetbrains.kotlin.load.java.structure.JavaPrimitiveType
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter import org.jetbrains.kotlin.load.kotlin.SignatureBuildingComponents
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
fun FirFunction<*>.computeJvmDescriptorReplacingKotlinToJava(): String = fun FirFunction<*>.computeJvmSignature(typeConversion: (FirTypeRef) -> ConeKotlinType? = FirTypeRef::coneTypeSafe): String? {
computeJvmDescriptor() if (this !is FirCallableMemberDeclaration<*>) return null
.replace("kotlin/Any", "java/lang/Object") val containingClass = containingClass() ?: return null
.replace("kotlin/String", "java/lang/String")
.replace("kotlin/Throwable", "java/lang/Throwable")
fun FirFunction<*>.computeJvmDescriptor(): String = buildString { return SignatureBuildingComponents.signature(containingClass.classId, computeJvmDescriptor(typeConversion = typeConversion))
if (this@computeJvmDescriptor is FirSimpleFunction) { }
append(name.asString())
fun FirFunction<*>.computeJvmDescriptor(
customName: String? = null,
includeReturnType: Boolean = true,
typeConversion: (FirTypeRef) -> ConeKotlinType? = FirTypeRef::coneTypeSafe
): String = buildString {
if (customName != null) {
append(customName)
} else { } else {
append("<init>") if (this@computeJvmDescriptor is FirSimpleFunction) {
append(name.asString())
} else {
append("<init>")
}
} }
append("(") append("(")
for (parameter in valueParameters) { for (parameter in valueParameters) {
appendErasedType(parameter.returnTypeRef) typeConversion(parameter.returnTypeRef)?.let(this::appendConeType)
} }
append(")") append(")")
if (this@computeJvmDescriptor !is FirSimpleFunction || returnTypeRef.isVoid()) { if (includeReturnType) {
append("V") if (this@computeJvmDescriptor !is FirSimpleFunction || returnTypeRef.isVoid()) {
} else { append("V")
appendErasedType(returnTypeRef) } else {
} typeConversion(returnTypeRef)?.let(this::appendConeType)
}
// TODO: primitive types, arrays, etc.
private fun StringBuilder.appendErasedType(typeRef: FirTypeRef) {
fun appendClass(klass: JavaClass) {
klass.fqName?.let {
append("L")
append(it.asString().replace(".", "/"))
append(";")
}
}
when (typeRef) {
is FirResolvedTypeRef -> appendConeType(typeRef.type)
is FirJavaTypeRef -> {
when (val javaType = typeRef.type) {
is JavaClassifierType -> {
when (val classifier = javaType.classifier) {
is JavaClass -> appendClass(classifier)
is JavaTypeParameter -> {
val representative = classifier.upperBounds.firstOrNull { it.classifier is JavaClass }
if (representative == null) {
append("Ljava/lang/Object;")
} else {
appendClass(representative.classifier as JavaClass)
}
}
else -> return
}
}
}
} }
} }
} }
@@ -101,7 +80,8 @@ private fun StringBuilder.appendConeType(coneType: ConeKotlinType) {
} }
fun appendClassLikeType(type: ConeClassLikeType) { fun appendClassLikeType(type: ConeClassLikeType) {
val classId = type.lookupTag.classId val baseClassId = type.lookupTag.classId
val classId = JavaToKotlinClassMap.mapKotlinToJava(baseClassId.asSingleFqName().toUnsafe()) ?: baseClassId
if (classId == StandardClassIds.Array) { if (classId == StandardClassIds.Array) {
append("[") append("[")
type.typeArguments.forEach { typeArg -> type.typeArguments.forEach { typeArg ->
@@ -29,11 +29,11 @@ class JvmMappedScope(
} }
val declaredSignatures by lazy { val declaredSignatures by lazy {
declared.mapTo(mutableSetOf()) { it.fir.computeJvmDescriptorReplacingKotlinToJava() } declared.mapTo(mutableSetOf()) { it.fir.computeJvmDescriptor() }
} }
javaMappedClassUseSiteScope.processFunctionsByName(name) { symbol -> javaMappedClassUseSiteScope.processFunctionsByName(name) { symbol ->
val jvmSignature = symbol.fir.computeJvmDescriptorReplacingKotlinToJava() val jvmSignature = symbol.fir.computeJvmDescriptor()
if (jvmSignature in visibleMethods && jvmSignature !in declaredSignatures) { if (jvmSignature in visibleMethods && jvmSignature !in declaredSignatures) {
processor(symbol) processor(symbol)
} }
@@ -49,7 +49,7 @@ class JvmMappedScope(
val hiddenConstructors = signatures.hiddenConstructors val hiddenConstructors = signatures.hiddenConstructors
if (hiddenConstructors.isNotEmpty()) { if (hiddenConstructors.isNotEmpty()) {
javaMappedClassUseSiteScope.processDeclaredConstructors { symbol -> javaMappedClassUseSiteScope.processDeclaredConstructors { symbol ->
val jvmSignature = symbol.fir.computeJvmDescriptorReplacingKotlinToJava() val jvmSignature = symbol.fir.computeJvmDescriptor()
if (jvmSignature !in hiddenConstructors) { if (jvmSignature !in hiddenConstructors) {
processor(symbol) processor(symbol)
} }
@@ -19,7 +19,7 @@ abstract class AbstractFirUseSiteMemberScope(
) : AbstractFirOverrideScope(session, overrideChecker) { ) : AbstractFirOverrideScope(session, overrideChecker) {
private val functions = hashMapOf<Name, Collection<FirNamedFunctionSymbol>>() private val functions = hashMapOf<Name, Collection<FirNamedFunctionSymbol>>()
private val directOverriddenFunctions = hashMapOf<FirNamedFunctionSymbol, Collection<FirNamedFunctionSymbol>>() protected val directOverriddenFunctions = hashMapOf<FirNamedFunctionSymbol, Collection<FirNamedFunctionSymbol>>()
protected val directOverriddenProperties = hashMapOf<FirPropertySymbol, MutableList<FirPropertySymbol>>() protected val directOverriddenProperties = hashMapOf<FirPropertySymbol, MutableList<FirPropertySymbol>>()
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) { override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
@@ -61,6 +61,15 @@ fun FirTypeScope.processOverriddenFunctionsAndSelf(
return processOverriddenFunctions(functionSymbol, processor) return processOverriddenFunctions(functionSymbol, processor)
} }
fun FirTypeScope.processOverriddenPropertiesAndSelf(
propertySymbol: FirPropertySymbol,
processor: (FirPropertySymbol) -> ProcessorAction
): ProcessorAction {
if (!processor(propertySymbol)) return ProcessorAction.STOP
return processOverriddenProperties(propertySymbol, processor)
}
enum class ProcessorAction { enum class ProcessorAction {
STOP, STOP,
NEXT, NEXT,
@@ -0,0 +1,85 @@
// FILE: CollectionStringImpl.java
import java.util.Collection;
import java.util.Iterator;
public class CollectionStringImpl implements Collection<String> {
@Override
public int size() {
return 0;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean contains(Object o) {
return false;
}
@Override
public Iterator<String> iterator() {
return null;
}
@Override
public Object[] toArray() {
return new Object[0];
}
@Override
public <T> T[] toArray(T[] a) {
return null;
}
@Override
public boolean add(String s) {
return false;
}
@Override
public boolean remove(Object o) {
return false;
}
@Override
public boolean containsAll(Collection<?> c) {
return false;
}
@Override
public boolean addAll(Collection<? extends String> c) {
return false;
}
@Override
public boolean removeAll(Collection<?> c) {
return false;
}
@Override
public boolean retainAll(Collection<?> c) {
return false;
}
@Override
public void clear() {
}
public boolean contains(String o) {
return true;
}
}
// FILE: main.kt
fun test(x: CollectionStringImpl) {
x.<!AMBIGUITY!>contains<!>("")
(x as Collection<String>).contains("")
}
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// FILE: CollectionStringImpl.java // FILE: CollectionStringImpl.java
import java.util.Collection; import java.util.Collection;
@@ -40,14 +40,14 @@ fun foo(
1 <!INAPPLICABLE_CANDIDATE!>in<!> a 1 <!INAPPLICABLE_CANDIDATE!>in<!> a
b.contains("") b.contains("")
b.contains(1) b.<!INAPPLICABLE_CANDIDATE!>contains<!>(1)
"" in b "" in b
1 in b 1 <!INAPPLICABLE_CANDIDATE!>in<!> b
ic.contains("") ic.contains("")
ic.contains(1) ic.<!INAPPLICABLE_CANDIDATE!>contains<!>(1)
"" in ic "" in ic
1 in ic 1 <!INAPPLICABLE_CANDIDATE!>in<!> ic
ka.contains("") ka.contains("")
ka.<!INAPPLICABLE_CANDIDATE!>contains<!>(1) ka.<!INAPPLICABLE_CANDIDATE!>contains<!>(1)
@@ -55,9 +55,9 @@ fun foo(
1 <!INAPPLICABLE_CANDIDATE!>in<!> ka 1 <!INAPPLICABLE_CANDIDATE!>in<!> ka
kb.contains("") kb.contains("")
kb.contains(1) kb.<!INAPPLICABLE_CANDIDATE!>contains<!>(1)
"" in kb "" in kb
1 in kb 1 <!INAPPLICABLE_CANDIDATE!>in<!> kb
al.contains("") al.contains("")
al.<!INAPPLICABLE_CANDIDATE!>contains<!>(1) al.<!INAPPLICABLE_CANDIDATE!>contains<!>(1)
@@ -40,16 +40,16 @@ fun foo(
a.<!INAPPLICABLE_CANDIDATE!>containsAll<!>(ca) a.<!INAPPLICABLE_CANDIDATE!>containsAll<!>(ca)
b.containsAll(cs) b.containsAll(cs)
b.containsAll(ca) b.<!INAPPLICABLE_CANDIDATE!>containsAll<!>(ca)
ic.containsAll(cs) ic.containsAll(cs)
ic.containsAll(ca) ic.<!INAPPLICABLE_CANDIDATE!>containsAll<!>(ca)
ka.containsAll(cs) ka.containsAll(cs)
ka.<!INAPPLICABLE_CANDIDATE!>containsAll<!>(ca) ka.<!INAPPLICABLE_CANDIDATE!>containsAll<!>(ca)
kb.containsAll(cs) kb.containsAll(cs)
kb.containsAll(ca) kb.<!INAPPLICABLE_CANDIDATE!>containsAll<!>(ca)
al.containsAll(cs) al.containsAll(cs)
al.<!INAPPLICABLE_CANDIDATE!>containsAll<!>(ca) al.<!INAPPLICABLE_CANDIDATE!>containsAll<!>(ca)
@@ -12,13 +12,13 @@ abstract class KA : A() {
} }
fun foo(a: A, ka: KA) { fun foo(a: A, ka: KA) {
a.contains("") a.<!AMBIGUITY!>contains<!>("")
a.contains(1) a.<!NONE_APPLICABLE!>contains<!>(1)
"" in a "" <!AMBIGUITY!>in<!> a
1 in a 1 <!NONE_APPLICABLE!>in<!> a
ka.contains("") ka.contains("")
ka.contains(1) ka.<!INAPPLICABLE_CANDIDATE!>contains<!>(1)
"" in ka "" in ka
1 in ka 1 <!INAPPLICABLE_CANDIDATE!>in<!> ka
} }
@@ -0,0 +1,19 @@
// FILE: Dict.java
public abstract class Dict<K, V> {
abstract public V get(Object key);
}
// FILE: MHashtable.java
abstract public class MHashtable<X, Y> extends Dict<X, Y> implements java.util.Map<X, Y> {
public V get(Object key) { return null; }
}
// FILE: main.kt
abstract class C1 : MHashtable<String, Int>()
abstract class C2 : MHashtable<String, Int>() {
override fun <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>get<!>(key: String) = 1
}
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// FILE: Dict.java // FILE: Dict.java
public abstract class Dict<K, V> { public abstract class Dict<K, V> {
@@ -1,32 +0,0 @@
// JAVAC_EXPECTED_FILE
// FILE: A.java
abstract public class A extends B {
public Integer removeAt(int x) { }
public boolean remove(Integer x) { }
}
// FILE: main.kt
import java.util.*;
abstract class B : MutableList<Int>, AbstractList<Int>() {
override fun removeAt(index: Int): Int = null!!
override fun remove(element: Int): <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>Boolean<!> = null!!
}
abstract class D : AbstractList<Int>() {
// removeAt() doesn't exist in java/util/AbstractList, it's a
// fake override of the method from kotlin/collections/MutableList
override fun removeAt(index: Int): Int = null!!
// AbstractList::remove() should return Int here. No fake overrides created.
// This may be a bug because the old compiler doesn't report a diagnostic here.
override fun remove(element: Int): <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>Boolean<!> = null!!
}
fun main(a: A, b: B, c: ArrayList<Int>) {
a.remove(1)
a.removeAt(0)
b.remove(1)
b.removeAt(0)
c.remove(1)
c.removeAt(0)
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// JAVAC_EXPECTED_FILE // JAVAC_EXPECTED_FILE
// FILE: A.java // FILE: A.java
abstract public class A extends B { abstract public class A extends B {
@@ -1,12 +0,0 @@
// JAVAC_EXPECTED_FILE
// FILE: A.java
abstract public class A extends java.util.ArrayList<String> {
public final int size() { return 0; }
}
// FILE: main.kt
class B : A() {
override val size: Int = 1
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// JAVAC_EXPECTED_FILE // JAVAC_EXPECTED_FILE
// FILE: A.java // FILE: A.java
@@ -0,0 +1,113 @@
// FILE: AbstractSpecializedMap.java
public abstract class AbstractSpecializedMap implements java.util.Map<Integer, Double> {
public abstract double put(int x, double y);
public abstract double remove(int k);
public abstract double get(int k);
public abstract boolean containsKey(int k);
public boolean containsKey(Object x) {
return false;
}
public abstract boolean containsValue(double v);
public boolean containsValue(Object x) {
return false;
}
}
// FILE: SpecializedMap.java
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
public class SpecializedMap extends AbstractSpecializedMap {
public double put(int x, double y) {
return 123.0;
}
@Override
public Double get(Object key) {
return null;
}
@Override
public Double put(Integer key, Double value) {
return null;
}
public double remove(int k) {
return 456.0;
}
public Double remove(Object ok) {
return null;
}
public double get(int k) {
return 789.0;
}
public boolean containsKey(int k) {
return true;
}
public boolean containsValue(double v) {
return true;
}
@Override
public void putAll(Map<? extends Integer, ? extends Double> m) {
}
@Override
public void clear() {
}
@NotNull
@Override
public Set<Integer> keySet() {
return null;
}
@NotNull
@Override
public Collection<Double> values() {
return null;
}
@NotNull
@Override
public Set<Entry<Integer, Double>> entrySet() {
return null;
}
@Override
public int size() {
return 0;
}
@Override
public boolean isEmpty() {
return false;
}
}
// FILE: main.kt
fun foo(x: SpecializedMap) {
x.containsKey(1)
x.<!INAPPLICABLE_CANDIDATE!>containsKey<!>(null)
x.<!AMBIGUITY!>get<!>(2)
x.<!NONE_APPLICABLE!>get<!>(null)
x.<!AMBIGUITY!>remove<!>(3)
x.<!NONE_APPLICABLE!>remove<!>(null)
x.put(4, 5.0)
x.put(4, null)
}
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// FILE: AbstractSpecializedMap.java // FILE: AbstractSpecializedMap.java
public abstract class AbstractSpecializedMap implements java.util.Map<Integer, Double> { public abstract class AbstractSpecializedMap implements java.util.Map<Integer, Double> {
public abstract double put(int x, double y); public abstract double put(int x, double y);
@@ -1,8 +1,6 @@
public open class ModalityOfFakeOverrides : R|java/util/AbstractList<ft<@FlexibleNullability kotlin/String, kotlin/String?>!>| { public open class ModalityOfFakeOverrides : R|java/util/AbstractList<ft<@FlexibleNullability kotlin/String, kotlin/String?>!>| {
@R|java/lang/Override|() @R|org/jetbrains/annotations/NotNull|() public open operator fun get(index: R|kotlin/Int|): R|@EnhancedNullability kotlin/String| @R|java/lang/Override|() @R|org/jetbrains/annotations/NotNull|() public open operator fun get(index: R|kotlin/Int|): R|@EnhancedNullability kotlin/String|
@R|java/lang/Override|() public open fun size(): R|kotlin/Int|
public constructor(): R|test/ModalityOfFakeOverrides| public constructor(): R|test/ModalityOfFakeOverrides|
} }
@@ -52,46 +52,25 @@ KtFirJavaFieldSymbol:
symbolKind: MEMBER symbolKind: MEMBER
visibility: PRIVATE visibility: PRIVATE
KtFirFunctionSymbol: KtFirSyntheticJavaPropertySymbol:
annotatedType: [] kotlin/Int annotatedType: [] kotlin/Int
annotationClassIds: [] annotationClassIds: []
annotations: [] annotations: []
callableIdIfNonLocal: java.lang.String.length callableIdIfNonLocal: java.lang.String.length
dispatchType: java/lang/String dispatchType: java/lang/String
isExtension: false
isExternal: false
isInline: false
isOperator: false
isOverride: false
isSuspend: false
modality: OPEN
name: length
origin: JAVA
receiverType: null
symbolKind: MEMBER
typeParameters: []
valueParameters: []
visibility: PUBLIC
KtFirKotlinPropertySymbol:
annotatedType: [] kotlin/Int
annotationClassIds: []
annotations: []
callableIdIfNonLocal: kotlin.CharSequence.length
dispatchType: kotlin/CharSequence
getter: KtFirPropertyGetterSymbol(<getter>) getter: KtFirPropertyGetterSymbol(<getter>)
hasBackingField: false hasBackingField: true
hasGetter: true hasGetter: true
hasSetter: false hasSetter: false
initializer: null initializer: null
isConst: false
isExtension: false isExtension: false
isLateInit: false
isOverride: false isOverride: false
isVal: true isVal: true
modality: ABSTRACT javaGetterName: length
javaSetterName: null
modality: OPEN
name: length name: length
origin: LIBRARY origin: JAVA_SYNTHETIC_PROPERTY
receiverType: null receiverType: null
setter: null setter: null
symbolKind: MEMBER symbolKind: MEMBER