diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt index b7cf532332d..cb3534acddd 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt @@ -7,21 +7,19 @@ package org.jetbrains.kotlin.fir.backend.generators import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.backend.common.ir.isMethodOfAny +import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.backend.* import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.isCompanion -import org.jetbrains.kotlin.fir.dispatchReceiverClassOrNull import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationCall import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression -import org.jetbrains.kotlin.fir.languageVersionSettings import org.jetbrains.kotlin.fir.references.FirDelegateFieldReference import org.jetbrains.kotlin.fir.references.FirReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.references.FirSuperReference import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference import org.jetbrains.kotlin.fir.references.impl.FirReferencePlaceholderForResolvedAnnotations -import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticFunctionSymbol import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor @@ -30,6 +28,7 @@ import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.builders.declarations.UNDEFINED_PARAMETER_INDEX import org.jetbrains.kotlin.ir.declarations.* @@ -43,6 +42,7 @@ import org.jetbrains.kotlin.ir.util.isFunctionTypeOrSubtype import org.jetbrains.kotlin.ir.util.isInterface import org.jetbrains.kotlin.psi2ir.generators.hasNoSideEffects import org.jetbrains.kotlin.psi2ir.generators.isUnchanging +import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator.commonSuperType import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull class CallAndReferenceGenerator( @@ -63,7 +63,8 @@ class CallAndReferenceGenerator( callableReferenceAccess: FirCallableReferenceAccess, explicitReceiverExpression: IrExpression? ): IrExpression { - val type = callableReferenceAccess.typeRef.toIrType() + val type = approximateFunctionReferenceType(callableReferenceAccess.typeRef.coneType).toIrType() + val callableSymbol = callableReferenceAccess.calleeReference.toResolvedCallableSymbol() if (callableSymbol?.origin == FirDeclarationOrigin.SamConstructor) { assert(explicitReceiverExpression == null) { @@ -171,6 +172,40 @@ class CallAndReferenceGenerator( } } + private fun approximateFunctionReferenceType(kotlinType: ConeKotlinType): ConeKotlinType { + // This is a hack to support intersection types in function references on JVM. + // Function reference type KFunctionN might contain intersection types in its top-level arguments. + // Intersection types in expressions and local variable declarations usually don't bother us. + // However, in case of function references type mapping affects behavior: + // resulting function reference class will have a bridge method, which will downcast its arguments to the expected types. + // This would cause ClassCastException in case of usual type approximation, + // because '{ X1 & ... & Xm }' would be approximated to 'Nothing'. + // JVM_OLD just relies on type mapping for generic argument types in such case. + if (!kotlinType.isKFunctionType(session)) + return kotlinType + if (kotlinType !is ConeSimpleKotlinType) + return kotlinType + if (kotlinType.typeArguments.none { it.type is ConeIntersectionType }) + return kotlinType + val functionParameterTypes = kotlinType.typeArguments.take(kotlinType.typeArguments.size - 1) + val functionReturnType = kotlinType.typeArguments.last() + return ConeClassLikeTypeImpl( + (kotlinType as ConeClassLikeType).lookupTag, + (functionParameterTypes.map { approximateFunctionReferenceParameterType(it) } + functionReturnType).toTypedArray(), + kotlinType.isNullable, + kotlinType.attributes + ) + } + + private fun approximateFunctionReferenceParameterType(typeProjection: ConeTypeProjection): ConeTypeProjection { + if (typeProjection.isStarProjection) return typeProjection + val intersectionType = typeProjection as? ConeIntersectionType ?: return typeProjection + val newType = intersectionType.alternativeType + ?: session.typeContext.commonSuperType(intersectionType.intersectedTypes.toList()) as? ConeKotlinType + ?: return typeProjection + return newType.toTypeProjection(typeProjection.kind) + } + private fun computeFieldSymbolForCallableReference( callableReferenceAccess: FirCallableReferenceAccess, symbol: IrFieldSymbol diff --git a/compiler/testData/codegen/box/callableReference/kt49526.kt b/compiler/testData/codegen/box/callableReference/kt49526.kt index fc80c659395..dc3d94790e4 100644 --- a/compiler/testData/codegen/box/callableReference/kt49526.kt +++ b/compiler/testData/codegen/box/callableReference/kt49526.kt @@ -1,6 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR -// FIR_STATUS: callable reference type approximation hack not implemented - // WITH_STDLIB // CHECK_BYTECODE_LISTING diff --git a/compiler/testData/codegen/box/callableReference/kt49526a.kt b/compiler/testData/codegen/box/callableReference/kt49526a.kt index 49a37759ffd..041c1fc1908 100644 --- a/compiler/testData/codegen/box/callableReference/kt49526a.kt +++ b/compiler/testData/codegen/box/callableReference/kt49526a.kt @@ -1,6 +1,4 @@ // IGNORE_BACKEND: NATIVE -// IGNORE_BACKEND_FIR: JVM_IR -// FIR_STATUS: callable reference type approximation hack not implemented fun id(x: T): T = x diff --git a/compiler/testData/codegen/box/callableReference/kt49526b.kt b/compiler/testData/codegen/box/callableReference/kt49526b.kt index 3ead82e5ba3..fe3d3a1076a 100644 --- a/compiler/testData/codegen/box/callableReference/kt49526b.kt +++ b/compiler/testData/codegen/box/callableReference/kt49526b.kt @@ -1,6 +1,4 @@ // IGNORE_BACKEND: NATIVE -// IGNORE_BACKEND_FIR: JVM_IR -// FIR_STATUS: callable reference type approximation hack not implemented // WITH_STDLIB diff --git a/compiler/testData/ir/irText/types/kt49526.fir.ir.txt b/compiler/testData/ir/irText/types/kt49526.fir.ir.txt index 983f2913d33..9ab76928000 100644 --- a/compiler/testData/ir/irText/types/kt49526.fir.ir.txt +++ b/compiler/testData/ir/irText/types/kt49526.fir.ir.txt @@ -2,7 +2,7 @@ FILE fqName: fileName:/kt49526.kt FUN name:test visibility:public modality:FINAL <> () returnType:kotlin.Boolean BLOCK_BODY VAR name:ref type:kotlin.reflect.KFunction1 [val] - FUNCTION_REFERENCE 'public abstract fun contains (element: E of kotlin.collections.List): kotlin.Boolean [operator] declared in kotlin.collections.List' type=kotlin.reflect.KFunction1 origin=null reflectionTarget= + FUNCTION_REFERENCE 'public abstract fun contains (element: E of kotlin.collections.List): kotlin.Boolean [operator] declared in kotlin.collections.List' type=kotlin.reflect.KFunction1 origin=null reflectionTarget= $this: CALL 'public final fun plus (element: T of kotlin.collections.CollectionsKt.plus): kotlin.collections.List [operator] declared in kotlin.collections.CollectionsKt' type=kotlin.collections.List origin=PLUS : kotlin.Comparable $receiver: CALL 'public final fun listOf (element: T of kotlin.collections.CollectionsKt.listOf): kotlin.collections.List declared in kotlin.collections.CollectionsKt' type=kotlin.collections.List origin=null