FIR2IR: element-wise SAM conversion for vararg

This commit is contained in:
Jinseong Jeon
2020-10-06 07:58:02 -07:00
committed by Mikhail Glukhikh
parent 5d394528f6
commit bc6693fa6e
6 changed files with 75 additions and 45 deletions
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.references.FirReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.references.FirSuperReference import org.jetbrains.kotlin.fir.references.FirSuperReference
import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.calls.getExpectedTypeForSAMConversion
import org.jetbrains.kotlin.fir.resolve.calls.isFunctional import org.jetbrains.kotlin.fir.resolve.calls.isFunctional
import org.jetbrains.kotlin.fir.resolve.inference.isBuiltinFunctionalType import org.jetbrains.kotlin.fir.resolve.inference.isBuiltinFunctionalType
import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.resolve.toSymbol
@@ -529,12 +530,37 @@ class CallAndReferenceGenerator(
private fun IrExpression.applySamConversionIfNeeded( private fun IrExpression.applySamConversionIfNeeded(
argument: FirExpression, argument: FirExpression,
parameter: FirValueParameter? parameter: FirValueParameter?,
shouldUnwrapVarargType: Boolean = false
): IrExpression { ): IrExpression {
if (parameter == null || !needSamConversion(argument, parameter)) { if (parameter == null) {
return this return this
} }
val samType = parameter.returnTypeRef.toIrType() if (this is IrVararg) {
// element-wise SAM conversion if and only if we can build 1-to-1 mapping for elements.
if (argument !is FirVarargArgumentsExpression || argument.arguments.size != elements.size) {
return this
}
val argumentMapping = this.elements.zip(argument.arguments).toMap()
// [IrElementTransformer] is not preferred, since it's hard to visit vararg elements only.
val irVarargElements = elements as MutableList<IrVarargElement>
irVarargElements.replaceAll { irVarargElement ->
if (irVarargElement is IrExpression) {
val firVarargArgument =
argumentMapping[irVarargElement] ?: error("Can't find the original FirExpression for ${irVarargElement.render()}")
irVarargElement.applySamConversionIfNeeded(firVarargArgument, parameter, shouldUnwrapVarargType = true)
} else
irVarargElement
}
return this
}
if (!needSamConversion(argument, parameter)) {
return this
}
var samType = parameter.returnTypeRef.toIrType()
if (shouldUnwrapVarargType) {
samType = samType.getArrayElementType(irBuiltIns)
}
// Make sure the converted IrType owner indeed has a single abstract method, since FunctionReferenceLowering relies on it. // Make sure the converted IrType owner indeed has a single abstract method, since FunctionReferenceLowering relies on it.
if (!samType.isSamType) return this if (!samType.isSamType) return this
return IrTypeOperatorCallImpl(this.startOffset, this.endOffset, samType, IrTypeOperator.SAM_CONVERSION, samType, this) return IrTypeOperatorCallImpl(this.startOffset, this.endOffset, samType, IrTypeOperator.SAM_CONVERSION, samType, this)
@@ -542,7 +568,8 @@ class CallAndReferenceGenerator(
private fun needSamConversion(argument: FirExpression, parameter: FirValueParameter): Boolean { private fun needSamConversion(argument: FirExpression, parameter: FirValueParameter): Boolean {
// If the expected type is a built-in functional type, we don't need SAM conversion. // If the expected type is a built-in functional type, we don't need SAM conversion.
if (parameter.returnTypeRef.coneType.isBuiltinFunctionalType(session)) { val expectedType = argument.getExpectedTypeForSAMConversion(parameter)
if (expectedType is ConeTypeParameterType || expectedType.isBuiltinFunctionalType(session)) {
return false return false
} }
// On the other hand, the actual type should be a functional type. // On the other hand, the actual type should be a functional type.
@@ -360,7 +360,7 @@ private fun Candidate.prepareExpectedType(
context: ResolutionContext context: ResolutionContext
): ConeKotlinType? { ): ConeKotlinType? {
if (parameter == null) return null if (parameter == null) return null
val basicExpectedType = argument.getExpectedType(session, parameter/*, LanguageVersionSettings*/) val basicExpectedType = argument.getExpectedTypeForSAMConversion(parameter/*, LanguageVersionSettings*/)
val expectedType = getExpectedTypeWithSAMConversion(session, argument, basicExpectedType, context) ?: basicExpectedType val expectedType = getExpectedTypeWithSAMConversion(session, argument, basicExpectedType, context) ?: basicExpectedType
return this.substitutor.substituteOrSelf(expectedType) return this.substitutor.substituteOrSelf(expectedType)
} }
@@ -391,8 +391,7 @@ fun FirExpression.isFunctional(session: FirSession): Boolean =
else -> typeRef.coneTypeSafe<ConeKotlinType>()?.isBuiltinFunctionalType(session) == true else -> typeRef.coneTypeSafe<ConeKotlinType>()?.isBuiltinFunctionalType(session) == true
} }
internal fun FirExpression.getExpectedType( fun FirExpression.getExpectedTypeForSAMConversion(
session: FirSession,
parameter: FirValueParameter/*, languageVersionSettings: LanguageVersionSettings*/ parameter: FirValueParameter/*, languageVersionSettings: LanguageVersionSettings*/
): ConeKotlinType { ): ConeKotlinType {
val shouldUnwrapVarargType = when (this) { val shouldUnwrapVarargType = when (this) {
@@ -1,5 +1,4 @@
// !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument // !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument
// IGNORE_BACKEND_FIR: JVM_IR
fun interface MyRunnable { fun interface MyRunnable {
fun run() fun run()
-1
View File
@@ -1,5 +1,4 @@
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// IGNORE_BACKEND_FIR: JVM_IR
// FILE: kt31908.kt // FILE: kt31908.kt
fun box(): String { fun box(): String {
var result = "failed" var result = "failed"
@@ -24,6 +24,7 @@ FILE fqName:<root> fileName:/samConversionInVarargs.kt
BLOCK_BODY BLOCK_BODY
CALL 'public final fun useVararg (vararg foos: <root>.IFoo): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null CALL 'public final fun useVararg (vararg foos: <root>.IFoo): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
foos: VARARG type=kotlin.Array<out <root>.IFoo> varargElementType=<root>.IFoo foos: VARARG type=kotlin.Array<out <root>.IFoo> varargElementType=<root>.IFoo
TYPE_OP type=<root>.IFoo origin=SAM_CONVERSION typeOperand=<root>.IFoo
FUN_EXPR type=kotlin.Function1<kotlin.Int, kotlin.Unit> origin=LAMBDA FUN_EXPR type=kotlin.Function1<kotlin.Int, kotlin.Unit> origin=LAMBDA
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit
VALUE_PARAMETER name:it index:0 type:kotlin.Int VALUE_PARAMETER name:it index:0 type:kotlin.Int
@@ -34,18 +35,21 @@ FILE fqName:<root> fileName:/samConversionInVarargs.kt
BLOCK_BODY BLOCK_BODY
CALL 'public final fun useVararg (vararg foos: <root>.IFoo): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null CALL 'public final fun useVararg (vararg foos: <root>.IFoo): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
foos: VARARG type=kotlin.Array<out <root>.IFoo> varargElementType=<root>.IFoo foos: VARARG type=kotlin.Array<out <root>.IFoo> varargElementType=<root>.IFoo
TYPE_OP type=<root>.IFoo origin=SAM_CONVERSION typeOperand=<root>.IFoo
FUN_EXPR type=kotlin.Function1<kotlin.Int, kotlin.Unit> origin=LAMBDA FUN_EXPR type=kotlin.Function1<kotlin.Int, kotlin.Unit> origin=LAMBDA
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit
VALUE_PARAMETER name:it index:0 type:kotlin.Int VALUE_PARAMETER name:it index:0 type:kotlin.Int
BLOCK_BODY BLOCK_BODY
RETURN type=kotlin.Nothing from='local final fun <anonymous> (it: kotlin.Int): kotlin.Unit declared in <root>.testSeveralLambdas' RETURN type=kotlin.Nothing from='local final fun <anonymous> (it: kotlin.Int): kotlin.Unit declared in <root>.testSeveralLambdas'
GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit
TYPE_OP type=<root>.IFoo origin=SAM_CONVERSION typeOperand=<root>.IFoo
FUN_EXPR type=kotlin.Function1<kotlin.Int, kotlin.Unit> origin=LAMBDA FUN_EXPR type=kotlin.Function1<kotlin.Int, kotlin.Unit> origin=LAMBDA
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit
VALUE_PARAMETER name:it index:0 type:kotlin.Int VALUE_PARAMETER name:it index:0 type:kotlin.Int
BLOCK_BODY BLOCK_BODY
RETURN type=kotlin.Nothing from='local final fun <anonymous> (it: kotlin.Int): kotlin.Unit declared in <root>.testSeveralLambdas' RETURN type=kotlin.Nothing from='local final fun <anonymous> (it: kotlin.Int): kotlin.Unit declared in <root>.testSeveralLambdas'
GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit GET_OBJECT 'CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.Unit
TYPE_OP type=<root>.IFoo origin=SAM_CONVERSION typeOperand=<root>.IFoo
FUN_EXPR type=kotlin.Function1<kotlin.Int, kotlin.Unit> origin=LAMBDA FUN_EXPR type=kotlin.Function1<kotlin.Int, kotlin.Unit> origin=LAMBDA
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> (it:kotlin.Int) returnType:kotlin.Unit
VALUE_PARAMETER name:it index:0 type:kotlin.Int VALUE_PARAMETER name:it index:0 type:kotlin.Int
@@ -61,6 +65,7 @@ FILE fqName:<root> fileName:/samConversionInVarargs.kt
BLOCK_BODY BLOCK_BODY
CALL 'public final fun useVararg (vararg foos: <root>.IFoo): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null CALL 'public final fun useVararg (vararg foos: <root>.IFoo): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
foos: VARARG type=kotlin.Array<out <root>.IFoo> varargElementType=<root>.IFoo foos: VARARG type=kotlin.Array<out <root>.IFoo> varargElementType=<root>.IFoo
TYPE_OP type=<root>.IFoo origin=SAM_CONVERSION typeOperand=<root>.IFoo
FUN_EXPR type=kotlin.Function1<kotlin.Int, kotlin.Unit> origin=ADAPTED_FUNCTION_REFERENCE FUN_EXPR type=kotlin.Function1<kotlin.Int, kotlin.Unit> origin=ADAPTED_FUNCTION_REFERENCE
FUN ADAPTER_FOR_CALLABLE_REFERENCE name:withVarargOfInt visibility:local modality:FINAL <> (p0:kotlin.Int) returnType:kotlin.Unit FUN ADAPTER_FOR_CALLABLE_REFERENCE name:withVarargOfInt visibility:local modality:FINAL <> (p0:kotlin.Int) returnType:kotlin.Unit
VALUE_PARAMETER ADAPTER_PARAMETER_FOR_CALLABLE_REFERENCE name:p0 index:0 type:kotlin.Int VALUE_PARAMETER ADAPTER_PARAMETER_FOR_CALLABLE_REFERENCE name:p0 index:0 type:kotlin.Int
@@ -26,6 +26,7 @@ FILE fqName:<root> fileName:/samConversionInVarargsMixed.kt
GET_VAR 'a: kotlin.Any declared in <root>.test' type=kotlin.Any origin=null GET_VAR 'a: kotlin.Any declared in <root>.test' type=kotlin.Any origin=null
then: CALL 'public final fun foo (vararg rs: <root>.MyRunnable): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null then: CALL 'public final fun foo (vararg rs: <root>.MyRunnable): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
rs: VARARG type=kotlin.Array<out <root>.MyRunnable> varargElementType=<root>.MyRunnable rs: VARARG type=kotlin.Array<out <root>.MyRunnable> varargElementType=<root>.MyRunnable
TYPE_OP type=<root>.MyRunnable origin=SAM_CONVERSION typeOperand=<root>.MyRunnable
FUN_EXPR type=kotlin.Function0<kotlin.Unit> origin=LAMBDA FUN_EXPR type=kotlin.Function0<kotlin.Unit> origin=LAMBDA
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> () returnType:kotlin.Unit FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> () returnType:kotlin.Unit
BLOCK_BODY BLOCK_BODY