PSI2IR: SAM conversion in method arguments of out-projected Java classes

It uses the same logic as an old back-end
(see SamType#createByValueParameter and genericSamProjectedOut.kt),
split into two parts:

1. When inserting SAM casts, use SamType#createByValueParamerer to get
the target SAM type.

2. When inserting implicit casts, cast SAM conversions as arguments of
methods of out-projected types to the original type of value parameter
instead of 'Nothing'.
This commit is contained in:
Dmitry Petrov
2019-12-31 16:35:44 +03:00
parent a55bce801e
commit d27593aeda
13 changed files with 145 additions and 19 deletions
@@ -6,10 +6,8 @@
package org.jetbrains.kotlin.backend.jvm
import org.jetbrains.kotlin.backend.common.ir.createParameterDeclarations
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.codegen.SamType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.FilteredAnnotations
import org.jetbrains.kotlin.ir.builders.declarations.buildClass
import org.jetbrains.kotlin.ir.declarations.IrClass
@@ -52,6 +50,11 @@ class JvmGeneratorExtensions(private val generateFacades: Boolean = true) : Gene
override fun isSamType(type: KotlinType): Boolean =
SingleAbstractMethodUtils.isSamType(type)
override fun getSamTypeInfoForValueParameter(valueParameter: ValueParameterDescriptor): KotlinType? {
val samType = SamType.createByValueParameter(valueParameter) ?: return null
return samType.type
}
override fun getSubstitutedFunctionTypeForSamType(samType: KotlinType): KotlinType {
val descriptor = samType.constructor.declarationDescriptor as? JavaClassDescriptor
?: throw AssertionError("SAM should be represented by a Java class: $samType")
@@ -405,8 +405,10 @@ private fun StatementGenerator.pregenerateValueArguments(call: CallBuilder, reso
}
fun StatementGenerator.generateSamConversionForValueArgumentsIfRequired(call: CallBuilder, resolvedCall: ResolvedCall<*>) {
val samConversion = context.extensions.samConversion
val originalDescriptor = resolvedCall.resultingDescriptor
val underlyingDescriptor = context.extensions.samConversion.getOriginalForSamAdapter(originalDescriptor) ?: originalDescriptor
val underlyingDescriptor = samConversion.getOriginalForSamAdapter(originalDescriptor) ?: originalDescriptor
val originalValueParameters = originalDescriptor.valueParameters
val underlyingValueParameters = underlyingDescriptor.valueParameters
@@ -432,34 +434,38 @@ fun StatementGenerator.generateSamConversionForValueArgumentsIfRequired(call: Ca
val typeSubstitutor = TypeSubstitutor.create(substitutionContext)
for (i in underlyingValueParameters.indices) {
val underlyingParameterType = underlyingValueParameters[i].type
val underlyingValueParameter = underlyingValueParameters[i]
val originalUnderlyingParameterType = underlyingValueParameter.original.type
if (partialSamConversionIsSupported && resolvedCall is NewResolvedCallImpl<*>) {
// TODO support SAM conversion of varargs
val argument = resolvedCall.valueArguments[originalValueParameters[i]]?.arguments?.singleOrNull() ?: continue
if (resolvedCall.getExpectedTypeForSamConvertedArgument(argument) == null) continue
resolvedCall.getExpectedTypeForSamConvertedArgument(argument) ?: continue
} else {
if (!context.extensions.samConversion.isSamType(underlyingParameterType)) continue
if (!context.extensions.samConversion.isSamType(originalUnderlyingParameterType)) continue
if (!originalValueParameters[i].type.isFunctionTypeOrSubtype) continue
}
val samKotlinType = samConversion.getSamTypeInfoForValueParameter(underlyingValueParameter) ?: continue
val originalArgument = call.irValueArgumentsByIndex[i] ?: continue
val expectedArgumentType = typeSubstitutor.substitute(underlyingParameterType, Variance.INVARIANT)
val substitutedSamType = typeSubstitutor.substitute(samKotlinType, Variance.INVARIANT)
?: throw AssertionError(
"Failed to substitute value argument type in SAM conversion: " +
"underlyingParameterType=$underlyingParameterType, " +
"underlyingParameterType=$originalUnderlyingParameterType, " +
"substitutionContext=$substitutionContext"
)
val targetType = expectedArgumentType.toIrType()
val irSamType = substitutedSamType.toIrType()
call.irValueArgumentsByIndex[i] =
IrTypeOperatorCallImpl(
originalArgument.startOffset, originalArgument.endOffset,
targetType,
irSamType,
IrTypeOperator.SAM_CONVERSION,
targetType,
castArgumentToFunctionalInterfaceForSamType(originalArgument, expectedArgumentType)
irSamType,
castArgumentToFunctionalInterfaceForSamType(originalArgument, substitutedSamType)
)
}
}
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.psi2ir.generators
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.ir.util.StubGeneratorExtensions
import org.jetbrains.kotlin.types.KotlinType
@@ -23,6 +24,9 @@ open class GeneratorExtensions : StubGeneratorExtensions() {
open fun isSamType(type: KotlinType): Boolean = false
open fun getSamTypeInfoForValueParameter(valueParameter: ValueParameterDescriptor): KotlinType? =
throw UnsupportedOperationException("SAM conversion is not supported in this configuration (valueParameter=$valueParameter)")
open fun getSubstitutedFunctionTypeForSamType(samType: KotlinType): KotlinType =
throw UnsupportedOperationException("SAM conversion is not supported in this configuration (samType=$samType)")
@@ -121,11 +121,23 @@ internal class InsertImplicitCasts(
for (index in substitutedDescriptor.valueParameters.indices) {
val argument = getValueArgument(index) ?: continue
val parameterType = substitutedDescriptor.valueParameters[index].type
putValueArgument(index, argument.cast(parameterType))
// Hack to support SAM conversions on out-projected types.
// See SamType#createByValueParameter and genericSamProjectedOut.kt for more details.
val expectedType =
if (argument.isSamConversion() && KotlinBuiltIns.isNothing(parameterType))
substitutedDescriptor.original.valueParameters[index].type.replaceArgumentsWithNothing()
else
parameterType
putValueArgument(index, argument.cast(expectedType))
}
}
}
private fun IrExpression.isSamConversion(): Boolean =
this is IrTypeOperatorCall && operator == IrTypeOperator.SAM_CONVERSION
override fun visitBlockBody(body: IrBlockBody): IrBody =
body.transformPostfix {
statements.forEachIndexed { i, irStatement ->