[FIR2IR] Function reference type approximation hack KT-49526

This commit is contained in:
Ivan Kochurkin
2022-01-18 14:48:05 +03:00
committed by TeamCityServer
parent 070e860669
commit ed836f28f4
5 changed files with 40 additions and 12 deletions
@@ -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<T1, ..., TN, R> 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
@@ -1,6 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
// FIR_STATUS: callable reference type approximation hack not implemented
// WITH_STDLIB
// CHECK_BYTECODE_LISTING
@@ -1,6 +1,4 @@
// IGNORE_BACKEND: NATIVE
// IGNORE_BACKEND_FIR: JVM_IR
// FIR_STATUS: callable reference type approximation hack not implemented
fun <T> id(x: T): T = x
@@ -1,6 +1,4 @@
// IGNORE_BACKEND: NATIVE
// IGNORE_BACKEND_FIR: JVM_IR
// FIR_STATUS: callable reference type approximation hack not implemented
// WITH_STDLIB
+1 -1
View File
@@ -2,7 +2,7 @@ FILE fqName:<root> fileName:/kt49526.kt
FUN name:test visibility:public modality:FINAL <> () returnType:kotlin.Boolean
BLOCK_BODY
VAR name:ref type:kotlin.reflect.KFunction1<kotlin.Nothing, kotlin.Boolean> [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<kotlin.Nothing, kotlin.Boolean> origin=null reflectionTarget=<same>
FUNCTION_REFERENCE 'public abstract fun contains (element: E of kotlin.collections.List): kotlin.Boolean [operator] declared in kotlin.collections.List' type=kotlin.reflect.KFunction1<kotlin.Any, kotlin.Boolean> origin=null reflectionTarget=<same>
$this: CALL 'public final fun plus <T> (element: T of kotlin.collections.CollectionsKt.plus): kotlin.collections.List<T of kotlin.collections.CollectionsKt.plus> [operator] declared in kotlin.collections.CollectionsKt' type=kotlin.collections.List<kotlin.Any> origin=PLUS
<T>: kotlin.Comparable<kotlin.Nothing>
$receiver: CALL 'public final fun listOf <T> (element: T of kotlin.collections.CollectionsKt.listOf): kotlin.collections.List<T of kotlin.collections.CollectionsKt.listOf> declared in kotlin.collections.CollectionsKt' type=kotlin.collections.List<kotlin.Char> origin=null