PSI2IR KT-49526 function reference type approximation hack

This commit is contained in:
Dmitry Petrov
2021-12-17 16:18:11 +03:00
committed by Space
parent 9c0ea11c1b
commit 976998b56c
21 changed files with 360 additions and 1 deletions
@@ -74,7 +74,10 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St
fun generateCallableReference(ktCallableReference: KtCallableReferenceExpression): IrExpression {
val resolvedCall = getResolvedCall(ktCallableReference.callableReference)!!
val resolvedDescriptor = resolvedCall.resultingDescriptor
val callableReferenceType = getTypeInferredByFrontendOrFail(ktCallableReference)
val callableReferenceType =
context.typeTranslator.approximateFunctionReferenceType(
getTypeInferredByFrontendOrFail(ktCallableReference)
)
val callBuilder = unwrapCallableDescriptorAndTypeArguments(resolvedCall)
return when {
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.ir.util
import org.jetbrains.kotlin.builtins.isKFunctionType
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.*
@@ -203,6 +204,38 @@ abstract class TypeTranslator(
return properlyApproximatedType
}
fun approximateFunctionReferenceType(kotlinType: KotlinType): KotlinType {
// 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)
return kotlinType
if (kotlinType !is SimpleType)
return kotlinType
if (kotlinType.arguments.none { it.type.constructor is IntersectionTypeConstructor })
return kotlinType
val functionParameterTypes = kotlinType.arguments.subList(0, kotlinType.arguments.size - 1)
val functionReturnType = kotlinType.arguments.last()
return kotlinType.replace(
newArguments = functionParameterTypes.map { approximateFunctionReferenceParameterType(it) } + functionReturnType
)
}
private fun approximateFunctionReferenceParameterType(typeProjection: TypeProjection): TypeProjection {
if (typeProjection.isStarProjection) return typeProjection
val typeConstructor = typeProjection.type.constructor as? IntersectionTypeConstructor
?: return typeProjection
// 'mapType' takes common supertype for intersection type supertypes, regardless of variance.
val newType = typeConstructor.getAlternativeType()
?: commonSupertype(typeConstructor.supertypes)
return TypeProjectionImpl(typeProjection.projectionKind, newType)
}
private val isWithNewInference = languageVersionSettings.supportsFeature(LanguageFeature.NewInference)
private fun approximateByKotlinRules(ktType: KotlinType): KotlinType =